def mouseReleased(self, evt): """Tear selected instances and set selection tool as cursor""" self.log.debug("TearingTool: mouseReleased") if evt.getButton() == fife.MouseEvent.LEFT: coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) selection_list_copy = [building for building in self.selected] for building in selection_list_copy: self.session.view.renderer['InstanceRenderer'].removeColored(building._instance) if (not building.id in BUILDINGS.EXPAND_RANGE) or self.confirm_ranged_delete(building): Tear(building).execute(self.session) else: if self._hovering_over: # we're hovering over a building, but none is selected, so this tear action isn't allowed warehouses = [ b for b in self._hovering_over if b.id == BUILDINGS.WAREHOUSE and b.owner.is_local_player] if warehouses: # tried to tear a warehouse, this is especially non-tearable pos = warehouses[0].position.origin self.session.ingame_gui.message_widget.add(point=pos, string_id="WAREHOUSE_NOT_TEARABLE" ) self.selected = WeakList() self._hovering_over = WeakList() if not evt.isShiftPressed() and not horizons.globals.fife.get_uh_setting('UninterruptedBuilding'): self.tear_tool_active = False self.on_escape() evt.consume()
def _mark(self, *edges): """Highights building instances and keeps self.selected up to date.""" self._restore_transparent_instances() self.log.debug("TearingTool: mark") if len(edges) == 1: edges = (edges[0], edges[0]) elif len(edges) == 2: edges = ((min(edges[0][0], edges[1][0]), min(edges[0][1], edges[1][1])), (max(edges[0][0], edges[1][0]), max(edges[0][1], edges[1][1]))) else: edges = None if self.oldedges != edges or edges is None: for i in self.selected: self.session.view.renderer['InstanceRenderer'].removeColored(i._instance) self.selected = WeakList() self.oldedges = edges if edges is not None: self._hovering_over = WeakList() for x in xrange(edges[0][0], edges[1][0] + 1): for y in xrange(edges[0][1], edges[1][1] + 1): b = self.session.world.get_building(Point(x, y)) if b is not None: if b not in self._hovering_over: self._hovering_over.append(b) self._make_surrounding_transparent(b) self._remove_object_transparency(Point(x,y)) if b.tearable and b.owner is not None and b.owner.is_local_player: if b not in self.selected: self._make_surrounding_transparent(b) self.selected.append(b) self._remove_object_transparency(Point(x,y)) for i in self.selected: self.session.view.renderer['InstanceRenderer'].addColored(i._instance, *self.tear_selection_color) self.log.debug("TearingTool: mark done")
def mouseReleased(self, evt): """Tear selected instances and set selection tool as cursor""" self.log.debug("TearingTool: mouseReleased") if evt.getButton() == fife.MouseEvent.LEFT: coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) selection_list_copy = [building for building in self.selected] if self.selected: for building in selection_list_copy: self.session.view.renderer['InstanceRenderer'].removeColored(building._instance) if (building.id not in BUILDINGS.EXPAND_RANGE) or self.confirm_ranged_delete(building): Tear(building).execute(self.session) elif self._hovering_over: # we're hovering over a building, but none is selected, so this tear action isn't allowed warehouses = [ b for b in self._hovering_over if b.id == BUILDINGS.WAREHOUSE and b.owner.is_local_player] if warehouses: # tried to tear a warehouse, this is especially non-tearable pos = warehouses[0].position.origin self.session.ingame_gui.message_widget.add(point=pos, string_id="WAREHOUSE_NOT_TEARABLE" ) self.selected = WeakList() self._hovering_over = WeakList() if not evt.isShiftPressed() and not horizons.globals.fife.get_uh_setting('UninterruptedBuilding'): self.tear_tool_active = False self.on_escape() evt.consume()
def __init__(self, session): super(TearingTool, self).__init__(session) self.coords = None self.selected = WeakList() self.oldedges = None self.tear_tool_active = True self.session.ingame_gui.hide_menu() self.session.selected_instances.clear() horizons.globals.fife.set_cursor_image("tearing") self._hovering_over = WeakList() WorldObjectDeleted.subscribe(self._on_object_deleted)
def __init__(self, session): super(TearingTool, self).__init__(session) self._transparent_instances = set() # fife instances modified for transparency self.coords = None self.selected = WeakList() self.oldedges = None self.tear_tool_active = True self.session.ingame_gui.hide_menu() self.session.selected_instances.clear() horizons.globals.fife.set_cursor_image("tearing") self._hovering_over = WeakList() WorldObjectDeleted.subscribe(self._on_object_deleted)
def __init__(self, session): super(NavigationTool, self).__init__(session) self._last_mmb_scroll_point = [0, 0] # coordinates of last mouse positions self.last_exact_world_location = fife.ExactModelCoordinate() self._hover_instances_update_scheduled = False self.middle_scroll_active = False class CmdListener(fife.ICommandListener): pass self.cmdlist = CmdListener() horizons.globals.fife.eventmanager.addCommandListener(self.cmdlist) self.cmdlist.onCommand = self.onCommand if not self.__class__.send_hover_instances_update: # clear HoverInstancesChanged.broadcast(self, set()) self.__class__.last_hover_instances = WeakList() else: # need updates about scrolling here self.session.view.add_change_listener( self._schedule_hover_instance_update) self._schedule_hover_instance_update() class CoordsTooltip(object): @classmethod def get_instance(cls, cursor_tool): if cursor_tool.session.ingame_gui.coordinates_tooltip is not None: inst = cursor_tool.session.ingame_gui.coordinates_tooltip inst.cursor_tool = cursor_tool return inst else: return CoordsTooltip(cursor_tool) def __init__(self, cursor_tool, **kwargs): super(CoordsTooltip, self).__init__(**kwargs) cursor_tool.session.ingame_gui.coordinates_tooltip = self self.cursor_tool = cursor_tool self.enabled = False self.icon = Icon(position=( 1, 1)) # 0, 0 is currently not supported by tooltips def toggle(self): self.enabled = not self.enabled if not self.enabled and self.icon.tooltip_shown: self.icon.hide_tooltip() def show_evt(self, evt): if self.enabled: x, y = self.cursor_tool.get_world_location(evt).to_tuple() self.icon.helptext = u'%d, %d ' % (x, y) + _( "Press H to remove this hint") self.icon.position_tooltip(evt) self.icon.show_tooltip() self.tooltip = CoordsTooltip.get_instance(self)
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)
class NavigationTool(CursorTool): """Navigation Class to process mouse actions ingame""" last_event_pos = fife.ScreenPoint( 0, 0) # last received mouse event position, fife.ScreenPoint send_hover_instances_update = True HOVER_INSTANCES_UPDATE_DELAY = 1 # sec last_hover_instances = WeakList() def __init__(self, session): super(NavigationTool, self).__init__(session) self._last_mmb_scroll_point = [0, 0] # coordinates of last mouse positions self.last_exact_world_location = fife.ExactModelCoordinate() self._hover_instances_update_scheduled = False self.middle_scroll_active = False class CmdListener(fife.ICommandListener): pass self.cmdlist = CmdListener() horizons.globals.fife.eventmanager.addCommandListener(self.cmdlist) self.cmdlist.onCommand = self.onCommand if not self.__class__.send_hover_instances_update: # clear HoverInstancesChanged.broadcast(self, set()) self.__class__.last_hover_instances = WeakList() else: # need updates about scrolling here self.session.view.add_change_listener( self._schedule_hover_instance_update) self._schedule_hover_instance_update() class CoordsTooltip(object): @classmethod def get_instance(cls, cursor_tool): if cursor_tool.session.ingame_gui.coordinates_tooltip is not None: inst = cursor_tool.session.ingame_gui.coordinates_tooltip inst.cursor_tool = cursor_tool return inst else: return CoordsTooltip(cursor_tool) def __init__(self, cursor_tool, **kwargs): super(CoordsTooltip, self).__init__(**kwargs) cursor_tool.session.ingame_gui.coordinates_tooltip = self self.cursor_tool = cursor_tool self.enabled = False self.icon = Icon(position=( 1, 1)) # 0, 0 is currently not supported by tooltips def toggle(self): self.enabled = not self.enabled if not self.enabled and self.icon.tooltip_shown: self.icon.hide_tooltip() def show_evt(self, evt): if self.enabled: x, y = self.cursor_tool.get_world_location(evt).to_tuple() self.icon.helptext = u'%d, %d ' % (x, y) + _( "Press H to remove this hint") self.icon.position_tooltip(evt) self.icon.show_tooltip() self.tooltip = CoordsTooltip.get_instance(self) def remove(self): if self.__class__.send_hover_instances_update: self.session.view.remove_change_listener( self._schedule_hover_instance_update) horizons.globals.fife.eventmanager.removeCommandListener(self.cmdlist) super(NavigationTool, self).remove() def mousePressed(self, evt): if evt.getButton() == fife.MouseEvent.MIDDLE: if horizons.globals.fife.get_uh_setting("MiddleMousePan"): self._last_mmb_scroll_point = (evt.getX(), evt.getY()) self.middle_scroll_active = True def mouseReleased(self, evt): if evt.getButton() == fife.MouseEvent.MIDDLE: if horizons.globals.fife.get_uh_setting("MiddleMousePan"): self.middle_scroll_active = False def mouseDragged(self, evt): if evt.getButton() == fife.MouseEvent.MIDDLE: if horizons.globals.fife.get_uh_setting("MiddleMousePan"): if self.middle_scroll_active: scroll_by = (self._last_mmb_scroll_point[0] - evt.getX(), self._last_mmb_scroll_point[1] - evt.getY()) self.session.view.scroll(*scroll_by) self._last_mmb_scroll_point = (evt.getX(), evt.getY()) else: # Else the event will mistakenly be delegated if the left mouse button is hit while # scrolling using the middle mouse button if not self.middle_scroll_active: NavigationTool.mouseMoved(self, evt) # return new mouse position after moving def mouseMoved(self, evt): if not self.session.world.inited: return self.tooltip.show_evt(evt) # don't overwrite this last_event_pos instance. Due to class # hierarchy, it would write to the lowest class (e.g. SelectionTool) # and the attribute in NavigationTool would be left unchanged. self.__class__.last_event_pos.set(evt.getX(), evt.getY(), 0) mousepoint = self.__class__.last_event_pos # Status menu update current = self.get_exact_world_location(evt) distance_ge = lambda a, b, epsilon: abs((a.x - b.x)**2 + (a.y - b.y)**2) >= epsilon**2 if distance_ge(current, self.last_exact_world_location, 4): # update every 4 tiles for settlement info self.last_exact_world_location = current # update res bar with settlement-related info LastActivePlayerSettlementManager().update(current) # check if instance update is scheduled if self.__class__.send_hover_instances_update: self._schedule_hover_instance_update() # Mouse scrolling x, y = 0, 0 if mousepoint.x < VIEW.AUTOSCROLL_WIDTH: x -= VIEW.AUTOSCROLL_WIDTH - mousepoint.x elif mousepoint.x > (self.session.view.cam.getViewPort().right() - VIEW.AUTOSCROLL_WIDTH): x += VIEW.AUTOSCROLL_WIDTH + mousepoint.x - self.session.view.cam.getViewPort( ).right() if mousepoint.y < VIEW.AUTOSCROLL_WIDTH: y -= VIEW.AUTOSCROLL_WIDTH - mousepoint.y elif mousepoint.y > (self.session.view.cam.getViewPort().bottom() - VIEW.AUTOSCROLL_WIDTH): y += VIEW.AUTOSCROLL_WIDTH + mousepoint.y - self.session.view.cam.getViewPort( ).bottom() x *= 10 y *= 10 self.session.view.autoscroll(x, y) # move up mouse wheel = zoom in def mouseWheelMovedUp(self, evt): track_cursor = horizons.globals.fife.get_uh_setting( "CursorCenteredZoom") self.session.view.zoom_in(track_cursor) evt.consume() # move down mouse wheel = zoom out def mouseWheelMovedDown(self, evt): track_cursor = horizons.globals.fife.get_uh_setting( "CursorCenteredZoom") self.session.view.zoom_out(track_cursor) evt.consume() def onCommand(self, command): """Called when some kind of command-event happens. For "documentation", see: engine/core/eventchannel/command/ec_commandids.h engine/core/eventchannel/eventmanager.cpp in fife. It's usually about mouse/keyboard focus or window being iconified/restored. """ stop_scrolling_on = (fife.CMD_APP_ICONIFIED, fife.CMD_MOUSE_FOCUS_LOST, fife.CMD_INPUT_FOCUS_LOST) if command.getCommandType() in stop_scrolling_on: # it has been randomly observed twice that this code is reached with session being None or # partly deinitialized. Since it is unknown how fife handles this and why # removeCommandListener in remove() doesn't prevent further calls, we have to catch and ignore the error try: self.session.view.autoscroll(0, 0) # stop autoscroll except AttributeError: pass def get_hover_instances(self, where, layers=None): """ Utility method, returns the instances under the cursor @param where: anything supporting getX/getY @param layers: list of layer ids to search for. Default to OBJECTS """ if layers is None: layers = [LAYERS.OBJECTS] all_instances = [] for layer in layers: x = where.getX() y = where.getY() instances = self.session.view.cam.getMatchingInstances( fife.ScreenPoint(x, y), self.session.view.layers[layer], False) # False for accurate # if no instances found, try again and search within a 8px radius if not instances: selection_radius = 8 radius = fife.Rect(x - selection_radius, y - selection_radius, selection_radius * 2, selection_radius * 2) instances = self.session.view.cam.getMatchingInstances( radius, self.session.view.layers[layer]) all_instances.extend(instances) hover_instances = [] for i in all_instances: id = i.getId() # Check id, can be '' if instance is created and clicked on before # actual game representation class is created (network play) if id == '': continue instance = WorldObject.get_object_by_id(int(id)) hover_instances.append(instance) return hover_instances def end(self): super(NavigationTool, self).end() if self._hover_instances_update_scheduled: ExtScheduler().rem_all_classinst_calls(self) self.helptext = None def _schedule_hover_instance_update(self): """Hover instances have potentially changed, do an update in a timely fashion (but not right away)""" if not self._hover_instances_update_scheduled: self._hover_instances_update_scheduled = True ExtScheduler().add_new_object( self._send_hover_instance_upate, self, run_in=self.__class__.HOVER_INSTANCES_UPDATE_DELAY) 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)
class TearingTool(NavigationTool): """ Represents a dangling tool to remove (tear) buildings. """ tear_selection_color = (255, 255, 255) nearby_objects_radius = 4 def __init__(self, session): super(TearingTool, self).__init__(session) self._transparent_instances = set() # fife instances modified for transparency self.coords = None self.selected = WeakList() self.oldedges = None self.tear_tool_active = True self.session.ingame_gui.hide_menu() self.session.selected_instances.clear() horizons.globals.fife.set_cursor_image("tearing") self._hovering_over = WeakList() WorldObjectDeleted.subscribe(self._on_object_deleted) def remove(self): self._mark() self.tear_tool_active = False horizons.globals.fife.set_cursor_image("default") WorldObjectDeleted.unsubscribe(self._on_object_deleted) super(TearingTool, self).remove() def mouseDragged(self, evt): coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) evt.consume() def mouseMoved(self, evt): super(TearingTool, self).mouseMoved(evt) coords = self.get_world_location(evt).to_tuple() self._mark(coords) evt.consume() def on_escape(self): self.session.ingame_gui.set_cursor() def mouseReleased(self, evt): """Tear selected instances and set selection tool as cursor""" self.log.debug("TearingTool: mouseReleased") if evt.getButton() == fife.MouseEvent.LEFT: coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) selection_list_copy = [building for building in self.selected] for building in selection_list_copy: self.session.view.renderer['InstanceRenderer'].removeColored(building._instance) if (not building.id in BUILDINGS.EXPAND_RANGE) or self.confirm_ranged_delete(building): Tear(building).execute(self.session) else: if self._hovering_over: # we're hovering over a building, but none is selected, so this tear action isn't allowed warehouses = [ b for b in self._hovering_over if b.id == BUILDINGS.WAREHOUSE and b.owner.is_local_player] if warehouses: # tried to tear a warehouse, this is especially non-tearable pos = warehouses[0].position.origin self.session.ingame_gui.message_widget.add(point=pos, string_id="WAREHOUSE_NOT_TEARABLE" ) self.selected = WeakList() self._hovering_over = WeakList() if not evt.isShiftPressed() and not horizons.globals.fife.get_uh_setting('UninterruptedBuilding'): self.tear_tool_active = False self.on_escape() evt.consume() def confirm_ranged_delete(self, building): buildings_to_destroy = len(Tear.additional_removals_after_tear(building)[0]) if buildings_to_destroy == 0: return True title = _("Destroy all buildings") msg = _("This will destroy all the buildings that fall outside of" " the settlement range.") msg += u"\n\n" msg += N_("%s additional building will be destroyed.", "%s additional buildings will be destroyed", buildings_to_destroy) % buildings_to_destroy return building.session.ingame_gui.show_popup(title, msg, show_cancel_button=True) def mousePressed(self, evt): if evt.getButton() == fife.MouseEvent.RIGHT: self.on_escape() elif evt.getButton() == fife.MouseEvent.LEFT: self.coords = self.get_world_location(evt).to_tuple() self._mark(self.coords) else: return self.tear_tool_active = False evt.consume() def _mark(self, *edges): """Highights building instances and keeps self.selected up to date.""" self._restore_transparent_instances() self.log.debug("TearingTool: mark") if len(edges) == 1: edges = (edges[0], edges[0]) elif len(edges) == 2: edges = ((min(edges[0][0], edges[1][0]), min(edges[0][1], edges[1][1])), (max(edges[0][0], edges[1][0]), max(edges[0][1], edges[1][1]))) else: edges = None if self.oldedges != edges or edges is None: for i in self.selected: self.session.view.renderer['InstanceRenderer'].removeColored(i._instance) self.selected = WeakList() self.oldedges = edges if edges is not None: self._hovering_over = WeakList() for x in xrange(edges[0][0], edges[1][0] + 1): for y in xrange(edges[0][1], edges[1][1] + 1): b = self.session.world.get_building(Point(x, y)) if b is not None: if b not in self._hovering_over: self._hovering_over.append(b) self._make_surrounding_transparent(b) self._remove_object_transparency(Point(x,y)) if b.tearable and b.owner is not None and b.owner.is_local_player: if b not in self.selected: self._make_surrounding_transparent(b) self.selected.append(b) self._remove_object_transparency(Point(x,y)) for i in self.selected: self.session.view.renderer['InstanceRenderer'].addColored(i._instance, *self.tear_selection_color) self.log.debug("TearingTool: mark done") def _remove_object_transparency(self, coords): """helper function, used to remove transparency from object hovered upon, identified through its coordinates""" tile = self.session.world.get_tile(coords) if tile.object is not None and tile.object.buildable_upon: inst = tile.object.fife_instance inst.get2dGfxVisual().setTransparency(0) def _make_surrounding_transparent(self, building): """Makes the surrounding of building_position transparent""" world_contains = self.session.world.map_dimensions.contains_without_border for coord in building.position.get_radius_coordinates(self.nearby_objects_radius, include_self=True): p = Point(*coord) if not world_contains(p): continue tile = self.session.world.get_tile(p) if tile.object is not None and tile.object.buildable_upon: inst = tile.object.fife_instance inst.get2dGfxVisual().setTransparency(BUILDINGS.TRANSPARENCY_VALUE) self._transparent_instances.add(weakref.ref(inst)) def _restore_transparent_instances(self): """Removes transparency""" for inst_weakref in self._transparent_instances: fife_instance = inst_weakref() if fife_instance: # remove transparency only if trees aren't supposed to be transparent as default if not hasattr(fife_instance, "keep_translucency") or not fife_instance.keep_translucency: fife_instance.get2dGfxVisual().setTransparency(0) else: # restore regular translucency value, can also be different fife_instance.get2dGfxVisual().setTransparency( BUILDINGS.TRANSPARENCY_VALUE ) self._transparent_instances.clear() def _on_object_deleted(self, message): self.log.debug("TearingTool: on deletion notification %s", message.worldid) if message.sender in self.selected: self.log.debug("TearingTool: deleted obj present") self.selected.remove(message.sender)
class TearingTool(NavigationTool): """ Represents a dangling tool to remove (tear) buildings. """ tear_selection_color = (255, 255, 255) def __init__(self, session): super(TearingTool, self).__init__(session) self.coords = None self.selected = WeakList() self.oldedges = None self.tear_tool_active = True self.session.ingame_gui.hide_menu() self.session.selected_instances.clear() horizons.globals.fife.set_cursor_image("tearing") self._hovering_over = WeakList() WorldObjectDeleted.subscribe(self._on_object_deleted) def remove(self): self._mark() self.tear_tool_active = False horizons.globals.fife.set_cursor_image("default") WorldObjectDeleted.unsubscribe(self._on_object_deleted) super(TearingTool, self).remove() def mouseDragged(self, evt): coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) evt.consume() def mouseMoved(self, evt): super(TearingTool, self).mouseMoved(evt) coords = self.get_world_location(evt).to_tuple() self._mark(coords) evt.consume() def on_escape(self): self.session.ingame_gui.set_cursor() def mouseReleased(self, evt): """Tear selected instances and set selection tool as cursor""" self.log.debug("TearingTool: mouseReleased") if evt.getButton() == fife.MouseEvent.LEFT: coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) for i in [i for i in self.selected]: self.session.view.renderer['InstanceRenderer'].removeColored(i._instance) Tear(i).execute(self.session) else: if self._hovering_over: # we're hovering over a building, but none is selected, so this tear action isn't allowed warehouses = [ b for b in self._hovering_over if b.id == BUILDINGS.WAREHOUSE ] if warehouses: # tried to tear a warehouse, this is especially non-tearable pos = warehouses[0].position.origin self.session.ingame_gui.message_widget.add(point=pos, string_id="WAREHOUSE_NOT_TEARABLE" ) self.selected = WeakList() self._hovering_over = WeakList() if not evt.isShiftPressed() and not horizons.globals.fife.get_uh_setting('UninterruptedBuilding'): self.tear_tool_active = False self.on_escape() evt.consume() def mousePressed(self, evt): if evt.getButton() == fife.MouseEvent.RIGHT: self.on_escape() elif evt.getButton() == fife.MouseEvent.LEFT: self.coords = self.get_world_location(evt).to_tuple() self._mark(self.coords) else: return self.tear_tool_active = False evt.consume() def _mark(self, *edges): """Highights building instances and keeps self.selected up to date.""" self.log.debug("TearingTool: mark") if len(edges) == 1: edges = (edges[0], edges[0]) elif len(edges) == 2: edges = ((min(edges[0][0], edges[1][0]), min(edges[0][1], edges[1][1])), (max(edges[0][0], edges[1][0]), max(edges[0][1], edges[1][1]))) else: edges = None if self.oldedges != edges or edges is None: for i in self.selected: self.session.view.renderer['InstanceRenderer'].removeColored(i._instance) self.selected = WeakList() self.oldedges = edges if edges is not None: self._hovering_over = WeakList() for x in xrange(edges[0][0], edges[1][0] + 1): for y in xrange(edges[0][1], edges[1][1] + 1): b = self.session.world.get_building(Point(x, y)) if b is not None: if b not in self._hovering_over: self._hovering_over.append(b) if b.tearable and b.owner is not None and b.owner.is_local_player: if b not in self.selected: self.selected.append(b) for i in self.selected: self.session.view.renderer['InstanceRenderer'].addColored(i._instance, *self.tear_selection_color) self.log.debug("TearingTool: mark done") def _on_object_deleted(self, message): self.log.debug("TearingTool: on deletion notification %s", message.worldid) if message.sender in self.selected: self.log.debug("TearingTool: deleted obj present") self.selected.remove(message.sender)
class TearingTool(NavigationTool): """ Represents a dangling tool to remove (tear) buildings. """ tear_selection_color = (255, 255, 255) nearby_objects_radius = 4 def __init__(self, session): super(TearingTool, self).__init__(session) self._transparent_instances = set() # fife instances modified for transparency self.coords = None self.selected = WeakList() self.oldedges = None self.tear_tool_active = True self.session.ingame_gui.hide_menu() self.session.selected_instances.clear() horizons.globals.fife.set_cursor_image("tearing") self._hovering_over = WeakList() WorldObjectDeleted.subscribe(self._on_object_deleted) def remove(self): self._mark() self.tear_tool_active = False horizons.globals.fife.set_cursor_image("default") WorldObjectDeleted.unsubscribe(self._on_object_deleted) super(TearingTool, self).remove() def mouseDragged(self, evt): coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) evt.consume() def mouseMoved(self, evt): super(TearingTool, self).mouseMoved(evt) coords = self.get_world_location(evt).to_tuple() self._mark(coords) evt.consume() def on_escape(self): self.session.ingame_gui.set_cursor() def mouseReleased(self, evt): """Tear selected instances and set selection tool as cursor""" self.log.debug("TearingTool: mouseReleased") if evt.getButton() == fife.MouseEvent.LEFT: coords = self.get_world_location(evt).to_tuple() if self.coords is None: self.coords = coords self._mark(self.coords, coords) selection_list_copy = [building for building in self.selected] if self.selected: for building in selection_list_copy: self.session.view.renderer['InstanceRenderer'].removeColored(building._instance) if (building.id not in BUILDINGS.EXPAND_RANGE) or self.confirm_ranged_delete(building): Tear(building).execute(self.session) elif self._hovering_over: # we're hovering over a building, but none is selected, so this tear action isn't allowed warehouses = [ b for b in self._hovering_over if b.id == BUILDINGS.WAREHOUSE and b.owner.is_local_player] if warehouses: # tried to tear a warehouse, this is especially non-tearable pos = warehouses[0].position.origin self.session.ingame_gui.message_widget.add(point=pos, string_id="WAREHOUSE_NOT_TEARABLE" ) self.selected = WeakList() self._hovering_over = WeakList() if not evt.isShiftPressed() and not horizons.globals.fife.get_uh_setting('UninterruptedBuilding'): self.tear_tool_active = False self.on_escape() evt.consume() def confirm_ranged_delete(self, building): buildings_to_destroy = len(Tear.additional_removals_after_tear(building)[0]) if buildings_to_destroy == 0: return True title = T("Destroy all buildings") msg = T("This will destroy all the buildings that fall outside of" " the settlement range.") msg += u"\n\n" msg += NT("%s additional building will be destroyed.", "%s additional buildings will be destroyed", buildings_to_destroy) % buildings_to_destroy return building.session.ingame_gui.open_popup(title, msg, show_cancel_button=True) def mousePressed(self, evt): if evt.getButton() == fife.MouseEvent.RIGHT: self.on_escape() elif evt.getButton() == fife.MouseEvent.LEFT: self.coords = self.get_world_location(evt).to_tuple() self._mark(self.coords) else: return self.tear_tool_active = False evt.consume() def _mark(self, *edges): """Highights building instances and keeps self.selected up to date.""" self._restore_transparent_instances() self.log.debug("TearingTool: mark") if len(edges) == 1: edges = (edges[0], edges[0]) elif len(edges) == 2: edges = ((min(edges[0][0], edges[1][0]), min(edges[0][1], edges[1][1])), (max(edges[0][0], edges[1][0]), max(edges[0][1], edges[1][1]))) else: edges = None if self.oldedges != edges or edges is None: for i in self.selected: self.session.view.renderer['InstanceRenderer'].removeColored(i._instance) self.selected = WeakList() self.oldedges = edges if edges is not None: self._hovering_over = WeakList() for x in xrange(edges[0][0], edges[1][0] + 1): for y in xrange(edges[0][1], edges[1][1] + 1): b = self.session.world.get_building(Point(x, y)) if b is not None: if b not in self._hovering_over: self._hovering_over.append(b) self._make_surrounding_transparent(b) self._remove_object_transparency(Point(x,y)) if b.tearable and b.owner is not None and b.owner.is_local_player: if b not in self.selected: self._make_surrounding_transparent(b) self.selected.append(b) self._remove_object_transparency(Point(x,y)) for i in self.selected: self.session.view.renderer['InstanceRenderer'].addColored(i._instance, *self.tear_selection_color) self.log.debug("TearingTool: mark done") def _remove_object_transparency(self, coords): """helper function, used to remove transparency from object hovered upon, identified through its coordinates""" tile = self.session.world.get_tile(coords) if tile.object is not None and tile.object.buildable_upon: inst = tile.object.fife_instance inst.get2dGfxVisual().setTransparency(0) def _make_surrounding_transparent(self, building): """Makes the surrounding of building_position transparent""" world_contains = self.session.world.map_dimensions.contains_without_border for coord in building.position.get_radius_coordinates(self.nearby_objects_radius, include_self=True): p = Point(*coord) if not world_contains(p): continue tile = self.session.world.get_tile(p) if tile.object is not None and tile.object.buildable_upon: inst = tile.object.fife_instance inst.get2dGfxVisual().setTransparency(BUILDINGS.TRANSPARENCY_VALUE) self._transparent_instances.add(weakref.ref(inst)) def _restore_transparent_instances(self): """Removes transparency""" for inst_weakref in self._transparent_instances: fife_instance = inst_weakref() if fife_instance: # remove transparency only if trees aren't supposed to be transparent as default if not hasattr(fife_instance, "keep_translucency") or not fife_instance.keep_translucency: fife_instance.get2dGfxVisual().setTransparency(0) else: # restore regular translucency value, can also be different fife_instance.get2dGfxVisual().setTransparency( BUILDINGS.TRANSPARENCY_VALUE ) self._transparent_instances.clear() def _on_object_deleted(self, message): self.log.debug("TearingTool: on deletion notification %s", message.worldid) if message.sender in self.selected: self.log.debug("TearingTool: deleted obj present") self.selected.remove(message.sender)