def create_node(instance=None, point=None, layer=None): """ creates a node of one of these types: - attached to an instance - attached to an instance with offset - attached to a point FIXME: - add location node @type: instance: object @param instance: fife instance object @type point: tuple @param point: x,y,z tuple @type layer: object @param layer: fife layer object """ node = None if not layer: return node # node at layer coordinates if point and not instance: point = fife.Point(point[0], point[1]) node = fife.RendererNode(point); # node with offset if instance and point: node = fife.RendererNode(instance, layer, fife.Point(point[0], point[1])) # node attached to instance if instance and not point: node = fife.RendererNode(instance, layer) if node: node.thisown = 0 return node
def __render_icon(self, instance, group, res, amount): """ This renders the icon. It calculates the position of the icon. Most parts of this were copied from horizons/world/managers/statusiconmanager.py """ # TODO: Try to unify the __render methods of this class and statusiconmanager.py! self.renderer.removeAll(group) pos = instance.position # self.run[group] is used for the moving up animation # use -50 here to get some more offset in height bg_rel = fife.Point(0, -50 - self.run[group]) rel = fife.Point(-14, -50 - self.run[group]) self.run[group] += self.animation_steps loc = fife.Location(self.layer) loc.setExactLayerCoordinates( fife.ExactModelCoordinate( pos.origin.x + float(pos.width) / 4, pos.origin.y + float(pos.height) / 4, )) bg_node = fife.RendererNode(loc, bg_rel) node = fife.RendererNode(loc, rel) bg_image = horizons.globals.fife.imagemanager.load(self.background) res_icon = horizons.globals.fife.imagemanager.load( get_res_icon_path(res)) font = horizons.globals.fife.pychanmanager.getFont('mainmenu') self.renderer.addImage(group, bg_node, bg_image) self.renderer.resizeImage(group, node, res_icon, 24, 24) self.renderer.addText(group, node, font, ' ' * 9 + '{amount:>2d}'.format(amount=amount))
def reset(self): """Reset image to original image""" # reload self.rendertarget.removeAll() size = self.minimap.get_size() self.rendertarget.addQuad(self.minimap._get_render_name("background"), fife.Point(0, 0), fife.Point(0, size[1]), fife.Point(size[0], size[1]), fife.Point(size[0], 0), *Minimap.COLORS["water"])
def mouseDragged(self, evt): if evt.getButton() == fife.MouseEvent.LEFT and hasattr( self, 'select_begin'): x, y = self.select_begin xx, yy = evt.getX(), evt.getY() do_multi = ((x - xx)**2 + (y - yy)**2) >= 10 # from 3px (3*3 + 1) self.session.view.renderer['GenericRenderer'].removeAll( self.__class__._SELECTION_RECTANGLE_NAME) if do_multi: # draw a rectangle xmin, xmax = min(x, xx), max(x, xx) ymin, ymax = min(y, yy), max(y, yy) a = fife.Point(xmin, ymin) b = fife.Point(xmax, ymin) c = fife.Point(xmax, ymax) d = fife.Point(xmin, ymax) self._draw_rect_line(a, b) self._draw_rect_line(b, c) self._draw_rect_line(d, c) self._draw_rect_line(d, a) area = fife.Rect(xmin, ymin, xmax - xmin, ymax - ymin) else: area = fife.ScreenPoint(xx, yy) instances = self.session.view.cam.getMatchingInstances( area, self.session.view.layers[LAYERS.OBJECTS], False) # False for accurate # get selection components instances = (self.fife_instance_to_uh_instance(i) for i in instances) instances = [i for i in instances if i is not None] # We only consider selectable items when dragging a selection box. instances = self.filter_selectable(instances) # If there is at least one player unit, we don't select any enemies. # This applies to both buildings and ships. if any( (self.is_owned_by_player(instance) for instance in instances)): instances = self.filter_owner(instances) self._update_selection(instances, do_multi) elif evt.getButton() == fife.MouseEvent.RIGHT: pass else: super(SelectionTool, self).mouseDragged(evt) return evt.consume()
def high(i=0): i += 1 render_name = self._get_render_name("highlight") + str(tup) self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) if i > STEPS: if finish_callback: finish_callback() return part = i # grow bigger if i > STEPS // 2: # after the first half part = STEPS - i # become smaller radius = MIN_RAD + int( (float(part) / (STEPS // 2)) * (MAX_RAD - MIN_RAD)) draw_point = self.minimap_image.rendertarget.addPoint for x, y in Circle(Point(*tup), radius=radius).get_border_coordinates(): draw_point(render_name, fife.Point(x, y), *color) ExtScheduler().add_new_object(lambda: high(i), self, INTERVAL, loops=1)
def __render_status(self, instance, status): status_string = self.get_status_string(instance) # Clean icons self.renderer.removeAll(status_string) # pixel-offset on screen (will be constant across zoom-levels) rel = fife.Point(0, -30) pos = instance.position # trial and error has brought me to this (it's supposed to hit the center) loc = fife.Location(self.layer) loc.setExactLayerCoordinates( fife.ExactModelCoordinate( pos.origin.x + float(pos.width) / 4, pos.origin.y + float(pos.height) / 4, )) node = fife.RendererNode(loc, rel) try: # to load an animation anim = horizons.globals.fife.animationloader.loadResource( status.icon) self.renderer.addAnimation(status_string, node, anim) except ValueError: img = horizons.globals.fife.imagemanager.load(status.icon) self.renderer.addImage(status_string, node, img)
def _recalculate(self, where=None): """Calculate which pixel of the minimap should display what and draw it @param where: minimap coords. If this is given only that pixel will be redrawn """ self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") if where is None: rt.removeAll(render_name) points = self.transform.iter_points() else: points = [where] # Is this really worth it? draw_point = rt.addPoint fife_point = fife.Point(0, 0) island_color = self.COLORS["island"] water_color = self.COLORS["water"] for (x, y) in points: world_coords = self.transform.minimap_to_world((x, y)) color = get_minimap_color(world_coords, self.world, island_color, water_color) fife_point.set(x, y) draw_point(render_name, fife_point, *color)
def update_cam(self): """Redraw camera border.""" if self.world is None or not self.world.inited: return # don't draw while loading self.renderer.removeAll("minimap_cam_border") # draw rect for current screen displayed_area = self.session.view.get_displayed_area() minimap_corners_as_renderer_node = [] for corner in displayed_area.get_corners(): # check if the corners are outside of the screen corner = list(corner) if corner[0] > self.world.max_x: corner[0] = self.world.max_x if corner[0] < self.world.min_x: corner[0] = self.world.min_x if corner[1] > self.world.max_y: corner[1] = self.world.max_y if corner[1] < self.world.min_y: corner[1] = self.world.min_y corner = tuple(corner) minimap_coords = self._get_rotated_coords( self._world_coord_to_minimap_coord(corner)) minimap_corners_as_renderer_node.append( fife.GenericRendererNode( \ fife.Point(*minimap_coords) ) ) for i in xrange(0, 3): self.renderer.addLine("minimap_cam_border", minimap_corners_as_renderer_node[i], \ minimap_corners_as_renderer_node[i+1], *self.colors[self.cam_border]) # close the rect self.renderer.addLine("minimap_cam_border", minimap_corners_as_renderer_node[3], \ minimap_corners_as_renderer_node[0], *self.colors[self.cam_border])
def update_cam(self): """Redraw camera border.""" if not self.cam_border or self.view is None: # needs view return if self.world is None or not self.world.inited: return # don't draw while loading use_rotation = self._get_rotation_setting() self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(self._get_render_name("cam")) # draw rect for current screen displayed_area = self.view.get_displayed_area() minimap_corners_as_point = [] for corner in displayed_area.get_corners(): # check if the corners are outside of the screen corner = list(corner) if corner[0] > self.world.max_x: corner[0] = self.world.max_x if corner[0] < self.world.min_x: corner[0] = self.world.min_x if corner[1] > self.world.max_y: corner[1] = self.world.max_y if corner[1] < self.world.min_y: corner[1] = self.world.min_y corner = tuple(corner) coords = self._world_to_minimap(corner, use_rotation) minimap_corners_as_point.append(fife.Point(coords[0], coords[1])) for i in xrange(0, 4): self.minimap_image.rendertarget.addLine( self._get_render_name("cam"), minimap_corners_as_point[i], minimap_corners_as_point[(i + 1) % 4], *self.COLORS["cam"])
def _recalculate(self, where=None): """Calculate which pixel of the minimap should display what and draw it @param where: Rect of minimap coords. Defaults to self.location """ self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") if where is None: rt.removeAll(render_name) location_left = self.location.left location_top = self.location.top draw_point = rt.addPoint fife_point = fife.Point(0, 0) use_rotation = self._get_rotation_setting() for (x, y), color in iter_minimap_points(self.location, self.world, self.COLORS["island"], self.COLORS["water"], where): if use_rotation: # inlined _get_rotated_coords rot_x, rot_y = self._rotate( (location_left + x, location_top + y), self._rotations) fife_point.set(rot_x - location_left, rot_y - location_top) else: fife_point.set(x, y) draw_point(render_name, fife_point, *color)
def show_unit_path(self, unit): """Show the path a unit is moving along""" path = unit.path.path if path is None: # show at least the position path = [unit.position.to_tuple()] # the path always contains the full path, the unit might be somewhere in it position_of_unit_in_path = 0 unit_pos = unit.position.to_tuple() for i, pos in enumerate(path): if pos == unit_pos: position_of_unit_in_path = i break # display units one ahead if possible, it looks nicer if the unit is moving if len(path) > 1 and position_of_unit_in_path + 1 < len(path): position_of_unit_in_path += 1 # path = path[position_of_unit_in_path:] # draw every step-th coord step = 1 relevant_coords = [path[0]] for i in range(step, len(path), step): relevant_coords.append(path[i]) relevant_coords.append(path[-1]) # get coords, actual drawing use_rotation = self._get_rotation_setting() self.minimap_image.set_drawing_enabled() p = fife.Point(0, 0) render_name = self._get_render_name("ship_route") + str( next(self.__class__.__ship_route_counter)) color = unit.owner.color.to_tuple() last_coord = None draw_point = self.minimap_image.rendertarget.addPoint for i in relevant_coords: coord = self._world_to_minimap(i, use_rotation) if last_coord is not None and \ sum(abs(last_coord[i] - coord[i]) for i in (0, 1)) < 2: # 2 is min dist in pixels continue last_coord = coord p.x = coord[0] p.y = coord[1] draw_point(render_name, p, *color) def cleanup(): self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) speed = 1.0 + math.sqrt(5) / 2 self.highlight(path[-1], factor=0.4, speed=speed, finish_callback=cleanup, color=color) return True
def mouseDragged(self, event): instancerenderer = fife.InstanceRenderer.getInstance( self._test._camera) instancerenderer.removeAllColored() instancerenderer.removeAllOutlines() if event.getButton() == fife.MouseEvent.LEFT and hasattr( self, 'select_begin'): do_multi = ((self.select_begin[0] - event.getX())**2 + (self.select_begin[1] - event.getY())** 2) >= 10 # from 3px (3*3 + 1) genericrenderer = fife.GenericRenderer.getInstance( self._test._camera) genericrenderer.removeAll("selection") if do_multi: # draw a rectangle a = fife.Point(min(self.select_begin[0], event.getX()), \ min(self.select_begin[1], event.getY())) b = fife.Point(max(self.select_begin[0], event.getX()), \ min(self.select_begin[1], event.getY())) c = fife.Point(max(self.select_begin[0], event.getX()), \ max(self.select_begin[1], event.getY())) d = fife.Point(min(self.select_begin[0], event.getX()), \ max(self.select_begin[1], event.getY())) genericrenderer.addLine("selection", \ fife.RendererNode(a), fife.RendererNode(b), 200, 200, 200) genericrenderer.addLine("selection", \ fife.RendererNode(b), fife.RendererNode(c), 200, 200, 200) genericrenderer.addLine("selection", \ fife.RendererNode(d), fife.RendererNode(c), 200, 200, 200) genericrenderer.addLine("selection", \ fife.RendererNode(a), fife.RendererNode(d), 200, 200, 200) instances = self._test._camera.getMatchingInstances(\ fife.Rect(min(self.select_begin[0], event.getX()), \ min(self.select_begin[1], event.getY()), \ abs(event.getX() - self.select_begin[0]), \ abs(event.getY() - self.select_begin[1])) if do_multi else fife.ScreenPoint(event.getX(), event.getY()), self._test._actorlayer, 0) # False for accurate for instance in instances: instancerenderer.addColored(instance, 250, 50, 250) instancerenderer.addOutlined(instance, 255, 255, 0, 2)
def _send_hover_instance_upate(self): """Broadcast update with new instances below mouse (hovered). At most called in a certain interval, not after every mouse move in order to prevent delays.""" self._hover_instances_update_scheduled = False where = fife.Point(self.__class__.last_event_pos.x, self.__class__.last_event_pos.y) instances = set(self.get_hover_instances(where)) # only send when there were actual changes if instances != set(self.__class__.last_hover_instances): self.__class__.last_hover_instances = WeakList(instances) HoverInstancesChanged.broadcast(self, instances)
def draw_health(self): """Draws the units current health as a healthbar over the unit.""" renderer = self.session.view.renderer['GenericRenderer'] renderer.removeAll("health_" + str(self.worldid)) zoom = self.session.view.get_zoom() height = int(5 * zoom) width = int(50 * zoom) y_pos = int(self.health_bar_y * zoom) mid_node_up = fife.GenericRendererNode(self._instance, \ fife.Point(-width/2+int(((self.health/self.max_health)*width)),\ y_pos-height) ) mid_node_down = fife.GenericRendererNode(self._instance, \ fife.Point( -width/2+int(((self.health/self.max_health)*width)) ,y_pos) ) if self.health != 0: renderer.addQuad("health_" + str(self.worldid), \ fife.GenericRendererNode(self._instance, \ fife.Point(-width/2, y_pos-height)), \ mid_node_up, \ mid_node_down, \ fife.GenericRendererNode(self._instance, fife.Point(-width/2, y_pos)), \ 0, 255, 0) if self.health != self.max_health: renderer.addQuad("health_" + str(self.worldid), mid_node_up, \ fife.GenericRendererNode(self._instance, fife.Point(width/2, y_pos-height)), \ fife.GenericRendererNode(self._instance, fife.Point(width/2, y_pos)), \ mid_node_down, 255, 0, 0)
def findPoint(self, event): point = None pos = -1 thickness = self.point_graph.thickness points = self.point_graph.coordinates rec = fife.Rect(event.getX() - thickness, event.getY() - thickness, thickness * 2, thickness * 2) for i, p in enumerate(points): if rec.contains(fife.Point(p.x, p.y)): point = p pos = i break return (point, pos)
def draw_data(self, data): """Display data from dump_data""" # only icon mode for now self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") drawPoint = rt.addPoint point = fife.Point() for x, y, r, g, b in json.loads(data): point.set(x, y) drawPoint(render_name, point, r, g, b)
def update_cam(self): """Redraw camera border.""" if not self.cam_border or self.view is None: # needs view return if self.world is None or not self.world.inited: return # don't draw while loading self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(self._get_render_name("cam")) # draw rect for current screen displayed_area = self.view.get_displayed_area() minimap_corners_as_point = [] for (x, y) in displayed_area: coords = self.transform.world_to_minimap((x, y)) minimap_corners_as_point.append(fife.Point(coords[0], coords[1])) for i in range(0, 4): self.minimap_image.rendertarget.addLine( self._get_render_name("cam"), minimap_corners_as_point[i], minimap_corners_as_point[(i + 1) % 4], *self.COLORS["cam"])
def draw(self): """Recalculates and draws the whole minimap of self.session.world or world. The world you specified is reused for every operation until the next draw(). """ if self.world is None and self.session.world is not None: self.world = self.session.world # in case minimap has been constructed before the world if not self.world.inited: return # don't draw while loading if self.transform is None: self.transform = _MinimapTransform(self.world.map_dimensions, self.location, 0, self._get_rotation_setting()) self.update_rotation() self.__class__._instances.append(self) # update cam when view updates if self.view is not None and not self.view.has_change_listener( self.update_cam): self.view.add_change_listener(self.update_cam) if not hasattr(self, "icon"): # add to global generic renderer with id specific to this instance self.renderer.removeAll("minimap_image" + self._id) self.minimap_image.reset() # NOTE: this is for the generic renderer interface, the offrenderer has slightly different methods node = fife.RendererNode( fife.Point(self.location.center.x, self.location.center.y)) self.renderer.addImage("minimap_image" + self._id, node, self.minimap_image.image, False) else: # attach image to pychan icon (recommended) self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.update_cam() self._recalculate() if not self.preview: self._timed_update(force=True) ExtScheduler().rem_all_classinst_calls(self) ExtScheduler().add_new_object(self._timed_update, self, self.SHIP_DOT_UPDATE_INTERVAL, -1)
def draw_data(self, data): """Display data from dump_data""" # only icon mode for now self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") draw_point = rt.addPoint point = fife.Point() # XXX There have been reports about `data` containing Fife debug # output (e.g. #2193). As temporary workaround, we try to only # parse what looks like valid json in there and ignore the rest. found_json = re.findall(r'\[\[.*\]\]', data)[0] for x, y, r, g, b in json.loads(found_json): point.set(x, y) draw_point(render_name, point, r, g, b)
def draw_health(self, remove_only=False, auto_remove=False): """Draws the units current health as a healthbar over the unit.""" if not self.has_component(HealthComponent): return render_name = "health_" + str(self.worldid) renderer = self.session.view.renderer['GenericRenderer'] renderer.removeAll(render_name) if remove_only or (auto_remove and not self._last_draw_health_call_on_damage): # only remove on auto_remove if this health was actually displayed as reacton to _on_damage # else we might remove something that the user still wants self._health_displayed = False return self._last_draw_health_call_on_damage = False self._health_displayed = True health_component = self.get_component(HealthComponent) health = health_component.health max_health = health_component.max_health zoom = self.session.view.zoom height = int(5 * zoom) width = int(50 * zoom) y_pos = int(self.health_bar_y * zoom) relative_x = int((width * health) // max_health - (width // 2)) # mid_node is the coord separating healthy (green) and damaged (red) quads mid_node_top = fife.RendererNode(self._instance, fife.Point(relative_x, y_pos - height)) mid_node_btm = fife.RendererNode(self._instance, fife.Point(relative_x, y_pos)) left_upper = fife.RendererNode(self._instance, fife.Point(-width // 2, y_pos - height)) right_upper = fife.RendererNode(self._instance, fife.Point(width // 2, y_pos - height)) left_lower = fife.RendererNode(self._instance, fife.Point(-width // 2, y_pos)) right_lower = fife.RendererNode(self._instance, fife.Point(width // 2, y_pos)) if health > 0: # draw healthy part of health bar renderer.addQuad(render_name, left_upper, left_lower, mid_node_btm, mid_node_top, 0, 255, 0) if health < max_health: # draw damaged part renderer.addQuad(render_name, mid_node_top, mid_node_btm, right_lower, right_upper, 255, 0, 0)
class Minimap(object): """A basic minimap. USAGE: Minimap can be drawn via GenericRenderer on an arbitrary position (determined by rect in ctor) or via Pychan Icon. In this case, the rect parameter only determines the size, the Minimap will scroll by default on clicks, overwrite on_click if you don't want that. TODO: * Remove renderer when used in icon node * Clear up distinction of coords where the minimap image or screen is the origin * Create a minimap tag for pychan ** Handle clicks, remove overlay icon """ COLORS = { "island": (137, 117, 87), "cam": (1, 1, 1), "water": (198, 188, 165), "highlight": (255, 0, 0), # for events } WAREHOUSE_IMAGE = "content/gui/icons/minimap/warehouse.png" SHIP_NEUTRAL = "content/gui/icons/minimap/ship_neutral.png" SHIP_PIRATE = "content/gui/icons/minimap/pirate.png" SHIP_DOT_UPDATE_INTERVAL = 0.5 # seconds RENDER_NAMES = { # alpha-ordering determines the order "background": "c", "base": "d", # islands, etc. "warehouse": "e", "ship": "f", "cam": "g", "ship_route": "h", "highlight": "l" } __minimap_id_counter = itertools.count() __ship_route_counter = itertools.count() _instances = [] # all active instances _dummy_fife_point = fife.Point( 0, 0) # use when you quickly need a temporary point def __init__(self, position, session, view, targetrenderer, imagemanager, renderer=None, world=None, cam_border=True, use_rotation=True, on_click=None, preview=False, tooltip=None): """ @param position: a Rect or a Pychan Icon, where we will draw to @param world: World object or fake thereof @param view: View object for cam control. Can be None to disable this @param renderer: renderer to be used if position isn't an icon @param targetrenderer: fife target rendererfor drawing on icons @param imagemanager: fife imagemanager for drawing on icons @param cam_border: boolean, whether to draw the cam border @param use_rotation: boolean, whether to use rotation (it must also be enabled in the settings) @param on_click: function taking 1 argument or None for scrolling @param preview: flag, whether to only show the map as preview @param tooltip: always show this tooltip when cursor hovers over minimap """ if isinstance(position, Rect): self.location = position self.renderer = renderer else: # assume icon self.location = Rect.init_from_topleft_and_size( 0, 0, position.width, position.height) self.icon = position self.use_overlay_icon(self.icon) self.session = session self.world = world if self.world: self._update_world_to_minimap_ratio() self.view = view self.rotation = 0 self.fixed_tooltip = tooltip if on_click is not None: self.on_click = on_click self.cam_border = cam_border self.use_rotation = use_rotation self.preview = preview self.location_center = self.location.center self._id = str(self.__class__.__minimap_id_counter.next() ) # internal identifier, used for allocating resources self._image_size_cache = {} # internal detail self.imagemanager = imagemanager self.minimap_image = _MinimapImage(self, targetrenderer) #import random #ExtScheduler().add_new_object(lambda : self.highlight( (50+random.randint(-50,50), random.randint(-50,50) + 50 )), self, 2, loops=-1) self._rotation_setting = horizons.globals.fife.get_uh_setting( "MinimapRotation") if self.use_rotation: MinimapRotationSettingChanged.subscribe( self._on_rotation_setting_change) def end(self): self.disable() self.world = None self.session = None self.renderer = None if self.use_rotation: MinimapRotationSettingChanged.unsubscribe( self._on_rotation_setting_change) def disable(self): """Due to the way the minimap works, there isn't really a show/hide, but you can disable it with this and enable again with draw(). Stops all updates.""" ExtScheduler().rem_all_classinst_calls(self) if self.view is not None and self.view.has_change_listener( self.update_cam): self.view.remove_change_listener(self.update_cam) if self in self.__class__._instances: self.__class__._instances.remove(self) def draw(self): """Recalculates and draws the whole minimap of self.session.world or world. The world you specified is reused for every operation until the next draw(). @param recalculate: do a full recalculation """ if self.world is None and self.session.world is not None: self.world = self.session.world # in case minimap has been constructed before the world self._update_world_to_minimap_ratio() if not self.world.inited: return # don't draw while loading self.__class__._instances.append(self) # update cam when view updates if self.view is not None and not self.view.has_change_listener( self.update_cam): self.view.add_change_listener(self.update_cam) if not hasattr(self, "icon"): # add to global generic renderer with id specific to this instance self.renderer.removeAll("minimap_image" + self._id) self.minimap_image.reset() # NOTE: this is for the generic renderer interface, the offrenderer has slightly different methods node = fife.RendererNode( fife.Point(self.location.center.x, self.location.center.y)) self.renderer.addImage("minimap_image" + self._id, node, self.minimap_image.image, False) else: # attach image to pychan icon (recommended) self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.update_cam() self._recalculate() if not self.preview: self._timed_update(force=True) ExtScheduler().rem_all_classinst_calls(self) ExtScheduler().add_new_object(self._timed_update, self, self.SHIP_DOT_UPDATE_INTERVAL, -1) def dump_data(self): """Returns a string representing the minimap data""" return self._recalculate(dump_data=True) def draw_data(self, data): """Display data from dump_data""" # only icon mode for now self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") drawPoint = rt.addPoint point = fife.Point() for x, y, r, g, b in json.loads(data): point.set(x, y) drawPoint(render_name, point, r, g, b) def _get_render_name(self, key): return self.RENDER_NAMES[key] + self._id def update_cam(self): """Redraw camera border.""" if not self.cam_border or self.view is None: # needs view return if self.world is None or not self.world.inited: return # don't draw while loading use_rotation = self._get_rotation_setting() self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(self._get_render_name("cam")) # draw rect for current screen displayed_area = self.view.get_displayed_area() minimap_corners_as_point = [] for corner in displayed_area.get_corners(): # check if the corners are outside of the screen corner = list(corner) if corner[0] > self.world.max_x: corner[0] = self.world.max_x if corner[0] < self.world.min_x: corner[0] = self.world.min_x if corner[1] > self.world.max_y: corner[1] = self.world.max_y if corner[1] < self.world.min_y: corner[1] = self.world.min_y corner = tuple(corner) coords = self._world_to_minimap(corner, use_rotation) minimap_corners_as_point.append(fife.Point(coords[0], coords[1])) for i in xrange(0, 4): self.minimap_image.rendertarget.addLine( self._get_render_name("cam"), minimap_corners_as_point[i], minimap_corners_as_point[(i + 1) % 4], *self.COLORS["cam"]) @classmethod def update(cls, tup): for minimap in cls._instances: minimap._update(tup) def _update(self, tup): """Recalculate and redraw minimap for real world coord tup @param tup: (x, y)""" if self.world is None or not self.world.inited: return # don't draw while loading minimap_point = self._world_to_minimap(tup, self._get_rotation_setting()) world_to_minimap = self._world_to_minimap_ratio # TODO: remove this remnant of the old implementation, perhaps by refactoring recalculate() minimap_point = ( minimap_point[0] + self.location.left, minimap_point[1] + self.location.top, ) rect = Rect.init_from_topleft_and_size( minimap_point[0], minimap_point[1], int(round(1 / world_to_minimap[0])) + 1, int(round(1 / world_to_minimap[1])) + 1) self._recalculate(rect) def use_overlay_icon(self, icon): """Configures icon so that clicks get mapped here. The current gui requires, that the minimap is drawn behind an icon.""" self.overlay_icon = icon icon.mapEvents({ icon.name + '/mousePressed': self._on_click, icon.name + '/mouseDragged': self._on_drag, icon.name + '/mouseEntered': self._mouse_entered, icon.name + '/mouseMoved': self._mouse_moved, icon.name + '/mouseExited': self._mouse_exited, }) def on_click(self, event, drag): """Handler for clicks (pressed and dragged) Scrolls screen to the point, where the cursor points to on the minimap. Overwrite this method to your convenience. """ if self.preview: return # we don't do anything in this mode map_coords = event.map_coords moveable_selecteds = [ i for i in self.session.selected_instances if i.movable ] if moveable_selecteds and event.getButton() == fife.MouseEvent.RIGHT: if drag: return for i in moveable_selecteds: Act(i, *map_coords).execute(self.session) elif event.getButton() == fife.MouseEvent.LEFT: if self.view is None: print "Warning: Can't handle minimap clicks since we have no view object" else: self.view.center(*map_coords) def _on_click(self, event): if self.world is not None: # supply world coords if there is a world event.map_coords = self._get_event_coords(event) if event.map_coords: self.on_click(event, drag=False) else: self.on_click(event, drag=True) def _on_drag(self, event): if self.world is not None: # supply world coords if there is a world event.map_coords = self._get_event_coords(event) if event.map_coords: self.on_click(event, drag=True) else: self.on_click(event, drag=True) def _get_event_coords(self, event): """Returns position of event as uh map coordinate tuple or None""" mouse_position = Point(event.getX(), event.getY()) if not hasattr(self, "icon"): icon_pos = Point(*self.overlay_icon.getAbsolutePos()) abs_mouse_position = icon_pos + mouse_position if not self.location.contains(abs_mouse_position): # mouse click was on icon but not actually on minimap return abs_mouse_position = abs_mouse_position.to_tuple() else: abs_mouse_position = mouse_position.to_tuple() if self._get_rotation_setting(): abs_mouse_position = self._get_from_rotated_coords( abs_mouse_position) return self._minimap_coords_to_world_coords(abs_mouse_position) def _mouse_entered(self, event): self._show_tooltip(event) def _mouse_moved(self, event): self._show_tooltip(event) def _mouse_exited(self, event): if hasattr(self, "icon"): # only supported for icon mode atm self.icon.hide_tooltip() def _show_tooltip(self, event): if hasattr(self, "icon"): # only supported for icon mode atm if self.fixed_tooltip is not None: self.icon.helptext = self.fixed_tooltip self.icon.position_tooltip(event) #self.icon.show_tooltip() else: coords = self._get_event_coords(event) if not coords: # no valid/relevant event location self.icon.hide_tooltip() return tile = self.world.get_tile(Point(*coords)) if tile is not None and tile.settlement is not None: new_helptext = tile.settlement.get_component( NamedComponent).name if self.icon.helptext != new_helptext: self.icon.helptext = new_helptext self.icon.show_tooltip() else: self.icon.position_tooltip(event) else: # mouse not over relevant part of the minimap self.icon.hide_tooltip() def highlight(self, tup, factor=1.0, speed=1.0, finish_callback=None, color=(0, 0, 0)): """Try to get the users attention on a certain point of the minimap. @param tuple: world coords @param factor: float indicating importance of event @param speed: animation speed as factor @param finish_callback: executed when animation finishes @param color: color of anim, (r,g,b), r,g,b of [0,255] @return duration of full animation in seconds""" tup = self._world_to_minimap(tup, self._get_rotation_setting()) # grow the circle from MIN_RAD to MAX_RAD and back with STEPS steps, where the # interval between steps is INTERVAL seconds MIN_RAD = int(3 * factor) # pixel MAX_RAD = int(12 * factor) # pixel STEPS = int(20 * factor) INTERVAL = (math.pi / 16) * factor def high(i=0): i += 1 render_name = self._get_render_name("highlight") + str(tup) self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) if i > STEPS: if finish_callback: finish_callback() return part = i # grow bigger if i > STEPS // 2: # after the first half part = STEPS - i # become smaller radius = MIN_RAD + int( (float(part) / (STEPS // 2)) * (MAX_RAD - MIN_RAD)) for x, y in Circle(Point(*tup), radius=radius).get_border_coordinates(): self.minimap_image.rendertarget.addPoint( render_name, fife.Point(x, y), *color) ExtScheduler().add_new_object(lambda: high(i), self, INTERVAL, loops=1) high() return STEPS * INTERVAL def show_unit_path(self, unit): """Show the path a unit is moving along""" path = unit.path.path if path is None: # show at least the position path = [unit.position.to_tuple()] # the path always contains the full path, the unit might be somewhere in it position_of_unit_in_path = 0 unit_pos = unit.position.to_tuple() for i in xrange(len(path)): if path[i] == unit_pos: position_of_unit_in_path = i break # display units one ahead if possible, it looks nicer if the unit is moving if len(path) > 1 and position_of_unit_in_path + 1 < len(path): position_of_unit_in_path += 1 # path = path[position_of_unit_in_path:] # draw every step-th coord step = 1 relevant_coords = [path[0]] for i in xrange(step, len(path), step): relevant_coords.append(path[i]) relevant_coords.append(path[-1]) # get coords, actual drawing use_rotation = self._get_rotation_setting() self.minimap_image.set_drawing_enabled() p = fife.Point(0, 0) render_name = self._get_render_name("ship_route") + str( self.__class__.__ship_route_counter.next()) color = unit.owner.color.to_tuple() last_coord = None for i in relevant_coords: coord = self._world_to_minimap(i, use_rotation) if last_coord is not None and \ sum( abs(last_coord[i] - coord[i]) for i in (0, 1) ) < 2: # 2 is min dist in pixels continue last_coord = coord p.x = coord[0] p.y = coord[1] self.minimap_image.rendertarget.addPoint(render_name, p, *color) def cleanup(): self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) self.highlight(path[-1], factor=0.4, speed=((1.0 + math.sqrt(5) / 2)), finish_callback=cleanup, color=color) return True def _recalculate(self, where=None, dump_data=False): """Calculate which pixel of the minimap should display what and draw it @param where: Rect of minimap coords. Defaults to self.location @param dump_data: Don't draw but return calculated data""" self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") if where is None: where = self.location rt.removeAll(render_name) # calculate which area of the real map is mapped to which pixel on the minimap pixel_per_coord_x, pixel_per_coord_y = self._world_to_minimap_ratio # calculate values here so we don't have to do it in the loop pixel_per_coord_x_half_as_int = int(pixel_per_coord_x / 2) pixel_per_coord_y_half_as_int = int(pixel_per_coord_y / 2) world_min_x = self.world.min_x world_min_y = self.world.min_y island_col = self.COLORS["island"] water_col = self.COLORS["water"] location_left = self.location.left location_top = self.location.top if dump_data: data = [] drawPoint = lambda name, fife_point, r, g, b: data.append( (fife_point.x, fife_point.y, r, g, b)) else: drawPoint = rt.addPoint fife_point = fife.Point(0, 0) use_rotation = self._get_rotation_setting() full_map = self.world.full_map # loop through map coordinates, assuming (0, 0) is the origin of the minimap # this faciliates calculating the real world coords for x in xrange(where.left - self.location.left, where.left + where.width - self.location.left): for y in xrange(where.top - self.location.top, where.top + where.height - self.location.top): """ This code should be here, but since python can't do inlining, we have to inline ourselves for performance reasons covered_area = Rect.init_from_topleft_and_size( int(x * pixel_per_coord_x)+world_min_x, \ int(y * pixel_per_coord_y)+world_min_y), \ int(pixel_per_coord_x), int(pixel_per_coord_y)) real_map_point = covered_area.center """ # use center of the rect that the pixel covers real_map_x = int( x * pixel_per_coord_x ) + world_min_x + pixel_per_coord_x_half_as_int real_map_y = int( y * pixel_per_coord_y ) + world_min_y + pixel_per_coord_y_half_as_int real_map_coords = (real_map_x, real_map_y) # check what's at the covered_area if real_map_coords in full_map: # this pixel is an island tile = full_map[real_map_coords] settlement = tile.settlement if settlement is None: # island without settlement if tile.id <= 0: color = water_col else: color = island_col else: # pixel belongs to a player color = settlement.owner.color.to_tuple() else: color = water_col if use_rotation: # inlined _get_rotated_coords rot_x, rot_y = self._rotate( (location_left + x, location_top + y), self._rotations) fife_point.set(rot_x - location_left, rot_y - location_top) else: fife_point.set(x, y) drawPoint(render_name, fife_point, *color) if dump_data: return json.dumps(data) def _timed_update(self, force=False): """Regular updates for domains we can't or don't want to keep track of.""" # OPTIMISATION NOTE: there can be pretty many ships, don't rely on the loop being rarely executed # update ship icons self.minimap_image.set_drawing_enabled() render_name = self._get_render_name("ship") self.minimap_image.rendertarget.removeAll(render_name) use_rotation = self._get_rotation_setting() # make use of this dummy points instead of creating a fife.point instances which are consuming a lot of resources dummy_point0 = fife.Point(0, 0) dummy_point1 = fife.Point(0, 0) for ship in self.world.ships: if not ship.in_ship_map: continue # no fisher ships, etc coord = self._world_to_minimap(ship.position.to_tuple(), use_rotation) color = ship.owner.color.to_tuple() # set correct icon if ship.owner is self.session.world.pirate: ship_icon_path = self.__class__.SHIP_PIRATE else: ship_icon_path = self.__class__.SHIP_NEUTRAL ship_icon = self.imagemanager.load(ship_icon_path) dummy_point1.set(coord[0], coord[1]) self.minimap_image.rendertarget.addImage(render_name, dummy_point1, ship_icon) if ship.owner.regular_player is True: # add the 'flag' over the ship icon, with the color of the owner dummy_point0.set(coord[0] - 5, coord[1] - 5) dummy_point1.set(coord[0], coord[1] - 5) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) dummy_point0.set(coord[0] - 6, coord[1] - 6) dummy_point1.set(coord[0], coord[1] - 6) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) dummy_point0.set(coord[0] - 4, coord[1] - 4) dummy_point1.set(coord[0], coord[1] - 4) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) # add black border around the flag dummy_point0.set(coord[0] - 6, coord[1] - 7) dummy_point1.set(coord[0], coord[1] - 7) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) dummy_point0.set(coord[0] - 4, coord[1] - 3) dummy_point1.set(coord[0], coord[1] - 4) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) dummy_point0.set(coord[0] - 6, coord[1] - 7) dummy_point1.set(coord[0] - 4, coord[1] - 3) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) # TODO: nicer selected view dummy_point0.set(coord[0], coord[1]) if ship in self.session.selected_instances: self.minimap_image.rendertarget.addPoint( render_name, dummy_point0, *Minimap.COLORS["water"]) for x_off, y_off in ((-2, 0), (+2, 0), (0, -2), (0, +2)): dummy_point1.set(coord[0] + x_off, coord[1] + y_off) self.minimap_image.rendertarget.addPoint( render_name, dummy_point1, *color) # draw settlement warehouses if something has changed settlements = self.world.settlements # save only worldids as to not introduce actual coupling cur_settlements = set(i.worldid for i in settlements) if force or \ (not hasattr(self, "_last_settlements") or cur_settlements != self._last_settlements): # update necessary warehouse_render_name = self._get_render_name("warehouse") self.minimap_image.rendertarget.removeAll(warehouse_render_name) for settlement in settlements: coord = settlement.warehouse.position.center.to_tuple() coord = self._world_to_minimap(coord, use_rotation) self._update_image(self.__class__.WAREHOUSE_IMAGE, warehouse_render_name, coord) self._last_settlements = cur_settlements def _update_image(self, img_path, name, coord_tuple): """Updates image as part of minimap (e.g. when it has moved)""" img = self.imagemanager.load(img_path) size_tuple = self._image_size_cache.get(img_path) if size_tuple is None: ratio = sum(self._world_to_minimap_ratio) / 2.0 ratio = max(1.0, ratio) size_tuple = int(img.getWidth() / ratio), int(img.getHeight() / ratio) self._image_size_cache[img_path] = size_tuple new_width, new_height = size_tuple p = self.__class__._dummy_fife_point p.set(*coord_tuple) # resizeImage also means draw self.minimap_image.rendertarget.resizeImage(name, p, img, new_width, new_height) def rotate_right(self): # keep track of rotation at any time, but only apply # if it's actually used self.rotation -= 1 self.rotation %= 4 if self._get_rotation_setting(): self.draw() def rotate_left(self): # see above self.rotation += 1 self.rotation %= 4 if self._get_rotation_setting(): self.draw() ## CALC UTILITY def _world_to_minimap(self, coords, use_rotation): """Complete coord transformation, batteries included. The methods below are for more specialised purposes.""" coords = self._world_coords_to_minimap_coords(coords) if use_rotation: coords = self._get_rotated_coords(coords) # transform from screen coords to minimap coords coords = (coords[0] - self.location.left, coords[1] - self.location.top) return coords def _get_rotation_setting(self): if not self.use_rotation: return False return self._rotation_setting def _on_rotation_setting_change(self, message): self._rotation_setting = horizons.globals.fife.get_uh_setting( "MinimapRotation") self.draw() _rotations = {0: 0, 1: 3 * math.pi / 2, 2: math.pi, 3: math.pi / 2} def _get_rotated_coords(self, tup): """Rotates according to current rotation settings. Input coord must be relative to screen origin, not minimap origin""" return self._rotate(tup, self._rotations) _from_rotations = {0: 0, 1: math.pi / 2, 2: math.pi, 3: 3 * math.pi / 2} def _get_from_rotated_coords(self, tup): return self._rotate(tup, self._from_rotations) def _rotate(self, tup, rotations): rotation = rotations[self.rotation] x = tup[0] y = tup[1] # rotate around center of minimap x -= self.location_center.x y -= self.location_center.y new_x = x * cos(rotation) - y * sin(rotation) new_y = x * sin(rotation) + y * cos(rotation) new_x += self.location_center.x new_y += self.location_center.y new_x = int(round(new_x)) new_y = int(round(new_y)) #some points may get out of range new_x = max(self.location.left, new_x) new_x = min(self.location.right, new_x) new_y = max(self.location.top, new_y) new_y = min(self.location.bottom, new_y) return (new_x, new_y) def _update_world_to_minimap_ratio(self): world_height = self.world.map_dimensions.height world_width = self.world.map_dimensions.width minimap_height = self.location.height minimap_width = self.location.width pixel_per_coord_x = float(world_width) / minimap_width pixel_per_coord_y = float(world_height) / minimap_height self._world_to_minimap_ratio = (pixel_per_coord_x, pixel_per_coord_y) def _world_coords_to_minimap_coords(self, tup): """Calculates which pixel in the minimap contains a coord in the real map. @param tup: (x, y) as ints @return tuple""" pixel_per_coord_x, pixel_per_coord_y = self._world_to_minimap_ratio return ( int(round(float(tup[0] - self.world.min_x) / pixel_per_coord_x)) + self.location.left, int(round(float(tup[1] - self.world.min_y) / pixel_per_coord_y)) + self.location.top) def _minimap_coords_to_world_coords(self, tup): """Inverse to _world_coords_to_minimap_coords""" pixel_per_coord_x, pixel_per_coord_y = self._world_to_minimap_ratio return (int(round((tup[0] - self.location.left) * pixel_per_coord_x)) + self.world.min_x, int(round((tup[1] - self.location.top) * pixel_per_coord_y)) + self.world.min_y) def get_size(self): return (self.location.height, self.location.width)
def __create_render_node(self, agent=None, layer=None, location=None, point=None): """Creatss a fife.RendererNode. Arguments: agent: The name of the agent the light should be attached too. If empty or None this will be ignored. Please note that the layer and location have to be set if this is empty or None. layer: The name of the layer the light originates from. Lights will illuminate lower layers, but not higher ones. If empty or None this will be ignored. location: The relative or absolute location of the light depending on whether the agent was set or not. A list with two or three values. If None this will be ignored. point: The relative or absolute window position of the light as a list with 2 values or a fife.Point. This differs from location as it is in pixels and (0, 0) is the upper left position of the window. """ if agent is not None and agent != "": entity = self.__application.world.get_entity(agent) if entity in self.entities: fifeagent = getattr(entity, FifeAgent.registered_as) agent = fifeagent.instance else: raise TypeError("The map %s has no entity %s" % (self.name, agent)) else: agent = None if layer is not None and layer != "": map_layer = self.get_layer(layer) if map_layer is None: raise TypeError("No such layer: %s" % (layer)) layer = map_layer elif agent is not None: layer = fifeagent.layer else: layer = None if location: if layer is not None: coords = fife.DoublePoint3D(*location) location = fife.Location() location.setLayer(layer) location.setMapCoordinates(coords) else: raise TypeError( "The location was set, but not agent or layer.") else: location = None if point is not None and not isinstance(point, fife.Point): point = fife.Point(*point) arguments = [] if agent is not None: arguments.append(agent) if location is not None: arguments.append(location) if layer is not None: arguments.append(layer) self.get_light_renderer().addActiveLayer(layer) if point is not None: arguments.append(point) if not arguments: raise TypeError("A light needs either an agent" ", a location and a layer" ", or a point") node = fife.RendererNode(*arguments) return node
def _timed_update(self, force=False): """Regular updates for domains we can't or don't want to keep track of.""" # OPTIMIZATION NOTE: There can be pretty many ships. # Don't rely on the loop being rarely executed! # update ship icons self.minimap_image.set_drawing_enabled() render_name = self._get_render_name("ship") self.minimap_image.rendertarget.removeAll(render_name) # Make use of these dummy points instead of creating fife.Point instances # (which are consuming a lot of resources). dummy_point0 = fife.Point(0, 0) dummy_point1 = fife.Point(0, 0) for ship in self.world.ships: if not ship.in_ship_map: continue # no fisher ships, etc coord = self.transform.world_to_minimap(ship.position.to_tuple()) color = ship.owner.color.to_tuple() # set correct icon if ship.owner is self.session.world.pirate: ship_icon_path = self.__class__.SHIP_PIRATE else: ship_icon_path = self.__class__.SHIP_NEUTRAL ship_icon = self.imagemanager.load(ship_icon_path) dummy_point1.set(coord[0], coord[1]) self.minimap_image.rendertarget.addImage(render_name, dummy_point1, ship_icon) if ship.owner.regular_player: # add the 'flag' over the ship icon, with the color of the owner dummy_point0.set(coord[0] - 5, coord[1] - 5) dummy_point1.set(coord[0], coord[1] - 5) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) dummy_point0.set(coord[0] - 6, coord[1] - 6) dummy_point1.set(coord[0], coord[1] - 6) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) dummy_point0.set(coord[0] - 4, coord[1] - 4) dummy_point1.set(coord[0], coord[1] - 4) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) # add black border around the flag dummy_point0.set(coord[0] - 6, coord[1] - 7) dummy_point1.set(coord[0], coord[1] - 7) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) dummy_point0.set(coord[0] - 4, coord[1] - 3) dummy_point1.set(coord[0], coord[1] - 4) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) dummy_point0.set(coord[0] - 6, coord[1] - 7) dummy_point1.set(coord[0] - 4, coord[1] - 3) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) # TODO: nicer selected view dummy_point0.set(coord[0], coord[1]) draw_point = self.minimap_image.rendertarget.addPoint if ship in self.session.selected_instances: draw_point(render_name, dummy_point0, *Minimap.COLORS["water"]) for x_off, y_off in ((-2, 0), (+2, 0), (0, -2), (0, +2)): dummy_point1.set(coord[0] + x_off, coord[1] + y_off) draw_point(render_name, dummy_point1, *color) # draw settlement warehouses if something has changed settlements = self.world.settlements # save only worldids as to not introduce actual coupling cur_settlements = set(i.worldid for i in settlements) if force or \ (not hasattr(self, "_last_settlements") or cur_settlements != self._last_settlements): # update necessary warehouse_render_name = self._get_render_name("warehouse") self.minimap_image.rendertarget.removeAll(warehouse_render_name) for settlement in settlements: coord = settlement.warehouse.position.center.to_tuple() coord = self.transform.world_to_minimap(coord) self._update_image(self.__class__.WAREHOUSE_IMAGE, warehouse_render_name, coord) self._last_settlements = cur_settlements
def _recalculate(self, where=None, dump_data=False): """Calculate which pixel of the minimap should display what and draw it @param where: Rect of minimap coords. Defaults to self.location @param dump_data: Don't draw but return calculated data""" self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") if where is None: where = self.location rt.removeAll(render_name) # calculate which area of the real map is mapped to which pixel on the minimap pixel_per_coord_x, pixel_per_coord_y = self._world_to_minimap_ratio # calculate values here so we don't have to do it in the loop pixel_per_coord_x_half_as_int = int(pixel_per_coord_x / 2) pixel_per_coord_y_half_as_int = int(pixel_per_coord_y / 2) world_min_x = self.world.min_x world_min_y = self.world.min_y island_col = self.COLORS["island"] water_col = self.COLORS["water"] location_left = self.location.left location_top = self.location.top if dump_data: data = [] drawPoint = lambda name, fife_point, r, g, b: data.append( (fife_point.x, fife_point.y, r, g, b)) else: drawPoint = rt.addPoint fife_point = fife.Point(0, 0) use_rotation = self._get_rotation_setting() full_map = self.world.full_map # loop through map coordinates, assuming (0, 0) is the origin of the minimap # this faciliates calculating the real world coords for x in xrange(where.left - self.location.left, where.left + where.width - self.location.left): for y in xrange(where.top - self.location.top, where.top + where.height - self.location.top): """ This code should be here, but since python can't do inlining, we have to inline ourselves for performance reasons covered_area = Rect.init_from_topleft_and_size( int(x * pixel_per_coord_x)+world_min_x, \ int(y * pixel_per_coord_y)+world_min_y), \ int(pixel_per_coord_x), int(pixel_per_coord_y)) real_map_point = covered_area.center """ # use center of the rect that the pixel covers real_map_x = int( x * pixel_per_coord_x ) + world_min_x + pixel_per_coord_x_half_as_int real_map_y = int( y * pixel_per_coord_y ) + world_min_y + pixel_per_coord_y_half_as_int real_map_coords = (real_map_x, real_map_y) # check what's at the covered_area if real_map_coords in full_map: # this pixel is an island tile = full_map[real_map_coords] settlement = tile.settlement if settlement is None: # island without settlement if tile.id <= 0: color = water_col else: color = island_col else: # pixel belongs to a player color = settlement.owner.color.to_tuple() else: color = water_col if use_rotation: # inlined _get_rotated_coords rot_x, rot_y = self._rotate( (location_left + x, location_top + y), self._rotations) fife_point.set(rot_x - location_left, rot_y - location_top) else: fife_point.set(x, y) drawPoint(render_name, fife_point, *color) if dump_data: return json.dumps(data)
class Minimap: """A basic minimap. USAGE: Minimap can be drawn via GenericRenderer on an arbitrary position (determined by rect in ctor) or via Pychan Icon. In this case, the rect parameter only determines the size, the Minimap will scroll by default on clicks, overwrite on_click if you don't want that. TODO: * Remove renderer when used in icon node * Clear up distinction of coords where the minimap image or screen is the origin * Create a minimap tag for pychan ** Handle clicks, remove overlay icon """ COLORS = { "island": (137, 117, 87), "cam": (1, 1, 1), "water": (198, 188, 165), "highlight": (255, 0, 0), # for events } WAREHOUSE_IMAGE = "content/gui/icons/minimap/warehouse.png" SHIP_NEUTRAL = "content/gui/icons/minimap/ship_neutral.png" SHIP_PIRATE = "content/gui/icons/minimap/pirate.png" GROUND_UNIT_IMAGE = "content/gui/icons/minimap/groundunit.png" SHIP_DOT_UPDATE_INTERVAL = 0.5 # seconds # Alpha-ordering determines the order: RENDER_NAMES = { "background": "c", "base": "d", # islands, etc. "warehouse": "e", "ship": "f", "cam": "g", "ship_route": "h", "highlight": "l", } __minimap_id_counter = itertools.count() __ship_route_counter = itertools.count() # all active instances _instances = [] # type: List[Minimap] _dummy_fife_point = fife.Point( 0, 0) # use when you quickly need a temporary point def __init__(self, position, session, view, targetrenderer, imagemanager, renderer=None, world=None, cam_border=True, use_rotation=True, on_click=None, preview=False, tooltip=None): """ @param position: a Rect or a Pychan Icon, where we will draw to @param world: World object or fake thereof @param view: View object for cam control. Can be None to disable this @param renderer: renderer to be used if position isn't an icon @param targetrenderer: fife target renderer for drawing on icons @param imagemanager: fife imagemanager for drawing on icons @param cam_border: boolean, whether to draw the cam border @param use_rotation: boolean, whether to use rotation (it must also be enabled in the settings) @param on_click: function taking 1 argument or None for scrolling @param preview: flag, whether to only show the map as preview @param tooltip: always show this tooltip when cursor hovers over minimap NOTE: Preview generation in a different process overwrites this method. """ if isinstance(position, Rect): self.location = position self.renderer = renderer else: # assume icon self.location = Rect.init_from_topleft_and_size( 0, 0, position.width, position.height) self.icon = position self.use_overlay_icon(self.icon) # FIXME PY3 width / height of icon is sometimes zero. Why? if self.location.height == 0 or self.location.width == 0: self.location = Rect.init_from_topleft_and_size(0, 0, 128, 128) self.session = session self.world = world self.view = view self.fixed_tooltip = tooltip self.click_handler = on_click if on_click is not None else self.default_on_click self.cam_border = cam_border self.use_rotation = use_rotation self.preview = preview self.location_center = self.location.center self._id = str(next(self.__class__.__minimap_id_counter) ) # internal identifier, used for allocating resources self._image_size_cache = {} # internal detail self.imagemanager = imagemanager self.minimap_image = _MinimapImage(self, targetrenderer) self._rotation_setting = horizons.globals.fife.get_uh_setting( "MinimapRotation") if self.use_rotation: SettingChanged.subscribe(self._on_setting_changed) self.transform = None def end(self): self.disable() self.world = None self.session = None self.renderer = None if self.use_rotation: SettingChanged.unsubscribe(self._on_setting_changed) def disable(self): """Due to the way the minimap works, there isn't really a show/hide, but you can disable it with this and enable again with draw(). Stops all updates.""" ExtScheduler().rem_all_classinst_calls(self) if self.view is not None: self.view.discard_change_listener(self.update_cam) if self in self.__class__._instances: self.__class__._instances.remove(self) def draw(self): """Recalculates and draws the whole minimap of self.session.world or world. The world you specified is reused for every operation until the next draw(). """ if self.world is None and self.session.world is not None: self.world = self.session.world # in case minimap has been constructed before the world if not self.world.inited: return # don't draw while loading if self.transform is None: self.transform = _MinimapTransform(self.world.map_dimensions, self.location, 0, self._get_rotation_setting()) self.update_rotation() self.__class__._instances.append(self) # update cam when view updates if self.view is not None and not self.view.has_change_listener( self.update_cam): self.view.add_change_listener(self.update_cam) if not hasattr(self, "icon"): # add to global generic renderer with id specific to this instance self.renderer.removeAll("minimap_image" + self._id) self.minimap_image.reset() # NOTE: this is for the generic renderer interface, the offrenderer has slightly different methods node = fife.RendererNode( fife.Point(self.location.center.x, self.location.center.y)) self.renderer.addImage("minimap_image" + self._id, node, self.minimap_image.image, False) else: # attach image to pychan icon (recommended) self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.update_cam() self._recalculate() if not self.preview: self._timed_update(force=True) ExtScheduler().rem_all_classinst_calls(self) ExtScheduler().add_new_object(self._timed_update, self, self.SHIP_DOT_UPDATE_INTERVAL, -1) def draw_data(self, data): """Display data from dump_data""" # only icon mode for now self.minimap_image.reset() self.icon.image = fife.GuiImage(self.minimap_image.image) self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") draw_point = rt.addPoint point = fife.Point() for x, y, r, g, b in data: point.set(x, y) draw_point(render_name, point, r, g, b) def _get_render_name(self, key): return self.RENDER_NAMES[key] + self._id def update_cam(self): """Redraw camera border.""" if not self.cam_border or self.view is None: # needs view return if self.world is None or not self.world.inited: return # don't draw while loading self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(self._get_render_name("cam")) # draw rect for current screen displayed_area = self.view.get_displayed_area() minimap_corners_as_point = [] for (x, y) in displayed_area: coords = self.transform.world_to_minimap((x, y)) minimap_corners_as_point.append(fife.Point(coords[0], coords[1])) for i in range(0, 4): self.minimap_image.rendertarget.addLine( self._get_render_name("cam"), minimap_corners_as_point[i], minimap_corners_as_point[(i + 1) % 4], *self.COLORS["cam"]) @classmethod def update(cls, tup): for minimap in cls._instances: minimap._update(tup) def _update(self, tup): """Recalculate and redraw minimap for real world coord tup @param tup: (x, y)""" if self.world is None or not self.world.inited: return # don't draw while loading minimap_point = self.transform.world_to_minimap(tup) self._recalculate(minimap_point) def use_overlay_icon(self, icon): """Configures icon so that clicks get mapped here. The current gui requires, that the minimap is drawn behind an icon.""" self.overlay_icon = icon icon.mapEvents({ icon.name + '/mousePressed': self._on_click, icon.name + '/mouseDragged': self._on_drag, icon.name + '/mouseEntered': self._mouse_entered, icon.name + '/mouseMoved': self._mouse_moved, icon.name + '/mouseExited': self._mouse_exited, }) def default_on_click(self, event, drag): """Handler for clicks (pressed and dragged) Scrolls screen to the point, where the cursor points to on the minimap. Overwrite this method to your convenience. """ if self.preview: return # we don't do anything in this mode button = event.getButton() map_coords = event.map_coords if button == fife.MouseEvent.RIGHT: if drag: return for i in self.session.selected_instances: if i.movable: Act(i, *map_coords).execute(self.session) elif button == fife.MouseEvent.LEFT: if self.view is None: print( "Warning: Can't handle minimap clicks since we have no view object" ) else: self.view.center(*map_coords) def _on_click(self, event): if self.world is not None: # supply world coords if there is a world event.map_coords = self._get_event_coords(event) if event.map_coords: self.click_handler(event, drag=False) else: self.click_handler(event, drag=True) def _on_drag(self, event): if self.world is not None: # supply world coords if there is a world event.map_coords = self._get_event_coords(event) if event.map_coords: self.click_handler(event, drag=True) else: self.click_handler(event, drag=True) def _get_event_coords(self, event): """Returns position of event as uh map coordinate tuple or None""" mouse_position = Point(event.getX(), event.getY()) if not hasattr(self, "icon"): icon_pos = Point(*self.overlay_icon.getAbsolutePos()) abs_mouse_position = icon_pos + mouse_position if not self.location.contains(abs_mouse_position): # mouse click was on icon but not actually on minimap return None return self.transform.minimap_to_world((event.getX(), event.getY())) def _mouse_entered(self, event): self._show_tooltip(event) def _mouse_moved(self, event): self._show_tooltip(event) def _mouse_exited(self, event): if hasattr(self, "icon"): # only supported for icon mode atm self.icon.hide_tooltip() def _show_tooltip(self, event): if not hasattr(self, "icon"): # only supported for icon mode atm return if self.fixed_tooltip is not None: self.icon.helptext = self.fixed_tooltip self.icon.position_tooltip(event) #self.icon.show_tooltip() else: coords = self._get_event_coords(event) if not coords: # no valid/relevant event location self.icon.hide_tooltip() return tile = self.world.get_tile(Point(*coords)) if tile is not None and tile.settlement is not None: new_helptext = tile.settlement.get_component( NamedComponent).name if self.icon.helptext != new_helptext: self.icon.helptext = new_helptext self.icon.show_tooltip() else: self.icon.position_tooltip(event) else: # mouse not over relevant part of the minimap self.icon.hide_tooltip() def highlight(self, tup, factor=1.0, speed=1.0, finish_callback=None, color=(0, 0, 0)): """Try to get the users attention on a certain point of the minimap. @param tup: world coords @param factor: float indicating importance of event @param speed: animation speed as factor @param finish_callback: executed when animation finishes @param color: color of anim, (r,g,b), r,g,b of [0,255] @return duration of full animation in seconds""" tup = self.transform.world_to_minimap(tup) # grow the circle from MIN_RAD to MAX_RAD and back with STEPS steps, where the # interval between steps is INTERVAL seconds MIN_RAD = int(3 * factor) # pixel MAX_RAD = int(12 * factor) # pixel STEPS = int(20 * factor) INTERVAL = (math.pi / 16) * factor def high(i=0): i += 1 render_name = self._get_render_name("highlight") + str(tup) self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) if i > STEPS: if finish_callback: finish_callback() return part = i # grow bigger if i > STEPS // 2: # after the first half part = STEPS - i # become smaller radius = MIN_RAD + int( (float(part) / (STEPS // 2)) * (MAX_RAD - MIN_RAD)) draw_point = self.minimap_image.rendertarget.addPoint for x, y in Circle(Point(*tup), radius=radius).get_border_coordinates(): draw_point(render_name, fife.Point(x, y), *color) ExtScheduler().add_new_object(lambda: high(i), self, INTERVAL, loops=1) high() return STEPS * INTERVAL def show_unit_path(self, unit): """Show the path a unit is moving along""" path = unit.path.path if path is None: # show at least the position path = [unit.position.to_tuple()] # the path always contains the full path, the unit might be somewhere in it position_of_unit_in_path = 0 unit_pos = unit.position.to_tuple() for i, pos in enumerate(path): if pos == unit_pos: position_of_unit_in_path = i break # display units one ahead if possible, it looks nicer if the unit is moving if len(path) > 1 and position_of_unit_in_path + 1 < len(path): position_of_unit_in_path += 1 path = path[position_of_unit_in_path:] # draw every step-th coord step = 1 relevant_coords = [path[0]] for i in range(step, len(path), step): relevant_coords.append(path[i]) relevant_coords.append(path[-1]) # get coords, actual drawing self.minimap_image.set_drawing_enabled() p = fife.Point(0, 0) render_name = self._get_render_name("ship_route") + str( next(self.__class__.__ship_route_counter)) color = unit.owner.color.to_tuple() last_coord = None draw_point = self.minimap_image.rendertarget.addPoint for i in relevant_coords: coord = self.transform.world_to_minimap(i) if last_coord is not None and \ sum(abs(last_coord[i] - coord[i]) for i in (0, 1)) < 2: # 2 is min dist in pixels continue last_coord = coord p.x = coord[0] p.y = coord[1] draw_point(render_name, p, *color) def cleanup(): self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) speed = 1.0 + math.sqrt(5) / 2 self.highlight(path[-1], factor=0.4, speed=speed, finish_callback=cleanup, color=color) return True def _recalculate(self, where=None): """Calculate which pixel of the minimap should display what and draw it @param where: minimap coords. If this is given only that pixel will be redrawn """ self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") if where is None: rt.removeAll(render_name) points = self.transform.iter_points() else: points = [where] # Is this really worth it? draw_point = rt.addPoint fife_point = fife.Point(0, 0) island_color = self.COLORS["island"] water_color = self.COLORS["water"] for (x, y) in points: world_coords = self.transform.minimap_to_world((x, y)) color = get_minimap_color(world_coords, self.world, island_color, water_color) fife_point.set(x, y) draw_point(render_name, fife_point, *color) def _timed_update(self, force=False): """Regular updates for domains we can't or don't want to keep track of.""" # OPTIMIZATION NOTE: There can be pretty many ships. # Don't rely on the loop being rarely executed! # update ship icons self.minimap_image.set_drawing_enabled() render_name = self._get_render_name("ship") self.minimap_image.rendertarget.removeAll(render_name) # Make use of these dummy points instead of creating fife.Point instances # (which are consuming a lot of resources). dummy_point0 = fife.Point(0, 0) dummy_point1 = fife.Point(0, 0) for ship in self.world.ships: if not ship.in_ship_map: continue # no fisher ships, etc coord = self.transform.world_to_minimap(ship.position.to_tuple()) color = ship.owner.color.to_tuple() # set correct icon if ship.owner is self.session.world.pirate: ship_icon_path = self.__class__.SHIP_PIRATE else: ship_icon_path = self.__class__.SHIP_NEUTRAL ship_icon = self.imagemanager.load(ship_icon_path) dummy_point1.set(coord[0], coord[1]) self.minimap_image.rendertarget.addImage(render_name, dummy_point1, ship_icon) if ship.owner.regular_player: # add the 'flag' over the ship icon, with the color of the owner dummy_point0.set(coord[0] - 5, coord[1] - 5) dummy_point1.set(coord[0], coord[1] - 5) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) dummy_point0.set(coord[0] - 6, coord[1] - 6) dummy_point1.set(coord[0], coord[1] - 6) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) dummy_point0.set(coord[0] - 4, coord[1] - 4) dummy_point1.set(coord[0], coord[1] - 4) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, color[0], color[1], color[2]) # add black border around the flag dummy_point0.set(coord[0] - 6, coord[1] - 7) dummy_point1.set(coord[0], coord[1] - 7) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) dummy_point0.set(coord[0] - 4, coord[1] - 3) dummy_point1.set(coord[0], coord[1] - 4) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) dummy_point0.set(coord[0] - 6, coord[1] - 7) dummy_point1.set(coord[0] - 4, coord[1] - 3) self.minimap_image.rendertarget.addLine( render_name, dummy_point0, dummy_point1, 0, 0, 0) # TODO: nicer selected view dummy_point0.set(coord[0], coord[1]) draw_point = self.minimap_image.rendertarget.addPoint if ship in self.session.selected_instances: draw_point(render_name, dummy_point0, *Minimap.COLORS["water"]) for x_off, y_off in ((-2, 0), (+2, 0), (0, -2), (0, +2)): dummy_point1.set(coord[0] + x_off, coord[1] + y_off) draw_point(render_name, dummy_point1, *color) # draw settlement warehouses if something has changed settlements = self.world.settlements # save only worldids as to not introduce actual coupling cur_settlements = set(i.worldid for i in settlements) if force or \ (not hasattr(self, "_last_settlements") or cur_settlements != self._last_settlements): # update necessary warehouse_render_name = self._get_render_name("warehouse") self.minimap_image.rendertarget.removeAll(warehouse_render_name) for settlement in settlements: coord = settlement.warehouse.position.center.to_tuple() coord = self.transform.world_to_minimap(coord) self._update_image(self.__class__.WAREHOUSE_IMAGE, warehouse_render_name, coord) self._last_settlements = cur_settlements def _update_image(self, img_path, name, coord_tuple): """Updates image as part of minimap (e.g. when it has moved)""" img = self.imagemanager.load(img_path) size_tuple = self._image_size_cache.get(img_path) if size_tuple is None: ratio = sum(self.transform.world_to_minimap_ratio) / 2.0 ratio = max(1.0, ratio) size_tuple = int(img.getWidth() / ratio), int(img.getHeight() / ratio) self._image_size_cache[img_path] = size_tuple new_width, new_height = size_tuple p = self.__class__._dummy_fife_point p.set(*coord_tuple) # resizeImage also means draw self.minimap_image.rendertarget.resizeImage(name, p, img, new_width, new_height) def update_rotation(self): # ensure the minimap rotation matches the main view rotation self.transform.set_rotation(self.view.cam.getRotation()) self.draw() def _get_rotation_setting(self): return self.use_rotation and self._rotation_setting def _on_setting_changed(self, message): if message.setting_name == "MinimapRotation": self._rotation_setting = message.new_value self.transform.set_use_rotation(self._get_rotation_setting()) self.draw() def get_size(self): return (self.location.height, self.location.width)
def to_fife_point(self): """Returns point as fife.Point""" return fife.Point(self.x, self.y)
def mouseDragged(self, evt): if evt.getButton() == fife.MouseEvent.LEFT and hasattr( self, 'select_begin'): do_multi = ((self.select_begin[0] - evt.getX())**2 + (self.select_begin[1] - evt.getY())** 2) >= 10 # ab 3px (3*3 + 1) self.session.view.renderer['GenericRenderer'].removeAll("select") if do_multi: a = fife.Point(min(self.select_begin[0], evt.getX()), \ min(self.select_begin[1], evt.getY())) b = fife.Point(max(self.select_begin[0], evt.getX()), \ min(self.select_begin[1], evt.getY())) c = fife.Point(max(self.select_begin[0], evt.getX()), \ max(self.select_begin[1], evt.getY())) d = fife.Point(min(self.select_begin[0], evt.getX()), \ max(self.select_begin[1], evt.getY())) self.session.view.renderer['GenericRenderer'].addLine("select", \ fife.GenericRendererNode(a), fife.GenericRendererNode(b), 200, 200, 200) self.session.view.renderer['GenericRenderer'].addLine("select", \ fife.GenericRendererNode(b), fife.GenericRendererNode(c), 200, 200, 200) self.session.view.renderer['GenericRenderer'].addLine("select", \ fife.GenericRendererNode(d), fife.GenericRendererNode(c), 200, 200, 200) self.session.view.renderer['GenericRenderer'].addLine("select", \ fife.GenericRendererNode(a), fife.GenericRendererNode(d), 200, 200, 200) selectable = [] instances = self.session.view.cam.getMatchingInstances(\ fife.Rect(min(self.select_begin[0], evt.getX()), \ min(self.select_begin[1], evt.getY()), \ abs(evt.getX() - self.select_begin[0]), \ abs(evt.getY() - self.select_begin[1])) if do_multi else fife.ScreenPoint(evt.getX(), evt.getY()), self.session.view.layers[LAYERS.OBJECTS]) # Only one unit, select anyway if len(instances) == 1: instance = WorldObject.get_object_by_id( int(instances[0].getId())) if instance.is_selectable: selectable.append(instance) else: for i in instances: instance = WorldObject.get_object_by_id(int(i.getId())) if instance.is_selectable and instance.owner == self.session.world.player: selectable.append(instance) if len(selectable) > 1: if do_multi: for instance in selectable[:]: # iterate through copy for safe removal if instance.is_building: selectable.remove(instance) else: selectable = [selectable.pop(0)] if do_multi: selectable = set(self.select_old | frozenset(selectable)) else: selectable = set(self.select_old ^ frozenset(selectable)) for instance in self.session.selected_instances - selectable: instance.deselect() for instance in selectable - self.session.selected_instances: instance.select() self.session.selected_instances = selectable elif (evt.getButton() == fife.MouseEvent.RIGHT): pass else: super(SelectionTool, self).mouseDragged(evt) return evt.consume()
def updateLighting(self): hour = self.application.world.getHour() # optimization: this method is expensive, so only run it: # - after loading a new map (self.last_hour is None) # - if the hour has changed since the last run # - if new instances were added since the last run (a listener sets self.last_hour to None) # TODO: in the last case: only update the new instances, not all of them if hour == self.last_hour: return self.last_hour = hour day = self.application.world.getDay() # tooltip clock #self.application.gui.tooltip.printMessage("Day " + str(day) # + "; Time: " + str(int(hour)) + ":" + str(int((hour%1.0)*60))) # day-night cycle self.light_renderer.removeAll("pc") cycle = customanimations.light_cycle for next_hour in cycle: if hour < next_hour: factor = (hour - prev_hour) / (next_hour - prev_hour) # global light self.camera.setLightingColor( cycle[prev_hour][0] + factor * (cycle[next_hour][0] - cycle[prev_hour][0]), cycle[prev_hour][1] + factor * (cycle[next_hour][1] - cycle[prev_hour][1]), cycle[prev_hour][2] + factor * (cycle[next_hour][2] - cycle[prev_hour][2])) # local lights #if self.application.current_character: # if self.application.current_character.visual: # for i in xrange(2): # self.light_renderer.addSimpleLight("pc", # fife.RendererNode( # self.application.current_character.visual.instance), # 255, 64, 32, 3.0, 1.875, # int((cycle[prev_hour][3]+factor* # (cycle[next_hour][3]-cycle[prev_hour][3]))*255), # int((cycle[prev_hour][4]+factor* # (cycle[next_hour][4]-cycle[prev_hour][4]))*255), # int((cycle[prev_hour][5]+factor* # (cycle[next_hour][5]-cycle[prev_hour][5]))*255)) for instance in self.application.maplayer.getInstances(): if not instance: print("getInstances() error!", instance) #self.application.maplayer.getInstances() continue if instance.getObject().getId( ) not in customanimations.light_circles: continue circle = customanimations.light_circles[ instance.getObject().getId()] if not ((circle[0] < hour < circle[1]) or (hour < circle[1] < circle[0]) or (circle[1] < circle[0] < hour)): continue for i in xrange(2): # double light power! self.light_renderer.addSimpleLight( group="pc", n=fife.RendererNode( instance, fife.Point( int(circle[7]), #int(circle[7] * self.camera.getZoom()), int(circle[8]))), #int(circle[8] * self.camera.getZoom()))), intensity=255, radius=64, subdivisions=32, xstretch=circle[2], ystretch=circle[3], r=int(circle[4] * 255), g=int(circle[5] * 255), b=int(circle[6] * 255)) break prev_hour = next_hour # set transparency for light overlay instances for instance in self.application.maplayer.getInstances(): if not instance: print("getInstances() error!", instance) #self.application.maplayer.getInstances() continue if instance.getObject().getId( ) in customanimations.light_instances: hours = customanimations.light_instances[ instance.getObject().getId()] if ((hours[0] < hour < hours[1]) or (hour < hours[1] < hours[0]) or (hours[1] < hours[0] < hour)): instance.get2dGfxVisual().setTransparency(0) else: instance.get2dGfxVisual().setTransparency(255)