def __init__(self, name, color, position): super(SpellBase, self).__init__(color, position, 20) self.__name = name self.name_label = SpellLabel(self, name, (0, -self.radius*1.3)) self.efficiency_label = SpellLabel(self, '{}%'.format(int(self.efficiency*100))) self.is_template = False self.links_in = list() self.links_out = list()
class SpellLumen(SpellBase): def __init__(self, position): self.color = (255, 255, 255) super(SpellLumen, self).__init__('Lumen', self.color, position) self.__lumen_label = SpellLabel(self, '0', (0, self.radius * 1.3)) self.__lumen = 0.0 # set properties self.lumen = 3000 def create_instance(self): return SpellLumen(self.position) def calculate_local_power_requirement(self): # 1 cd = 1 lumen/sr # 1 cd = 683 W/sr # 1 lumen/sr = 1/683 W/sr # 1 lumen = 1/683 W return self.__lumen / 683.0 def draw(self, surface): super(SpellLumen, self).draw(surface) if self.is_activated: self.__lumen_label.draw(surface) @property def lumen(self): return self.__lumen @lumen.setter def lumen(self, lumen): self.__lumen = lumen self.__lumen_label.text = '{} lm'.format(int(self.__lumen)) @property def total_links_in(self): return 1 @property def total_links_out(self): return 0 @property def efficiency(self): # Based on the chemical reaction of luciferase with Adenosine triphosphate (ATP) (what fireflies use), # the most efficient energy-to-ligth conversion lies somewhere between 80% and 90%. # https://en.wikipedia.org/wiki/Luciferase return 0.85 @property def heat_dissipation(self): # luciferase reactions create almost no heat whatsoever. return 0.01
class Horn(SpellBase): def __init__(self, position): self.color = (255, 180, 255) super(Horn, self).__init__("Horn", self.color, position) self.is_draggable = False self.__num_supported_links = 2 self.__power_label = SpellLabel(self, '0 W', (-20, 0)) self.update_power() def draw(self, surface): super(Horn, self).draw(surface) pygame.draw.circle(surface, self.color, (self.position[0] - 32, self.position[1]), 30, 1) pygame.draw.circle(surface, self.color, (self.position[0] - 70, self.position[1]), 40, 1) pygame.draw.circle(surface, (0, 0, 0), (self.position[0] - 32, self.position[1]), 29) pygame.draw.circle(surface, (0, 0, 0), (self.position[0] - 70, self.position[1]), 39) pygame.draw.circle(surface, (0, 0, 0), self.position, 19) self.__power_label.draw(surface) def create_instance(self): raise RuntimeError("Can't create instances of horns!") def calculate_local_power_requirement(self): return 0.0 def calculate_total_power_requirement(self): power = super(Horn, self).calculate_total_power_requirement() return power / self.efficiency def update_power(self): power = self.calculate_total_power_requirement() self.__power_label.text = Utils.metric_scale(power, 'W', decimal_places=1) @property def total_links_in(self): return 0 @property def total_links_out(self): return self.__num_supported_links @property def efficiency(self): return 0.90 @property def heat_dissipation(self): return 0.05
def __init__(self, position): self.color = (255, 180, 255) super(Horn, self).__init__("Horn", self.color, position) self.is_draggable = False self.__num_supported_links = 2 self.__power_label = SpellLabel(self, '0 W', (-20, 0)) self.update_power()
def __init__(self, position): self.color = (255, 255, 255) super(SpellLumen, self).__init__('Lumen', self.color, position) self.__lumen_label = SpellLabel(self, '0', (0, self.radius * 1.3)) self.__lumen = 0.0 # set properties self.lumen = 3000
class SpellBase(DraggableCircle): def __init__(self, name, color, position): super(SpellBase, self).__init__(color, position, 20) self.__name = name self.name_label = SpellLabel(self, name, (0, -self.radius*1.3)) self.efficiency_label = SpellLabel(self, '{}%'.format(int(self.efficiency*100))) self.is_template = False self.links_in = list() self.links_out = list() def calculate_total_power_requirement(self): power = self.calculate_local_power_requirement() for link in self.links_out: power += link.calculate_total_power_requirement() return power / self.efficiency def distance_to_squared(self, other_spell): dx = self.position[0] - other_spell.position[0] dy = self.position[1] - other_spell.position[1] return dx**2 + dy**2 def process_event(self, event): super(SpellBase, self).process_event(event) if event.type == Event.DRAGGABLECIRCLECLICKED and event.draggable_circle is self: self.__notify_spell_clicked() if event.type == Event.DRAGGABLECIRCLERELEASED and event.draggable_circle is self: self.__notify_spell_released() def update(self, time_step): super(SpellBase, self).update(time_step) self.__keep_spell_on_screen() def draw(self, surface): super(SpellBase, self).draw(surface) self.__draw_spell_links(surface) self.name_label.draw(surface) self.efficiency_label.draw(surface) def __draw_spell_links(self, surface): for spell in self.links_out: pygame.draw.line(surface, self.color, self.position, spell.position) def link_input_to(self, other_spell): if not self.is_linkable or not other_spell.is_linkable: raise RuntimeError("Spells can't link!") if self.free_link_slots_in == 0 or other_spell.free_link_slots_out == 0: raise RuntimeError("Spells don't have any free link slots") self.links_in.append(other_spell) other_spell.links_out.append(self) def link_output_to(self, other_spell): if not self.is_linkable or not other_spell.is_linkable: raise RuntimeError("Spells can't link!") if self.free_link_slots_out == 0 or other_spell.free_link_slots_in == 0: raise RuntimeError("Spells don't have any free link slots") self.links_out.append(other_spell) other_spell.links_in.append(self) def unlink_local(self): for linked in self.links_out: linked.links_in.remove(self) for linked in self.links_in: linked.links_out.remove(self) self.links_out = list() self.links_in = list() def unlink_chain(self): self.unlink_chain_out() self.unlink_chain_in() def unlink_chain_out(self): for linked in self.links_out: linked.unlink_chain_out() self.links_out = list() def unlink_chain_in(self): for linked in self.links_in: linked.unlink_chain() self.links_in = list() def is_linked_to(self, other_spell): return other_spell in self.outward_spells or other_spell in self.inward_spells @property def is_linkable(self): return not self.is_template @property def outward_spells(self): spells = list() self.get_outward_spells(spells) return spells @property def inward_spells(self): spells = list() self.get_inward_spells(spells) return spells def get_outward_spells(self, spells): spells.append(self) for linked in self.links_out: linked.get_outward_spells(spells) def get_inward_spells(self, spells): spells.append(self) for linked in self.links_in: linked.get_inward_spells(spells) @property def free_link_slots_in(self): return self.total_links_in - len(self.links_in) @property def free_link_slots_out(self): return self.total_links_out - len(self.links_out) @property def name(self): return self.__name @name.setter def name(self, name): self.__name = name self.name_label = SpellLabel(self, name) @property def is_activated(self): return not self.is_template and not self.is_dragging def __keep_spell_on_screen(self): info = pygame.display.Info() width, height = info.current_w, info.current_h self.position = (min(max(self.position[0], self.radius), width - self.radius), min(max(self.position[1], self.radius), height - self.radius)) def __notify_spell_clicked(self): evt = pygame.event.Event(Event.SPELLCLICKED, spell=self) pygame.event.post(evt) def __notify_spell_released(self): evt = pygame.event.Event(Event.SPELLRELEASED, spell=self) pygame.event.post(evt) #################################################################################################################### # Interface for derived spells #################################################################################################################### def create_instance(self): raise NotImplementedError('Spells must be able to create instances of themselves') # return a new instance of this class. The new instance shouldn't be an exact copy of all members, it just needs # to have bare initialisation (e.g. set the position and name). # # something like: # return MySpell(self.position) def calculate_local_power_requirement(self): raise NotImplementedError('Spells must provide their power requirement in Watts') @property def total_links_in(self): raise NotImplementedError('Spells must provide information on how many links they can handle as input') @property def total_links_out(self): raise NotImplementedError('Spells must provide information on how many links they can handle as output') @property def efficiency(self): raise NotImplementedError('Spells must provide an efficiency factor [0..1]') @property def heat_dissipation(self): raise NotImplementedError('Spells must provide a heat dissipation factor [0..1]')
def name(self, name): self.__name = name self.name_label = SpellLabel(self, name)