#####################################################################
#                SEXBOT LAB
#####################################################################
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 bot_smart = ConditionSwitch(
    "True", "03_minigames/mgsb_doll.png"
)
image bot_arms_smart = ConditionSwitch(
    "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')", "03_minigames/mgsb_legs.png",
    "True", "05_gui/px_1x1_empty.png")
    
image bot_head_smart = ConditionSwitch(
    "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')", "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",
    "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

#####################################################################
#					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.parts = []
            self.orders = [] # tuples with [description, cond1, ..., condn]
            self.current = Sexbot() # empty bot holder

        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 retrieveBot(self, bot):
            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):
            if not bot.hasComp("cortex"): # empty shell
                return()
            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 findOrder(self,bot): # returns first fulfilled order found 
            for i in self.orders:
                if all([eval(x) for x in i[1:]]): 
                    return(i)
            return(False)

        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(5)]
            self.attitude = "rebellious"
            self.tags = []
            self.tally = {}
            self.IQ = 100
            self.price = 500
            self.autoTrain = False
            self.partNames = ["cortex", "head", "torso", "arms", "legs"]

        def rollNew(self, cortex, attr={}):
            # sexbot name 
            self.name = random.choice(GAME.sbData["names"])

            # sexbot mandatory parts 
            self.parts["cortex"] = cortex
            self.IQ = int (capValue(random.gauss(120,20),80,200))

            #rebelliousness 
            self.attitude = random.choice(GAME.sbData["attitudes"])
             
            # sexbot talents (defined by torso, augmented by parts) 
            buff = {1: 0, 2:20, 3:40}[self.parts["cortex"].cat]                 
            for k in ["ANA", "ORA", "VAG", "BON", "SPE"]:
                self.talents[k] = random.randint(buff,100)

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

            # sexbot traits 
            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])])

            # sexbot likes / dislikes
            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")
            return()

        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 getIQRating(self):
            if self.hasComp("cortex") == False:
                return("n/a")
            rating = "Of limited intelligence"
            if self.IQ >= 180:
                rating = "Dangerously intelligent"
            if self.IQ >= 160:
                rating = "Highly intelligent"
            if self.IQ >= 140:
                rating = "Very intelligent"
            if self.IQ >= 120:
                rating = "Quite intelligent"
            if self.IQ >= 100:
                rating = "Of average intelligence"
            return(rating)

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

        def getShortDesc(self):
            desc = ""
            desc += "Gen {} | IQ {} | {} | Risk {:.1f}%\n{}".format(self.parts["cortex"].cat, self.getIQ(), self.attitude.title(), self.getEscapeProb()*100, " | ".join(self.traits).title())
            return(desc)

        def assessUniformity(self):
            tally = []
            for k in self.parts.keys():
                if self.parts[k] not in [None, False]:
                    tally.append(self.parts[k].cat) 
            best = 0
            for i in set(tally):
                if tally.count(i) > best:
                    best = tally.count(i)
            return(best)

        def uninstallAddOn(self,slot):
            GAME.lab.parts.append(self.addOns[slot])
            self.addOns[slot] = False
            # pare skills down 
            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"):
            effect = 5
            if GAME.sbData["skillDict"][skill] in self.likes:
                effect +=3
            elif GAME.sbData["skillDict"][skill] in self.dislikes:
                effect -=3
                
            self.skills[skill] = min(self.skills[skill]+effect, self.getTalents()[skill], 100)
            if mode != "auto":
                GAME.lab.status["energy"] = max(0, GAME.lab.status["energy"]-10)
            return()

        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 getIQ(self):
            bonus = 0
            for i in [x for x in self.addOns if x]:
                bonus += i.getIQBoost()
            return(self.IQ+bonus)

        def getPrice(self):
            # sum up base prices incl. wear condition
            price = self.price
            for k in self.parts.keys():
                if self.parts[k] not in [None, False]:
                    price += self.parts[k].getPrice()

            # uniformity bonus 
            uBonus = 1.0
            if float(self.assessUniformity()) == 5:
                uBonus = 1.3
            price = price * uBonus 

            # skill bonus 
            sBonus = 0.5 + sum(self.skills.values())/250.0
            price = price * sBonus 

            # talent bonus 
            tBonus = 1.0 + 0.4* sum(1 for x in self.talents.values() if x >= 90)
            price = price * tBonus 

            # IQ bonus
            iBonus = 1.0 + ((self.getIQ()/100.0)-1.0)
            price = price * iBonus # * iBonus
            #self.tags.append("iBonus: {:2f}".format(iBonus))
            for i in self.tags:
                if "iBonus" in i:
                    self.tags.remove(i)

            # like/dislike effect 
            for k in self.talents.keys():
                if GAME.sbData["skillDict"][k].lower() in self.likes:
                    price *= 1.05 
                elif GAME.sbData["skillDict"][k].lower() in self.dislikes:
                    price *= 0.95

            # special combos 
            # ++ flexible + submissive 
            if all([x in self.traits for x in ["flexible", "submissive"]]):
                price = price * (1.0 + 2.0 * self.skills["BON"]/500.0)
            # ++ perverted + aggressive 
            if all([x in self.traits for x in ["perverted", "aggressive"]]):
                price = price * (1.0 + 2.0 * self.skills["ANA"]/500.0)
            # ++ curious + nympho 
            if all([x in self.traits for x in ["curious", "nympho"]]):
                price = price * (1.0 + 2.0 * self.skills["SPE"]/500.0)
            # - frigid 
            if all([x in self.traits for x in ["frigid"]]):
                price = price * 0.8
            # - chaste 
            if all([x in self.traits for x in ["chaste"]]):
                price = price * 0.8
            # - conservative
            if all([x in self.traits for x in ["conservative"]]):
                price = price * 0.8
            # - inflexible
            if all([x in self.traits for x in ["inflexible"]]):
                self.talents["BON"] = min(self.talents["BON"], 50)
                self.skills["BON"] = min(self.talents["BON"], self.skills["BON"])
                price = price * 0.8


            return(int(price))

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

        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"] == "SBPAddOnImmo":
                        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])

        def getEscapeProb(self):
            prob = 0.0
            if self.hasAddOn("SBPAddOnImmo") or not self.isComplete():
                return(0.0)
            if self.getIQ() < 110 or self.attitude not in ["rebellious", "devious", "indifferent"]:
                return(0.0)
            prob = (self.getIQ()-110.0)/(200.0-110.0)*0.1
            if self.attitude == "devious":
                prob += 0.05
            if self.attitude == "rebellious":
                prob += 0.03
            prob = capValue(prob,0,0.15)
            return(prob)

    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 
            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 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):
            prefVal = {"subpar": 0.5, "basic": 1.0, "advanced": 1.5, "military grade": 3.0}
            price = self.price * self.cat * (1.1-self.wear) * prefVal[self.pref] 
            return(int(price))

        def getName(self, para=[]):
            name = "{} - Gen {}, {}".format(self.name, self.cat, self.pref.title())
            return(name)

        def getTalentBoost(self, talent):
            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):
            result = []
            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 clone(self):
            # 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
            #clone.cat = random.choice([1,1,1,1,2,2]) 
            clone.pref = random.choice(["subpar"]*8+["basic"]*4+["advanced"]+["military grade"])

            # some random settings
            prefVal = {"subpar": 0.5, "basic": 1.0, "advanced": 1.5, "military grade": 3.0} 
            if kind == "sexbot parts":
                clone.wear = random.uniform(0.0,1.0) 
            elif self.ID == "SBPAddOnIQ":
                clone.attr["IQ"] = int(10.0*(prefVal[clone.pref] + random.uniform(-0.3,+0.3)))
            elif "SBPAddOnSkill" in self.ID:
                skill = self.ID[-3:]
                clone.attr[skill] = int(10.0*(prefVal[clone.pref] + random.uniform(-0.3,+0.3)))
            elif self.ID == "SBPAddOnPER":
                clone.attr["ALL"] = int(10.0*(prefVal[clone.pref] + random.uniform(-0.3,+0.3)))
            elif self.ID == "SBPAddOnImmo":
                clone.attr["ESC"] = int(1.0*(prefVal[clone.pref] + random.uniform(-0.3,+0.3)))

            return(clone)

        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
        #####################################################################
        GAME.sbData = {
            "names": ["Cloud", "Hollow", "M4GAN", "Umbra", "Ycari", "Azure", "Doria", "Andromeda", "Oyza", "Leeloo", "Jen", "Cybel", "Clade", "Aurora", "Nereid", "Anoka", "Phantasma", "Quicksilver", "Amaxia", "Enigma", "Elanthe", "Kheri", "Lucy", "Sibel", "Sparkle", "Pixie", "Dilara",
            "Elektra", "Neutronia", "Cortina", "Syrix", "Eris", "Xena", "Apollonia",
            "Indra", "Mercury", "Titania", "Xenia", "Aura", "Argenta", "Celia"],
            "traits": [ ["frigid", "nympho"], ["submissive", "aggressive"], ["flexible", "inflexible"], ["perverted", "wholesome"], ["conservative", "curious"]],
            "likes": ["anal", "oral", "vaginal", "bondage", "special"],
            "dislikes": ["anal", "oral", "vaginal", "bondage", "special"],
            "skillDict": {"ANA": "anal", "ORA": "oral", "VAG": "vaginal", "BON": "bondage", "SPE": "special"},
            "attitudes": ["compliant", "indifferent", "rebellious", "devious"]
        }

        #####################################################################
        #                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", 1000, [],{"LVL": 5}],
        ["SBPArms1", "Arms", "A", "sexbot parts", "arms", 1, "Articulated limbs for environmental interaction", 300, [],{"LVL": 5}],
        ["SBPLegs1", "Legs", "A", "sexbot parts", "legs", 1, "Articulated limbs for locomotion and other purposes", 500, [],{"LVL": 5}],
        ["SBPTorso1", "Torso", "A", "sexbot parts", "torso", 1, "A syntheskin covered torso unit providing all required interfaces for assembly and use", 2000, [],{"LVL": 5}],

        ["SBPCortex2", "Cortical Unit", "A", "sexbot parts", "cortex", 2, "A data processing unit providing levels of reasoning and environmental interaction", 6000, [],{"LVL": 5}],
        ["SBPHead2", "Head", "A", "sexbot parts", "head", 2, "A duralloy articulated head with syntheskin cover", 1500, [],{"LVL": 5}],
        ["SBPArms2", "Arms", "A", "sexbot parts", "arms", 2, "Articulated limbs for environmental interaction", 500, [],{"LVL": 5}],
        ["SBPLegs2", "Legs", "A", "sexbot parts", "legs", 2, "Articulated limbs for locomotion and other purposes", 800, [],{"LVL": 5}],
        ["SBPTorso2", "Torso", "A", "sexbot parts", "torso", 2, "A syntheskin covered torso unit providing all required interfaces for assembly and use", 3000, [],{"LVL": 5}],        
    
        ["SBPAddOnIQ", "IQ Booster", "A", "sexbot addons", "addon", 1, "A positron module, which enhances cognitive abilities", 1000, [],{"LVL": 5}],
        ["SBPAddOnImmo", "Immobilizer", "A", "sexbot addons", "addon", 1, "A module, which effectively suppresses escape tendencies", 5000, [],{"LVL": 8}],            
        ["SBPAddOnSkillANA", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's anal talents", 1000, [],{"LVL": 5}],          ["SBPAddOnSkillORA", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's oral talents", 1000, [],{"LVL": 5}],          ["SBPAddOnSkillVAG", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's vaginal talents", 1000, [],{"LVL": 5}],          
        ["SBPAddOnSkillBON", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's bondage talents", 1000, [],{"LVL": 5}],              
        ["SBPAddOnSkillSPE", "Talent Booster", "A", "sexbot addons", "addon", 1, "A module, which improves a bot's special talents", 1000, [],{"LVL": 5}],   
        ["SBPAddOnPER", "Perversion Amp", "A", "sexbot addons", "addon", 1, "A module, which improves all of a bot's talents", 5000, [],{"LVL": 5}]               
        ]
        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()

#####################################################################
#					SUPPORT ROUTINES												  #
#####################################################################

screen mg_sexbot_orders_scr(*args):

    text C_HI_B+"BOT ORDER LIST"+C_HI_E size FSIZE_LARGE pos 960, 80 xalign 0.5

    side "c r":
        area 440,240,1420,684
        viewport id "vp":
            draggable True
            mousewheel True
            
            vbox pos 500,120 xsize 1300 spacing 5:
                $ n = 0
                for i in GAME.lab.orders:
                    frame xsize 1300 ysize 60 background Solid("#0099cc88"):
                        $ n+= 1
                        text "{}".format(n) size FSIZE yalign 0.5 xpos 40
                        text i[0] size FSIZE yalign 0.5 xpos 80

        vbar value YScrollValue("vp")
    image "03_minigames/ico bg1.png" pos 40, 400 zoom 0.5
    image "03_minigames/ico_BotShip.png" pos 40, 400 zoom 0.5

    

    #####################################################################
    #                EXIT BUTTON
    #####################################################################
    frame xsize 350 ysize 64 pos 0,900 background Solid("#00000000") padding 0,0:
        image "05_gui/gui_menu_option.png" yalign 0.5
        textbutton "Exit (X)" yalign 0.5 action Return(False)xpos 20
        button action Return(False)xpos 20
        key "x" action Return (False)
        key "X" action Return (False) 

label mg_sexbot_orders():
    show image "room_lab_smart" 
    call screen mg_sexbot_orders_scr()   
    hide image "room_lab_smart" 
    return()           
  
label bot_roll_order():
    $ orders = [
        ["A bot with all components being Generation 1 parts", 
        "all([x.cat == 1 for x in bot.parts.values()])",
        "bot.isComplete()"],
        ["A bot with all components being Generation 2 parts", 
        "all([x.cat == 2 for x in bot.parts.values()])",
        "bot.isComplete()"],
        ["A bot with all skills at least 70 points", 
        "all([x >= 70 for x in bot.skills.values()])",
        "bot.isComplete()"],
        ["A bot with at least three expert level skills (90+ points)", 
        "[x >= 70 for x in bot.skills.values()].count(True) >= 3",
        "bot.isComplete()"],
        ["A bot with IQ of 150 or higher and the 'Perversion' trait", 
        "'perverted' in bot.traits", "bot.IQ >= 150",
        "bot.isComplete()"],
        ["A bot that likes anal and bondage", 
        "'anal' in bot.likes", "'bondage' in bot.likes",
        "bot.isComplete()"],
        ["A bot with full military grade component quality", 
        "all([x.pref == 'military grade' for x in bot.parts.values()])",
        "bot.isComplete()"],
        ["Any kind of bot configuration", 
        "bot.isComplete()"]
    ]  
    $ GAME.lab.orders += [random.choice(orders)] 
    $ GAME.lab.orders = GAME.lab.orders[:3] # max 3 in queue
    return()

label bot_roll_order_new():
    # generation 
    # IQ 
    # trait 
    # skills 
    # build order 

    return()

label mg_sexbot_sell(bot):
    queue sound "audio/notify.wav"
    $ amount = bot.getPrice()
    $ amount = int(random.uniform(0.4,0.6)*amount)
    call simple_notify("AUCTION", "You advertise {0} for sale...".format(bot.name), ["Continue"]) from _call_simple_notify_13
    queue sound "audio/success.wav"
    call large_notify("AUCTION", "You receive a number of offers. You accept the best offer of {1:,} Cr for {0}.\n\nYou package her and dispatch her to the buyer.".format(bot.name, amount), ["Continue"]) from _call_large_notify_7
    queue sound "audio/coin.wav"
    $ GAME.money += amount 
    $ GAME.mc.tallyUp("Bot Sales (Cr)", amount)    
    if bot == GAME.lab.current:
        $ GAME.lab.clearLab()    
    else:
        $ GAME.lab.clearBot(bot)
    return()

label mg_sexbot_ship(bot):
    queue sound "audio/notify.wav"
    $ amount = bot.getPrice()

    hide screen mg_sexbot_prep_scr with fade
    call large_notify("ORDER COMPLETED", "You package {} and dispatch her to the buyer for {:,} Cr.".format(bot.name, amount), ["Continue"], "03_minigames/ico_BotShip.png") from _call_large_notify_8
    $ GAME.mc.tallyUp("Bots Shipped",1) 
    if GAME.mc.getTally("Bots Shipped") == 3:             
        call achievement_notify(GAME.mc, "Power Seller", "20_gallery/gallery 22.jpg") from _call_achievement_notify  
        $ GAME.galleryAdd(22)   
    show screen mg_sexbot_prep_scr()

    queue sound "audio/coin.wav"
    $ GAME.money += amount 
    $ GAME.mc.tallyUp("Bot Sales (Cr)", amount)
    if bot == GAME.lab.current:
        $ GAME.lab.clearLab()    
    else:
        $ GAME.lab.clearBot(bot)
    return()

label sexbot_escape():
    $ escapees = []
    python:
        for bot in GAME.lab.getCompleted():
            if random.uniform(0.01,1.0) < bot.getEscapeProb():
                escapees.append(bot)
    while escapees:
        $ GAME.ship.destroySubunit()
        $ bot = escapees.pop(0)
        scene room_lab_smart
        queue sound "audio/red alert.wav"
        call large_notify("SEXBOT ESCAPE!", "Owing to {0}'s supreme intelligence and your negligence in securing her, {0} has escaped. She has damaged critical ship components in the process.".format(C_RE_B+bot.name+C_RE_E), ["Continue"], "03_minigames/ico SBPCortex2.png", "red") from _call_large_notify_9   
        $ GAME.lab.clearBot(bot)       
    return()

label sexbot_arcade_earnings():
    $ earnings = 0 
    $ best = False
    python:
        for bot in GAME.arcade:
            
            # cleanup  bugfix:
            for i in bot.tags:
                if "iBonus" in i:
                    bot.tags.remove(i)    

            bot.earnedToday = 0
            bot.earnedToday += int(bot.getPrice() * random.uniform(0.05,0.07))
            if best:
                if best.earnedToday < bot.earnedToday:
                    best = bot
            else:
                best = bot 
            earnings += bot.earnedToday  
            bot.tallyUp("Arcade Earnings",bot.earnedToday)
            GAME.mc.tallyUp("Arcade Earnings",bot.earnedToday)
    if earnings:
        #scene expression("11_backgrounds/backdrop_planet_sunrise_02.png")        
        queue sound "audio/notify.wav"
        call large_notify("ARCADE REPORT", "Your Arcade Bots have earned a total of {:,} Cr. Your best earner was {} with {:,} Cr.".format(earnings, C_SU_B+best.name+C_SU_E, best.earnedToday), ["Continue"], "03_minigames/ico_BotArcade.png", "pink") from _call_large_notify_10
        queue sound "audio/coin.wav"
        $ GAME.money += earnings
    if GAME.mc.getTally("Arcade Earnings") > 100000 and "arcade tycoon" not in GAME.mc.achievements: 
        call achievement_notify(GAME.mc, "arcade tycoon", "20_gallery/gallery 21.jpg") from _call_achievement_notify_1        
        $ GAME.galleryAdd(21)
    return()

label sexbot_autotrainer():
    # Init 
    $ complete = []
    # execute training on auto bots 
    python:
        for i in GAME.lab.getCompleted():
            skillBefore = sum(i.skills.values()) 
            for k in i.skills.keys():
                i.train(k, "auto")
                i.train(k, "auto")
                i.train(k, "auto")
            skillAfter = sum(i.skills.values())  
            if (skillBefore < skillAfter) and (skillAfter >= sum(i.getTalents().values())):
                complete.append(i) # training finished

                
    # report on completed bots
    while complete:
        $ bot = complete.pop(0)
        play sound "audio/success.wav"
        #scene expression("11_backgrounds/backdrop_planet_sunrise_02.png")        
        call large_notify("AUTOTRAIN COMPLETE", "The auto-train unit has completed the training of {0}.\n\nShe is now ready to be sold or rented out.".format(C_OR_B+bot.name+C_OR_E), ["Continue"], "03_minigames/ico_BotAuto.png", "orange") from _call_large_notify_11 
        $ GAME.mc.tallyUp("Autotrained Bots", 1)
        if GAME.mc.getTally("Autotrained Bots") == 1: # WIP
            call achievement_notify(GAME.mc, "autotrainer", "20_gallery/gallery 23.jpg") from _call_achievement_notify_2
            $ GAME.galleryAdd(23)
    return()

#####################################################################
#					STORAGE PART / BOT SELECTION		
#####################################################################
screen mg_sexbot_part_storage_scr(*args):
    $ parts = args[0]
    text C_HI_B+"PARTS STORAGE"+C_HI_E size FSIZE_LARGE pos 960, 80 xalign 0.5

    side "c r":
        area 440,240,1420,684
        viewport id "vp":
            draggable True
            mousewheel True
            
            vbox pos 500,120 xsize 1200 spacing 5:
                for i in parts:
                    frame xsize 1200 ysize 80 background Solid("#0099cc88"):
                        image "{}{}".format(i.getPath(),i.getIco()) zoom 0.2 xpos 20 yalign 0.5
                        text i.getName() size FSIZE yalign 0.5 xpos 160
                        text "{}".format(percentColorized(i.getGrade())) size FSIZE yalign 0.5 xpos 620 
                        text "{:,} Cr".format(i.getPrice()) size FSIZE yalign 0.5 xpos 780 xalign 1.0
                        button action Return(i) tooltip i
                        frame xsize 80 ysize 30 background Solid ("#0099ccff") yalign 0.5 xpos 900:
                            button action Return(i)
                            text "Use" xalign 0.5 yalign 0.5 size FSIZE_MED
                        frame xsize 80 ysize 30 background Solid ("#cc6600ff") yalign 0.5 xpos 1000:
                            button action Return(["scrap",i])
                            text "Scrap" xalign 0.5 yalign 0.5 size FSIZE_MED

        vbar value YScrollValue("vp")



    $ tooltip = GetTooltip()
    if tooltip and tooltip in parts:
        frame padding 20,20 xsize 560 ysize 920 pos 40 , 200 background Solid("#39210000"):
            image "08_items/ico bg1.png" zoom 0.5 xpos 50 ypos 100
            image "{}/{}".format(tooltip.getPath(),tooltip.getIco()) zoom 0.5 xpos 50 ypos 100
            vbox xsize 400 ypos 420 xpos -25 spacing 20:
                text tooltip.getName() xalign 0.5 size FSIZE
                text tooltip.getDesc() xalign 0.5 size FSIZE
                if tooltip.getBoostOneLiner():
                    text tooltip.getBoostOneLiner() xalign 0.5 size FSIZE
                text "Value: {:,} Cr".format(tooltip.getPrice()) xalign 0.5 size FSIZE

    #####################################################################
    #                EXIT BUTTON
    #####################################################################
    frame xsize 350 ysize 64 pos 0,900 background Solid("#00000000") padding 0,0:
        image "05_gui/gui_menu_option.png" yalign 0.5
        textbutton "Exit (X)" yalign 0.5 action Return(False)xpos 20
        button action Return(False)xpos 20
        key "x" action Return (False)
        key "X" action Return (False)            

label mg_sexbot_part_storage(partsKey):
    show image "room_lab_smart"
    # show image  "05_gui/mask 50.png"
    $ interactive = True
    while interactive:
        if partsKey == "addonn":
            $ parts = [x for x in GAME.lab.parts if x.kind == "sexbot addon"]
        else:
            $ parts = [x for x in GAME.lab.parts if x.slot == partsKey]
        $ parts = sorted(parts, key=lambda x: x.ID, reverse = True)
        call screen mg_sexbot_part_storage_scr(parts)
        if _return == False:
            $ interactive = False

        elif _return in parts:
            # queue sound "audio/click.wav"
            $ interactive = False

        elif "scrap" in _return:
            $ part = _return[1]
            $ GAME.money += part.getPrice() 
            play sound "audio/coin.wav"
            $ GAME.lab.parts.remove(part)

    hide image "room_lab_smart"
    # hide image  "05_gui/mask 50.png"
    return(_return)

#####################################################################
#					BOT STORAGE MANAGER												  #
#####################################################################
  
screen mg_sexbot_bot_storage_scr(*args):

    text C_HI_B+"BOT STORAGE"+C_HI_E size FSIZE_LARGE pos 960, 80 xalign 0.5

    side "c r":
        area 440,240,1420,684
        viewport id "vp":
            draggable True
            mousewheel True
            
            vbox pos 500,120 xsize 1300 spacing 5:
                for i in [x for x in GAME.lab.cells if x]:
                    frame xsize 1300 ysize 80 background Solid("#0099cc88"):
                        hbox yalign 0.5 xpos 20:
                            for s in range(i.getStars()):
                                image "03_minigames/star.png"  zoom 0.5
                        text i.name size FSIZE yalign 0.5 xpos 140
                        text "{}".format({True: "Complete", False: "Unfinished"}[i.isComplete()]) size FSIZE yalign 0.5 xpos 260
                        vbox xsize 500 xpos 400 yalign 0.5:
                            text i.getShortDesc() yalign 0.5 size FSIZE
                        #text "{}".format(percentColorized(i.getGrade())) size FSIZE yalign 0.5 xpos 620 
                        text "{:,} Cr".format(i.getPrice()) size FSIZE yalign 0.5 xpos 960 xalign 1.0
                        #button action Return(i) tooltip i
                        frame xsize 80 ysize 30 background Solid ("#0099ccff") yalign 0.5 xpos 1000:
                            button action Return(["config_bot", i])
                            text "Config" xalign 0.5 yalign 0.5 size FSIZE_MED
                        # if i.isComplete():
                        #     frame xsize 80 ysize 30 background Solid ("#0099ccff") yalign 0.5 xpos 1100:
                        #         button action Return(["sell_bot", i])
                        #         text "Sell" xalign 0.5 yalign 0.5 size FSIZE_MED
                        frame xsize 80 ysize 30 background Solid ("#cc6600ff") yalign 0.5 xpos 1200:
                            button action Return(["scrap_bot",i])
                            text "Scrap" xalign 0.5 yalign 0.5 size FSIZE_MED

        vbar value YScrollValue("vp")

    #####################################################################
    #                EXIT BUTTON
    #####################################################################
    frame xsize 350 ysize 64 pos 0,900 background Solid("#00000000") padding 0,0:
        image "05_gui/gui_menu_option.png" yalign 0.5
        textbutton "Exit (X)" yalign 0.5 action Return(False)xpos 20
        button action Return(False)xpos 20
        key "x" action Return (False)
        key "X" action Return (False)            

label mg_sexbot_bot_storage():

    show image "room_lab_smart"
    # show image  "05_gui/mask 50.png"
    $ interactive = True
    while interactive:
        call screen mg_sexbot_bot_storage_scr()
        if _return == False:
            $ interactive = False

        elif "config_bot" in _return:
            $ newBot = _return[1]
            $ GAME.lab.storeBot(GAME.lab.current)
            $ GAME.lab.retrieveBot(newBot)
            queue sound "audio/load.wav"
            $ interactive = False

        elif "sell_bot" in _return:
            $ bot = _return[1]
            call mg_sexbot_sell(bot) from _call_mg_sexbot_sell

    hide image "room_lab_smart"
    # hide image  "05_gui/mask 50.png"
    return()

#####################################################################
#					CONFIGURATION SCREENS		
#####################################################################
    
screen mg_sexbot_prep_scr():
    image "03_minigames/mgsb_bg.png"
    # Bot composite image 
    $ bot = GAME.lab.current
    image "bot_doll"

    #####################################################################
    #					DATA PANEL LEFT								  #
    #####################################################################
    # Bot Data
    vbox spacing 10 pos 50, 40:
        text C_HI_B+"BOT POTENTIAL"+C_HI_E 
        image "05_gui/px_1x1_orange.png" xsize 200 ysize 3 yalign 0.5
        hbox:
            for i in range(bot.getStars()):
                image "03_minigames/star.png" 

    vbox spacing 10 pos 50, 160:
        text C_HI_B+"DESIGNATION"+C_HI_E 
        image "05_gui/px_1x1_orange.png" xsize 200 ysize 3 yalign 0.5
        if bot.hasComp("cortex"):
            text "{}".format(bot.name)
        else:
            text "None"

    vbox spacing 10 pos 50, 280:
        text C_HI_B+"VALUE"+C_HI_E 
        image "05_gui/px_1x1_orange.png" xsize 200 ysize 3 yalign 0.5
        if bot.hasComp("cortex"):
            text "{:,} Cr".format(bot.getPrice())
        else:
            text "n/a"


    # Parts Data Fixed
    
    $ pPosY = [40, 160, 280, 440, 940]
    for i in range(5):

        vbox xsize 200 spacing 10 pos 600, pPosY[i]:
            text C_HI_B+bot.partNames[i].upper()+C_HI_E xalign 1.0
            # image "05_gui/px_1x1_orange.png" xsize 200 ysize 3 yalign 0.5
            if bot.hasComp(bot.partNames[i]):
                $ part = bot.parts[bot.partNames[i]]
                frame xsize 140 ysize 40 xalign 1.0 background Solid ("#00000000"):
                    text "{}".format( percentColorized(part.getGrade()) ) xalign 1.0

    
    #####################################################################
    #					DATA PANEL RIGHT		  					  
    #####################################################################
    frame xsize 1080 ysize 1080 background Solid ("ffffff00") xalign 1.0: 
        text C_HI_B+"BOT CONFIGURATION & TRAINING"+C_HI_E xalign 0.5 ypos 20 size FSIZE_LARGE
        image "05_gui/px_1x1_orange.png" xsize 800 ysize 3 xalign 0.5 ypos 80

        # Lab charging status 
        vbox xpos 700 ypos 140 xsize 300 spacing 10:
            text C_HI_B+"LAB CHARGE STATUS"+C_HI_E
            for k in ["Bio","Energy", "Nano"]:
                frame xsize 340 ysize 50 background Solid("#ffffff22"):
                    text "{}".format(k) size FSIZE yalign 0.5 xpos 20
                    text "{}".format(percentColorized(GAME.lab.status[k.lower()])) size FSIZE yalign 0.5 xpos 160 xalign 1.0
                    $ item = GAME.items["ITMPod{}".format(k)]
                    $ pods = GAME.ship.cargo.count(item)
                    if pods > 0:
                        frame xsize 120 ysize 30 xpos 200  background Solid("#0099ccff") yalign 0.5:
                            text "Cargo {}x".format(pods) xalign 0.5 yalign 0.5 size FSIZE_MED
                            button action Return(["charge", item.ID])


        # Information     
        vbox xpos 80 ypos 140 spacing 20:
            text C_HI_B+"INFORMATION"+C_HI_E 
            if bot.hasComp("cortex"):
                $ colRange = ["green", "yellow", "orange", "red"]
                text "IQ {} | {} | Escape Risk (%): {}".format(
                    bot.getIQ(), 
                    kwTint(bot.attitude.title()), 
                    colMap (round(bot.getEscapeProb()*100,1), colRange, [-5, 15])
                    )
                text  "[C_HI_B]TRAITS:[C_HI_E] {}".format(" | ".join(
                    [ kwTint(x.title()) for x in bot.traits])) 

        # ADD-Ons
        text C_HI_B+"ADD-ON MODULES"+C_HI_E xpos 80 ypos 340
        hbox xpos 80 ypos 402 spacing 20:
            if bot.hasComp("cortex"):
                for i in range(5):
                    frame xsize 96 ysize 96:
                        if bot.addOns[i]:
                            image "{}{}".format(bot.addOns[i].getPath(), bot.addOns[i].getIco()) zoom 0.375 xalign 0.5 yalign 0.5 
                            button action Return(["module", i]) tooltip bot.addOns[i]
                        else:
                            button action Return(["module", i])
                
        # Training
        vbox xpos 80 ypos 600 spacing 20:
            text C_HI_B+"SKILLS"+C_HI_E 
            if bot.hasComp("cortex"):
                for k in sorted(GAME.sbData["skillDict"].keys()):
                    frame xsize 600 ysize 40 background Solid("#ffffff00"):
                        if GAME.sbData["skillDict"][k].lower() in bot.dislikes:
                            image "03_minigames/gui_mini_dislike.png" xpos -64 ypos -10
                        elif GAME.sbData["skillDict"][k].lower() in bot.likes:
                            image "03_minigames/gui_mini_heart.png" xpos -64 ypos -10
                        text GAME.sbData["skillDict"][k].title() yalign 0.5 
                        if bot.skills[k] >= bot.getTalents()[k]:
                            text "{}".format(bot.skills[k]) xpos 280 xalign 0.5
                            if bot.skills[k] >= 90:
                                text C_SU_B+"{} Expert".format(GAME.sbData["skillDict"][k].title())+C_SU_E xpos 400
                        else:
                            text "{}".format(0) xpos 140 yalign 0.5
                            bar xsize 200 ysize 28 yalign 0.5 xpos 180 value bot.skills[k] range 100
                            text "{}".format(100) xpos 400  yalign 0.5
                            # frame xsize 100 ysize 40 xpos 520 background Solid("#0099cc") yalign 0.5:
                            #     text "Train" xalign 0.5 yalign 0.5 size FSIZE 
                            #     button action Return(["train", k])
        
        # Autotrain Frame 
        if bot.isComplete():
            frame xsize 328 ysize 248 pos 700, 665:
                image "03_minigames/mgsb_thumb_autotrain.png" xalign 0.5 yalign 0.5 zoom 0.5
                if bot.autoTrain:
                    text "ACTIVE" xalign 0.5 yalign 0.1 size FSIZE 
                    image "05_gui/px_1x1_green.png" xsize 40 ysize 40 
                else:
                    text "INACTIVE" xalign 0.5 yalign 0.1 size FSIZE 
         
screen mg_sexbot_scr():
    $ bot = GAME.lab.current

    #####################################################################
    #                EXIT BUTTON
    #####################################################################
    frame xsize 350 ysize 64 pos 0,960 background Solid("#00000000") padding 0,0:
        image "05_gui/gui_menu_option.png" yalign 0.5
        textbutton "Exit (X)" yalign 0.5 action Return(False)xpos 20
        button action Return(False)xpos 20
        key "x" action Return (False)
        key "X" action Return (False)     

    #####################################################################
    #					INTERACTIVE											  #
    #####################################################################
    # bot cells
    vbox spacing 10 pos 50, 420:
        text C_HI_B+"BOT CELLS"+C_HI_E 
        image "05_gui/px_1x1_orange.png" xsize 200 ysize 3 yalign 0.5
        grid 5 GAME.lab.cellsMax/5 spacing 5:
            for i in range(GAME.lab.cellsMax):
                if GAME.lab.cells[i]:
                    frame xsize 25 ysize 25 background Solid("#0099ccff"):
                        button action Return(["bot", GAME.lab.cells[i] ]) tooltip GAME.lab.cells[i]
                        if GAME.lab.cells[i].autoTrain:
                            image "/05_gui/px_1x1_green.png" xsize 8 ysize 8 pos -4, -4
                else:
                    frame xsize 25 ysize 25 background Solid("#00000055")
        frame xsize 40 ysize 40 background Solid ("#00000000"):
            image "03_minigames/gui_mini_list.png" xalign 0.5 yalign 0.5
            button action Return(["list", None])
        frame xsize 40 ysize 40 background Solid ("#00000000"):
            image "03_minigames/gui_mini_list.png" xalign 0.5 yalign 0.5
            button action Return(["orders", None])




    # Bot management buttons
    if bot.isComplete():
        $ options = ["store", "sex arcade"]
        if GAME.lab.findOrder(bot):
            $ options += ["fulfill"]
        vbox spacing 20 yalign 1.0 ypos 840 xpos 40:
            for i in options:
                frame xsize 160 ysize 50 background Solid("#0099ccff"):
                    button action Return(["process", i]) 
                    text i.title() size FSIZE xalign 0.5 yalign 0.5
    elif bot.hasComp("cortex"):
        $ options = ["store"]
        vbox spacing 20 yalign 1.0 ypos 840 xpos 40:
            for i in options:
                frame xsize 160 ysize 50 background Solid("#0099ccff"):
                    button action Return(["process", i]) 
                    text i.title() size FSIZE xalign 0.5 yalign 0.5

    #####################################################################
    #					DATA PANEL LEFT								  #
    #####################################################################
    # Change Name 
    if bot.hasComp("cortex"):
        frame xsize 32 ysize 32 pos 220, 156 background Solid ("#00000000"):
            image "03_minigames/gui_mini_pen.png" xalign 0.5 yalign 0.5
            button action Return(["rename", None])

    # Config screen /  Buttons and Active Elements 
    $ pPosY = [40, 160, 280, 440, 940]
    for i in range(5):
        if bot.hasComp(bot.partNames[i]):
            $ part = bot.parts[bot.partNames[i]]
            frame xsize 96 ysize 96 pos 600, pPosY[i]:
                image "{}{}".format(part.getPath(), part.getIco()) zoom 0.25 xalign 0.5 yalign 0.5
                button action Return(["remove", bot.partNames[i]]) tooltip part 

        elif bot.partNames[i] != "cortex" and bot.hasComp("cortex"):
            frame xsize 96 ysize 96 pos 600, pPosY[i]:
                button action Return(["add", bot.partNames[i]]) 
        elif bot.partNames[i] == "cortex":
            frame xsize 96 ysize 96 pos 600, pPosY[i]:
                button action Return(["add", bot.partNames[i]]) 

        vbox xsize 200 spacing 10 pos 600, pPosY[i]:
            text " "
            #image "05_gui/px_1x1_orange.png" xsize 200 ysize 3 yalign 0.5
            if bot.hasComp(bot.partNames[i]):
                frame xsize 140 ysize 40 xalign 1.0 background Solid ("#00000000"):
                    # text "{:.0f}%".format(bot.parts[bot.partNames[i]].getGrade()) xalign 1.0
                    if bot.parts[bot.partNames[i]].getGrade() < 100:
                        frame xsize 40 ysize 40 background Solid ("#00000000") ypos -5 xpos 140:
                            image "03_minigames/gui_mini_wrench.png" xalign 0.5 yalign 0.5
                            button action Return(["repair", bot.partNames[i]])
            else:
                text "n/a" xalign 1.0

    #####################################################################
    #					TRAINING BUTTONS				  
    #####################################################################
    # Training
    vbox xpos 80 ypos 600 spacing 20:
        text  " "
        if bot.isComplete() and not bot.autoTrain:
            for k in sorted(GAME.sbData["skillDict"].keys()):
                frame xsize 600 ysize 40 background Solid("#ffffff00"):
                    if bot.skills[k] < bot.getTalents()[k]:
                        frame xsize 100 ysize 40 xpos 1320 background Solid("#0099cc") yalign 0.5:
                            text "Train" xalign 0.5 yalign 0.5 size FSIZE 
                            button action Return(["train", k]) 
    # Autotrain Button
    if bot.isComplete():
        frame xsize 140 ysize 40 xpos 1630 background Solid("#0099cc") ypos 850:
            text "Auto-Train" xalign 0.5 yalign 0.5 size FSIZE 
            button action Return(["autotrain", k])


    #####################################################################
    #					TOOLTIPS						  
    #####################################################################
    $ tooltip = GetTooltip()
    if tooltip:
        # bot? 
        if tooltip in GAME.lab.cells +[GAME.lab.current]:
            vbox  pos 1300,960 xalign 0.5 spacing 10:
                bar xsize 300 ysize 2 xalign 0.5
                hbox spacing 10 xalign 0.5:
                    hbox:
                        for i in range(tooltip.getStars()):
                            image "03_minigames/star.png" zoom 0.5 yalign 0.5
                    text "{} -".format(tooltip.name)xalign 0.5 size FSIZE
                    text "Gen {} -".format(tooltip.parts["cortex"].cat)xalign 0.5 size FSIZE
                    text "{:,} Cr".format(tooltip.getPrice()) xalign 0.5 size FSIZE
                bar xsize 300 ysize 2 xalign 0.5

 
            
        # part?
        else:
            vbox  pos 1300,960 xalign 0.5 spacing 10:
                bar xsize 300 ysize 2 xalign 0.5
                if tooltip.getBoostOneLiner():
                    text "{} - {}".format(tooltip.getName(), tooltip.getBoostOneLiner()) xalign 0.5 size FSIZE
                else:
                    text "{}".format(tooltip.getName()) xalign 0.5 size FSIZE
                bar xsize 300 ysize 2 xalign 0.5

#####################################################################
#					MAIN LAB MODULE	
#####################################################################

label mg_sexbot_lab():

    #scene room_lab_smart with fade
    play music "audio/ambient_dark.wav" volume 0.2
    show screen mg_sexbot_prep_scr() with dissolve
    # Lab Loop
    while True:
        $ bot = GAME.lab.current
        call screen mg_sexbot_scr()
        #queue sound "audio/click.wav"

        if _return == False:
            if GAME.lab.current.hasComp("cortex"):
                queue sound "audio/beep.wav"
                call simple_notify("LAB STATUS", "Put {} back into storage first.".format(C_HI_B+GAME.lab.current.name+C_HI_E), ["Continue"]) from _call_simple_notify_14
            else:
                stop music fadeout 1.0 
                hide screen mg_sexbot_prep_scr with fade 
                return()
        elif "rename" in _return:
            $ n = renpy.input("Rename {}".format(GAME.lab.current.name))
            $ n = n.strip() 
            $ n = n[:16]
            $ GAME.lab.current.name = n.title()

        elif "add" in _return and _return[1] == "cortex":
            queue sound "audio/click.wav"
            # get core 
            hide screen mg_sexbot_prep_scr
            call mg_sexbot_part_storage("cortex") from _call_mg_sexbot_part_storage
            if _return:
                $ cortex = _return          
                call activity_bar("Analyzing Core...", "{}{}".format(cortex.getPath(), cortex.getIco())) from _call_activity_bar
                queue sound "audio/success.wav"
                # install part 
                $ bot.parts["cortex"]=cortex 
                # remove part from storage     
                $ GAME.lab.parts.remove(cortex)     
                $ bot.rollNew(cortex)
            show screen mg_sexbot_prep_scr()

        elif "add" in _return and not bot.hasComp("cortex"):
            queue sound "audio/beep.wav"
            call simple_notify("CONFIGURATION", "You need to insert a core first.", ["Continue"]) from _call_simple_notify_15

        elif "add" in _return:   
            #pause 0.5
            queue sound "audio/click.wav"
            $ partsKey = _return[1]
            # get part
            hide screen mg_sexbot_prep_scr
            call mg_sexbot_part_storage(partsKey) from _call_mg_sexbot_part_storage_1
            show screen mg_sexbot_prep_scr()
            if _return:
                $ part = _return          
                # install part 
                $ bot.parts[partsKey]=part 
                # remove part from storage 
                $ GAME.lab.parts.remove(part)        
                queue sound "audio/install.wav"

                # check first assembly quest 
                if bot.isComplete() and GAME.questSys.inProgress("QID_BOT_FIRST"):
                    pause 2.0
                    $ GAME.mc.hasDone("first_bot") 
                    call quest_updater() from _call_quest_updater_10


        elif "scrap" in _return:
            $ part = _return[1]
            queue sound "audio/coin.wav"
            $ GAME.money += part.getPrice()
            $ GAME.lab.parts.remove(part)      

        elif "bot" in _return:
            $ retrieveBot = _return[1]
            $ cellNum = GAME.lab.cells.index(retrieveBot)
            queue sound "audio/load.wav"
            # configurator occupied?
            if GAME.lab.current.hasComp("cortex"):
                $ storeBot = GAME.lab.current 
                # swap bots 
                $ GAME.lab.current = retrieveBot
                $ GAME.lab.cells[cellNum] = storeBot
            # retrieve bot into empty configurator
            else:
                $ GAME.lab.current = retrieveBot 
                $ GAME.lab.cells[cellNum] = False

        elif "list" in _return:
            queue sound "audio/click.wav"
            hide screen mg_sexbot_prep_scr
            call mg_sexbot_bot_storage() from _call_mg_sexbot_bot_storage
            show screen mg_sexbot_prep_scr() 

        elif "orders" in _return:
            queue sound "audio/click.wav"
            hide screen mg_sexbot_prep_scr
            call mg_sexbot_orders() from _call_mg_sexbot_orders
            show screen mg_sexbot_prep_scr() 


        elif "charge" in _return:
            queue sound "audio/load.wav"
            $ GAME.lab.charge(_return[1])

        elif "repair" in _return:
            $ slot = _return[1] 
            $ repairMatReq = {"cortex": "bio", "torso": "nano", "legs": "nano", "arms": "nano", "head": "nano" }
            $ mat = repairMatReq[slot]
            $ bot = GAME.lab.current
            if GAME.lab.status[mat] >= 20:
                queue sound "audio/install.wav"
                $ bot.parts[slot].wear = 0.0 
                $ GAME.lab.status[mat] = max(0, GAME.lab.status[mat]-20) 
            else:
                queue sound "audio/tech_fail.wav"
                call simple_notify("CONFIGURATOR", "20% {} required.".format(mat.title()), ["Continue"]) from _call_simple_notify_16

        elif "remove" in _return:
            $ slot = _return[1]
            $ bot = GAME.lab.current
            if slot == "cortex":
                queue sound "audio/notify.wav"
                call large_notify("WARNING", "Removal will destroy the current cortex. All parts will be returned to storage. \n\nDo you want to proceed?", ["Proceed", "Abort"], "08_items/ico ITMSpanner.png", "red") from _call_large_notify_12
                if _return == "Proceed":
                    queue sound "audio/install.wav"
                    # return all parts except core to storage
                    python:
                        for k in bot.parts.keys():
                            if k != "cortex":
                                bot.uninstallPart(k) 
                    # wipe doll 
                    $ GAME.lab.clearLab()
                    call simple_notify("CONFIGURATION", "Parts disassembled and stored.", ["Continue"]) from _call_simple_notify_17
                else:
                    pass
            else:
                # remove part and return to storage
                queue sound "audio/install.wav"
                $ bot.uninstallPart(slot) 


        elif "module" in _return:
            $ iSlot = _return[1]
            $ bot = GAME.lab.current
            # base bot configured?
            if not bot.hasComp("cortex"):
                queue sound "audio/tech_fail.wav"
            # uninstall?    
            elif bot.addOns[iSlot]:
                $ bot.uninstallAddOn(iSlot)
                queue sound "audio/install.wav"
            #select new from storage?
            else:
                queue sound "audio/click.wav"
                hide screen mg_sexbot_prep_scr
                call mg_sexbot_part_storage("addon") from _call_mg_sexbot_part_storage_2
                show screen mg_sexbot_prep_scr() 
                if _return:   
                    $ item = _return
                    $ bot.addOns[iSlot]= item 
                    $ GAME.lab.parts.remove(item)       
                    queue sound "audio/tech_success.wav"
                
        elif "autotrain" in _return:
            queue sound "audio/switch.wav"
            $ GAME.lab.current.autoTrain = not  GAME.lab.current.autoTrain
        elif "train" in _return:
            $ skill = _return[1]
            $ bot = GAME.lab.current
            if not bot.isComplete():
                queue sound "audio/beep.wav"
                call simple_notify("CONFIGURATOR", "Complete configuration first.", ["Continue"]) from _call_simple_notify_18
            elif GAME.lab.status["energy"] >= 10:
                $ sPre = bot.skills[skill]
                queue sound "audio/train_tech.wav"
                $ GAME.lab.current.train(skill)
                $ sPost = bot.skills[skill]
                if bot.skills[skill] >= bot.getTalents()[skill] and sPost >= 90:
                    play sound "audio/success.wav"
            else:
                queue sound "audio/tech_fail.wav"

        elif "process" in _return and _return[1] == "sex arcade":
            $ bot = GAME.lab.current
            $ GAME.mc.tallyUp("Arcade Deliveries",1)
            $ GAME.arcade.append(bot)
            $ GAME.lab.clearLab()
            queue sound "audio/install.wav"
            call large_notify("SEX ARCADE", "You install {} on an arcade platform and prepare her for shipping to a Sex Arcade.\n\nEarnings will be credited to your account once per day.".format(bot.name), ["Continue"], "03_minigames/ico_BotArcade.png") from _call_large_notify_13
            # check first arcade quest 
            if GAME.questSys.inProgress("QID_BOT_ARCADE"):
                call quest_updater() from _call_quest_updater_11            

        elif "process" in _return and _return[1] == "store":
            if GAME.lab.hasFreeCell():
                queue sound "audio/load.wav"
                $ bot = GAME.lab.current          
                $ GAME.lab.storeBot(GAME.lab.current) 
                #show image "fullscreen_wip.png" with fade
                if not GAME.mc.hasDone("stored_bot"):
                    hide screen mg_sexbot_prep_scr
                    call large_notify("STORAGE", "You deactivate {0} and move her into the Lab's storage area.\nYou can retrieve your bot for configuration and training at any time from within the configurator menu.".format(bot.name), ["Continue"]) from _call_large_notify_14
                    show screen mg_sexbot_prep_scr() with fade

            else:
                queue sound "audio/notify.wav"
                call simple_notify("STORAGE", "No free storage cell.".format(GAME.lab.current.name), ["Continue"]) from _call_simple_notify_19

        elif "process" in _return and _return[1] == "fulfill":
            $ bot = GAME.lab.current
            $ order = GAME.lab.findOrder(bot)
            queue sound "audio/notify.wav"            
            call large_notify("ORDER FULFILLMENT", "Order {}'{}'{}\n\nFulfill order with {} for {:,} Cr?".format(C_HI_B, order[0], C_HI_E, bot.name, bot.getPrice()), ["Fulfill", "Cancel"], "03_minigames/ico_BotShip.png") from _call_large_notify_15
            if _return == "Fulfill":
                call mg_sexbot_ship(GAME.lab.current) from _call_mg_sexbot_ship 
           
    return()