def __init__(self, instances, show_number=True): self.log = logging.getLogger("gui.tabs") self.instances = instances self.widget = load_uh_widget("unit_entry_widget.xml") # get the icon of the first instance i = instances[0] if i.id < UNITS.DIFFERENCE_BUILDING_UNIT_ID: # A building. Generate dynamic thumbnail from its action set. imgs = list(ActionSetLoader.get_set(i._action_set_id).items())[0][1] thumbnail = list(imgs[45].keys())[0] else: # Units use manually created thumbnails because those need to be # precise and recognizable in combat situations. thumbnail = self.get_unit_thumbnail(i.id) self.widget.findChild(name="unit_button").up_image = thumbnail if show_number: self.widget.findChild(name="instance_number").text = str(len(self.instances)) # only two callbacks are needed so drop unwanted changelistener inheritance for i in instances: if not i.has_remove_listener(Callback(self.on_instance_removed, i)): i.add_remove_listener(Callback(self.on_instance_removed, i)) health_component = i.get_component(HealthComponent) if not health_component.has_damage_dealt_listener(self.draw_health): health_component.add_damage_dealt_listener(self.draw_health) self.draw_health()
def add_overlay(self, overlay_name, z_order): """Creates color overlay recoloring the area defined in *overlay_set* and adds it to fife instance. Note that a color overlay on *z_order* can only be visible if an animation overlay with that specific order exists as well. For order 0, `convertToOverlays()` makes sure they do. """ if not self.fife_instance.isAnimationOverlay(self.identifier): # parameter False: do not convert color overlays attached to base self.fife_instance.convertToOverlays(self.identifier, False) try: overlay_set = ActionSetLoader.get_set(self.action_set)[overlay_name] except KeyError: self.log.warning( 'Could not find overlay action set `%s` defined for object ' '`%s` with id `%s`. Not adding overlay for this action.', overlay_name, self.instance, self.identifier) return self.current_overlays[z_order] = overlay_set for rotation, frames in overlay_set.iteritems(): ov_anim = fife.Animation.createAnimation() for frame_img, frame_data in frames.iteritems(): try: frame_length = frame_data[0] except TypeError: # not using atlases frame_length = frame_data pic = horizons.globals.fife.animationloader.load_image(frame_img, self.action_set, overlay_name, rotation) frame_milliseconds = int(frame_length * 1000) ov_anim.addFrame(pic, frame_milliseconds) overlay = fife.OverlayColors(ov_anim) self.fife_instance.addColorOverlay(self.identifier, rotation, z_order, overlay)
def __init__(self, instances, show_number=True): self.log = logging.getLogger("gui.tabs") self.instances = instances self.widget = load_uh_widget("unit_entry_widget.xml") # get the icon of the first instance i = instances[0] if i.id < UNITS.DIFFERENCE_BUILDING_UNIT_ID: # A building. Generate dynamic thumbnail from its action set. imgs = ActionSetLoader.get_set(i._action_set_id).items()[0][1] thumbnail = imgs[45].keys()[0] else: # Units use manually created thumbnails because those need to be # precise and recognizable in combat situations. thumbnail = self.get_unit_thumbnail(i.id) self.widget.findChild(name="unit_button").up_image = thumbnail if show_number: self.widget.findChild(name="instance_number").text = unicode(len(self.instances)) # only two callbacks are needed so drop unwanted changelistener inheritance for i in instances: if not i.has_remove_listener(Callback(self.on_instance_removed, i)): i.add_remove_listener(Callback(self.on_instance_removed, i)) health_component = i.get_component(HealthComponent) if not health_component.has_damage_dealt_listener(self.draw_health): health_component.add_damage_dealt_listener(self.draw_health) self.draw_health()
def on_settler_level_change(self, message): assert isinstance(message, SettlerUpdate) setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, message.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = str(taxes) imgs = list(ActionSetLoader.get_set(self.instance._action_set_id).items())[0][1] self.widget.findChild(name="building_image").image = list(imgs[45].keys())[0]
def on_settler_level_change(self, message): assert isinstance(message, SettlerUpdate) setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, message.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = unicode(taxes) imgs = ActionSetLoader.get_set( self.instance._action_set_id).items()[0][1] self.widget.findChild(name="building_image").image = imgs[45].keys()[0]
def update_overlay(self, res_id, new_amount): """Called when inventory amount of one resource changes. Looks for a fitting animation overlay based on the new inventory amount for that resource. If that overlay is different from the currently displayed one, removes the old overlay for that resource and adds a new one based on what fits *new_amount* best. """ try: overlay_order = self.overlays[self.action_set][ self.instance._action][res_id] except KeyError: self.log.warning( 'No overlays defined for resource `%s`, action `%s` and action set `%s`. ' 'Consider using `null` overlays for amount 0 in this action set.', res_id, self.instance._action, self.action_set) self.current_overlays[res_id] = None return # We use max(0, new_amount) restricted to what exists in overlay_order. # E.g. for # new_amount = 3 # overlay_order = [[0, None], [2, 'as_2'], [5, 'as_full']] # we drop 5 (because it is defined but too large), # ignore 4 and 3 (because they are not defined in overlay_order), # and find 'as_2' as our new overlay for the amount 2. for (amount, overlay_name) in sorted(overlay_order, reverse=True): if amount > new_amount: # This `if` drops defined-but-too-large candidates (i.e. case `5` in above example). continue if amount == self.current_overlays[res_id]: # Nothing to do, continue using the same overlay return if overlay_name is None: # Empty overlay, only display base action set (i.e. case `0` in above example) self.remove_overlay(res_id) return try: overlay_set = ActionSetLoader.get_set( self.action_set)[overlay_name] except KeyError: self.log.warning( 'Could not find overlay action set defined for object ' '`%s` with id `%s` for resource `%s` and amount `%s`. ' 'Falling back to next lower overlay.', self.instance, self.identifier, res_id, amount) continue self.remove_overlay(res_id) self.add_overlay(overlay_set, z_order=res_id) self.current_overlays[res_id] = amount return
def getInstance(cls, session, x, y, action='idle', level=0, rotation=45, action_set_id=None, world_id=""): """Get a Fife instance @param x, y: The coordinates @param action: The action, defaults to 'idle' @param level: object level. Relevant for choosing an action set @param rotation: rotation of the object. Any of [ 45 + 90*i for i in xrange(0, 4) ] @param action_set_id: can be set if the action set is already known. If set, level isn't considered. @return: tuple (fife_instance, action_set_id) """ assert isinstance(x, int) assert isinstance(y, int) #rotation = cls.check_build_rotation(session, rotation, x, y) # TODO: replace this with new buildable api # IDEA: save rotation in savegame width, length = cls.size if rotation == 135 or rotation == 315: # if you look at a non-square builing from a 45 degree angle it looks # different than from a 135 degree angle # when you rotate it the width becomes the length and the length becomes the width width, length = length, width # the drawing origin is the center of it's area, minus 0.5 # the 0.5 isn't really necessary, but other code is aligned with the 0.5 shift # this is at least for the gridoverlay, and the location of a build preview relative to the mouse # it this is changed, it should also be changed for ground tiles (world/ground.py) and units instance_coords = [x + width / 2 - 0.5, y + length / 2 - 0.5, 0] instance = session.view.layers[cls.layer].createInstance( cls._fife_object, fife.ExactModelCoordinate(*instance_coords), world_id) if action_set_id is None: action_set_id = cls.get_random_action_set(level=level) fife.InstanceVisual.create(instance) action_set = ActionSetLoader.get_set(action_set_id) if action not in action_set: if 'idle' in action_set: action = 'idle' elif 'idle_full' in action_set: action = 'idle_full' else: # set first action action = list(action_set.keys())[0] instance.actRepeat("{}_{}".format(action, action_set_id), rotation) return (instance, action_set_id)
def update_overlay(self, res_id, new_amount): """Called when inventory amount of one resource changes. Looks for a fitting animation overlay based on the new inventory amount for that resource. If that overlay is different from the currently displayed one, removes the old overlay for that resource and adds a new one based on what fits *new_amount* best. """ try: overlay_order = self.overlays[self.action_set][self.instance._action][res_id] except KeyError: self.log.warning( 'No overlays defined for resource `%s`, action `%s` and action set `%s`. ' 'Consider using `null` overlays for amount 0 in this action set.', res_id, self.instance._action, self.action_set) self.current_overlays[res_id] = None return # We use max(0, new_amount) restricted to what exists in overlay_order. # E.g. for # new_amount = 3 # overlay_order = [[0, None], [2, 'as_2'], [5, 'as_full']] # we drop 5 (because it is defined but too large), # ignore 4 and 3 (because they are not defined in overlay_order), # and find 'as_2' as our new overlay for the amount 2. for (amount, overlay_name) in sorted(overlay_order, reverse=True): if amount > new_amount: # This `if` drops defined-but-too-large candidates (i.e. case `5` in above example). continue if amount == self.current_overlays[res_id]: # Nothing to do, continue using the same overlay return if overlay_name is None: # Empty overlay, only display base action set (i.e. case `0` in above example) self.remove_overlay(res_id) return try: overlay_set = ActionSetLoader.get_set(self.action_set)[overlay_name] except KeyError: self.log.warning( 'Could not find overlay action set defined for object ' '`%s` with id `%s` for resource `%s` and amount `%s`. ' 'Falling back to next lower overlay.', self.instance, self.identifier, res_id, amount) continue self.remove_overlay(res_id) self.add_overlay(overlay_set, z_order=res_id) self.current_overlays[res_id] = amount return
def init_widget(self): super(SettlerOverviewTab, self).init_widget() name = self.instance.settlement.get_component(NamedComponent).name self.widget.findChild(name="headline").text = name setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, self.instance.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = unicode(taxes) action_set = ActionSetLoader.get_set(self.instance._action_set_id) action_gfx = action_set.items()[0][1] image = action_gfx[45].keys()[0] self.widget.findChild(name="building_image").image = image
def init_widget(self): super().init_widget() name = self.instance.settlement.get_component(NamedComponent).name self.widget.findChild(name="headline").text = name setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, self.instance.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = str(taxes) action_set = ActionSetLoader.get_set(self.instance._action_set_id) action_gfx = list(action_set.items())[0][1] image = list(action_gfx[45].keys())[0] self.widget.findChild(name="building_image").image = image
def add_overlay(self, overlay_name, z_order): """Creates color overlay recoloring the area defined in *overlay_set* and adds it to fife instance. Note that a color overlay on *z_order* can only be visible if an animation overlay with that specific order exists as well. For order 0, `convertToOverlays()` makes sure they do. """ if not self.fife_instance.isAnimationOverlay(self.identifier): # parameter False: do not convert color overlays attached to base self.fife_instance.convertToOverlays(self.identifier, False) try: overlay_set = ActionSetLoader.get_set( self.action_set)[overlay_name] except KeyError: self.log.warning( 'Could not find overlay action set `%s` defined for object ' '`%s` with id `%s`. Not adding overlay for this action.', overlay_name, self.instance, self.identifier) return animationmanager = horizons.globals.fife.animationmanager self.current_overlays[z_order] = overlay_set for rotation, frames in overlay_set.items(): id = '{}+{}'.format(self.identifier, rotation) if animationmanager.exists(id): ov_anim = animationmanager.getPtr(id) else: ov_anim = animationmanager.create(id) for frame_img, frame_data in frames.items(): try: frame_length = frame_data[0] except TypeError: # not using atlases frame_length = frame_data pic = horizons.globals.fife.animationloader.load_image( frame_img, self.action_set, overlay_name, rotation) frame_milliseconds = int(frame_length * 1000) ov_anim.addFrame(pic, frame_milliseconds) overlay = fife.OverlayColors(ov_anim) self.fife_instance.addColorOverlay(self.identifier, rotation, z_order, overlay)
def init_widget(self): super(SignalFireOverviewTab, self).init_widget() action_set = ActionSetLoader.get_set(self.instance._action_set_id) action_gfx = action_set.items()[0][1] image = action_gfx[45].keys()[0] self.widget.findChild(name="building_image").image = image
def has_action(self, action): """Checks if this unit has a certain action. @param action: animation id as string""" return (action in ActionSetLoader.get_set(self._action_set_id))
def init_widget(self): super(SignalFireOverviewTab, self).init_widget() action_set = ActionSetLoader.get_set(self.instance._action_set_id) action_gfx = list(action_set.items())[0][1] image = list(action_gfx[45].keys())[0] self.widget.findChild(name="building_image").image = image
def getInstance(cls, session, x, y, action='idle', level=0, rotation=45, action_set_id=None, world_id=""): """Get a Fife instance @param x, y: The coordinates @param action: The action, defaults to 'idle' @param level: object level. Relevant for choosing an action set @param rotation: rotation of the object. Any of [ 45 + 90*i for i in xrange(0, 4) ] @param action_set_id: can be set if the action set is already known. If set, level isn't considered. @return: tuple (fife_instance, action_set_id) """ assert isinstance(x, int) assert isinstance(y, int) #rotation = cls.check_build_rotation(session, rotation, x, y) # TODO: replace this with new buildable api # IDEA: save rotation in savegame facing_loc = fife.Location(session.view.layers[cls.layer]) instance_coords = list((x, y, 0)) layer_coords = list((x, y, 0)) width, length = cls.size # NOTE: # nobody actually knows how the code below works. # it's for adapting the facing location and instance coords in # different rotations, and works with all quadratic buildings (tested up to 4x4) # for the first unquadratic building (2x4), a hack fix was put into it. # the plan for fixing this code in general is to wait until there are more # unquadratic buildings, and figure out a pattern of the placement error, # then fix that generally. if rotation == 45: layer_coords[0] = x + width + 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] -= 1 instance_coords[1] += 1 elif rotation == 135: instance_coords[1] = y + length - 1 layer_coords[1] = y - length - 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] += 1 instance_coords[1] -= 1 elif rotation == 225: instance_coords = list(( x + width - 1, y + length - 1, 0)) layer_coords[0] = x - width - 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] += 1 instance_coords[1] -= 1 elif rotation == 315: instance_coords[0] = x + width - 1 layer_coords[1] = y + length + 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] += 1 instance_coords[1] -= 1 else: return None instance = session.view.layers[cls.layer].createInstance( cls._fife_object, fife.ModelCoordinate(*instance_coords), world_id) facing_loc.setLayerCoordinates(fife.ModelCoordinate(*layer_coords)) if action_set_id is None: action_set_id = cls.get_random_action_set(level=level) fife.InstanceVisual.create(instance) action_set = ActionSetLoader.get_set(action_set_id) if action not in action_set: if 'idle' in action_set: action = 'idle' elif 'idle_full' in action_set: action = 'idle_full' else: # set first action action = list(action_set.keys())[0] instance.actRepeat(action + "_" + str(action_set_id), facing_loc) return (instance, action_set_id)
def getInstance(cls, session, x, y, action='idle', level=0, rotation=45, action_set_id=None, world_id=""): """Get a Fife instance @param x, y: The coordinates @param action: The action, defaults to 'idle' @param level: object level. Relevant for choosing an action set @param rotation: rotation of the object. Any of [ 45 + 90*i for i in xrange(0, 4) ] @param action_set_id: can be set if the action set is already known. If set, level isn't considered. @return: tuple (fife_instance, action_set_id) """ assert isinstance(x, int) assert isinstance(y, int) #rotation = cls.check_build_rotation(session, rotation, x, y) # TODO: replace this with new buildable api # IDEA: save rotation in savegame facing_loc = fife.Location(session.view.layers[cls.layer]) instance_coords = list((x, y, 0)) layer_coords = list((x, y, 0)) width, length = cls.size # NOTE: # nobody actually knows how the code below works. # it's for adapting the facing location and instance coords in # different rotations, and works with all quadratic buildings (tested up to 4x4) # for the first unquadratic building (2x4), a hack fix was put into it. # the plan for fixing this code in general is to wait until there are more # unquadratic buildings, and figure out a pattern of the placement error, # then fix that generally. if rotation == 45: layer_coords[0] = x + width + 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] -= 1 instance_coords[1] += 1 elif rotation == 135: instance_coords[1] = y + length - 1 layer_coords[1] = y - length - 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] += 1 instance_coords[1] -= 1 elif rotation == 225: instance_coords = list(( x + width - 1, y + length - 1, 0)) layer_coords[0] = x - width - 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] += 1 instance_coords[1] -= 1 elif rotation == 315: instance_coords[0] = x + width - 1 layer_coords[1] = y + length + 3 if width == 2 and length == 4: # HACK: fix for 4x2 buildings instance_coords[0] += 1 instance_coords[1] -= 1 else: return None instance = session.view.layers[cls.layer].createInstance( cls._fife_object, fife.ModelCoordinate(*instance_coords), world_id) facing_loc.setLayerCoordinates(fife.ModelCoordinate(*layer_coords)) if action_set_id is None: action_set_id = cls.get_random_action_set(level=level) fife.InstanceVisual.create(instance) action_set = ActionSetLoader.get_set(action_set_id) if not action in action_set: if 'idle' in action_set: action = 'idle' elif 'idle_full' in action_set: action = 'idle_full' else: # set first action action = action_set.keys()[0] if (Fife.getVersion() >= (0, 3, 6)): instance.actRepeat(action+"_"+str(action_set_id), facing_loc) else: instance.act(action+"_"+str(action_set_id), facing_loc, True) return (instance, action_set_id)