Пример #1
0
 def __init__(self,name='Place Holder',station=None, logger=None):
     self.my_tasks = TaskTracker()
     self.id = util.register(self) 
     self.name = name
     if logger:
         self.logger = logging.getLogger(logger.name + '.' + self.name)
     elif station:
         self.logger = logging.getLogger(station.logger.name + '.' + self.name)
     else: 
         self.logger = logging.getLogger(util.generic_logger.name + '.' + self.name)
     self.loggername = self.logger.name    
         
     self.needs = dict()
     self.needs['Idle Curiosity']=Need('Idle Curiosity', self, 100, 0, 0, self.new_idle_task, None)
     self.task = None #currently active task
     self.action = None #placeholder for graphical animation or tweening or w/e
     self.task_abandon_value = 5 #if a task's importance outweighs the current task by this much, we drop and switch
     self.location = None
     self.station = station
     self.inventory = Stowage(0.5)
     self.path=None
     self.held=None
     self.sprite=None
     self.xyz = np.array([ 0, 0, 0 ])
     self.orientation = np.array([ 0, 0, 0 ])
     self.speed = 1.0 #meter per second travel time, "A leisurely float"
     
     if self.station is not None:
         self.station.actors[self.id] = self
         self.location = self.station.random_location()[1]
         self.xyz = self.station.loc_to_xyz(self.location)                         
     
     self.refresh_image()
Пример #2
0
 def __init__(self,initial_module=None, name=None, logger=None):
     self.modules=dict()
     self.exterior_objects=[]
     self.resources=ResourceBundle()
     self.paths=nx.Graph()
     self.tasks=TaskTracker()
     self.actors=dict()
     self.name = name if name else "GenericStation"
     self.logger = logging.getLogger(logger.name + '.' + self.name) if logger else util.generic_logger        
     
     if initial_module: self.berth_module(None,None,initial_module,None)                        
Пример #3
0
 def __init__(self,name='Place Holder',station=None, logger=None):
     self.my_tasks = TaskTracker()
     self.id = str(uuid.uuid4())  
     self.name = name
     self.logger = logging.getLogger(logger.name + '.' + self.name) if logger else util.generic_logger
     self.needs = dict()
     self.needs['Idle Curiosity']=Need('Idle Curiosity', self, 100, 0, 0, self.new_idle_task, None)
     self.task = None #currently active task
     self.action = None #placeholder for graphical animation or tweening or w/e
     self.task_abandon_value = 5 #if a task's importance outweighs the current task by this much, we drop and switch
     self.location = None
     self.station = station
     self.inventory = Stowage(0.5)
     self.path=None
     self.held=None
     self.xyz = np.array([ 0, 0, 0 ])
     self.orientation = np.array([ 0, 0, 0 ])
     self.speed = 1.0 #meter per second travel time, "A leisurely float"
     
     if not hasattr(self,'imgfile'): self.imgfile = "images/placeholder_actor.tif"
     self.refresh_image()
Пример #4
0
    def __init__(self,initial_module=None, name=None, logger=None, mission_control = None):
        self.modules=dict()
        self.id = util.register(self)
        self.exterior_objects=[]
        self.resources=ResourceBundle()
        self.paths=nx.Graph()
        self.tasks=TaskTracker()
        self.actors=dict()
        self.name = name if name else "GenericStation"
        self.logger = logging.getLogger(logger.name + '.' + self.name) if logger else util.generic_logger        
        self.loggername = self.logger.name
        
        self.docked_stations = []

        self.location = 'LEO'
        self.position = 'Approach'
        
        self.mission_control = mission_control
        
        self.sprite = None
        self.refresh_image()
        
        if initial_module: self.dock_module(None,None,initial_module,None)                        
Пример #5
0
class Station():
    def __init__(self,initial_module=None, name=None, logger=None, mission_control = None):
        self.modules=dict()
        self.id = util.register(self)
        self.exterior_objects=[]
        self.resources=ResourceBundle()
        self.paths=nx.Graph()
        self.tasks=TaskTracker()
        self.actors=dict()
        self.name = name if name else "GenericStation"
        self.logger = logging.getLogger(logger.name + '.' + self.name) if logger else util.generic_logger        
        self.loggername = self.logger.name
        
        self.docked_stations = []

        self.location = 'LEO'
        self.position = 'Approach'
        
        self.mission_control = mission_control
        
        self.sprite = None
        self.refresh_image()
        
        if initial_module: self.dock_module(None,None,initial_module,None)                        

    def __getstate__(self):
        d = dict(self.__dict__)
        del d['logger']
        return d    
        
    def __setstate__(self, d):
        self.__dict__.update(d)   
        self.logger = logging.getLogger(self.loggername) if self.loggername else util.generic_logger    
                       
    def refresh_image(self):
        if util.GRAPHICS == 'cocos2d':
            if self.sprite is None:               
                self.sprite = util.Layer()
                       
    def dock_module(self, my_module, my_dock, module, mod_dock, instant = False):        
        if module and not self.modules:
            self.modules[module.id]=module
            module.station = self
            module.refresh_station()
            self.paths = module.paths.copy()
            self.position = 'Docked'
            return
            
        if not my_module: my_module = random.choice( [ self.modules[m] for m in self.modules if self.modules[m].get_random_dock() ] )    
        if not my_module.id in self.modules: assert False, "Requested module not part of station"
        if module.id in self.modules: assert False, "Requested new module already part of station"
                
        if not my_dock: my_dock = my_module.get_random_dock()                 
        if not mod_dock: mod_dock = module.get_random_dock(side_port_allowed=False)                                        
                
        #attempt docking
        assert module.dock(mod_dock, my_module, my_dock)
        
        if module.station != self and not module.station in self.docked_stations:
            #merge stations
            other_station = module.station
            
            n=self.paths.nodes()
            e=self.paths.edges(data=True)
            
            self.paths.add_nodes_from(other_station.paths.nodes())
            self.paths.add_edges_from(other_station.paths.edges(data=True))
            other_station.paths.add_nodes_from(n)
            other_station.paths.add_edges_from(e)
            
            self.docked_stations.append(other_station)
            other_station.docked_stations.append(self)
                
        module.connect(mod_dock, my_module, my_dock, instant)        
        
        #remove if hanging outside
        if module in self.exterior_objects: self.exterior_objects.remove(module)
        
        #merge resources
        self.resources.grow()
        
        self.modules[module.id]=module  
        self.logger.info(''.join(["Modules berthed: ",my_module.short_id,'(',my_dock,')',' to ',module.short_id,'(',mod_dock,')']))
        
    def join_station(self, other_station):          
        if not other_station in self.docked_stations:
            self.logger.warning('Attempting to join two stations that are apparently not docked.')
            
        for m in other_station.modules.keys():
            other_station.modules[m].station = self
            other_station.modules[m].refresh_station()
            if not m in self.modules:
                self.modules[m] = other_station.modules[m]
            else:
                self.modules[other_station.modules[m].id] = other_station.modules[m]
            other_station.modules.pop(m)
                    
        for a in other_station.actors.keys():
            other_station.actors[a].station = self
            self.actors[a] = other_station.actors[a]
            self.actors[a].refresh_station()
            other_station.actors.pop(a)
            
        if other_station in self.docked_stations: 
            self.docked_stations.remove(other_station)


    def percolate_location(self,init_mod):
        self.reset_module_touches()
        init_mod.percolate_location()
        
    
    def split_station(self, docking_ring):
        '''Splits off the module WITH the given docking ring into a new station, percolates it to connected modules'''
        if not docking_ring or not docking_ring.installed or not docking_ring.docked: return
        self.reset_module_touches()
        docking_ring.docked.touched = True
        new_station_list = docking_ring.installed.percolate()
        if len(new_station_list) == len(self.modules.values()) -1:
            self.logger.warning(''.join(["Attempting to split a doubly or-higher connected module from station"])) 
            pass
            
        #Validate new station
        for n in new_station_list:
            if n.manifest and not n.manifest.satisfied:
                self.logger.info("Station splitting failed: at least one split module has unsatisfied manifest!")
                return False    
            
        new_station = Station(None, "Return Station", self.logger)
        new_station.position='Docked'
        self.docked_stations.append(new_station)
        new_station.docked_stations.append(self)
        new_station.paths.add_nodes_from(self.paths.nodes())
        new_station.paths.add_edges_from(self.paths.edges(data=True))
                     
        util.scenario.add_station(new_station)
        
        for a in self.actors.keys():
            if self.get_module_from_loc(self.actors[a].location) in new_station_list:
                self.actors[a].station = new_station
                new_station.actors[a] = self.actors[a]
                self.actors[a].refresh_station()
                self.actors.pop(a)
        
        for m in new_station_list:
            m.station = new_station
            new_station.modules[m.id] = m
            m.refresh_station()
            self.modules.pop(m.id)
                                                            
        self.logger.info(''.join(["Successfully split station! New station: ",util.short_id(new_station.id)]))
        
        return new_station

    def undock_station(self,other_station):
        if other_station == self: return
        if other_station in self.docked_stations: 
            self.docked_stations.remove(other_station)
        else:
            return
        
        #prune nodes, edges
        for n in self.paths.nodes(): 
            if self.get_module_from_loc(n) == None:
                self.paths.remove_node(n)
        for e in self.paths.edges():
            if not (e[0] in self.paths and e[1] in self.paths):
                self.paths.remove_edge(e)
                
        other_station.undock_station(self)
        
    def reset_module_touches(self):
        for m in self.modules.values():
            m.touched=False

    def get_safe_distance_orient(self):
        return np.array([-30,-30+60*random.random(),0]), np.array([ -math.pi +2*math.pi*random.random(), 0 ])

    def position_at_safe_distance(self,module):
        station=None
        if isinstance(module, Station):
            station = module
            module = module.modules.values()[0]
        #TODO calculate boundary of station, multiply by 1.25
        safe_location, safe_orient = self.get_safe_distance_orient()
        
        module.location = 2*safe_location
        module.orientation = safe_orient
        
        if station is not None:
            station.percolate_location(module)
        
        if not module.station: self.exterior_objects.append(module)
        module.refresh_image()                
        
        
    def begin_docking_approach(self,module,dock=None):                
        dock_comp, d, d = self.search( EquipmentFilter( target='Docking Computer' ) )
        if not dock_comp:
            #TODO fail more gracefully
            assert False, 'Docking initialized with no active docking computer!  WTF mang?'
        dock_comp.dock_module([module,dock],[None,None])

    def begin_undocking_approach(self,module,dock=None):                
        dock_comp, d, d = self.search( EquipmentFilter( target='Docking Computer' ) )
        if not dock_comp:
            #TODO fail more gracefully
            assert False, 'Undocking initialized with no active docking computer!  WTF mang?'
        dock_comp.undock_module([module,dock])

    def get_random_dock(self, side_port_allowed = True, modules_to_exclude=[]):
        hits=[]
        for m in [n for n in self.modules.values() if not n in modules_to_exclude]:
            d = m.get_random_dock( side_port_allowed )
            if d: hits.append( [ m, d ] )    
        random.shuffle(hits)  
        hits.sort(key=lambda tup: tup[0], reverse=True)
        return hits[0] if hits else [None, None]              
        
        
    def search(self, filter_, modules_to_exclude=[]):
        hits=[]
        if [m for m in self.modules.values() if not m in modules_to_exclude] == []:
            return [None, None, None]        
        for m in [n for n in self.modules.values() if not n in modules_to_exclude]:
            [obj, loc, score] = m.search(filter_)
            hits.append( [ obj, loc, score ] )
        random.shuffle(hits)    
        hits.sort(key=lambda tup: tup[2], reverse=True)
        #print hits
        return hits[0] if hits and hits[0][2] else [None, None, None]
        
        
    def random_location(self, modules_to_exclude=[]):        
        if [m for m in self.modules.values() if not m in modules_to_exclude] == []:
            return [None, None, None]
        module = random.choice([m for m in self.modules.values() if not m in modules_to_exclude])
        return [None, module.filterNode( module.node('Inside') ), None]
        
    def update(self,dt):
        self.resources.update(dt)
        self.tasks.update(dt)
        
        #print [[t.name,str(t.touched)] for t in self.tasks.tasks]
        for m in self.modules.keys():
            if m in self.modules: self.modules[m].update(dt)     
        for a in self.actors.values():
            a.update(dt)      
        
            
    def loc_to_xyz(self,loc, percolate=True):
        [ node, name ] = separate_node(loc)
        module = self.get_module_from_loc(loc, percolate)
        if name == "Inside": return module.location
        return module.getXYZ(module.nodes[loc])
        
    def xyz_to_module(self, xyz):
        pass      #needs collision detection
        
    def get_module_from_loc(self, loc, percolate=True):
        [ node, name ] = separate_node( loc )
        module = [ self.modules[ m ] for m in self.modules if self.modules[ m ].id == node ]        
        if not module: 
            if percolate:
                for s in self.docked_stations:
                    new_loc = s.get_module_from_loc(loc,False)
                    if new_loc != None: return new_loc
            return None
        return module[ 0 ]
        
    def draw(self, window):
        if not window: return self.logger.warning("Requested draw to Nonetype.")
        for m in self.exterior_objects:
            m.draw(window)
        for m in self.modules.values():
            m.draw(window)
        for a in self.actors:
            self.actors[a].update_location()
            self.actors[a].sprite.draw()
Пример #6
0
class Actor(object):
    def __init__(self,name='Place Holder',station=None, logger=None):
        self.my_tasks = TaskTracker()
        self.id = str(uuid.uuid4())  
        self.name = name
        self.logger = logging.getLogger(logger.name + '.' + self.name) if logger else util.generic_logger
        self.needs = dict()
        self.needs['Idle Curiosity']=Need('Idle Curiosity', self, 100, 0, 0, self.new_idle_task, None)
        self.task = None #currently active task
        self.action = None #placeholder for graphical animation or tweening or w/e
        self.task_abandon_value = 5 #if a task's importance outweighs the current task by this much, we drop and switch
        self.location = None
        self.station = station
        self.inventory = Stowage(0.5)
        self.path=None
        self.held=None
        self.xyz = np.array([ 0, 0, 0 ])
        self.orientation = np.array([ 0, 0, 0 ])
        self.speed = 1.0 #meter per second travel time, "A leisurely float"
        
        if not hasattr(self,'imgfile'): self.imgfile = "images/placeholder_actor.tif"
        self.refresh_image()
     
    def refresh_image(self):
        self.img = util.load_image(self.imgfile)
    
    def draw(self,window):
        zoom=util.ZOOM
        l= self.path.current_coords if (self.path and not self.path.completed) else self.station.loc_to_xyz( self.location )
        self.img.blit(zoom*l[0], zoom*l[1], 0)        
        
    def drop(self):
        pass #TODO drop held item        
        
    def new_idle_task(self,timeout,severity):
        t=Task(''.join(['Satisfy Idle Curiosity']), owner = self, timeout = None, task_duration = 150, severity='IGNORABLE', fetch_location_method=self.station.random_location,logger=self.logger)
        return t     
        
    def update(self,dt):
        self.my_tasks.update(dt)
        for n in self.needs.values():
            n.update(dt)
            
        self.inventory.update(dt)
        
        if self.task and self.task.task_ended():            
            self.task=None
        
        #'grab new task'                
        _curr_value = -5 if not self.task else self.task.task_value()
        _new_task = self.my_tasks.fetch_open_task() if not self.station else max(self.my_tasks.fetch_open_task(), self.station.tasks.fetch_open_task())
        #print "TASK VALUES", self.my_tasks.fetch_open_task()
        if _new_task and _new_task.task_value() > _curr_value + self.task_abandon_value:            
            if self.task: self.task.drop()
            self.task = _new_task
            self.task.assign(self)
        
        #'work on task'
        if not self.task: return
        if not self.task.location: 
            self.task.target, self.task.location, d = self.task.fetch_location() #Grab location
            #print self.task.name, self.task.target, self.task.location
            if not self.task.location: 
                self.task.drop()
                self.task=None
                return
        if self.location == self.task.location: 
            #work on task
            #print "Check", self.task.name
            self.task.do_work(dt)
            #print [[c.name, c.volume] for c in self.inventory.contents]
        else:
            if self.task.location and not self.task.location == self.location: 
                #go to location 
                if self.path and not self.path.completed:
                    self.path.traverse_step(dt*self.speed)
                else:
                    #print self.task.name, self.task.target, self.task.location
                    new_path = PathingWidget(self,self.station.paths,self.location, self.task.location, self.xyz)
                    if not new_path.valid: assert False, "ERROR: some kind of pathing error!"
                    self.path = new_path
                    self.path.traverse_step(dt*self.speed)
                    
    def transfer_node(self, new_loc ):
        '''moving to new node, exchange air flow'''
        my_mod = self.station.get_module_from_loc( self.location )
        new_mod = self.station.get_module_from_loc( new_loc )
        my_mod.atmo.mix( new_mod.atmo, 1.0 )             
       
    def summarize_needs(self, ret_string=False):
        if ret_string:
            out = 'Needs'
            for n in self.needs.keys():
                #print n, self.needs[n].current_severity()
                out = ''.join([out,'; ',n,'-',self.needs[n].current_severity()])
            return out #what a huge PITA
        return [[n, self.needs[n].current_severity()] for n in self.needs.keys()]
        
    def log_status(self):
        #TODO log task
        #self.logger.info(self.summarize_needs(True))
        self.logger.info(self.task.name if self.task else "Task: idling")        
Пример #7
0
class Actor(object):
    def __init__(self,name='Place Holder',station=None, logger=None):
        self.my_tasks = TaskTracker()
        self.id = util.register(self) 
        self.name = name
        if logger:
            self.logger = logging.getLogger(logger.name + '.' + self.name)
        elif station:
            self.logger = logging.getLogger(station.logger.name + '.' + self.name)
        else: 
            self.logger = logging.getLogger(util.generic_logger.name + '.' + self.name)
        self.loggername = self.logger.name    
            
        self.needs = dict()
        self.needs['Idle Curiosity']=Need('Idle Curiosity', self, 100, 0, 0, self.new_idle_task, None)
        self.task = None #currently active task
        self.action = None #placeholder for graphical animation or tweening or w/e
        self.task_abandon_value = 5 #if a task's importance outweighs the current task by this much, we drop and switch
        self.location = None
        self.station = station
        self.inventory = Stowage(0.5)
        self.path=None
        self.held=None
        self.sprite=None
        self.xyz = np.array([ 0, 0, 0 ])
        self.orientation = np.array([ 0, 0, 0 ])
        self.speed = 1.0 #meter per second travel time, "A leisurely float"
        
        if self.station is not None:
            self.station.actors[self.id] = self
            self.location = self.station.random_location()[1]
            self.xyz = self.station.loc_to_xyz(self.location)                         
        
        self.refresh_image()
     
    def __getstate__(self):
        d = dict(self.__dict__)
        del d['logger']
        del d['sprite']        
        #d.pop('station') #TODO delete
        #d.pop('needs')#TODO delete
        return d    
        
    def __setstate__(self, d):
        self.__dict__.update(d)   
        util.register(self,self.id) 
        self.logger = logging.getLogger(self.loggername) if self.loggername else util.generic_logger    
        self.sprite=None
        self.refresh_image()           
     
    def refresh_image(self):
        if gv.config['GRAPHICS'] == 'pyglet': 
            import graphics_pyglet           
            if self.sprite: self.sprite.delete()
            
            self.sprite = graphics_pyglet.LayeredSprite(name=self.name,batch=util.actor_batch )     
            self.sprite.add_layer('ActorBase',util.load_image("images/npc_crafty_bot__x1_idle0_png_1354839494_crop.png"))
            self.sprite.owner = self
        
    def update_location(self):
        zoom = gv.config['ZOOM']
        l= self.path.current_coords if (self.path and not self.path.completed) else self.station.loc_to_xyz( self.location )
        self.xyz=l
        self.sprite.update_sprite(zoom*l[0], zoom*l[1],0)
       
        
    def drop_held(self):
        if self.held is None: return
        module = self.station.get_module_from_loc( self.location )
        module.stowage.add(self.held)
        self.held = None
        
    def new_idle_task(self,timeout,severity):
        t=Task(''.join(['Satisfy Idle Curiosity']), owner = self, timeout = None, task_duration = 150, severity='IGNORABLE', fetch_location_method=self.station.random_location,logger=self.logger)
        return t     
        
    def refresh_station(self):
        self.task=None    
        self.path=None
        #self.xyz = self.station.loc_to_xyz( self.location )
        if self.station: self.logger = logging.getLogger(self.station.logger.name + '.' + self.name)
        
    def update(self,dt):
        self.my_tasks.update(dt)
        for n in self.needs.values():
            n.update(dt)
            
        self.inventory.update(dt)        
        
        if self.task and self.task.task_ended():            
            self.task=None
            return
        #'grab new task'                
        _curr_value = -5 if not self.task else self.task.task_value()
        _new_task = self.my_tasks.fetch_open_task() if not self.station else max(self.my_tasks.fetch_open_task(), self.station.tasks.fetch_open_task())
        #print "TASK VALUES", self.my_tasks.fetch_open_task()
        if _new_task and _new_task.task_value() > _curr_value + self.task_abandon_value:            
            if self.task: self.task.drop()
            self.task = _new_task
            self.task.assign(self)
            self.logger.info("New task:"+self.task.name)
        #'work on task'
        if not self.task: return
        if not self.task.location:
            self.task.target, self.task.location, d = self.task.fetch_location() #Grab location
            if not self.task.location: 
                self.task.drop()
                self.task=None
                return
                
               
        if self.location == self.task.location: 
            #work on task
            self.task.do_work(dt)
            
        else:
            if self.task.location and not self.task.location == self.location: 
                #go to location 
                if self.path and not self.path.completed:
                    if not self.path.traverse_step(dt*self.speed):
                        self.logger.warning("Pathing error: Path suddenly invalid "+self.task.name+"!")
                        self.task.drop()
                        self.task=None
                        return                    
                else:
                    #print self.task.name, self.task.target, self.task.location
                    new_path = PathingWidget(self,self.station.paths,self.location, self.task.location, self.xyz)
                    if not new_path.valid: 
                        self.logger.warning("Pathing error: "+self.task.name+": !")
                        self.task.drop()
                        self.task=None
                        return
                    self.path = new_path
                    self.path.traverse_step(dt*self.speed)
                    
    def transfer_node(self, new_loc ):
        '''moving to new node, exchange air flow'''
        my_mod = self.station.get_module_from_loc( self.location )
        new_mod = self.station.get_module_from_loc( new_loc )
        my_mod.mix_neighbor(new_mod, 1.0 )             
       
    def summarize_needs(self, ret_string=False):
        if ret_string:
            out = 'Needs'
            for n in self.needs.keys():
                #print n, self.needs[n].current_severity()
                out = ''.join([out,'; ',n,'-',self.needs[n].current_severity()])
            return out #what a huge PITA
        return [[n, self.needs[n].current_severity()] for n in self.needs.keys()]
        
    def log_status(self):
        #TODO log task
        #self.logger.info(self.summarize_needs(True))
        self.logger.info(self.task.name if self.task else "Task: idling")        
Пример #8
0
class Station():
    def __init__(self,initial_module=None, name=None, logger=None):
        self.modules=dict()
        self.exterior_objects=[]
        self.resources=ResourceBundle()
        self.paths=nx.Graph()
        self.tasks=TaskTracker()
        self.actors=dict()
        self.name = name if name else "GenericStation"
        self.logger = logging.getLogger(logger.name + '.' + self.name) if logger else util.generic_logger        
        
        if initial_module: self.berth_module(None,None,initial_module,None)                        
                       
    def berth_module(self, my_module, my_dock, module, mod_dock, instant = False):        
        if module and not self.modules:
            self.modules[module.id]=module
            module.station = self
            module.refresh_equipment()
            self.paths = module.paths.copy()
            return
            
        if not my_module: my_module = random.choice( [ self.modules[m] for m in self.modules if self.modules[m].get_random_dock() ] )    
        if not my_module.id in self.modules: assert False, "Requested module not part of station"
        if module.id in self.modules: assert False, "Requested new module already part of station"
                
        if not my_dock: my_dock = my_module.get_random_dock()                 
        if not mod_dock: mod_dock = module.get_random_dock(side_port_allowed=False)                                        
                
        #attempt docking
        assert module.berth(mod_dock, my_module, my_dock, instant)
        
        #remove if hanging outside
        if module in self.exterior_objects: self.exterior_objects.remove(module)
        
        #merge resources
        self.resources.grow()
        
        self.modules[module.id]=module  
        self.logger.info(''.join(["Modules berthed: ",my_module.short_id,'(',my_dock,')',' to ',module.short_id,'(',mod_dock,')']))
        
        
    def begin_docking_approach(self,module):
        #TODO calculate boundary of station, multiply by 1.25
        safe_location = np.array([-30,-30+60*random.random(),0])
        
        module.location = safe_location
        module.orientation = np.array([ -math.pi +2*math.pi*random.random(), 0 ])
        
        self.exterior_objects.append(module)
        module.refresh_image()
        
        dock_comp, d, d = self.search( EquipmentFilter( target='Docking Computer' ) )
        if not dock_comp:
            #TODO fail more gracefully
            assert False, 'Docking initialized with no active docking computer!  WTF mang?'
        dock_comp.dock_module([module,None],[None,None])

            
        
    def search(self, filter_):
        hits=[]
        for m in self.modules.values():
            [obj, loc, score] = m.search(filter_)
            hits.append( [ obj, loc, score ] )
        random.shuffle(hits)    
        hits.sort(key=lambda tup: tup[2], reverse=True)
        #print hits
        return hits[0] if hits and hits[0][2] else [None, None, None]
        
    def random_location(self):
        module = random.choice(self.modules.values())
        return None, module.filterNode( module.node('Inside') ), None
        
    def update(self,dt):
        self.resources.update(dt)
        self.tasks.update(dt)
        for m in self.modules:
            self.modules[m].update(dt)     
        for a in self.actors:
            self.actors[a].update(dt)       
            
    def loc_to_xyz(self,loc):
        [ node, name ] = separate_node(loc)
        module = [self.modules[m] for m in self.modules if self.modules[m].id == node]
        if not module: return None
        module = module[0]
        if name == "Inside": return module.location
        return module.getXYZ(module.nodes[loc])
        
    def xyz_to_module(self, xyz):
        pass      #needs collision detection
        
    def get_module_from_loc(self, loc):
        [ node, name ] = separate_node( loc )
        module = [ self.modules[ m ] for m in self.modules if self.modules[ m ].id == node ]        
        if not module: return None
        return module[ 0 ]
        
    def draw(self, window):
        if not window: return self.logger.warning("Requested draw to Nonetype.")
        for m in self.exterior_objects:
            m.draw(window)
        for m in self.modules.values():
            m.draw(window)
        for a in self.actors:
            self.actors[a].draw(window)