Package horizons :: Package component :: Module componentholder
[hide private]
[frames] | no frames]

Source Code for Module horizons.component.componentholder

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5   
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  from horizons.component import Component 
 23  from horizons.component.ambientsoundcomponent import AmbientSoundComponent 
 24  from horizons.component.collectingcomponent import CollectingComponent 
 25  from horizons.component.coloroverlaycomponent import ColorOverlayComponent 
 26  from horizons.component.commandablecomponent import CommandableComponent 
 27  from horizons.component.depositcomponent import DepositComponent 
 28  from horizons.component.fieldbuilder import FieldBuilder 
 29  from horizons.component.healthcomponent import HealthComponent 
 30  from horizons.component.inventoryoverlaycomponent import InventoryOverlayComponent 
 31  from horizons.component.namedcomponent import ( 
 32          InhabitantNameComponent, NamedComponent, PirateShipNameComponent, SettlementNameComponent, 
 33          ShipNameComponent, SoldierNameComponent) 
 34  from horizons.component.restrictedpickup import RestrictedPickup 
 35  from horizons.component.selectablecomponent import SelectableComponent 
 36  from horizons.component.storagecomponent import StorageComponent 
 37  from horizons.component.tradepostcomponent import TradePostComponent 
 38  from horizons.world.production.producer import ( 
 39          GroundUnitProducer, Producer, QueueProducer, ShipProducer) 
40 41 42 -class ComponentHolder:
43 """ 44 Class that manages Component plug-ins 45 It can be inherited by all objects that can hold components 46 47 TUTORIAL: 48 I can't explain component-oriented architecture to you here, but I can give you 49 an overview of how we use it: 50 Instead of putting all different features of entities into single classes, 51 as is common in OOP, each feature is put into a component. This should 52 increase the encapsulation, and it's easier if an object consists of 15 independent 53 building blocks than if it were 15 classes, where many override the same function call 54 and fight about who gets called first. 55 Check class_mapping for a complete list of the different components we use. 56 57 The components are stored in a dict, where the key is their name (a string). 58 This is necessary so objects can be defined as a collection of their components in 59 human readable format. This is done via yaml files in content/objects in our case. 60 You could check out e.g. content/objects/buildings/lumberjackcamp.yaml to see what 61 it looks like. 62 63 This class manages the components; it stores them and makes them accessible. 64 Check out the actual component class in horizons/component/__init__.py. 65 """ 66 67 class_mapping = { 68 'AmbientSoundComponent': AmbientSoundComponent, 69 'CommandableComponent': CommandableComponent, 70 'CollectingComponent': CollectingComponent, 71 'ColorOverlayComponent': ColorOverlayComponent, 72 'DepositComponent': DepositComponent, 73 'FieldBuilder': FieldBuilder, 74 'HealthComponent': HealthComponent, 75 'InventoryOverlayComponent': InventoryOverlayComponent, 76 'NamedComponent': NamedComponent, 77 'PirateShipNameComponent': PirateShipNameComponent, 78 'SoldierNameComponent': SoldierNameComponent, 79 'InhabitantNameComponent': InhabitantNameComponent, 80 'ProducerComponent': Producer, 81 'SettlementNameComponent': SettlementNameComponent, 82 'ShipNameComponent': ShipNameComponent, 83 'StorageComponent': StorageComponent, 84 'QueueProducerComponent': QueueProducer, 85 'RestrictedPickup': RestrictedPickup, 86 'SelectableComponent': SelectableComponent, 87 'TradePostComponent': TradePostComponent, 88 'ShipProducerComponent': ShipProducer, 89 'GroundUnitProducerComponent': GroundUnitProducer, 90 } 91
92 - def __init__(self, *args, **kwargs):
93 super().__init__(*args, **kwargs) # TODO: check if this line is needed 94 self.components = {}
95
96 - def initialize(self, **kwargs):
97 """Has to be called every time a componentholder is created. This is not 98 in __init__() because we need to make sure that all other sub/parent classes 99 have been inited, for example the ConcreteObject class. This is to ensure 100 that all member variables of sub/parent classes are correctly set when we 101 init the components. If someday all code is moved to components, this will 102 not be necessary any more.""" 103 for component in self.__create_components(): 104 self.add_component(component)
105
106 - def __create_components(self):
107 tmp_comp = [] 108 if hasattr(self, 'component_templates'): 109 for entry in self.component_templates: 110 if isinstance(entry, dict): 111 for key, value in entry.items(): 112 # TODO: try to pass read-only data to get_instance, since it's usually 113 # cached and changes would apply to all instances 114 # dict views of python2.7 could be a start. 115 component = self.class_mapping[key].get_instance(value) 116 tmp_comp.append(component) 117 else: 118 component = self.class_mapping[entry].get_instance() 119 tmp_comp.append(component) 120 # 'Resolve' dependencies by utilizing overloaded gt/lt 121 tmp_comp.sort() 122 return tmp_comp
123
124 - def remove(self):
125 for component in list(self.components.values()): 126 component.remove() 127 super().remove()
128
129 - def load(self, db, worldid):
130 super().load(db, worldid) 131 self.components = {} 132 for component in self.__create_components(): 133 component.instance = self 134 component.load(db, worldid) 135 self.components[component.NAME] = component
136
137 - def save(self, db):
138 super().save(db) 139 for component in self.components.values(): 140 component.save(db)
141
142 - def add_component(self, component):
143 """ 144 Adds new component to holder and sets the instance attribute on the component 145 @param component: a component instance that is to be added 146 all components will have the init only with instance attribute 147 """ 148 if not isinstance(component, Component): 149 print(component, type(component), component.__class__) 150 assert isinstance(component, Component) 151 component.instance = self 152 self.components[component.NAME] = component 153 component.initialize()
154
155 - def remove_component(self, component_class):
156 """ 157 Removes component from holder. 158 """ 159 if self.has_component(component_class): 160 self.components[component_class.NAME].remove() 161 del self.components[component_class.NAME]
162
163 - def has_component(self, component_class):
164 """ 165 Check if holder has component with component name 166 """ 167 return component_class.NAME in self.components
168
169 - def get_component(self, component):
170 return self.components.get(component.NAME)
171
172 - def get_component_by_name(self, name):
173 return self.components.get(name)
174 175 @classmethod
176 - def get_component_template(cls, component):
177 """Returns the component template data given a component NAME""" 178 for entry in cls.component_templates: 179 if isinstance(entry, dict): 180 for key, value in entry.items(): 181 if cls.class_mapping[key] == component or key == component: 182 return value 183 raise KeyError("This class does not contain a component with name: {0}".format(component))
184