
#####################################################################
#                CLASSES AND FUNCTIONS
#####################################################################

#OhWee: Pick shell type to build mod

default shell_constructed = None
default ShellGen = 1
default new_shell_types = ["TypeJ"]
default available_shell_types = ["TypeC", "TypeI", "TypeK", "TypeL", "TypeN", "TypeP", "TypeV"]
default newshells_in_storage = []
default newshells_items = []
default newshells_available = False
default shells_built = 0
default Shell_TypeC_built = 0
default Shell_TypeI_built = 0
default Shell_TypeJ_built = 0
default Shell_TypeK_built = 0
default Shell_TypeL_built = 0
default Shell_TypeN_built = 0
default Shell_TypeP_built = 0
default Shell_TypeV_built = 0
default Shell_Crafting_Selection = ""

default bio_restock = 5
default energy_restock = 10
default nano_restock = 5
### end OhWee mod stuff

#####################################################################
#                USEFUL FUNCTIONS
#####################################################################

init python:

    def hardRandInt(a,b):
        # easy mode?
        if GAME.diff() > 0:
            return(random.randint(a,b))
        # check random stack
        if len(GAME.rand) < 100:
            for i in range(100):
                GAME.rand.append(random.uniform(0.0,1.0))
        # get number
        x1 = GAME.rand.pop(0)
        x2 = float(a)+ x1*(float(b)-float(a))
        x2 = int(round(x2))
        # map range
        return(x2)

    def force_number(s):
        try:
            float(s)
            return(float(s))
        except ValueError:
            pass
        return(0)

    def titlePlus(string):
        elems = string.split(" ")
        newElems = []
        for i in elems:
            newElems.append(i[:1].upper()+ i[1:])
        string = " ".join(newElems)
        return(string)

    def simple_prod(a,b):
        return(a*b)

    def vary(string):
        elements = re.split('<(.*?)>',string)
        result = ""
        for i in elements:
            result += random.choice(i.split("|"))
        return(result)

    def occurrence(prob):
        if random.uniform(0.0,1.0) <= prob:
            return(True)
        return(False)

    def capValue(val, minLim,maxLim):
        result = val
        if result < minLim:
            result = minLim
        if result > maxLim:
            result = maxLim
        return(result)

    def article(noun): # cheap workaround, not gramatically correct
        if noun[0] in "eaiou":
            return("an "+noun)
        return("a "+noun)

    def colorize(msg, col):
        mu = {"gold": C_GO_B, "blue": C_BL_B, "purple": C_SU_B, "acc": C_HI_B}
        return(mu[col]+msg+C_WH_E)

    def percentColorized(val, interval= [0.0,100.0], reverse = False): 
        CB, CE = C_WH_B, C_WH_E
        rating = {  
            100: C_GR_B,
            80: C_YE_B,
            50: C_OR_B, 
            30: C_RE_B
        }
        # normalize value
        nx = int(float(val)/(interval[1]-interval[0]) * 100)
        if reverse:
            nxR = 100 - nx
        else:
            nxR = nx

        for i in sorted(rating.keys()):
            if nxR <= i:
                CB, CE = rating[i], "{/color}"
                return([CB+"{}%".format(nx)+CE, CB, CE,nx] ) 

        return( [CB+"{}%".format(nx)+CE, CB, CE, nx] )      

    def levelUpAt(start, level, fac = 2.0):
        lu = start * (1-fac**level)/(1-fac)
        return(lu)

    def calcReward(amount):
        result = int(round(float(amount)/10.0+pow(float(amount)/50.0,1.1)))
        return(result)

    def dye(msg, colGrade): # grades 0 to 5
        # crimson, bright-red,orange, dark yellow,yellow,light-green, green
        return(C_B+"{}".format(msg)+C_E)

    def colMap(number, cols, interval):
        # ex: colMap("someText", ["red", "yellow", "green"], [f_min, f_max])
        # generate the value step array 
        i0 = interval[0]
        i1 = interval[1]
        capNum = capValue(number, i0,i1)
        step = (float(i1) - float(i0))/ float(len(cols)) 
        # find matching step from left to right 
        for i in range(len(cols)):
            if capNum <=  float(i0)+step*(i+1):
                # colorize and return string  
                return("{}{}{}".format(ACCENTS[cols[i]][0], number, ACCENTS[cols[i]][1])) 
        return(str(number)) 

    #####################################################################
    #                CLASSES
    #####################################################################

    #####################################################################
    #                GAME CORE
    #####################################################################
            

    class Game():
        def __init__(self,name):
            self.name = name
            self.act = 1
            self.day = 0
            self.money = 0
            self.hour = 14
            self.ap = 5
            self.version = 0
            self.IDcounter = 1
            self.tags = []


            # sandbox data
            self.locs = {} # room locations
            self.surfaces = {} # surface locations
            self.starSys = {} # star system systems
            self.locState = "loc_interior"
            self.sandboxLoc = None

            # GUI data
            self.guiInt = {"ship_stats": True, "hotkeys": True, "inventory": True, "ship_decks": True} # subsystem status widget on/off

            # other data elements
            self.items = {}
            self.arcade = [] # bots working in remote sex arcade
            self.drones = [] # transport etc. drones



            self.miniMaps = {}
            self.aElem = {}
            self.missions = []
            self.missionSys = MissionSystem()
            self.questSys = None
            self.lab = None # bot lab

            self.attr = {"SUB": "Submission", "STS": "Stress", "MOD": "Mood", "SLU": "Sluttiness", "ARO": "Arousal", "INT": "Intelligence", "RSP": "Respect", "APP": "Appreciation", "NOT": "Notoriety", "REP": "Reputation", "AFF": "Affection", "EXH": "Exhibitionism", "ENE": "Energy", "TEC": "Tech Skill"}
            self.para = []
            self.mc = False
            self.npc = False
            self.idleTalk = []
            self.locHistory = ["LOCEngine", None]
            self.log = []
            self.logging = False
            self.trophies = {"scholar": 0, "traveller": 1, "casanova": 0, "admiral": 0, "pharaoh": 0, "trainer": 0}
            self.trophyActive = {"scholar": False, "traveller": False, "casanova": False, "admiral": False, "pharaoh": False, "trainer": False}
            self.reports = []
            self.infractions = []
            self.repQueue = []
            self.repMisses = []
            self.books = []
            self.debugMode = []
            self.extensions = []
            self.difficulty = 1 # 1 Normal, 2: Easy
            # self.inQueue = [] # active in-queue for bots Access via cargo terminal to "unpack" DEPRECATED               
            self.inQueuePending = [] # all kind of incoming stuff  / item=["kind", <object>, <arrival day>]

            # Queue item format
            # item = ["kind", <object>, <label>]
            self.inQueueNew = []
            self.inQueuePendingNew = []

            self.adv = {}
            self.minigameDB = {}
            self.activityDB = {}
            self.jobDB = {}
            self.uniformDB = {}
            self.combatDB = {}
            self.investments = []
            self.timers = {}
            self.occurredToday = []

            self.rand = []
            self.gallery = {}
            #self.galleryMax = 20

        #####################################################################
        #					GAME MANAGEMENT AND PATCHES		
        #####################################################################
        def hint(self, msg, tags=[]):
            if GAME.difficulty == 1:
                msg = ""
            return(msg)

        def patch(self):
            patched = False

    
            #####################################################################
            #              PARANOID COMPATIBILITY SCRUBBERS & BETA PATCHES
            #####################################################################

            for i in GAME.lab.parts:
                # data scrubber for legacy data
                i.update()

            # ===== CR-0843 START
            # cargo scrubber
            for item in [x for x in GAME.ship.cargo]:
                if item.ID in ["ITMLiquorVortex"]:
                    GAME.ship.cargo.remove(item)
            # ===== CR-0843 END


            # ===== CR-0754 START 
            if "petOwner" not in globals():
                global petOwner
                petOwner = Actor("ACTPTO", "Pet Owner", "Krell" , {})
                GAME.adv["ACTPTO"] = pto
            # ===== CR-0754 END 

            # ===== CR-0849 BETA START Fragment Scrubbing 
            if "pre_quthor_3" in GAME.mc.done:
                if GAME.mc.inventory.count(GAME.items["ITMMapPrecursor"]) < 4:
                    GAME.mc.addItem("ITMMapPrecursor")
            # ===== CR-0489 END

            # #####################################################################
            # #			PATCHES V090_BETA To V090_EA1				
            # #####################################################################
            # if "patch_090_EA1" not in GAME.extensions:
            #     patched = True
            #     GAME.extensions.append("patch_090_EA1")
            #     # #####################################################################
            #     # #					ACTIVE ELEMENTS		
            #     # #####################################################################
            #     # #def __init__(self, ID, name, coord, loc, cond, tags, attr = {}):
            #     # elemDataPatch  = [               
            #     #     ]   
            #     # for i in elemDataPatch:
            #     #     GAME.aElem[i[0]].ID = i[0]
            #     #     GAME.aElem[i[0]].name = i[1]
            #     #     GAME.aElem[i[0]].coord = i[2]
            #     #     GAME.aElem[i[0]].loc = i[3]
            #     #     GAME.aElem[i[0]].cond = i[4]
            #     #     GAME.aElem[i[0]].tags = i[5]
            #     #####################################################################
            #     #					QUESTS		
            #     #####################################################################
            #     #####################################################################
            #     #					BOT PARTS		
            #     #####################################################################
            #     botDataPatch = [
            #         # New Gen1 Shells
            #         ["SBPShell1TypeJ", "Type 'J' Shell", "A", "sexbot addons", "addon", 1, "A personalized bot shell kit for visual customization.", 7500, ["no_find", "no_sell"],{"LVL": 5}],
            #         ["SBPShell1TypeV", "Type 'V' Shell", "A", "sexbot addons", "addon", 1, "A Voronian bot shell kit for visual customization.", 15000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
            #         ["SBPShell1TypeL", "Type 'L' Shell", "A", "sexbot addons", "addon", 1, "A Supermodel shell kit for visual customization.", 20000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
            #         ["SBPShell1TypeN", "Type 'N' Shell", "A", "sexbot addons", "addon", 1, "A Ni'Larian Temptress shell for visual customization.", 17000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
            #         ["SBPShell1TypeK", "Type 'K' Shell", "A", "sexbot addons", "addon", 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", "addon", 1, "A Krell Concubine shell kit for visual bot customization.", 12000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}],

            #         # New Gen 2 Shells
            #         ["SBPShell2TypeJ", "Type 'J' Shell", "A", "sexbot addons", "addon", 2, "A personalized bot shell kit for visual customization.", 15000, ["no_find", "no_sell"],{"LVL": 5}],
            #         ["SBPShell2TypeV", "Type 'V' Shell", "A", "sexbot addons", "addon", 2, "A Voronian bot shell kit for visual customization.", 30000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
            #         ["SBPShell2TypeL", "Type 'L' Shell", "A", "sexbot addons", "addon", 2, "A Supermodel shell kit for visual customization.", 40000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
            #         ["SBPShell2TypeN", "Type 'N' Shell", "A", "sexbot addons", "addon", 2, "A Ni'Larian Temptress shell for visual customization.", 35000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
            #         ["SBPShell2TypeK", "Type 'K' Shell", "A", "sexbot addons", "addon", 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", "addon", 2, "A Krell Concubine shell kit for visual bot customization.", 25000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}],

            #         # New Gen3 Shells
            #         ["SBPShell3TypeJ", "Type 'J' Shell", "A", "sexbot addons", "addon", 3, "A personalized bot shell kit for visual customization.", 25000, ["no_find", "no_sell"],{"LVL": 5}],
            #         ["SBPShell3TypeV", "Type 'V' Shell", "A", "sexbot addons", "addon", 3, "A Voronian bot shell kit for visual customization.", 50000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
            #         ["SBPShell3TypeL", "Type 'L' Shell", "A", "sexbot addons", "addon", 3, "A Supermodel shell kit for visual customization.", 70000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
            #         ["SBPShell3TypeN", "Type 'N' Shell", "A", "sexbot addons", "addon", 3, "A Ni'Larian Temptress shell for visual customization.", 60000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
            #         ["SBPShell3TypeK", "Type 'K' Shell", "A", "sexbot addons", "addon", 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", "addon", 3, "A Krell Concubine shell kit for visual bot customization.", 50000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}],

            #         # LEGACY GEN1 Shells
            #         ["SBPShellTypeJ", "Type 'J' Shell", "A", "sexbot addons", "addon", 1, "A personalized bot shell kit for visual customization.", 75000, ["no_find", "no_sell"],{"LVL": 5}],
            #         ["SBPShellTypeV", "Type 'V' Shell", "A", "sexbot addons", "addon", 1, "A Voronian bot shell kit for visual customization.", 15000, ["no_find"],{"LVL": 5, "personality": "voronian concubine"}],
            #         ["SBPShellTypeL", "Type 'L' Shell", "A", "sexbot addons", "addon", 1, "A Supermodel shell kit for visual customization.", 20000, ["no_find"],{"LVL": 5, "personality": "supermodel"}],
            #         ["SBPShellTypeN", "Type 'N' Shell", "A", "sexbot addons", "addon", 1, "A Ni'Larian Temptress shell for visual customization.", 17000, ["no_find"],{"LVL": 5, "personality": "temptress"}],
            #         ["SBPShellTypeK", "Type 'K' Shell", "A", "sexbot addons", "addon", 1, "A Krell Palace Mistress shell kit for visual bot customization.", 17000, ["no_find"],{"LVL": 5, "personality": "palace mistress"}],
            #         ["SBPShellTypeC", "Type 'C' Shell", "A", "sexbot addons", "addon", 1, "A Krell Concubine shell kit for visual bot customization.", 12000, ["no_find"],{"LVL": 5, "personality": "krell concubine"}]                
            #     ]
            #     for i in botDataPatch:
            #         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])

            # #####################################################################
            # #			PATCHES V090_BETA To V090_EA2				
            # #####################################################################
            # if "patch_090_EA2" not in GAME.extensions:
            #     patched = True
            #     GAME.extensions.append("patch_090_EA2")
            #     if GAME.mc.getTally("Galaxina Episodes") >= 24:
            #         GAME.mc.tally["Galaxina Episodes"] = 23
            #     for i in [audra, verity, veronica]:     # CR-0555
            #         if i not in GAME.npc:                # CR-0555
            #             GAME.npc.append(i)              # CR-0555
            return(patched)


        #####################################################################
        #              SANDBOX MANAGEMENT
        #####################################################################
        def setSandboxLoc(self, loc):
            GAME.sandboxLoc = loc
            # interior 
            if loc.ID[:3] == "LOC":
                self.setLocID(loc.ID)
                
            return()

        def determineStarSys(self, spaceLocID):
            for k in GAME.starSys.keys():
                if spaceLocID in GAME.starSys[k].locs.keys():
                    return (GAME.starSys[k])
            return(False)
        
        def getSandboxLoc(self, locID = False): # current or specified
            if locID:
                # space
                if locID[:3] == "LID":
                    loc = GAME.getStarSys().locs[locID] 
                # interior 
                elif locID[:3] == "LOC":
                    loc = GAME.locs[locID]
                # surface
                elif locID[:3] == "SUI":
                    loc = GAME.surfaces[locID]
                else:
                    loc = False
                return(loc)
            else:
                return(GAME.sandboxLoc)

        #####################################################################
        #                LOCATION INFO
        #####################################################################
        # ===== CR-0466 END

        def timerPurge(self, k):
            if k in self.timers.keys():
                self.timers.pop(k)
            return()

        def timerSet(self, k,days):
            self.timers[k] = days
            return()

        def timerGet(self, k):
            if k not in self.timers.keys():
                return(0)
            return(self.timers[k])

        def hasRepSpot(self, locID):
            repTags = {"LOCCargo": "AERepCargo", "LOCEngine": "AERepEngine", "LOCHallway1": "AERepHallOne", "LOCHallway2": "AERepHallTwo"}
            if locID in repTags.keys():
                if any([x for x in GAME.repQueue if repTags[locID] in x]):
                    return(True)
            else:
                return(False)
            return(False)

        def getMiniMapMatch(self, locID=False):
            if not locID:
                locID = GAME.getLoc().ID 
            for miniMap in GAME.miniMaps.values():
                for cRow in miniMap.cells:
                    for cell in cRow:
                        if cell:
                            if cell.locID == locID:
                                return(miniMap) 
            return(False)

        def getUniqueID(self):
            self.IDcounter += 1
            return(self.IDcounter)

        def getDueMissions(self): # DEPRECATED / MOVED TO MISSIONSYS
            missions = []
            for i in self.missions:
                if i.due == GAME.day:
                    missions.append(i)
            return(missions)

        def getFailedMissions(self):
            missions = []
            for i in self.missions:
                if i.due < GAME.day:
                    missions.append(i)
            return(missions)

        def getExteriorBackdrop(self):
            bg = "05_gui/intermission.png"
            return(bg)

        def getStarSys(self):
            return(self.starSys[self.ship.starSysID])


        def swapStarSys(self, starSysID, locID):
            # local? 
            if starSysID == self.getSpaceLoc().ID:
                return(False)
            # intersystem?
            elif locID in self.starSys[starSysID].locs.keys():
                self.ship.spaceLocID = "LOCDummy" # paranoid default for screens
                self.ship.starSysID = starSysID
                self.setSpaceLocID(locID)
                return(True)
            return(False)

        
        def getSpaceLoc(self, sLocID = False):
            if not sLocID:
                starSys = self.starSys[self.ship.starSysID]
                if self.ship.spaceLocID in starSys.locs.keys():
                    return(starSys.locs[self.ship.spaceLocID])
                return(False)
                
            else: # search through starmaps for location OD
                for x in self.starSys.values():
                    for y in x.locs.values():
                        if sLocID == y.ID:
                            return(y)
            return(False)

        def setSpaceLocID(self, spaceLocID):
            self.ship.spaceLocID = spaceLocID
            return()

        # def getLocByMiniMapID(self, miniMapID): # OBSOLETE
        #     for k in self.locs.keys():
        #         if self.locs[k].miniMapID == miniMapID:
        #             return(self.locs[k])
        #     return("Noname")

        def locHasChanged(self):
            if len(self.locHistory) >= 2:                       # CR-0274
                if self.locHistory[0] != self.locHistory[1]:    # CR-0274
                    return(True)                                # CR-0274
            return(False)                                       # CR-0274

        def getLoc(self):
            return(self.locs[self.locHistory[0]])

        def setLocID(self, locID):  
            if locID == self.locHistory[0]:                                 # CR-0274
                return(False)                                                # CR-0274
            self.locHistory = [locID] + self.locHistory # shift by one      # CR-0274
            if len (self.locHistory) > 20:                                  # CR-0274
                self.locHistory = self.locHistory[:10]                      # CR-0274
            return(True)                                                    # CR-0274

        def getSpriteCoord(self, actKey, activity):
            result = False
            for i in self.activityDB[actKey]:
                if i[1] == activity:
                    return(i[2])
            return(result)

        #####################################################################
        #                MISC GAME SERVICES
        #####################################################################

        def hasOccurredToday(self,topic, n=1):
            if self.occurredToday.count(topic) >= n:
                return(True)
            else:
                self.occurredToday.append(topic)
                return(False)

        def galleryAdd(self, slot):
            GAME.gallery[slot].found = True
            return()

        def galleryGetMax(self):
            return(max(self.gallery.keys()))


        def diff(self,mode = "factor"):
            return(self.difficulty)

        def hasExt(self, ID):
            if ID in self.extensions:
                return(True)
            return(False)


        def rollItem(self, isKind, notKind, level, cond="True"):
            item = None
            # build filtered list
            iList = []
            for i in GAME.items.values():
                passed = True
                if i.kind in notKind:
                    passed = False
                if i.kind not in isKind and "any" not in isKind:
                    passed = False
                for actor in GAME.npc + [GAME.mc]:
                    if i in actor.inventory and "unique" in i.tags:
                        passed = False
                if "no_find" in i.tags:
                    passed = False
                if i.price < level * 20 and random.randint(1,100) > 50:
                    passed = False
                if not eval(cond):
                    passed = False

                if passed:
                    iList.append(i)
            # roll item
            i = 0
            while item == None:
                rollPrice = abs(random.gauss(0,50*GAME.mc.lvl))
                rollItem = random.choice(iList)
                if rollPrice > rollItem.price:
                    item = rollItem
                i += 1
                if i > 100: # DEBUGGING PURPOSE
                    item = GAME.items["ITMLampOil"]
            if item == None: # redundant / paranoid
                item = GAME.items["ITMLampOil"]

            return(item)


    class GalleryItem():
        def __init__(self,ID,name, hint, found, anim):
            self.ID = ID
            self.name = name
            self.hint = hint
            self.found = found
            self.anim = anim




    #####################################################################
    #                ITEMS
    #####################################################################
    class Item():
        def __init__(self, ID, name, art, kind, description, slot, price, tags, req = {}):
            self.art = art
            self.ID = ID
            self.name = name
            self.description =description
            self.slot = slot
            self.kind = kind
            self.price = price
            self.tags = tags
            self.req = req
            self.attr = self.req
            self.accent = self.getAccent()

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

        def lacksLicense(self):
            for i in ["grade_1", "grade_2", "grade_3"]:
                if i in self.tags:
                    if i not in GAME.mc.done:
                        return(i.replace("_", " ").title())
            return(False)

        def getBoostOneLiner(self): # tooltip compatibility
            return("")

        def getPath(self):
            return("08_items/")

        def getIco(self):
            return("ico {}.png".format(self.ID))

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

        def getGrade(self): # wrapper for weapons grade etc:
            if "grade_1" in self.tags:
                return(1)
            if "grade_2" in self.tags:
                return(2)
            if "grade_3" in self.tags:
                return(3)      
            return(False)

        def getSound(self):
            sounds = {
                "gem": ["crystal"],
                "reading": ["scroll"],
                "equip key": ["key"],
                "potion": ["fragrance", "glitter fragrance", "milk", "liquor"]
            }
            for k in sounds.keys():
                if self.kind in sounds[k]:
                    return(k+".wav")
            return("equip.wav")


        def getAccent(self):
            # generic colors
            categories = {
                "gold": ["jewelry", "accessory", "ware"],
                "orange": ["build", "liquor"],
                "bright blue": ["tech", "subsys", "subunit", "tool"],
                "pink": ["pod", "ai", "map"],
                "brown": ["misc"],
                "cyan": ["crate"]
            }
            # special overrides
            if self.getAttr("accent"):
                return(self.attr["accent"])

            if "no_sell" in self.tags:
                return("yellow")

            if "krell" in self.ID.lower():
                return("green")
            
            # weapon
            if any(x in self.tags for x in ["grade_1", "grade_2", "grade_3"]):
                return("orange")

            if self.ID in ["ITMBomb"]:
                return("orange")

            for k in categories.keys():
                if self.kind in categories[k]:
                    return(k)

            return("blue")

        def getName(self, tags=[]):
            blank = " "
            C_B, C_E = "", ""
            art = self.art.lower()
            if "noArt" in tags:
                art = ""
                blank = ""

            else:
                if art == "":
                    blank = ""
            if "col" in tags:
                C_B = ACCENTS[self.getAccent()][0]
                C_E = ACCENTS[self.getAccent()][1]
            if "cap" in tags:

                result = art+blank+C_B+titlePlus(self.name)+C_E
            else:
                result = art+blank+C_B+self.name+C_E
                result = result.lower()
            return(result)

    #####################################################################
    #                ACTOR
    #####################################################################
    class Job():
        def __init__(self,ID, name, description, req, effect):
            self.ID = ID
            self.name = name 
            self.description = description
            self.req = req
            self.effect = effect

    class UniformOrder():
        def __init__(self,ID, name, description, req, effect, unlocked, itemID):
            self.ID = ID  
            self.name = name
            self.description = description
            self.req = req
            self.effect = effect
            self.unlocked = unlocked 
            self.itemID = itemID
            

    class Actor():
        def __init__(self, ID, name, race = "Unknown", attr = {}):
            # pc or npc
            self.ID = ID
            self.name = name
            self.status = []
            self.attr = attr
            self.attrFrac = {}  
            for k in self.attr.keys():
                self.attrFrac[k] = float(attr[k])
            self.attrMax = {}
            self.done = []
            self.doneToday = []
            self.actions = []
            self.inventory = []
            self.invHistory = []
            self.invHistPos = -1
            self.money = 0
            self.tags = []
            self.tally = {}
            self.counters = {}
            self.race = race
            self.missions = []
            self.achievements = []
            self.factions = {} # standing voron, krell, swarm, iltari...
            self.activities = []
            self.backstory = "Not much is known about {}'s background".format(self.name)
            self.jobs = ["JOB_Idle"] # available jobs
            #self.uniform = "free" # uniform order, key for outfitDB

            # npc use only
            self.defaultOutfit = False
            self.outfit = [] # item worn
            self.wardrobe = [] # everything equippable, not inventory
            self.progOutfits = []
            self.uniform = "UID1_Free"
            self.fp = 0
            self.favours = []
            self.locOverrideReg = {}

            # pc relevant
            self.lvl = 1
            self.exp = 0



        #####################################################################
        #                ACTOR: Task Related
        #####################################################################
        def torpReload(self, attr={}): # in combat reload
            # check torps in cargo 
            torpsInCargo = [x for x in GAME.ship.cargo if x in GAME.ship.supportedAddOns("launcher") and "torpedo" in x.ID.lower() ]
            torpsInCargo.sort(key=lambda x: x.price, reverse=True)
            maxLoadout = GAME.ship.getLoadoutMax("launcher")
            if torpsInCargo == []:
                return(False)
            # check free slots 
            slotNums = [ x for x in range(6) if x < maxLoadout and GAME.ship.getAddOns("launcher")[x]==False]
            if slotNums == []:
                return(False)
            # reload one torp 
            slot = slotNums[0] 
            torp = torpsInCargo[0]
            GAME.ship.equipAddOn("launcher", torp.ID, slot)
            GAME.ship.unloadWare(torp.ID)

            return("Slot {} reloaded with {}".format(slot, torp.getName(["noArt", "cap", "col"])))

        def workAI(self):

            if GAME.hour <= 8:
                return()

            #GAME.log.append("{} job check - {}:00 - {}".format(actor.name, GAME.hour, self.getJob().ID))

            if "job" in self.doneToday:
                return()

            #GAME.log.append("DONE 1.")

            # stress and mood impact of job 
            job = self.getJob()
            if not job:
                return()

            if not self.hasDoneToday("job_impact"): # only once for multiple executions daily
                for k in [x for x in job.effect.keys() if x in self.attr.keys()]:
                    impact = self.getJob().effect[k]
                    self.attr[k] = capValue(self.attr[k]+impact,0,100)
            
            # parts procurement (Nimhe)
            if self.getJob().ID == "JOB_Parts":
                if GAME.ship.cargoFree() >= 2 and GAME.money > 80000:
                    crates = 2 
                elif GAME.ship.cargoFree() >= 1 and GAME.money > 75000:
                    crates = 1 
                else:
                    crates = 0 
                while crates:# and GAME.ship.cargo.count(GAME.items["ITMCrateBots"]) < 5:
                    crates -= 1 
                    GAME.ship.loadWare("ITMCrateBots")
                    GAME.money -= 15000
                self.hasDoneToday("job")

            # Space Survey (Vee)
            elif self.getJob().ID == "JOB_Astro":
                if random.randint(1,100) > 50: # WIP Balance
                    if GAME.getStarSys().ID == "SSIDIltari":
                        sLoc = GAME.getStarSys().locs["LIDOddityIltari"]
                        dynLocPara = random.choice(sLoc.tags)
                        sLoc.coord = dynLocPara[:2]
                        sLoc.zone = dynLocPara[2]
                        sLoc.found = True
                        GAME.reports.append(["SPACE SURVEY", "{} has identified a Space Oddity and marked the site on your map.\n\nIt might be worthwile to explore the location".format(self.name), ["Continue"], "05_gui/ico_gui_hostile_swarm.png", "pink"])   
                    
                self.hasDoneToday("job") 

            # parts upgrading (Nimhe)
            elif self.getJob().ID == "JOB_Upgrade":
                upgradeReport = ""

                if self.getAttr("TEC") < 15:
                    exclude_grade = []                      
                elif self.getAttr("TEC") < 20:
                    exclude_grade = ["basic"]                        
                else:
                    exclude_grade = ["basic", "advanced"]                       

                for i in range(3):
                    part = GAME.lab.upgradeParts("pref", ["any"], exclude_grade)
                    self.doneToday.append("parts attempt")
                    self.doneToday.append(part)
                    if part:
                        upgradeReport += part.getName(["col", "noArt", "cap"])+"\n"
                        self.incAttrFrac("TEC",0.3,20.0)

                if upgradeReport:
                    GAME.reports.append(["BOTLAB REPORT", "{} has created new bot parts from scrap:\n\n{}".format(self.name,upgradeReport), ["Continue"], "08_items/ico ITMSpanner.png", "blue"])   
                self.hasDoneToday("job")                

            # pod restocking (T'Ris)
            elif self.getJob().ID == "JOB_Pods":
                pods = ["ITMPodEnergy", "ITMPodBio", "ITMPodNano", "ITMPodEnergy"]
                for i in range(3):
                    for k in pods:
                        item = GAME.items[k]
                        if GAME.ship.cargoFree() and GAME.ship.cargo.count(item) < [k + "_restock"] and GAME.money > 50000:
                            GAME.ship.loadWare(item.ID)
                            amount = item.price
                            GAME.books.append([GAME.day, "Wares Bought", -amount])
                            GAME.money -= amount 
                self.hasDoneToday("job")

            # custom bots (Nimhe)(cost: 1 crate)

            elif self.getJob().ID in ["JOB_Shell_J"]:
                #types = {"JOB_Shell_J": ["SBPShell1TypeJ", "SBPShell2TypeJ", "SBPShell2TypeJ", "SBPShell3TypeJ"]}
                #sType = random.choice(types[self.getJob().ID])
                #if GAME.ship.hasCargo(["ITMCrateBots"]):
                    #GAME.ship.unloadWare("ITMCrateBots")

                #OhWee: choose shell type mod

                if Shell_Crafting_Selection == "TypeJ":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeJ_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeJ_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 8000 * ShellGen
                    if Shell_TypeJ_built < 1:
                        GAME.money -= 12000
                    globals()['Shell_TypeJ_built'] += 1
                    globals()['shells_built'] += 1

                elif Shell_Crafting_Selection == "TypeC":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeC_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeC_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 12500 * ShellGen
                    if Shell_TypeC_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeC_built'] += 1
                    globals()['shells_built'] += 1

                elif Shell_Crafting_Selection == "TypeV":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeV_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeV_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 15000 * ShellGen
                    if Shell_TypeV_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeV_built'] += 1
                    globals()['shells_built'] += 1

                elif Shell_Crafting_Selection == "TypeN":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeN_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeN_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 17500 * ShellGen
                    if Shell_TypeN_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeN_built'] += 1
                    globals()['shells_built'] += 1

                elif Shell_Crafting_Selection == "TypeK":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeK_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeK_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 20000 * ShellGen
                    if Shell_TypeK_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeK_built'] += 1

                elif Shell_Crafting_Selection == "TypeL":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeL_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeL_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 25000 * ShellGen
                    if Shell_TypeL_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeL_built'] += 1
                    globals()['shells_built'] += 1

                elif Shell_Crafting_Selection == "TypeP":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeP_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeP_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= 25000 * ShellGen
                    if Shell_TypeP_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeP_built'] += 1
                    globals()['shells_built'] += 1

                else: #if Shell_Crafting_Selection == "TypeI":
                    if shells_built < 6:
                        ShellGen = int(Shell_TypeI_built / 2) + 1
                    else:
                        ShellGen = int(Shell_TypeI_built + 1)
                    if ShellGen > 3:
                        ShellGen = 3
                    GAME.money -= (20000 * ShellGen) + 20000
                    if Shell_TypeI_built < 1:
                        GAME.money -= 10000
                    globals()['Shell_TypeI_built'] += 1
                    globals()['shells_built'] += 1

                sType = "SBPShell" + str(ShellGen) + Shell_Crafting_Selection
                globals()['ShellGen'] = ShellGen

                #OhWee mod code ends here

                if self.getAttr("TEC") < 12:
                    attr = {"prefs": ["basic", "advanced"] }                        
                elif self.getAttr("TEC") < 15:
                    attr = {"prefs": ["basic", "advanced", "military grade"]}                        
                elif self.getAttr("TEC") < 20:
                    attr = {"prefs": ["advanced", "military grade"]}                        
                else:
                    attr = {"prefs": ["military grade"]}     

                #OhWee shell build mod code:                
                globals()['shell_constructed'] = part = GAME.sbPartsData[sType].clone(attr)
                self.incAttrFrac("TEC",0.3,20.0)
                ### end OhWee mod code
                
                #part = GAME.sbPartsData[sType].clone(attr)                
                #GAME.lab.parts.append(part)
                #self.incAttrFrac("TEC",0.3,20.0)

                self.hasDoneToday("job")

            # torpedo loadout (Khelara)
            elif self.getJob().ID == "JOB_Loadout" and GAME.ship.hasSubSys("launcher"): # CR-0600
                # check empty slots 
                maxLoadout =GAME.ship.getLoadoutMax("launcher")

                # check available torpedos 
                torpsInCargo = [x for x in GAME.ship.cargo if x in GAME.ship.supportedAddOns("launcher")]

                # sort torpedoes 
                torpsInCargo = sorted(torpsInCargo, key = lambda x: x.price, reverse = True )

                # loadout torpedos 
                for i in range(6):
                    if i < maxLoadout:
                        #GAME.log.append(( i , len(torpsInCargo)))
                        if torpsInCargo and GAME.ship.getAddOns("launcher")[i]==False:
                            torp = torpsInCargo.pop(0)
                            GAME.ship.equipAddOn("launcher", torp.ID, i) 
                            GAME.ship.unloadWare(torp.ID)   

                            #GAME.log.append((i,torp.ID, len(torpsInCargo)))  # DEBUG 
          
            # repair ship (Khelara)
            elif self.getJob().ID == "JOB_Repair": 
                # fix all issues
                self.tallyUp("Repairs", len(GAME.repQueue))
                GAME.mc.hasDoneToday("repairs_assigned")
                GAME.repQueue = []
                GAME.repMisses = []
                GAME.mc.hasDoneToday("repair")

            # surprise crate shopping (Seraphine)
            elif self.getJob().ID == "JOB_Shop" and seraphine.getLocOverride() == False and GAME.money > 10000000: # CR-0733
                # intitialize 
                content = []
                name = "Standard Crate"
                # roll surprise crate 
                level = hardRandInt(1,100)
                # Ultimate Bot Box
                if level > 95:
                    name = "Ultimate Bot Crate"
                    content = random.sample(["ITMContainerBot"]*2+["ITMAICoreElite"]*2+["ITMCrateBotsPre"]+ ["ITMCrateBotsMil"],random.randint(3,5))
                # Elite Gear Box
                if level > 85:
                    name = "Elite Gear Crate"
                    content = random.sample(["ITMTorpedoVoron"]*3+["ITMMineCluster"]*3+["ITMTorpedoBFT"]*4,random.randint(3,5)) # CR-0751
                # Luxury Box
                elif level > 70:
                    name = "Luxury Crate"
                    content = random.sample(["ITMEternium"]+["ITMLiquorVortex"]*3+["ITMCrateBotsRare"]*2+["ITMCrateExclusive"]+["ITMCrateLiquor"]*2,random.randint(3,5))
                # Odd Crate First
                elif level > 50 and "odd_crate" not in self.done:
                    name = "Odd Crate"
                    content =["ITMCandle"]*16
                # Exckusive Goods Box
                elif level > 50:
                    name = "Exclusive Crate"
                    content = random.sample(["ITMClustersHalidium"]+["ITMDiscsTellurium"]+["ITMBioActivators"]+["ITMPlasmoidSpheres"]+["ITMNanotubes"]+["ITMOreInfernium"]+["ITMInferniumPlating"]+["ITMOrePromethium"],random.randint(3,5))
                # Random Box
                elif level > 10:
                    name = "Random Crate"
                    content = random.sample([x.ID for x in GAME.items.values() if x.kind == "ware"]+["ITMCandle"],random.randint(3,5))
                else:
                    pass

                # store surprise crate
                if content:
                    self.surpriseCrate = [name, [GAME.items[x] for x in content]]
                self.hasDoneToday("job")

            return()

        def nextJob(self):
            if self.jobs == []:
                return(False)
            self.jobs = self.jobs[1:] + self.jobs[:1]
            return(True) 

        def getJob(self):
            return(GAME.jobDB[self.jobs[0]])

        def setJob(self, jobID):
            if jobID not in self.jobs:
                return()
            while self.jobs[0] != jobID: 
                self.nextJob()
            return()

        def giveFP(self,amount):
            self.tallyUp("FP Awarded", amount)
            self.fp += amount
            return()

        def getFavours(self, locID, mode="close"):
            fav = dict((k,GAME.favours[k])
                        for k in self.favours
                        if GAME.favours[k][3] == locID and eval (GAME.favours[k][4]))
            return(fav)

        def checkFavour(self, favour):
            if self.fp < GAME.favours[favour][0]:
                result = [False, 'You need more points for that favour...']
            elif favour in self.favours:
                result = [True, GAME.favours[favour][5]]
            else:
                result = [False, "I will not do such stuff."]

            return(result)
        
        # ===== CR-0440 START =====
        def hasAchievement(self, achievement):
            for i in self.achievements:
                if i.lower() == achievement.lower():
                    return(True)
            return(False)
        # ===== CR-0440 END =====

        #####################################################################
        #                ACTOR: Items and Outfits
        #####################################################################

        def canWear(self, itemID):
            item = GAME.items[itemID]
            for k in self.attr.keys():
                if k in item.req.keys():
                    if self.attr[k] < item.req[k]:
                        return(False)
            return(True)

        def adjustProgOutfit(self): # make lewder outfit if conditions are met
            if self.hasDoneToday("adjust_outfit"):
                return()
            # clothing kit?
            if not self.hasItem("ITMClothingKit"):
                return(False)

            if self.hasDoneToday("ITMClothingKit"): # delay until night_passes
                return()

            # anything to do / more lewd accepted / paranoid check
            if self.getProgOutfit() == self.getProgOutfit("max"):
                return(False)

            # worn current lewdest at least 3 days
            if self.counters.get(self.getProgOutfit("available").ID, 0) < 3:
                return(False)

            # anything lewder to make left?
            if self.getProgOutfit("max") in self.wardrobe:
                return(False)

            # make clothing and add to wardrobe
            item = self.getProgOutfit("next")
            self.wardrobe.append(item)

            # remove kit
            self.remItem("ITMClothingKit")
            return(True)


        def getProgOutfit(self, mode="available"): # available, next, max (accepted)
            if mode == "max":
                progOutfit = [GAME.items[x] for x in self.progOutfits if self.canWear(x)][-1]
                return(progOutfit)
            if mode == "available":
                progOutfit = [GAME.items[x] for x in self.progOutfits if self.hasItem(x)][-1]
                return(progOutfit)
            # next in upgrade path 
            best = self.getProgOutfit("available")
            if best.ID == self.progOutfits[-1]:  # last one reached?
                return(best)
            i = self.progOutfits.index(best.ID)
            if not self.canWear(self.progOutfits[i+1]):
                return(best)
            return(GAME.items[self.progOutfits[i+1]])


        def getSlot(self, slotID):
            slot = [i for i in self.outfit if i.slot == slotID]
            if slot:
                return(slot[0])
            return(False)

        def meetsReq(self, attr, reqAttr):
            if attr in reqAttr.keys():
                if self.attr[attr] < reqAttr[attr]:
                    return(False)
            return(True)

        def refuseItem(item):
            for k in item.req.keys():
                if self.attr[k] < item.req[k]:
                    return(k)
            return(False)

        def getNaked(self):
            self.outfit = []
            return()

        def passItem(self, ID, recipient, mode = "inventory"):
            self.remItem(ID)
            recipient.addItem(ID, mode)
            return()

        def addItem(self, ID, mode = "inventory"):
            if mode == "inventory":
                self.inventory.append(GAME.items[ID])
            elif mode == "wardrobe":
                if GAME.items[ID] not in self.wardrobe:
                    self.wardrobe.append(GAME.items[ID])
            return()

        def remItem(self, ID):
            self.inventory.remove(GAME.items[ID])
            return()

        def hasItem(self,ID, amount = 1):
            if ID not in [x.ID for x in GAME.items.values()]:
                return(False)
            if (self.inventory + self.wardrobe).count(GAME.items[ID]) >= amount:
                return (True)
            return(False)

        def getActivityOutfit(self):
            outfitID = False
            for i in self.activities:
                if eval(i[0]):
                    outfitID = i[3]
            return(outfitID)

        def uniformOK(self, uniformID, keys=False):
            uniformOrder = GAME.uniformDB[uniformID]
            # special cases
            if uniformID == "UID4_Krell" and GAME.getStarSys().race == "Krell":
                return(True)
            # attributes to check 
            if not keys:
                keys = uniformOrder.req.keys()             
            result = all([self.meetsReq(k, uniformOrder.req) for k in keys])      
            return(result)

        def getDressed(self, mode = "auto"): # only works for crew, auto/no_nude
            # loc override ?
            if self.getLocOverride() and mode == "auto": # no autonomy 
                return()
            if self in [bb_2]:
                return()

            # activity outfit?
            if self in GAME.crew:
                outfit = self.getActivityOutfit()
                uniformID  = GAME.uniformDB[self.uniform].itemID
                # autonomy? 
                if uniformID == "ITMUniformFree":
                    # get maximum lewd available progressive free uniform if data available
                    self.putOn(self.getProgOutfit("available").ID)
                # nude activity?                    
                elif uniformID == "ITMUniformNude" and mode != "no_nude":
                    self.getNaked()
                else:
                    self.putOn(uniformID)
                #elif outfit == False:
                #    self.getNaked()    

            return()

        def putOn(self, itemID, mode = "autonomy"):
            # CR-0832 START ============
            # exception processing / progressive outfits
            if mode == "autonomy":
                if self.ID == "ACTTRIS" and itemID == "ITMOutfitValkora":
                    itemID = self.getProgOutfit("available").ID
            # CR-0832 END ==============

            # apply uniform
            if itemID == False:
                self.getNaked()
                return()
            item = GAME.items[itemID]
            for i in self.outfit:
                if i.slot == item.slot:
                    self.undress(i.ID)
            self.outfit.append(item)
            return()

        def undress(self, itemID):
            item = GAME.items[itemID]
            for i in self.outfit:
                if i.ID == itemID:
                    self.outfit.remove(item)
            return()

        def wears(self, itemID):
            for i in self.outfit:
                if i.ID == itemID:
                    return(True)
            return(False)

        def isNaked(self):
            if self.getSlot("body"):
                return(False)
            return(True)

        def isPlugged(self):
            for i in self.outfit:
                if "plug" in i.ID.lower():
                    return(True)
            return(False)

        #####################################################################
        #                ACTOR: States etc.
        #####################################################################
        def setAttr(self, k, amount):
            if k not in self.attr.keys():
                self.attr[k] = 0
            if k not in self.attrFrac.keys():
                self.attrFrac[k] = 0.0
            self.attr[k] = amount
            self.attrFrac[k] = float(amount)
            return()

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

        def getAttrMax(self,k):
            if k in self.attrMax.keys():
                return(self.attrMax[k])
            return(100.0)

        def getAttrFrac(self, k):
            if k in self.attrFrac.keys():
                return(self.attrFrac[k])
            return(0)

        def incAttrFrac(self, attr, inc, limit=False): # fractional attribute increment
            if not limit:
                limit = self.getAttrMax(attr)

            if attr not in self.attrFrac.keys():
                self.attrFrac[attr] = 0.0

            limit = min(limit, self.getAttrMax(attr))
            inc = min(inc, limit-self.attrFrac[attr])
            inc = max(0, inc)
            self.attrFrac[attr] += inc
            round(self.attrFrac[attr],3)
            return(self.attrFrac[attr])

        def getRating(self):
            rating = {1: "Harmless", 2: "Mostly Harmless", 3:"Average" , 4: "Capable" , 5: "Proficient", 6: "Superior", 7:"Unparalleled", 8: "Paragon", 9: "Galactic Paragon"}[capValue(self.lvl,1,9)]
            return(rating)

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

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

        #def meetsReq(self, req): # STUB
        #    return(True)

        def setActivities(self, ID):
            self.activities = GAME.activityDB[ID]
            return()

        def getActivity(self):
            activity = None
            for i in self.activities:
                if eval(i[0]):
                    return(i[2])
            return(activity)

        def getActivitySpritePos(self):
            activity = None
            for i in self.activities:
                if eval(i[0]):
                    if len(i) >= 3:
                        return(i[2]) # position data found in db
                    else:
                        return([0,0]) # default full screen sprite (lazy)
            return([0,0])

        def getActions(self):
            actions = []
            i = 0
            while i < len(self.actions):
                if eval(self.actions[i+1]):
                    actions.append(self.actions[i])
                i += 2
            return(actions)

        def hasDone(self,topic, n=1):
            if self.done.count(topic) >= n:
                return(True)
            else:
                self.done.append(topic)
                return(False)

        def hasDoneToday(self,topic, n=1):
            if self.doneToday.count(topic) >= n:
                return(True)
            else:
                self.doneToday.append(topic)
                return(False)

        def isAsleep(self):
            if self.getActivity() in ["sleep"]:
                return(True)
            return(False)

        #####################################################################
        #					NEW LOC OVERRIDE		
        #####################################################################

        def setLocOverride(self, locID, timeSlot = False):
            if timeSlot:
                self.locOverrideReg[timeSlot] = locID # CR-0509
            else:
                self.clearLocOverride() # clear registry
                self.locOverrideReg[99] = locID
            return()

        def getLocOverride(self):
            if 99 in self.locOverrideReg.keys():
                return(self.locOverrideReg[99])
            elif GAME.hour in self.locOverrideReg.keys():
                return(self.locOverrideReg[GAME.hour])
            return(False)

        def clearLocOverride(self, timeSlot=False):
            if timeSlot:
                if timeSlot in self.locOverrideReg.keys():
                    del self.locOverrideReg[timeSlot]
                    return(True)
            else:
                self.locOverrideReg = {}
                return(True)
            return(False)

        #####################################################################
        #					MISC		
        #####################################################################


        def isAt(self):
            if self.getLocOverride():
                return(self.getLocOverride()) 
            for i in self.activities:
                if eval(i[0]):
                    return(i[1])
            return(False)

        def isHere(self):
            locID = GAME.getLoc().ID
            if self.getLocOverride():
                if self.getLocOverride() == locID:
                    return(True)
                return(False)
            for i in self.activities:
                if i[1] == locID:
                    if eval(i[0]):
                        return(True)
            return(False)

        def updateAI(self, mode = "standard"):
            # Overrides 
            if self.getLocOverride():
                self.getDressed()
                return()

            # Sleeping
            if self.getActivity() == "sleep":
                if "sleeping" not in self.status:
                    self.status.append("sleeping")
            elif "sleeping" in self.status:
                self.status.remove("sleeping")

            # Location


            # Attribute management
            if not self.hasDoneToday("impact_uniform"):
                uniform = GAME.uniformDB[self.uniform]
                for k in [x for x in uniform.effect.keys() if x in self.attr.keys()]:
                    impact = uniform.effect[k]

                    # EXH level attenuating effect on MOD drops
                    if "EXH" in uniform.req.keys():
                        if k == "MOD":
                            impact += self.attr["EXH"]*2

                    self.attr[k] = capValue(self.attr[k]+impact,0,100)


            # Job 
            self.workAI()
            # khalera repair adjustments in engine room 
            if khelara in GAME.crew:
                GAME.repQueue = [x for x in GAME.repQueue if "AERepEngine" not in x]
                GAME.repMisses = [x for x in GAME.repMisses if "AERepEngine" not in x]
             
            # Dress
            self.adjustProgOutfit()
            self.getDressed()

            # Other
            if "facial" in self.tags:
                self.tags.remove("facial")

            return()


