#####################################################################
#              SEXBOT CORE DATA
#####################################################################


image ani_thumb_auto:
    "10_anims/ani_thumb_auto_00.jpg"
    0.3
    "10_anims/ani_thumb_auto_01.jpg"
    0.1
    "10_anims/ani_thumb_auto_02.jpg"
    0.05
    "10_anims/ani_thumb_auto_03.jpg"
    0.05
    "10_anims/ani_thumb_auto_04.jpg"
    0.1
    "10_anims/ani_thumb_auto_05.jpg"
    0.2
    "10_anims/ani_thumb_auto_04.jpg"
    0.1
    "10_anims/ani_thumb_auto_03.jpg"
    0.05
    "10_anims/ani_thumb_auto_02.jpg"
    0.05
    "10_anims/ani_thumb_auto_01.jpg"
    0.1
    repeat  

image bot_doll = LiveComposite(
    (0,0),
    (0,0), "bot_smart",
    (0,0), "bot_head_smart",
    (0,0), "bot_torso_smart",
    (0,0), "bot_arms_smart",
    (0,0), "bot_legs_smart", 
    (346,144), "bot_eyes_smart"
    )

image lab_bg = ConditionSwitch(
    "GAME.lab.current.getShellConfig()in ['I']", "03_minigames/mgsb_bg_red.png",
    "True", "03_minigames/mgsb_bg.png"
)

image analyzer_lBot = ConditionSwitch(
    "botData['left'][0].getShellConfig() == 'X'", "03_minigames/mgsb_full_bot_x.png",
    "botData['left'][0].getShellConfig() == 'G'", "03_minigames/mgsb_full_bot_g.png",
    "botData['left'][0].getShellConfig() == 'P'", "03_minigames/mgsb_full_bot_p.png",
    "botData['left'][0].getShellConfig() == 'N'", "03_minigames/mgsb_full_bot_n.png",
    "botData['left'][0].getShellConfig() == 'V'", "03_minigames/mgsb_full_bot_v.png",
    "botData['left'][0].getShellConfig() == 'K'", "03_minigames/mgsb_full_bot_k.png",
    "botData['left'][0].getShellConfig() == 'C'", "03_minigames/mgsb_full_bot_c.png",
    "botData['left'][0].getShellConfig() == 'L'", "03_minigames/mgsb_full_bot_l.png",
    "botData['left'][0].getShellConfig() == 'I'", "03_minigames/mgsb_full_bot_i.png",
    "botData['left'][0].getShellConfig() == 'J'", "03_minigames/mgsb_full_bot_j.png",
    "True", "03_minigames/mgsb_full_bot.png"    
)

image analyzer_rBot = ConditionSwitch(
    "botData['right'][0].getShellConfig() == 'X'", "03_minigames/mgsb_full_bot_x.png",
    "botData['right'][0].getShellConfig() == 'G'", "03_minigames/mgsb_full_bot_g.png",
    "botData['right'][0].getShellConfig() == 'P'", "03_minigames/mgsb_full_bot_p.png",
    "botData['right'][0].getShellConfig() == 'N'", "03_minigames/mgsb_full_bot_n.png",
    "botData['right'][0].getShellConfig() == 'V'", "03_minigames/mgsb_full_bot_v.png",
    "botData['right'][0].getShellConfig() == 'K'", "03_minigames/mgsb_full_bot_k.png",
    "botData['right'][0].getShellConfig() == 'C'", "03_minigames/mgsb_full_bot_c.png",
    "botData['right'][0].getShellConfig() == 'L'", "03_minigames/mgsb_full_bot_l.png",
    "botData['right'][0].getShellConfig() == 'I'", "03_minigames/mgsb_full_bot_i.png",
    "botData['right'][0].getShellConfig() == 'J'", "03_minigames/mgsb_full_bot_j.png",
    "True", "03_minigames/mgsb_full_bot.png"    
)

image full_bot = ConditionSwitch(
    "GAME.lab.current.getShellConfig() == 'X'", "03_minigames/mgsb_full_bot_x.png",
    "GAME.lab.current.getShellConfig() == 'G'", "03_minigames/mgsb_full_bot_g.png",
    "GAME.lab.current.getShellConfig() == 'P'", "03_minigames/mgsb_full_bot_p.png",
    "GAME.lab.current.getShellConfig() == 'N'", "03_minigames/mgsb_full_bot_n.png",
    "GAME.lab.current.getShellConfig() == 'V'", "03_minigames/mgsb_full_bot_v.png",
    "GAME.lab.current.getShellConfig() == 'K'", "03_minigames/mgsb_full_bot_k.png",
    "GAME.lab.current.getShellConfig() == 'C'", "03_minigames/mgsb_full_bot_c.png",
    "GAME.lab.current.getShellConfig() == 'L'", "03_minigames/mgsb_full_bot_l.png",
    "GAME.lab.current.getShellConfig() == 'I'", "03_minigames/mgsb_full_bot_i.png",
    "GAME.lab.current.getShellConfig() == 'J'", "03_minigames/mgsb_full_bot_j.png",
    "True", "03_minigames/mgsb_full_bot.png"    
)

# background image
image bot_smart = ConditionSwitch(
    "GAME.lab.current.getShellConfig() == 'J'", "03_minigames/mgsb_doll_j.png",
    "True", "03_minigames/mgsb_doll.png"
)
image bot_arms_smart = ConditionSwitch(
    "GAME.lab.current.hasComp('arms') and GAME.lab.current.getShellConfig() == 'J'", "03_minigames/mgsb_arms_j.png",
    "GAME.lab.current.hasComp('arms')", "03_minigames/mgsb_arms.png",
    "True", "05_gui/px_1x1_empty.png"
)
image bot_legs_smart = ConditionSwitch(
    "GAME.lab.current.hasComp('legs') and GAME.lab.current.getShellConfig() == 'J'", "03_minigames/mgsb_legs_j.png",
    "GAME.lab.current.hasComp('legs')", "03_minigames/mgsb_legs.png",
    
    "True", "05_gui/px_1x1_empty.png")
    
image bot_head_smart = ConditionSwitch(
    "GAME.lab.current.hasComp('head') and GAME.lab.current.getShellConfig() == 'J'", "03_minigames/mgsb_head_j.png",
    "GAME.lab.current.hasComp('head')", "03_minigames/mgsb_head.png",
    "True", "05_gui/px_1x1_empty.png"
)

image bot_torso_smart = ConditionSwitch(
    "GAME.lab.current.hasComp('torso') and GAME.lab.current.getShellConfig() == 'J'", "03_minigames/mgsb_torso_j.png",
    "GAME.lab.current.hasComp('torso')", "03_minigames/mgsb_torso.png",
    "True", "05_gui/px_1x1_empty.png"
)

image bot_eyes_smart = ConditionSwitch(
    "GAME.lab.current.getCat('cortex')==1", "bot_eyes_a_ani",
    "GAME.lab.current.getCat('cortex')==2", "bot_eyes_b_ani",
    "GAME.lab.current.getCat('cortex')==3", "bot_eyes_c_ani",
    "True", "05_gui/px_1x1_empty.png"
)

image bot_eyes_a_ani:
    "03_minigames/mgsb_eyes_a_1.png"
    2.0
    "03_minigames/mgsb_eyes_a_2.png"
    0.2
    "03_minigames/mgsb_eyes_a_3.png"
    0.2
    "03_minigames/mgsb_eyes_a_2.png"
    0.2
    repeat

image bot_eyes_b_ani:
    "03_minigames/mgsb_eyes_b_1.png"
    2.0
    "03_minigames/mgsb_eyes_b_2.png"
    0.2
    "03_minigames/mgsb_eyes_b_3.png"
    0.2
    "03_minigames/mgsb_eyes_b_2.png"
    0.2
    repeat

image bot_eyes_c_ani:
    "03_minigames/mgsb_eyes_c_1.png"
    2.0
    "03_minigames/mgsb_eyes_c_2.png"
    0.2
    "03_minigames/mgsb_eyes_c_3.png"
    0.2
    "03_minigames/mgsb_eyes_c_2.png"
    0.2
    repeat
#####################################################################
#					CLASSES ETC												  
#####################################################################
init python:
    class BotLab():
        def __init__(self):
            self.status = {"bio": 100, "energy": 100, "nano":100}
            self.cellsMax = 10
            self.cells = [False for i in range(self.cellsMax)]
            self.hiddenCells = [] # bots moved into hidden cells (quest, ...)
            self.parts = []
            self.orders = [] # tuples with [description, cond1, ..., condn]
            self.current = Sexbot() # empty bot holder
            self.specialOffers = []
            self.extensions = [False, False, False, False]
            self.botsCreated = set()

        def dumpData(self, bot):
            # base data 
            bData = {}
            bData["name"] = bot.name 
            bData["talents"] = bot.talents 
            bData["skills"] = bot.skills 
            bData["attitude"] = bot.attitude
            bData["traits"] = bot.traits 
            bData["likes"] = list(bot.likes) 
            bData["dislikes"] = list(bot.dislikes) 
            bData["attr"] = bot.attr 
            bData["personality"] = bot.personality
            bData["tags"] = bot.tags 
            bData["tally"] = bot.tally 
            bData["IQ"] = bot.IQ 
            bData["qc"] = bot.qc
            bData["price"] = bot.price 
            bData["partNames"] = bot.partNames 
            # array of components 
            partList = []
            for i in bot.parts.values():
                pData = {}
                pData["ID"] = "RecalcOnAssemble"
                pData["name"] = i.name
                pData["kind"] = i.kind
                pData["slot"] = i.slot 
                pData["art"] = i.art
                pData["cat"] = i.cat
                pData["pref"] = i.pref
                pData["price"] = i.price 
                pData["description"] = i.description
                pData["tags"] = i.tags 
                pData["attr"] = i.attr 
                pData["identified"] = True
                pData["wear"] = i.wear
                partList.append(pData) 

            aList = []
            for i in [x for x in bot.addOns if x != False]:
                pData = {}
                pData["ID"] = "RecalcOnAssemble"
                pData["name"] = i.name
                pData["kind"] = i.kind
                pData["slot"] = i.slot 
                pData["art"] = i.art
                pData["cat"] = i.cat
                pData["pref"] = i.pref
                pData["price"] = i.price 
                pData["description"] = i.description
                pData["tags"] = i.tags 
                pData["attr"] = i.attr 
                pData["identified"] = True
                pData["wear"] = i.wear
                if i.isShell():
                    pData["shellPremium"] = i.shellPremium              
                    pData["shellLikes"] = i.shellLikes
                    pData["shellDislikes"] = i.shellDislikes
                    pData["shellPersonality"] = i.shellPersonality
                    pData["shellTraits"] = i.shellTraits
                    pData["shellAttitude"] = i.shellAttitude
                    pData["shellStats"] = i.shellStats
                    pData["shellIQ"] = i.shellIQ
                aList.append(pData) 

            flat = json.dumps([bData, partList, aList])
            checksum = hash(flat)
            #structured = json.loads(flat)
            return([flat, checksum])

        def reconstruct(self, botData, version = False):
            botString = botData[0]
            checksum = botData[1]
            bData = json.loads(botString)[0]
            partsList = json.loads(botString)[1]
            addonList = json.loads(botString)[2]

            # create clean BOT 
            bot = Sexbot()
            # fill bot data 
            bot.name = bData["name"]
            bot.talents = bData["talents"] 
            bot.skills = bData["skills"] 
            bot.traits = bData["traits"] 
            bot.attitude = bData["attitude"] 
            bot.likes = set(bData["likes"])
            bot.dislikes = set(bData["dislikes"])
            bot.attr = bData["attr"]
            bot.tags = bData["tags"]
            bot.tally = bData["tally"]
            bot.IQ = bData["IQ"]
            bot.price = bData["price"] 
            bot.partNames = bData["partNames"]

            # CR-0831 START ============
            bot.qc = bData.get("qc",0) 
            # CR-0831 END ==============

            # set bot ID 
            bot.ID = "BOT-{}".format(GAME.getUniqueID())

            # for all parts
            for i in partsList:
                pID = "{}-{}".format(i["attr"]["BaseID"],GAME.getUniqueID())
                part = SexbotPart(pID, i["name"], i["art"], i["kind"], i["slot"], i["cat"], i["description"], i["price"], i["tags"], i["attr"])
                # attach part to bot
                part.wear = i["wear"] 
                part.identified = i["identified"]
                part.pref = i["pref"] 
                bot.parts[part.slot]= part

            # for all addons
            slot = 0
            for i in addonList:
                # legacy shell substitution
                if "SBPShellType" in i["attr"]["BaseID"]:
                    i["attr"]["BaseID"] = "SBPShell1Type"+i["attr"]["BaseID"][-1]

                # create part
                pID = "{}-{}".format(i["attr"]["BaseID"],GAME.getUniqueID())
                part = SexbotPart(pID, i["name"], i["art"], i["kind"], i["slot"], i["cat"], i["description"], i["price"], i["tags"], i["attr"])

                # attach part to bot and fill other data
                part.wear = i["wear"] 
                part.identified = i["identified"]
                part.pref = i["pref"] 

                if part.isShell():
                    part.slot = "shell" # CR-0730

                    # paranoid legacy shell init # zzz
                    if "shellPremium" not in i.keys():
                        part.shellPremium = False
                        part.shellLikes = False
                        part.shellDislikes = False
                        part.shellTraits = False
                        part.shellPersonality = False
                        part.shellAttitude = False
                        part.shellStats = False
                        part.shellIQ = 0
                        part.foo = 1

                    # non-legacy shell init
                    else:        
                        part.shellPremium = True 
                        part.shellLikes = i["shellLikes"]
                        part.shellDislikes = i["shellDislikes"]
                        part.shellTraits = i["shellTraits"]
                        part.shellPersonality = i["shellPersonality"]
                        part.shellAttitude = i["shellAttitude"]
                        part.shellStats = i["shellStats"]
                        part.shellIQ = i["shellIQ"]

                    bot.addOns[5]= part # last slot for shells
                    
                else:   
                    bot.addOns[slot]= part
                    slot += 1

            #####################################################################
            #              LEGACY DATA SCRUBBING 111
            #####################################################################
            # version specific extensions 
            if version in ["POD_VERSION_060", "WAREHOUSE_VERSION_100"]:
                bot.personality = bData["personality"]

            # CR-0910 ===== START
            # legacy premium shell migration P , I
            if bot.addOns[5]:
                part = bot.addOns[5]

                if part.isShell() == "I" and not part.shellPremium: # infiltrator always premium
                    part.shellPremium = True
                    part.shellPersonality = "infiltrator"
                    part.shellTraits  = ["agressive", "perverted"]
                    part.shellAttitude = "insidious"

                elif part.isShell() == "P" and not part.shellPremium: # protector
                    part.shellPremium = True
                    part.shellPersonality = "protector"  
                    part.shellAttitude = "devoted"
                    part.shellTraits  = ["flexible", "aggressive", "calculating"]
                    stats = random.sample(["ANA", "ORA", "VAG", "BON", "SPE"],1)
                    part.shellLikes = [GAME.sbData["skillDict"][x] for x in stats]
                    bonus = (50.0+abs(random.gauss(0,20))) / (4.0-part.cat)
                    part.shellStats = dict()
                    for s in stats:
                        part.shellStats[s] = int(bonus)

                # legacy base data reconstruction and purging
                base = bot.attr.pop("BaseAttitude", None)
                if base:
                    bot.attitude = base

                base = bot.attr.pop("BasePersonality", None)
                if base:
                    bot.personality = base

                base = bot.attr.pop("BaseTraits", None)
                if base:
                    bot.traits = list(base)
            # CR-0910 ===== END

                
            return(bot)

        def infiltratorStandby(self, grades=None):
            infiltrators = [x for x in GAME.lab.cells if x and x.getShellConfig() == "I"]
            iSorted = []
            
            if grades:
                infiltrators = [x for x in infiltrators if all([y.pref in grades for y in x.parts.values()])]

            if infiltrators:
                iSorted = sorted(infiltrators, key=lambda x: x.getPrice(), reverse = True)

            if iSorted:
                return(iSorted[0])
            return(False)

            # for bot in [x for x in GAME.lab.cells if x]:
            #     if bot.getShellConfig() == "I": # infiltrator 
            #         if grades == None:
            #             return(bot)
            #         if all([x.pref in grades for x in bot.parts.values()]):
            #             return(bot)
            # return(False)

        def hasFreeCell(self):
            if False in self.cells:
                return(True)
            return(False)

        def clearBot(self, bot):
            for i in range(self.cellsMax):
                if self.cells[i] == bot:
                    self.cells[i] = False
                    return()
            return(False) 

        def getBotFromCell(self, bot): # take out bot from cells
            for i in range(self.cellsMax):
                if self.cells[i] == bot:
                    self.cells[i] = False
                    return(bot)
            return(False) 

        def retrieveBot(self, bot): # move bot into configurator
            for i in range(self.cellsMax):
                if self.cells[i] == bot:
                    self.cells[i] = False
                    self.current = bot 
                    return()
            return(False) 

        def storeBot(self, bot, slot = None):
            if not bot.hasComp("cortex"): # empty shell
                return()
            if slot != None:
                self.cells[slot] = bot
                self.current = Sexbot() # new empty bot holder            
            else: 
                for i in range(self.cellsMax):
                    if self.cells[i] == False:
                        self.cells[i] = bot
                        self.current = Sexbot() # new empty bot holder
                        return()
            return(False) 

        def charge(self,itemID):
            pod = itemID[6:].lower() # strip common ID item prefix
            GAME.lab.status[pod] += 100 
            GAME.ship.unloadWare(itemID)
            return()

        def clearLab(self):
            self.current = Sexbot()
            return()

        def upgradeParts(self, mode = "pref", include = ["any"], exclude_grade = []):

            P_NUM = 3
            eligible = []
            part = False
            for pref in ["subpar", "basic", "advanced"]:
                tally = {}
                for i in [x for x in GAME.lab.parts if x.pref == pref]: 
                    # build pref basket tally by baseID
                    if not i.attr["BaseID"] in tally.keys():
                        tally[i.attr["BaseID"]] = 0  
                    # ==== CR-0439 START =====
                    if i.slot not in ["cube"] and i.attr["BaseID"] not in ["SBPCortexPre"]: 
                        tally[i.attr["BaseID"]] += 1
                    # ==== CR-0439 END =====

                for k in tally.keys():
                    if tally[k] >= P_NUM:
                        eligible.append((pref,k,tally[k]))

            # select part to upgrade
            if eligible:
                target = eligible[0]
                prefs = {"subpar": "basic", "basic": "advanced", "advanced": "military grade"}
                # create one new part 
                pref = prefs[target[0]]

                # test for exceptional results jump 
                if "basic" in exclude_grade and pref == "basic":
                    pref = "advanced"
                if "advanced" in exclude_grade and pref == "advanced":
                    pref = "military grade"

                # create part
                part = GAME.sbPartsData[target[1]].clone({"pref": pref})
                # eliminate P_NUM old parts
                parts = [x for x in GAME.lab.parts if x.attr["BaseID"]==target[1] and x.pref == target[0]]
                for i in range(P_NUM):
                    GAME.lab.parts.remove(parts[i])
                GAME.lab.parts.append(part)
                GAME.lab.status["nano"] -= 25

            return(part)

        def craftShell(self, actor, tier): # xxxxx simple, exotic, luxury, rarified 
            # prepare data 
            materials = [] # items

    
            craftDict = {
                "simple": ["ITMCrateBots", ["K", "C", "J"], [1]*3 + [2]*2 + [3]*1, ["basic"]*3 + ["advanced"]*2 + ["military grade"]*1, "Standard"],
                "exotic": ["ITMCrateBotsRare", ["V", "N"], [1]*3 + [2]*2 + [3]*1, ["basic"]*3 + ["advanced"]*2 + ["military grade"]*1, "Rare"  ],
                "luxury": ["ITMCrateBotsMil", ["L", "P"], [1]*1 + [2]*2 + [3]*1, ["advanced"]*2 + ["military grade"]*1, "Military"  ],
                "rarified": ["ITMCrateBotsPre", ["G", "I"], [2]*2 + [3]*1, ["military grade"]*4+["elite"]*1, "Precursor"  ],
                
                }

            # check prerequisites & roll attributes & reserve materials
            if tier in craftDict.keys():
                kinds = [x for x in craftDict[tier][1] if "SHELL_"+x in actor.done]
                # CR-2137 START
                crateID = craftDict[tier][0]
                if not GAME.ship.hasCargo([craftDict[tier][0]]*3):
                    GAME.reports.append(["MISSING MATERIALS REPORT".format(tier.upper()), "{} could not create a {}{} Shell{}.\n\n{} (3x) not available in Cargo.".format(actor.name, C_HI_B, tier.title(), C_HI_E, GAME.items[crateID].getName(["cap", "col", "noArt"])), ["Continue"], "08_items/ico {}.png".format(crateID), "red"])  
                    GAME.consoleWrite("I: {} - Missing materials to craft shell...".format(actor.name))
                    return(False)
                
                if not kinds:
                    GAME.reports.append(["MISSING SHELL BLUEPRINT".format(tier.upper()), "{} could not create a {}{} Shell{}.\n\nShe has not studied any shell of that kind yet (give her at least one...).".format(actor.name, C_HI_B, tier.title(), C_HI_E), ["Continue"], "08_items/ico {}.png".format(crateID), "red"]) 
                    GAME.consoleWrite("I: {} - No shells of this tier known to craft...".format(actor.name))
                    return(False)
                # CR-2137 END

                #kind = random.choice(kinds) ### screw random!  OhWeeModstuff
                kind = Shell_Crafting_Selection
                gen = random.choice(craftDict[tier][2])
                pref = random.choice(craftDict[tier][3])

                materials = [GAME.items[craftDict[tier][0]]]*3
                baseID = "SBPShell{}{}".format(gen,kind)
                crateUsed = craftDict[tier][4]
            else:
                GAME.consoleWrite("I: {} - No shell to craft... (unspecified cause...)".format(actor.name))
                return(False)

            # elite lucky roll 
            pass

            # assemble shell or abort
            part = GAME.sbPartsData[baseID].clone({"pref": pref})

            # store shell
            GAME.lab.parts.append(part)

            # remove materials
            for i in [x for x in materials]:
                GAME.ship.cargo.remove(i)

            # fill report
            GAME.reports.append(["{} SHELL CREATED".format(tier.upper()), "{} has created a {}.\n\n3 {} Bot Crates have been removed from Cargo and the Shell has been added to Lab Storage.".format(actor.name, part.getName('col'), crateUsed), ["Continue"], "03_minigames/ico {}.png".format(baseID), "blue"])  
            GAME.consoleWrite("I: {} - {} crafted...".format(actor.name, part.getName('col')))
            nimhe.setJob("JOB_Idle")
        
            return(True, part)

        def findOrder(self,bot, order="any"): # returns first fulfilled order found 
            if order == "any":
                for i in self.orders:
                    if all([eval(x, globals(), locals()) for x in i[1:]]): 
                        return(i)
            return(False)

        def findBots(self,order): # returns first fulfilled order found 
            bots = []
            for bot in [c for c in GAME.lab.cells if c]:
                if all([eval(x, globals(), locals()) for x in order[1:]]): 
                    bots.append(bot)
            return(bots)

        def getCompleted(self):
            completed = []
            for i in [x for x in GAME.lab.cells if x]:
                if i.isComplete():
                    completed.append(i)
            return(completed)

    class Sexbot():
        def __init__(self, attr={}):
            self.ID = "BOT-{}".format(GAME.getUniqueID()) 
            self.name = None
            self.doneToday = []
            self.earnedToday = []
            self.parts = {}
            self.talents = {}
            self.skills = {}
            self.traits = []
            self.likes = set()
            self.dislikes = set()
            self.attr = {}
            self.addOns = [False for i in range(6)] # 0 to 4 normal addons, 5 is shell slot
            self.attitude = "rebellious"
            self.personality = "Unknown Pattern"
            self.tags = []
            self.tally = {}
            self.IQ = 100
            self.price = 500
            self.autoTrain = False
            self.partNames = ["cortex", "head", "torso", "arms", "legs"]
            self.qc = 0 # used qc points


        def getAttr(self, k):
            if k in self.attr.keys():
                return(self.attr[k])
            return(None)

        def rollNew(self, cortex, attr={}):

            # buff = {1: 0, 2:20, 3:40}[self.parts["cortex"].cat]  CR-0295

            # sexbot name 
            if "name" in attr.keys():
                self.name = attr["name"]
            else:
                self.name = random.choice(GAME.sbData["names"])

            # sexbot mandatory parts 
            self.parts["cortex"] = cortex
            buff = {1: 0, 2:20, 3:40}[self.parts["cortex"].cat]  # CR-0295
            self.IQ = int (capValue(random.gauss(120+buff,20),80,300))

            # quest "assist" intercept
            if GAME.questSys.inProgress('QID_PIRATE_PRESENT1') and random.randint(1,100) > 50:
                self.IQ = self.IQ + 30


            # rebelliousness 
            if "attitude" in attr.keys():
                self.attitude = attr["attitude"]
            else:
                self.attitude = random.choice(GAME.sbData["attitudes"][:-1])
             
            # sexbot talents (defined by cortex, augmented by parts)                
            for k in ["ANA", "ORA", "VAG", "BON", "SPE"]:
                self.talents[k] = random.randint(buff,100+buff)

            # personality 
            if "personality" in attr.keys():
                self.personality = attr["personality"]
            else:            
                self.personality = random.choice(GAME.sbData["personalities"])

            # sexbot skills 
            for k in ["ANA", "ORA", "VAG", "BON", "SPE"]:
                self.skills[k] = random.randint(0,int(float(self.talents[k])/5.0))

            # sexbot traits 
            if "traits" in attr.keys():
                self.traits = list(attr["traits"])
            else:
                traitPairs = random.sample(GAME.sbData["traits"],2) 
                self.traits.append(traitPairs[0][random.choice([0,1])])
                self.traits.append(traitPairs[1][random.choice([0,1])])
            # 3rd trait?
            if self.parts["cortex"].attr["BaseID"] == "SBPCortexPre" and len(self.traits)<3: # CR-0875
                self.traits.append(random.choice([x[2] for x in GAME.sbData["traits"]]))

            # sexbot likes / dislikes
            if "likes" in attr.keys():
                self.likes = set(attr["likes"])
            else: 
                likes = 1
                dislikes = 1
                if "perverted" in self.traits:
                    likes += 2
                elif "frigid" in self.traits:
                    likes -= 1
                    dislikes += 2
                self.likes.update(random.sample(GAME.sbData["likes"], likes))
                self.dislikes.update(random.sample(GAME.sbData["dislikes"], dislikes))
                for i in self.likes:
                    if i in self.dislikes:
                        self.dislikes.remove(i)

            # like/dislike adjustments
            if all([x in self.traits for x in ["frigid", "aggressive"]]):
                for k in self.talents.keys():
                    self.talents[k] = capValue(self.talents[k]-random.randint(1,20),0,100)
            if all([x in self.traits for x in ["flexible", "submissive"]]):
                self.likes.add("bondage")
                self.talents["BON"] = capValue(self.talents["BON"]+40,0,100)
            if all([x in self.traits for x in ["perverted", "aggressive"]]):
                self.likes.add("oral")
                self.talents["BON"] = capValue(self.talents["BON"]+20,0,100)
            if all([x in self.traits for x in ["curious"]]):
                self.likes.add("special")
            if all([x in self.traits for x in ["perverted"]]):
                self.talents["SPE"] = capValue(self.talents["SPE"]+20,0,100)
                self.talents["ANA"] = capValue(self.talents["ANA"]+20,0,100)
            if any([x in self.traits for x in ["conservative", "wholesome"]]):
                self.likes.discard("special")
                self.likes.discard("anal")

            # trait adjustments 
            if "inflexible" in self.traits:
                self.talents["BON"] = min(self.talents["BON"], 20)
                self.skills["BON"] = min(self.talents["BON"], self.skills["BON"])     

            if self.personality in ["princess", "celebrity"]:
                self.attitude = random.choice(["rebellious", "devious", "insidious"])
    
            return()

        def getWear(self): # 1.0 = 100% worn
            wear = 0.0
            # any part 100% wear -> full bot 100% (unusable)
            if any ([x.wear == 1.0 for x in self.parts.values()]):
                return(1.0)
            # calculate average wear
            partsCond = [x.wear for x in self.parts.values()]
            if partsCond: 
                wear = sum(partsCond)/float(len(partsCond))
            return(wear)

        def getShellConfig(self):
            addOns = [x for x in self.addOns if x]
            botShell = [x for x in addOns if "Shell" in x.ID] 
            if not botShell:
                return("A")
            shellType = botShell[0].attr["BaseID"][-1]
            return(shellType)

        def getIQRating(self):
            if self.hasComp("cortex") == False:
                return("n/a")
            rating = "Of limited intelligence"
            if self.getIQ() >= 180:
                rating = "Dangerously intelligent"
            elif self.getIQ() >= 160:
                rating = "Highly intelligent"
            elif self.getIQ() >= 140:
                rating = "Very intelligent"
            elif self.getIQ() >= 120:
                rating = "Quite intelligent"
            elif self.getIQ() >= 100:
                rating = "Of average intelligence"
            return(rating)

        def getStars(self):
            stars = sum([1 for x in self.getTalents().values() if x >= 85])
            return(stars)

        def getShortDesc(self):
            desc = ""
            fWear = percentColorized(self.getWear()*100, [0.0,100.0], True)
            desc += "Gen {} | IQ {} | {} | {} \n{} | Risk {:.1f}% ".format(self.parts["cortex"].cat, self.getIQ(), self.getAttitude().title(), fWear[1]+" Wear "+fWear[2]+fWear[0], " | ".join(self.getTraits()).title(), self.getEscapeProb()*100)
            return(desc)

        #####################################################################
        #              TRANSPARENT STAT SYSTEM WITH SHELL ETC OVERRIDES xxx
        #####################################################################

        def getTalents(self): # equipment corrected talent values
            talents = {}
            # base talents
            for k in self.talents.keys():
                talents[k] = self.talents[k] 
            # add-on bonus 
            for i in [x for x in self.addOns if x]:
                for k in self.talents.keys():
                    talents[k] += i.getTalentBoost(k)  
            # parts bonus 
            prefBonus = {"subpar": -1, "basic": 0, "advanced": 5, "military grade": 10, "elite": 20}
            partsMatrix = {"legs": ["BON"], "arms": ["BON"], "torso": ["ANA", "VAG", "SPE"], "head": ["ORA"], "cortex": ["SPE"]} 
            for k in self.parts.keys():
                bonus = 0
                if self.parts[k] not in [False, None]:
                    bonus += prefBonus[self.parts[k].pref]
                    for i in partsMatrix[k]:
                        talents[i] += bonus 
            #for k in self.talents.keys():
                #talents[k] = capValue(talents[k], 0, 100)
            return(talents)

        def getShell(self):
            return(self.addOns[5])

        def getLikes(self): # xxx
            likes = self.likes
            shell = self.getShell()
            if getattr(shell, "shellLikes", False):
                # merge in premium override likes
                likes = set(getattr(shell, "shellLikes", False)) | likes
            return(likes)

        def getDislikes(self):
            dislikes = self.dislikes
            shell = self.getShell()
            if getattr(shell, "shellDislikes", False):
                # merge in premium override dislikes
                dislikes = set(getattr(shell, "shellDislikes", False)) | dislikes
                # subtract likes (priority para)
                dislikes = dislikes - self.getLikes()
            return(dislikes)

        def getIQ(self):
            bonus = 0
            for i in [x for x in self.addOns if x]:
                bonus += i.getIQBoost()
            attBonus = {"insidious": 70, "devious": 30}
            traitBonus = {"calculating": 30}
            if self.getAttitude().lower() in attBonus.keys():
                bonus += attBonus[self.getAttitude().lower()] 
            for i in self.getTraits():
                if i in traitBonus.keys():
                    bonus += traitBonus[i] 
            return(self.IQ+bonus)

        def getPersonality(self):
            shell = self.getShell()
            if getattr(shell, "shellPersonality", False):
                return(shell.shellPersonality)
            return(self.personality)

        def getAttitude(self):
            shell = self.getShell()
            if getattr(shell, "shellAttitude", False):
                return(shell.shellAttitude)
            return(self.attitude)

        def getTraits(self):
            shell = self.getShell()
            if getattr(shell, "shellTraits", False):
                return(shell.shellTraits)
            return(self.traits)



       #####################################################################
        #              OTHER FUNCTIONS
        #####################################################################
        def assessUniformity(self):
            if "cortex" not in self.parts.keys():
                return(False)




            lead = self.parts["cortex"].cat
            # base parts
            for i in self.parts.values():
                if i.cat != lead:
                    return(False)

            # addons 
            for i in [x for x in self.addOns if x not in [None, False]]:
                if i.cat != lead:
                    return(False)
            return(True)


        def installAddOn(self,slot, item): 
            self.addOns[iSlot]= item 
            GAME.lab.parts.remove(item)
            return()

        def uninstallAddOn(self,slot):
            item = self.addOns[slot]
            GAME.lab.parts.append(item)
            self.addOns[slot] = False
            for k in self.getTalents().keys():
                self.skills[k] = min(self.skills[k], self.getTalents()[k])
            return()

        def uninstallPart(self, slot):
            if not self.hasComp(slot):
                return()
            # remove part
            part = self.parts[slot] 
            self.parts.pop(slot)
            # adjust overtrained skills (cut off)
            talents = self.getTalents()
            for k in self.skills.keys():
                self.skills[k] = min(self.skills[k], talents[k])
            # move part to storage
            GAME.lab.parts.append(part)
            return()

        def use(self, mode = "personal"):
            pass
            return()

        def train(self,skill, mode="manual"):
            hFactor = 1.0
            if mode == "hyper":
                hFactor = 2.5

            effect = 10 * hFactor

            if GAME.sbData["skillDict"][skill] in self.getLikes(): 
                effect *= 2.0
            elif GAME.sbData["skillDict"][skill] in self.getDislikes(): 
                effect /= 2.0 

            effect = int(effect)
                
            self.skills[skill] = min(self.skills[skill]+effect, self.getTalents()[skill])
            if mode != "auto":
                GAME.lab.status["energy"] = max(0, GAME.lab.status["energy"]-10)
            return()

        def colorize(self, string):
            baskets = {
                "red": ["rebellious", "insidious" ],
                "orange": ["devious", "calculating"],
                "yellow": ["aggressive", "conservative"],
                "green": ["compliant", "submissive", "curious","flexible", "actress", "holo starlet", "executive", "socialite", "concubine", "krell concubine", "palace mistress", "explorative", "active"],
                "pink": ["perverted", "nympho", "princess", "supermodel", "hive queen", "galaxina", "protector", "celebrity", "infiltrator", "syrax concubine", "voronian concubine", "temptress", "pet", "playful", "seductress"],
                "grey": ["chaste", "frigid", "inflexible","wholesome", "inflexible"]
            }
            for k in baskets.keys():
                if string.lower() in baskets[k]:
                    return(ACCENTS[k][0]+string+ACCENTS[k][1])
            return(string)

        def getCat(self, slot):
            if slot in self.parts.keys():
                if self.parts[slot] not in ["False"]:
                    return(self.parts[slot].cat)
            return(False)

        def getPrice(self, mode ="value"):
            analysis = {}

            # sum up base prices incl. wear condition
            analysis["Bot Name"] = self.name
            price = self.price
            analysis["baseprice"] = self.price
            analysis["partsPrice"] = 0.0
            analysis["addOnPrice"] = 0.0           

            for k in self.parts.keys():
                if self.parts[k] not in [None, False]:
                    element = self.parts[k].getPrice()
                    price += element
                    analysis["partsPrice"] +=  element
            for i in range(6): 
                if self.addOns[i]:
                    element = self.addOns[i].getPrice() 
                    price += self.addOns[i].getPrice()     
                    analysis["addOnPrice"] +=  element   

            # uniformity bonus 
            uBonus = 1.0
            if self.assessUniformity() and self.isComplete():
                uBonus = 1.3
            price = price * uBonus 
            analysis["facUniformity"] = uBonus

            # skill bonus 
            sBonus = 0.5 + sum(self.skills.values())/1500.0 # CR-0592
            price = price * sBonus 
            analysis["facSkill"] = sBonus

            # talent bonus 
            tBonus = 1.0 + sum(1 for x in self.talents.values() if x >= 90)/5.0*0.3
            price = price * tBonus 
            analysis["facTalents"] = tBonus

            # IQ bonus
            iBonus = (self.getIQ()/100.0)
            price = price * iBonus # * iBonus
            analysis["facIQ"] = iBonus

            # like/dislike effect 
            FAC_D = 0.8
            FAC_L = 1.2
            analysis["facLikes"] = 1.0
            analysis["facDislikes"] = 1.0
            for k in self.talents.keys():
                if GAME.sbData["skillDict"][k].lower() in self.getLikes():
                    price *= FAC_L
                    analysis["facLikes"] *= FAC_L
                if GAME.sbData["skillDict"][k].lower() in self.getDislikes():
                    price *= FAC_D
                    analysis["facDislikes"] *= FAC_D
                    
            # ==== CR-0752 START
            # special combos 
            combos = [
                [["nympho", "perverted"], {"ALL": 1.15}],
                [["submissive", "perverted"], {"ALL": 1.1}],
                [["curious", "perverted"], {"ALL": 1.05}],

                [["perverted", "aggressive"], {"ANA": 1.8, "BON": 1.5}],
                [["nympho", "aggressive"], {"ANA": 1.2}],
                [["flexible", "submissive"], {"BON": 1.2}],
                [["nympho", "submissive"], {"ORA": 1.2}],
                [["curious", "nympho"], {"SPE": 1.2}],
                [["nympho", "flexible"], {"VAG": 1.2}],

                [["wholesome", "curious"], {"SPE": 1.1}],
                [["frigid", "submissive"], {"ORA": 1.1}],
                [["conservative", "submissive"], {"VAG": 1.1}],
                [["frigid", "submissive"], {"BON": 1.1}],
                [["wholesome", "submissive"], {"ANA": 1.1}]
            ]

            # process combos
            analysis["facCombos"] = 1.0
            for combo in combos:
                bonus = 1.0
                if all([x in self.getTraits() for x in combo[0]]):
                    for c in combo[1].keys():
                        for k in self.skills.keys():
                            if k == c or c == "ALL":
                                bonus += self.skills[k]/1500.0 * combo[1][c]
                    price *= bonus
                    analysis["facCombos"] *= bonus
            # ==== CR-0752 END

            # single positives
            analysis["facSinglesPos"] = 1.0
            for i in ["nympho", "perverted", "flexible", "curious", "submissive"]:
                if i in self.getTraits():
                    price = price * 1.05
                    analysis["facSinglesPos"] *= 1.05

            # single negatives
            analysis["facSinglesNeg"] = 1.0
            for i in ["frigid", "conservative", "inflexible", "wholesome", "aggressive"]:
                if i in self.getTraits():
                    price = price * 0.95
                    analysis["facSinglesNeg"] *= 0.95
            
            # third trait
            specialTraits = [x[2] for x in GAME.sbData["traits"]]
            specialTraitBonus = {
                "pet": 1.15, "playful": 1.1, "explorative": 1.05, "active": 1.05, "calculating": 0.95 
            }
            for i in self.getTraits():
                if i in specialTraits and i in specialTraitBonus.keys():
                    price = price * specialTraitBonus[i] 



            # personality profiles
            analysis["facPersonality"] = 1.0
            pBonus = {  "delinquent": 0.8,
                        "actress": 1.11, 
                        "executive": 1.1, 
                        "holo starlet": 1.1,
                        "socialite":1.15,
                        "celebrity": 1.4, 
                        "concubine": 1.4, 
                        "supermodel": 1.4, # CR-0848
                        "palace mistress": 1.5, 
                        "voronian concubine": 1.5,
                        "krell concubine": 1.5, 
                        "temptress": 1.7, 
                        "princess": 1.8,
                        "infiltrator": 2.0,
                        "protector": 2.0,
                        "seductress": 2.0,
                        "hive queen": 2.5,
                        "galaxina": 2.5
                        }
            for k in pBonus.keys():
                if self.getPersonality() == k:
                    price = price * pBonus[k]
                    analysis["facPersonality"] *= pBonus[k]


            analysis["Grand Total"] = int(price)
            if mode == "analysis_log":
                with open(config.savedir + "/bot_analysis.json", "a") as f:
                    json.dump(analysis, f)   
            elif mode == "analysis":             
                return(analysis)

            return(int(price))


        def isComplete(self):
            for i in self.partNames:
                if self.hasComp(i) == False:
                    return(False)
            return(True)

        def isIllegal(self):
            if self.getIQ() >= 140:
                return(True)
            return(False)

        def isBlank(self):
            if self.hasComp("cortex"):
                return(False)
            return(True)

        def hasComp(self, slot):
            if slot not in self.parts.keys():
                return (False)
            if self.parts[slot] in [False, None]:
                return(False)
            return(self.parts[slot])

        def hasAddOn(self, ID):
            for i in self.addOns:
                if i:
                    if i.attr["BaseID"] == ID:
                        return(True)
            return(False)

        def tallyUp(self, k, amount):
            if k not in self.tally:
                self.tally[k] = amount 
            else:
                self.tally[k] += amount 
            return()

        def getTally(self, k):
            if k not in self.tally:
                return(0)
            return(self.tally[k])


        # =========== CR-0379 START ==========================
        def getEscapeProb(self): # 0.0 to 0.5 of 1.0 (=100%)

            if self.getAttitude() == "devoted":
                return(0.0)
            prob = 0.0
            IQ = self.getIQ()
            incDict = {"devoted": 0.1, "compliant": 0.2, "indifferent": 0.3, "rebellious": 0.4, "devious": 0.5, "insidious": 0.7}
            if self.getAttitude().lower() not in incDict.keys():
                return(prob) 
            inc = incDict[self.getAttitude().lower()]
            if self.getShellConfig() == 'I':         
                inc = 1.0      

            x_0 = 100
            x_1 = 240
            x_i = self.getIQ()
            prob = inc/(x_1-x_0)*(x_i-x_0)

            # apply immo effects 
            immoBaseEffects = {
                1: [10, 5, 2],
                2: [20, 10, 5],
                3: [40, 20, 10]
            }
            prefImpact = {"subpar": 0.5, "basic": 1.0, "advanced": 1.5, "military grade": 2.5, "elite": 3.5}

            immoImpact = 0.0
            botGen = self.parts["cortex"].cat
            immoEquipped = []
            for i in [x for x in self.addOns if x and "addonimmo" in x.ID.lower()]: # all non empty slots
                immoImpact -= immoBaseEffects[i.cat][botGen-1]*prefImpact[i.pref]/100.0

            prob = capValue(prob+immoImpact, 0.0, 1.0)

            return(round(prob,3))
        # =========== CR-0379 END ==========================

        def getEarnings(self, x_in = False):
            if x_in:
                x = float(x_in) 
            else:
                x = float(self.getPrice())

            x_points = [100000, 500000, 1000000, 10000000, 100000000]
            y_points = [15000, 60000, 100000, 500000, 1000000]
            result = 0
            # Check if x is out of the bounds of provided x_points
            if x <= x_points[0]:
                result = y_points[0]
            elif x >= x_points[-1]:
                result = y_points[-1]
            # Perform linear interpolation
            for i in range(4):
                if x_points[i] <= x <= x_points[i + 1]:
                    x0, y0 = x_points[i], y_points[i]
                    x1, y1 = x_points[i + 1], y_points[i + 1]
                    # Linear interpolation formula
                    y = y0 + (y1 - y0) * ((x - x0) / (x1 - x0))
                    result = y
                    
            return(int(result*random.uniform(0.9,1.0)))

    class SexbotPart():
        def __init__(self, ID, name, art, kind, slot, cat, description, price, tags, attr):
            # common data
            self.ID = ID
            self.name = name 
            self.kind = kind
            self.slot = slot 
            self.art = art
            self.description = description
            self.cat = cat # generation
            self.pref = False
            self.price = price
            self.tags = tags 
            self.attr = attr  
            self.identified = False
            # instance data / modifiable
            self.wear = 0.0 # to 1.0

        def clone(self, attr={}): # rolling of new bot part from template
            # copy data
            ID = "{}-{}".format(self.ID, GAME.getUniqueID())
            name = str(self.name)
            art = str(self.art)
            kind = self.kind  
            slot = self.slot 
            cat = self.cat
            description = str(self.description)
            price = self.price 
            clone = SexbotPart(ID, name, art, kind, slot, cat, description, price, [], {})
            clone.attr["BaseID"] = self.ID
            prefVal = GAME.sbData["prefVal"]

            # # cube case
            # if self.slot == "cube":
            #     clone.pref = random.choice(["basic"]*6+["advanced"]*3+["military grade"])
            #     clone.wear = 0.0
            #     return(clone)

            # normal case
            if "pref" in attr.keys():
                clone.pref = attr["pref"]
            elif "prefs" in attr.keys():
                clone.pref = random.choice(attr["prefs"])
            elif self.ID in ["SBPCortexPre", "SBPCubeAttitude", "SBPCubePersonality", "SBPCubeTraits"] :
                clone.pref = random.choice(["advanced"]*2+["military grade"]*3)                
            else: 
                clone.pref = random.choice(["subpar"]*4+["basic"]*6+["advanced"]*2+["military grade"])

            # some random settings
            C_S = 0.18
            C_BASE = {1: 2.0, 2: 5.0, 3: 12}[clone.cat]

            # Base parts
            if kind == "sexbot parts":
                if "wear" in attr:
                    clone.wear = attr["wear"]
                else:
                    clone.wear = random.uniform(0.0,1.0) 

            # Various addon types
            elif self.ID in ["SBPAddOnIQ", "SBPAddOnIQ2", "SBPAddOnIQ3"]:
                clone.attr["IQ"] = 1 + int( C_BASE*(prefVal[clone.pref]+ abs(random.gauss(0,C_S))))
            elif "SBPAddOnSkill" in self.ID:
                skill = self.attr["skill"]
                clone.attr[skill] = 1 + int(5*C_BASE*(prefVal[clone.pref]+ abs(random.gauss(0,C_S))))
            elif self.ID in ["SBPAddOnPER", "SBPAddOnPER2","SBPAddOnPER3" ]:
                clone.attr["ALL"] = 2 + int(C_BASE * (prefVal[clone.pref] + abs(random.gauss(0,C_S))))
            elif self.ID in ["SBPAddOnImmo","SBPAddOnImmo2","SBPAddOnImmo3"]:
                clone.attr["ESC"] = 1 + int(1.0*(prefVal[clone.pref]+ abs(random.gauss(0,C_S))))

            # shell data extensions xxx
            if clone.slot == "shell":
                clone.shellPremium = False 
                clone.shellPersonality = False 
                clone.shellAttitude = False 
                clone.shellTraits = False
                clone.shellIQ = 0
                clone.shellLikes = False # guaranteed likes
                clone.shellDislikes = False # guaranteed likes
                clone.shellStats = False # skill and talent buff 

                # premium shell bonus stat settings / rolls # zzz
                if clone.isShell() == "I": # infiltrator always premium
                    clone.shellPremium = True
                    clone.shellPersonality = "infiltrator"
                    clone.shellTraits  = ["agressive", "perverted"]
                    clone.shellAttitude = "insidious"

                elif clone.isShell() == "P": # protector
                    clone.shellPremium = True
                    clone.shellPersonality = "protector"  
                    clone.shellAttitude = "devoted"
                    clone.shellTraits  = ["flexible", "aggressive", "calculating"]
                    stats = random.sample(["ANA", "ORA", "VAG", "BON", "SPE"],1)
                    clone.shellLikes = [GAME.sbData["skillDict"][x] for x in stats]
                    bonus = (50.0+abs(random.gauss(0,20))) / (4.0-clone.cat)
                    clone.shellStats = dict()
                    for s in stats:
                        clone.shellStats[s] = int(bonus)

                elif clone.isShell() == "G": # seductress
                    clone.shellPremium = True
                    clone.shellPersonality = "seductress"  
                    clone.shellAttitude = "devious"
                    clone.shellTraits  = ["nympho", "perverted", "playful"]
                    stats = random.sample(["ANA", "ORA", "VAG", "BON", "SPE"],random.randint(2,clone.cat+1))
                    clone.shellLikes = [GAME.sbData["skillDict"][x] for x in stats]
                    clone.shellStats = dict()
                    for s in stats:
                        bonus = (50.0+abs(random.gauss(0,20))) / (4.0-clone.cat) / 3.5 * prefVal[clone.pref]
                        clone.shellStats[s] = int(bonus)

                elif clone.isShell() == "X": # galaxina
                    clone.shellPremium = True
                    clone.shellPersonality = "Galaxina"  
                    clone.shellAttitude = "devoted"
                    clone.shellTraits  = ["nympho", "perverted", "flexible"]
                    stats = random.sample(["ANA", "ORA", "VAG", "BON", "SPE"],random.randint(4,5))
                    clone.shellLikes = [GAME.sbData["skillDict"][x] for x in stats]
                    clone.shellStats = dict()
                    for s in stats:
                        bonus = (70.0+abs(random.gauss(0,20))) / (4.0-clone.cat) / 3.5 * prefVal[clone.pref]
                        clone.shellStats[s] = int(bonus)
                    clone.shellStats["BON"] = clone.shellStats.get("BON",0) + 150 # CR-2046

            return(clone)

        def update(self): # part scrubber to update master data of legacy parts
            # shell updates
            if self.isShell() and self.slot != "shell":
                self.slot = "shell"
            # sgell scrapping update / fix
            if self.isShell() not in ["I"] and "no_sell" in self.tags:
                self.tags.remove("no_sell")
            # price updates
            if self.attr["BaseID"] in GAME.sbPartsData.keys():
                self.price = GAME.sbPartsData[self.attr["BaseID"]].price
            return()

        def isMatch(self, x): # for parts filter
            # addon skill
            if x in ["ANA", "ORA", "VAG", "BON", "SPE"]:
                if x in self.attr.keys():
                    return(True) 
            # addon perv 
            elif x in ["PER"]:
                if "SBPAddOnPER" in self.ID:
                    return(True) 
            # addon iq
            elif x in ["IQ"]:
                if "SBPAddOnIQ" in self.ID:
                    return(True) 
            # addon immo
            elif x in ["Immo"]:
                if "SBPAddOnImmo" in self.ID:
                    return(True) 
            # addon shell
            elif x in ["Shell"]:
                if self.slot == "shell":
                    return(True) 
            # addon cube
            elif x in ["Cube"]:
                if self.slot == "cube":
                    return(True) 

            return(False)

        def isShell(self): # I, C, K, ... or False #
            if self.slot == "shell":
            #if "sbpshell" in self.ID.lower():
                return(self.attr["BaseID"][-1])
            return(False)

        def isPremiumShell(self): # True or False #
            if self.slot == "shell":
                if getattr(self, "shellPremium", False):
                    return(True)
            return(False)

        def getAddOnType(self): 
            if "sbpaddonskill" in self.ID.lower():
                return(self.attr["skill"])
            elif "sbpaddonper" in self.ID.lower():
                return("PER")
            elif "sbpaddonimmo" in self.ID.lower():
                return("IMMO")
            elif "sbpaddoniq" in self.ID.lower():
                return("IQ")
            elif "sbpshell" in self.ID.lower():
                return("SHELL")
            return(None) # no addon

        def getBasePart(self):
            if self.getAttr("BaseID"):
                return(GAME.sbPartsData[self.getAttr("BaseID")])
            return(None)

        def getAttr(self, k):
            if k in self.attr.keys():
                return(self.attr[k])
            return(None)

        def getPath(self):
            return("03_minigames/")

        def getSound(self):
            return("equip.wav")

        def getDesc(self):
            return(self.desc)

        def getIco(self):
            return("ico {}.png".format(self.attr["BaseID"]))

        def getKey(self):
            return(self.kind+self.ID)

        def getPrice(self):
            # add more detailed bonus impacts WIP
            
            # calculate price
            factorCat = {1: 2.5, 2: 2.75, 3: 3.0}.get(self.cat, 1.0)
            price = self.price * factorCat * (1.1-self.wear) * GAME.sbData["prefVal"][self.pref] 
            # price = self.price * self.cat * (1.1-self.wear) * GAME.sbData["prefVal"][self.pref] 
            return(int(price))

        def getName(self, para=[]):
            quali = ""                             
            if "addonskill" in self.ID.lower():        
                quali = " "+self.ID[13]            
            if "col" in para:
                name = "{}{} - {}, {}".format(self.name, quali, self.colorize("Gen "+str(self.cat)), self.colorize(self.pref.title()))
            else:
                name = "{}{} - Gen {}, {}".format(self.name, quali, self.cat, self.pref.title())
            return(name)

        def colorize(self, expr):
            expr = str(expr)
            exprLookup = expr.lower()
            accent = "white"
            C_B, C_E = C_WH_B, C_WH_E 
            baskets = {
                "gold": ["advanced"],
                "orange": ["military grade", "3", "gen 3"],
                "bright blue": ["1", "gen 1"],
                "grey": ["subpar"],
                "green": ["2", "gen 2"],
                "white": ["basic"],
                "pink": ["elite"]
            }
            for k in baskets.keys():
                if exprLookup in baskets[k]:
                    accent = k
            C_B = ACCENTS[accent][0]
            C_E = ACCENTS[accent][1]    

            return(C_B+expr+C_E)

        def getTalentBoost(self, talent):
            if self.isShell(): # premium shell
                if getattr(self, "shellStats", False): # super paranoid
                    if self.shellStats:
                        if self.shellStats.get("ALL", False):
                            return(self.shellStats["ALL"])
                        if self.shellStats.get(talent, False):
                            return(self.shellStats[talent])                    
            else: # addon
                if "ALL" in self.attr.keys():
                    return(self.attr["ALL"])
                if talent in self.attr.keys():
                    return(self.attr[talent])
            return(0)

        def getIQBoost(self):
            if "IQ" in self.attr.keys():
                return(self.attr["IQ"])
            return(0)

        def getBoostOneLiner(self, mode="full"): # xxx
            result = []

            if self.isPremiumShell() and mode == "short":
                result = "[C_GO_B]PREMIUM SHELL"+"[C_GO_E]"

            elif self.isPremiumShell():
            
                if self.shellPersonality:
                    result.append(self.shellPersonality.title()) 
                if self.shellAttitude:
                    result.append(self.shellAttitude.title()) 
                if self.shellTraits:
                    result.append("Traits: "+", ".join(self.shellTraits).title()) 
                if self.shellIQ:
                    result.append("+{} IQ".format(self.shellIQ))
                if self.shellLikes:
                    result.append("Likes: " + ", ".join(self.shellLikes).title())
                if self.shellDislikes:
                    result.append("Dislikes: " + ", ".join(self.shellDislikes).title())
                if self.shellStats:
                    for k in self.shellStats.keys():
                        result.append("{}: +{}".format(k, self.shellStats[k]))
                result = "[C_GO_B]SHELL ADDS: " + " | ".join(result) + "[C_GO_E]"
            else:
                if self.getIQBoost():
                    result.append("IQ +{}".format(self.getIQBoost()))
                if "ALL" in self.attr.keys():
                    result.append("ALL +{}".format(self.getTalentBoost("ALL")))
                else:        
                    for k in GAME.sbData["skillDict"].keys():
                        if self.getTalentBoost(k):
                            result.append("{} +{}".format(k, self.getTalentBoost(k)))
                result = " | ".join(result)

            return(result)

        def getMods(self):
            mods = ""
            if self.getIQBoost():
                mods += "IQ: +{}".format(self.getIQBoost()) 
            for k in GAME.sbData["skillDict"].keys():
                if self.getTalentBoost(k):
                    mods += "{}: +{}".format(k, self.getTalentBoost(k)) 
            return(mods)

        def rollCube(self, bot):
            result = []

            if self.attr["BaseID"] == "SBPCubeAttitude":
                attitudeNew = random.choice(["compliant"]+["indifferent"]*3+["rebellious"]*3+["devious"]*2+["insidious"])
                bot.attitude = attitudeNew
                result = ["Attitude", attitudeNew ]

            elif self.attr["BaseID"] == "SBPCubeTraits":
                traitPairs = random.sample(GAME.sbData["traits"],2) 
                trait1 = traitPairs[0][random.choice([0,1])]
                trait2 = traitPairs[1][random.choice([0,1])]          

                # adjust sexbot traits 
                bot.traits = []
                bot.traits.append(trait1)
                bot.traits.append(trait2)
                # 3rd trait?
                if bot.parts["cortex"].attr["BaseID"] == "SBPCortexPre" and len(bot.traits)<3: # CR-0915
                    bot.traits.append(random.choice([x[2] for x in GAME.sbData["traits"]]))

                # adjust sexbot likes / dislikes
                bot.likes = set()
                bot.dislikes = set()
                likes = 1
                dislikes = 1
                if "perverted" in bot.traits:
                    likes += 2
                elif "frigid" in bot.traits:
                    likes -= 1
                    dislikes += 2
                bot.likes.update(random.sample(GAME.sbData["likes"], likes))
                bot.dislikes.update(random.sample(GAME.sbData["dislikes"], dislikes))
                for i in bot.likes:
                    if i in bot.dislikes:
                        bot.dislikes.remove(i)
                # like/dislike adjustments
                if all([x in bot.traits for x in ["frigid", "aggressive"]]):
                    for k in bot.talents.keys():
                        bot.talents[k] = capValue(bot.talents[k]-random.randint(1,20),0,100)
                if all([x in bot.traits for x in ["flexible", "submissive"]]):
                    bot.likes.add("bondage")
                    bot.talents["BON"] = capValue(bot.talents["BON"]+40,0,100)
                if all([x in bot.traits for x in ["perverted", "submissive"]]):
                    bot.likes.add("oral")
                    bot.talents["BON"] = capValue(bot.talents["BON"]+20,0,100)
                if all([x in bot.traits for x in ["curious"]]):
                    bot.likes.add("special")
                if all([x in bot.traits for x in ["perverted"]]):
                    bot.talents["SPE"] = capValue(bot.talents["SPE"]+20,0,100)
                    bot.talents["ANA"] = capValue(bot.talents["ANA"]+20,0,100)
                if any([x in bot.traits for x in ["conservative", "wholesome"]]):
                    bot.likes.discard("special")
                    bot.likes.discard("anal")

                # trait adjustments 
                if "inflexible" in bot.traits:
                    bot.talents["BON"] = min(bot.talents["BON"], 20)
                    bot.skills["BON"] = min(bot.talents["BON"], bot.skills["BON"])     

                result = ["Traits", bot.traits ]

            elif self.attr["BaseID"] == "SBPCubePersonality":
                personalities = ["celebrity"]*5+["concubine"]*5+["palace mistress"]*3
                if self.pref in ["advanced","military grade"]:
                    personalities += ["voronian concubine"]*2+ ["princess"]*3
                if self.pref in ["military grade"]:
                    personalities += ["hive queen"]
                personalityNew = random.choice(personalities)

                result = ["Personality", personalityNew] 
                bot.personality = personalityNew 

            return(result)

        def getBaseID(self):
            if "BaseID" not in self.attr.keys():
                return(self.ID)
            return(self.attr["BaseID"])

        def getDesc(self):
            desc = "{}".format(self.description)   
            return(desc)
        
        def getGrade(self):
            return(int((1.0-self.wear)*100))

#####################################################################
#					INITIALIZE								
#####################################################################
label init_sexbots():
    python:
        #####################################################################
        #                Sexbot Data
        #####################################################################
        # ===== # CR-0848 START
        GAME.sbData = {
            "names": ['Aera', 'Aeryn', 'Algis', 'Alia', 'Alice', 'Alicia', 'Alisa', 'Alita', 'Alpha', 'Alsatia', 'Alsephina', 'Alya', 'Amaxia', 'Amelia', 'AnRyu', 'Andrea', 'Andromeda', 'Annalee', 'Anoka', 'Apollonia', 'April', 'Aquila', 'Arale', 'Arcee', 'Argenta', 'Arya', 'Ashley', 'Aster', 'Astrid', 'Athena', 'Atreya', 'Aura', 'Aurora', 'Auryn', 'Ava', 'Azure', 'Barbarella', 'Bella', 'Bellatrix', 'Betty', 'Bobbie', 'Buffy', 'Cameron', 'Carol', 'Cassandra', 'Cassopeia', 'Catherine', 'Celeste', 'Celia', 'Chacha', 'Chalmers', 'Cherry', 'Chii', 'Chrome', 'Cinder', 'Clade', 'Clare', 'Cloud', 'Copella', 'Cora', 'Cortina', 'Cybel', 'Danae', 'Danica', 'Darci', 'Dee', 'Demi', 'Diana', 'Dilara', 'Dina', 'Doctus', 'Doria', 'Dorothy', 'Dot', 'Dural', 'EDI', 'Eilon', 'Elanthe', 'Electra', 'Elektra', 'Elise', 'Elita', 'Elly', 'Elora', 'Elsa', 'Emma', 'Enigma', 'Eris', 'Estella', 'Eve', 'Ezri', 'Feel', 'Felice', 'Felicity', 'Fox', 'Freya', 'G2', 'Galatea', 'Galaxina', 'Gally', 'Haladay', 'Harumi', 'Helen', 'Helena', 'Helene', 'Hollow', 'Honey', 'Ida', 'Ilfa', 'Ilia', 'Inara', 'Indigo', 'Indra', 'Iris', 'Isabeau', 'Jacie', 'Jana', 'Jen', 'Jessica', 'Jin May', 'Joanna', 'Jocasta', 'Julie', 'Kosma', 'Kaelera', 'Karinka', 'Kay Em', 'Kheri', 'Kiku', 'KoRyu', 'Kurumi', 'Kyoko', 'Lal', 'Lana', 'Landaree', 'Leeloo', 'Lenore', 'Lesli', 'Lisa', 'Losira', 'Lucy', 'M4GAN', 'Mahoro', 'Maia', 'Maria', 'Mary', 'May', 'Maya', 'Meissa', 'Melfina', 'Mercury', 'Mia', 'Molly', 'Mona', 'Morgan', 'NAN', 'Naomi', 'Nashia', 'Nell', 'Nereid', 'Nerys', 'Neutronia', 'Nightbird', 'Nila', 'Niska', 'Nono', 'Nuku', 'Octavia', 'Olga', 'Olimpia', 'Oyza', 'Padme', 'Penny', 'Petra', 'Phantasma', 'Piper', 'Pixal', 'Pixie', 'Pris', 'Quicksilver', 'Rachael', 'Rayna', 'Reese', 'Rhoda', 'Ria', 'Riley', 'Robecca', 'Robotica', 'Rommie', 'Rosie', 'Ruatha', 'Ruth', 'Rya', 'Saki', 'Samantha', 'Sammi', 'Sari', 'Serenity', 'Serio', 'Seven', 'Sharon', 'Sibel', 'Sidra', 'Six', 'Skuld', 'Solty', 'Sonya', 'Sorsha', 'Sparkle', 'Stella', 'Strella', 'Sulla', 'Sylvia', 'Syrix', 'T-69', 'T-Elos', 'Talitha', 'Tallulah', 'Tauriel', 'Teela', 'TenRyu', 'Tima', 'Tina', 'Titania', 'Tyela', 'Umbra', 'Valeria', 'Valerie', 'Vanessa', 'Vela', 'Verda', 'Vivienne', 'Xena', 'Xenia', 'Ycari', 'Ysabell', 'Yumeni', 'Yuria', 'Yvaine', 'Zamara', 'Zaniah', 'Zazie', 'Zephyra', 'Zhora'],
        # ===== # CR-0848 END


            "traits": [ ["nympho", "frigid", "playful"], ["submissive", "aggressive", "pet"], ["flexible", "inflexible", "active"], ["perverted", "wholesome", "explorative"], ["curious", "conservative", "calculating"]],
            "likes": ["anal", "oral", "vaginal", "bondage", "special"],
            "dislikes": ["anal", "oral", "vaginal", "bondage", "special"],
            "skillDict": {"ANA": "anal", "ORA": "oral", "VAG": "vaginal", "BON": "bondage", "SPE": "special"},
            "prefVal": {"subpar": 0.5, "basic": 1.0, "advanced": 1.5, "military grade": 2.5, "elite": 3.5},
            "attitudes": ["compliant"]*3+ ["indifferent"]*6 +["rebellious"]*1 + ["devious"]*1 +["insidious"],
            "personalities" : ["delinquent", "housewife", "colonist", "politician", "merchant", 
                "business woman", "athlete", "teacher", "student", "explorer", "space marine", "cop"]*4 
                +   ["executive", "holo starlet", "actress"]*3
                +   ["socialite","celebrity", "supermodel"]*2
                +   ["princess"],
        }

        #####################################################################
        #                Part Data
        #####################################################################
        partsdata = [
        ["SBPCortex1", "Cortical Unit", "A", "sexbot parts", "cortex", 1, "A data processing unit providing levels of reasoning and environmental interaction", 4500, [],{"LVL": 5}],
        ["SBPHead1", "Head", "A", "sexbot parts", "head", 1, "A duralloy articulated head with syntheskin cover", 2000, [],{"LVL": 5}],
        ["SBPArms1", "Arms", "A", "sexbot parts", "arms", 1, "Articulated limbs for environmental interaction", 2000, [],{"LVL": 5}],
        ["SBPLegs1", "Legs", "A", "sexbot parts", "legs", 1, "Articulated limbs for locomotion and other purposes", 2000, [],{"LVL": 5}],
        ["SBPTorso1", "Torso", "A", "sexbot parts", "torso", 1, "A syntheskin covered torso unit providing all required interfaces for assembly and use", 3000, [],{"LVL": 5}],

        ["SBPCortex2", "Cortical Unit", "A", "sexbot parts", "cortex", 2, "A data processing unit providing levels of reasoning and environmental interaction", 8000, [],{"LVL": 5}],
        ["SBPHead2", "Head", "A", "sexbot parts", "head", 2, "A duralloy articulated head with syntheskin cover", 4000, [],{"LVL": 5}],
        ["SBPArms2", "Arms", "A", "sexbot parts", "arms", 2, "Articulated limbs for environmental interaction", 4000, [],{"LVL": 5}],
        ["SBPLegs2", "Legs", "A", "sexbot parts", "legs", 2, "Articulated limbs for locomotion and other purposes", 4000, [],{"LVL": 5}],
        ["SBPTorso2", "Torso", "A", "sexbot parts", "torso", 2, "A syntheskin covered torso unit providing all required interfaces for assembly and use", 6000, [],{"LVL": 5}],        

        ["SBPCortex3", "Cortical Unit", "A", "sexbot parts", "cortex", 3, "A data processing unit providing levels of reasoning and environmental interaction", 15000, [],{"LVL": 7}],
        ["SBPHead3", "Head", "A", "sexbot parts", "head", 3, "A duralloy articulated head with syntheskin cover", 8000, [],{"LVL": 7}],
        ["SBPArms3", "Arms", "A", "sexbot parts", "arms", 3, "Articulated limbs for environmental interaction", 8000, [],{"LVL": 7}],
        ["SBPLegs3", "Legs", "A", "sexbot parts", "legs", 3, "Articulated limbs for locomotion and other purposes", 8000, [],{"LVL": 7}],
        ["SBPTorso3", "Torso", "A", "sexbot parts", "torso", 3, "A syntheskin covered torso unit providing all required interfaces for assembly and use", 12000, [],{"LVL": 7}],        

        ["SBPCortexPre", "Precursor Cortex", "A", "sexbot parts", "cortex", 3, "A data processing unit assembled by the precursors. Providing superiopr levels of reasoning and environmental interaction", 25000, ["no_find"],{"LVL": 9}],

        ["SBPCubePersonality", "Personality Cube", "A", "sexbot parts", "cube", 3, "A data cube allowing for precise modification of precursor cortex personality properties.", 10000, ["no_find"],{"LVL": 9}],

        ["SBPCubeAttitude", "Attitude Cube", "A", "sexbot parts", "cube", 3, "A data cube allowing for precise modification of precursor cortex attitude properties.", 10000, ["no_find"],{"LVL": 9}],

        ["SBPCubeTraits", "Traits Cube", "A", "sexbot parts", "cube", 3, "A data cube allowing for precise modification of precursor cortex trait properties.", 10000, ["no_find"],{"LVL": 9}],
     

        # New Gen1 Shells
        ["SBPShell1TypeJ", "Type 'J' Shell", "A", "sexbot addons", "shell", 1, "A personalized bot shell kit for visual customization.", 15000, ["no_find"],{"LVL": 5}],
        ["SBPShell1TypeV", "Type 'V' Shell", "A", "sexbot addons", "shell", 1, "A Voronian bot shell kit for visual customization.", 15000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
        ["SBPShell1TypeL", "Type 'L' Shell", "A", "sexbot addons", "shell", 1, "A Supermodel shell kit for visual customization.", 20000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
        ["SBPShell1TypeN", "Type 'N' Shell", "A", "sexbot addons", "shell", 1, "A Ni'Larian Temptress shell for visual customization.", 17000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
        ["SBPShell1TypeK", "Type 'K' Shell", "A", "sexbot addons", "shell", 1, "A Krell Palace Mistress shell kit for visual bot customization.", 17000, ["no_find"],{"LVL": 5, "personality": "palace mistress"}],
        ["SBPShell1TypeC", "Type 'C' Shell", "A", "sexbot addons", "shell", 1, "A Krell Concubine shell kit for visual bot customization.", 12000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}],
        ["SBPShell1TypeI", "Infiltrator Shell", "A", "sexbot addons", "shell", 1, "A militarized bot shell kit with unique properties. Use at your own risk.", 40000, ["no_find", "no_sell"],{"LVL": 9}],        
        ["SBPShell1TypeP", "Protector", "A", "sexbot addons", "shell", 1, "A specialized bot shell kit with unique properties. Use at your own risk.", 20000, ["no_find"],{"LVL": 9}],  
        ["SBPShell1TypeG", "Golden Seductress", "A", "sexbot addons", "shell", 1, "A specialized bot shell kit with unique properties. Use at your own risk.", 22000, ["no_find"],{"LVL": 9}],  

        # New Gen 2 Shells
        ["SBPShell2TypeJ", "Type 'J' Shell", "A", "sexbot addons", "shell", 2, "A personalized bot shell kit for visual customization.", 25000, ["no_find"],{"LVL": 5}],
        ["SBPShell2TypeV", "Type 'V' Shell", "A", "sexbot addons", "shell", 2, "A Voronian bot shell kit for visual customization.", 30000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
        ["SBPShell2TypeL", "Type 'L' Shell", "A", "sexbot addons", "shell", 2, "A Supermodel shell kit for visual customization.", 40000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
        ["SBPShell2TypeN", "Type 'N' Shell", "A", "sexbot addons", "shell", 2, "A Ni'Larian Temptress shell for visual customization.", 35000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
        ["SBPShell2TypeK", "Type 'K' Shell", "A", "sexbot addons", "shell", 2, "A Krell Palace Mistress shell kit for visual bot customization.", 35000, ["no_find"],{"LVL": 5, "personality": "palace mistress"}],
        ["SBPShell2TypeC", "Type 'C' Shell", "A", "sexbot addons", "shell", 2, "A Krell Concubine shell kit for visual bot customization.", 25000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}],
        ["SBPShell2TypeI", "Infiltrator Shell", "A", "sexbot addons", "shell", 2, "A militarized bot shell kit with unique properties. Use at your own risk.", 60000, ["no_find", "no_sell"],{"LVL": 9}],  
        ["SBPShell2TypeP", "Protector", "A", "sexbot addons", "shell", 2, "A specialized bot shell kit with unique properties. Use at your own risk.", 40000, ["no_find"],{"LVL": 9}],  
        ["SBPShell2TypeG", "Golden Seductress", "A", "sexbot addons", "shell", 2, "A specialized bot shell kit with unique properties. Use at your own risk.", 44000, ["no_find"],{"LVL": 9}],  

        # New Gen3 Shells
        ["SBPShell3TypeJ", "Type 'J' Shell", "A", "sexbot addons", "shell", 3, "A personalized bot shell kit for visual customization.", 55000, ["no_find"],{"LVL": 5}],
        ["SBPShell3TypeV", "Type 'V' Shell", "A", "sexbot addons", "shell", 3, "A Voronian bot shell kit for visual customization.", 50000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
        ["SBPShell3TypeL", "Type 'L' Shell", "A", "sexbot addons", "shell", 3, "A Supermodel shell kit for visual customization.", 70000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
        ["SBPShell3TypeN", "Type 'N' Shell", "A", "sexbot addons", "shell", 3, "A Ni'Larian Temptress shell for visual customization.", 60000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
        ["SBPShell3TypeK", "Type 'K' Shell", "A", "sexbot addons", "shell", 3, "A Krell Palace Mistress shell kit for visual bot customization.", 60000, ["no_find"],{"LVL": 5, "personality": "palace mistress"}],
        ["SBPShell3TypeC", "Type 'C' Shell", "A", "sexbot addons", "shell", 3, "A Krell Concubine shell kit for visual bot customization.", 50000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}],
        ["SBPShell3TypeI", "Infiltrator Shell", "A", "sexbot addons", "shell", 3, "A militarized bot shell kit with unique properties. Use at your own risk.", 80000, ["no_find", "no_sell"],{"LVL": 9}],  
        ["SBPShell3TypeP", "Protector", "A", "sexbot addons", "shell", 3, "A specialized bot shell kit with unique properties. Use at your own risk.", 70000, ["no_find"],{"LVL": 9}],  
        ["SBPShell3TypeG", "Golden Seductress", "A", "sexbot addons", "shell", 3, "A specialized bot shell kit with unique properties. Use at your own risk.", 75000, ["no_find"],{"LVL": 9}],  
        ["SBPShell3TypeX", "Galaxina Prototype", "A", "sexbot addons", "shell", 3, "A specialized bot shell kit with unique properties. Use at your own risk.", 80000, ["no_find"],{"LVL": 9}],  

        # Gen 1
        ["SBPAddOnIQ", "IQ Booster", "A", "sexbot addons", "addon", 1, "A positron module, which enhances cognitive abilities", 3000, [],{"LVL": 5}],
        ["SBPAddOnImmo", "Immobilizer", "A", "sexbot addons", "addon", 1, "A module, which effectively suppresses escape tendencies", 1500, [],{"LVL": 8}],            
        ["SBPAddOnSkillANA", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's anal talents", 3000, [],{"LVL": 5, "skill": "ANA"}],
        ["SBPAddOnSkillORA", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's oral talents", 3000, [],{"LVL": 5, "skill": "ORA"}],
        ["SBPAddOnSkillVAG", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's vaginal talents", 3000, [],{"LVL": 5, "skill": "VAG"}],          
        ["SBPAddOnSkillBON", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's bondage talents", 3000, [],{"LVL": 5, "skill": "BON"}],              
        ["SBPAddOnSkillSPE", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's special talents", 3000, [],{"LVL": 5, "skill": "SPE"}],   
        ["SBPAddOnPER", "Perversion Amp", "A", "sexbot addons", "addon", 1, "A module, which improves all of a bot's talents", 3000, [],{"LVL": 5}], # CR-0543

        # Gen 2
        ["SBPAddOnIQ2", "IQ Booster", "A", "sexbot addons", "addon", 2, "A positron module, which enhances cognitive abilities", 4500, [],{"LVL": 5}],
        ["SBPAddOnImmo2", "Immobilizer", "A", "sexbot addons", "addon", 2, "A module, which effectively suppresses escape tendencies", 3000, [],{"LVL": 8}],            
        ["SBPAddOnSkillANA2", "Talent Booster", "A", "sexbot addons", "addon", 2, "A module, which improves a bot's anal talents", 4500, [],{"LVL": 5, "skill": "ANA"}],
        ["SBPAddOnSkillORA2", "Talent Booster", "A", "sexbot addons", "addon", 2, "A module, which improves a bot's oral talents", 4500, [],{"LVL": 5, "skill": "ORA"}],          
        ["SBPAddOnSkillVAG2", "Talent Booster", "A", "sexbot addons", "addon", 2, "A module, which improves a bot's vaginal talents", 4500, [],{"LVL": 5, "skill": "VAG"}],          
        ["SBPAddOnSkillBON2", "Talent Booster", "A", "sexbot addons", "addon", 2, "A module, which improves a bot's bondage talents", 4500, [],{"LVL": 5, "skill": "BON"}],              
        ["SBPAddOnSkillSPE2", "Talent Booster", "A", "sexbot addons", "addon", 2, "A module, which improves a bot's special talents", 4500, [],{"LVL": 5, "skill": "SPE"}],   
        ["SBPAddOnPER2", "Perversion Amp", "A", "sexbot addons", "addon", 2 , "A module, which improves all of a bot's talents", 4500, [],{"LVL": 5}], # CR-0543

        # Gen 3
        ["SBPAddOnIQ3", "IQ Booster", "A", "sexbot addons", "addon", 3, "A positron module, which enhances cognitive abilities", 10000, [],{"LVL": 8}],
        ["SBPAddOnImmo3", "Immobilizer", "A", "sexbot addons", "addon", 3, "A module, which effectively suppresses escape tendencies", 10000, [],{"LVL": 8}],            
        ["SBPAddOnSkillANA3", "Talent Booster", "A", "sexbot addons", "addon", 3, "A module, which improves a bot's anal talents", 10000, [],{"LVL": 8, "skill": "ANA"}],
        ["SBPAddOnSkillORA3", "Talent Booster", "A", "sexbot addons", "addon", 3, "A module, which improves a bot's oral talents", 10000, [],{"LVL": 8, "skill": "ORA"}],          
        ["SBPAddOnSkillVAG3", "Talent Booster", "A", "sexbot addons", "addon", 3, "A module, which improves a bot's vaginal talents", 10000, [],{"LVL": 8, "skill": "VAG"}],          
        ["SBPAddOnSkillBON3", "Talent Booster", "A", "sexbot addons", "addon", 3, "A module, which improves a bot's bondage talents", 10000, [],{"LVL": 8, "skill": "BON"}],              
        ["SBPAddOnSkillSPE3", "Talent Booster", "A", "sexbot addons", "addon", 3, "A module, which improves a bot's special talents", 10000, [],{"LVL": 8, "skill": "SPE"}],   
        ["SBPAddOnPER3", "Perversion Amp", "A", "sexbot addons", "addon", 3 , "A module, which improves all of a bot's talents", 10000, [],{"LVL": 8}], # CR-0543


        ]
        GAME.sbPartsData = {}
        for i in partsdata:
            GAME.sbPartsData[i[0]] = SexbotPart(i[0],i[1],i[2],i[3],i[4], i[5], i[6], i[7], i[8], i[9])
    return()
