Package horizons :: Package world :: Package units :: Module ship
[hide private]
[frames] | no frames]

Source Code for Module horizons.world.units.ship

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import weakref 
 23   
 24  from fife import fife 
 25   
 26  import horizons.globals 
 27  from horizons.component.commandablecomponent import CommandableComponent 
 28  from horizons.component.namedcomponent import NamedComponent, ShipNameComponent 
 29  from horizons.component.selectablecomponent import SelectableComponent 
 30  from horizons.constants import LAYERS 
 31  from horizons.i18n import gettext as T 
 32  from horizons.messaging import ShipDestroyed 
 33  from horizons.scheduler import Scheduler 
 34  from horizons.util.pathfinding import PathBlockedError 
 35  from horizons.util.pathfinding.pather import FisherShipPather, ShipPather 
 36  from horizons.world.traderoute import TradeRoute 
 37  from horizons.world.units.collectors import FisherShipCollector 
 38  from horizons.world.units.unit import Unit 
 39   
 40   
41 -class Ship(Unit):
42 """Class representing a ship 43 @param x: int x position 44 @param y: int y position 45 """ 46 pather_class = ShipPather 47 health_bar_y = -150 48 is_ship = True 49 50 in_ship_map = True # (#1023) 51
52 - def __init__(self, x, y, **kwargs):
53 super().__init__(x=x, y=y, **kwargs) 54 self.__init()
55
56 - def save(self, db):
57 super().save(db) 58 if hasattr(self, 'route'): 59 self.route.save(db)
60
61 - def load(self, db, worldid):
62 super().load(db, worldid) 63 self.__init() 64 65 # if ship did not have route configured, do not add attribute 66 if TradeRoute.has_route(db, worldid): 67 self.create_route() 68 self.route.load(db)
69
70 - def __init(self):
71 # register ship in world 72 self.session.world.ships.append(self) 73 if self.in_ship_map: 74 self.session.world.ship_map[self.position.to_tuple()] = weakref.ref(self)
75
76 - def set_name(self, name):
78
79 - def remove(self):
80 self.session.world.ships.remove(self) 81 self.session.view.discard_change_listener(self.draw_health) 82 if self.in_ship_map: 83 if self.position.to_tuple() in self.session.world.ship_map: 84 del self.session.world.ship_map[self.position.to_tuple()] 85 else: 86 self.log.error("Ship %s had in_ship_map flag set as True " 87 "but tuple %s was not found in world.ship_map", 88 self, self.position.to_tuple()) 89 if self._next_target.to_tuple() in self.session.world.ship_map: 90 del self.session.world.ship_map[self._next_target.to_tuple()] 91 self.in_ship_map = False 92 ShipDestroyed.broadcast(self) 93 super().remove()
94
95 - def create_route(self):
96 self.route = TradeRoute(self)
97
98 - def _move_tick(self, resume=False):
99 """Keeps track of the ship's position in the global ship_map""" 100 101 # TODO: Originally, only self.in_ship_map should suffice here, 102 # but KeyError is raised during combat. 103 if self.in_ship_map and self.position.to_tuple() in self.session.world.ship_map: 104 del self.session.world.ship_map[self.position.to_tuple()] 105 elif self.in_ship_map: # logging purposes only 106 self.log.error("Ship %s had in_ship_map flag set as True but tuple %s was " 107 "not found in world.ship_map", self, self.position.to_tuple()) 108 109 try: 110 super()._move_tick(resume) 111 except PathBlockedError: 112 # if we fail to resume movement then the ship should still be on the map 113 # but the exception has to be raised again. 114 if resume: 115 if self.in_ship_map: 116 self.session.world.ship_map[self.position.to_tuple()] = weakref.ref(self) 117 raise 118 119 if self.in_ship_map: 120 # save current and next position for ship, since it will be between them 121 self.session.world.ship_map[self.position.to_tuple()] = weakref.ref(self) 122 self.session.world.ship_map[self._next_target.to_tuple()] = weakref.ref(self)
123
124 - def _movement_finished(self):
125 if self.in_ship_map: 126 # if the movement somehow stops, the position sticks, and the unit isn't at next_target any more 127 if self._next_target is not None: 128 ship = self.session.world.ship_map.get(self._next_target.to_tuple()) 129 if ship is not None and ship() is self: 130 del self.session.world.ship_map[self._next_target.to_tuple()] 131 super()._movement_finished()
132
133 - def go(self, x, y):
134 # Disable trade route, direct commands overwrite automated ones. 135 if hasattr(self, 'route'): 136 self.route.disable() 137 if self.get_component(CommandableComponent).go(x, y) is None: 138 self._update_buoy()
139
140 - def move(self, *args, **kwargs):
141 super().move(*args, **kwargs) 142 if self.has_component(SelectableComponent) and \ 143 self.get_component(SelectableComponent).selected and \ 144 self.owner.is_local_player: # handle buoy 145 # if move() is called as move_callback, tmp() from above might 146 # be executed after this, so draw the new buoy after move_callbacks have finished. 147 Scheduler().add_new_object(self._update_buoy, self, run_in=0)
148
149 - def _update_buoy(self, remove_only=False):
150 """Draw a buoy at the move target if the ship is moving.""" 151 if self.owner is None or not self.owner.is_local_player: 152 return 153 move_target = self.get_move_target() 154 155 ship_id = self.worldid 156 session = self.session # this has to happen here, 157 # cause a reference to self in a temporary function is implemented 158 # as a hard reference, which causes a memory leak 159 def tmp(): 160 session.view.renderer['GenericRenderer'].removeAll("buoy_" + str(ship_id))
161 tmp() # also remove now 162 163 if remove_only: 164 return 165 166 if move_target is not None: 167 # set remove buoy callback 168 self.add_move_callback(tmp) 169 170 loc = fife.Location(self.session.view.layers[LAYERS.OBJECTS]) 171 loc.thisown = 0 # thisown = 0 because the genericrenderernode might delete it 172 coords = fife.ModelCoordinate(move_target.x, move_target.y) 173 coords.thisown = 1 # thisown = 1 because setLayerCoordinates will create a copy 174 loc.setLayerCoordinates(coords) 175 self.session.view.renderer['GenericRenderer'].addAnimation( 176 "buoy_" + str(self.worldid), fife.RendererNode(loc), 177 horizons.globals.fife.animationloader.loadResource("as_buoy0+idle+45") 178 )
179
180 - def find_nearby_ships(self, radius=15):
181 # TODO: Replace 15 with a distance dependent on the ship type and any 182 # other conditions. 183 ships = self.session.world.get_ships(self.position, radius) 184 if self in ships: 185 ships.remove(self) 186 return ships
187
188 - def get_tradeable_warehouses(self, position=None):
189 """Returns warehouses this ship can trade with w.r.t. position, which defaults to the ships ones.""" 190 if position is None: 191 position = self.position 192 return self.session.world.get_warehouses(position, self.radius, self.owner, 193 include_tradeable=True)
194
195 - def get_location_based_status(self, position):
196 warehouses = self.get_tradeable_warehouses(position) 197 if warehouses: 198 warehouse = warehouses[0] # TODO: don't ignore the other possibilities 199 player_suffix = '' 200 if warehouse.owner is not self.owner: 201 player_suffix = ' ({name})'.format(name=warehouse.owner.name) 202 return '{name}{suffix}'.format(name=warehouse.settlement.get_component(NamedComponent).name, 203 suffix=player_suffix) 204 return None
205
206 - def get_status(self):
207 """Return the current status of the ship.""" 208 if hasattr(self, 'route') and self.route.enabled: 209 return self.route.get_ship_status() 210 elif self.is_moving(): 211 target = self.get_move_target() 212 location_based_status = self.get_location_based_status(target) 213 if location_based_status is not None: 214 return (T('Going to {location}').format(location=location_based_status), target) 215 return (T('Going to {x}, {y}').format(x=target.x, y=target.y), target) 216 else: 217 location_based_status = self.get_location_based_status(self.position) 218 if location_based_status is not None: 219 return (T('Idle at {location}').format(location=location_based_status), self.position) 220 return (T('Idle at {x}, {y}').format(x=self.position.x, y=self.position.y), self.position)
221 222
223 -class TradeShip(Ship):
224 """Represents a trade ship.""" 225 health_bar_y = -220 226
227 - def __init__(self, x, y, **kwargs):
228 super().__init__(x, y, **kwargs)
229
230 - def _possible_names(self):
231 return [T('Trader')]
232 233
234 -class FisherShip(FisherShipCollector, Ship):
235 """Represents a fisher ship.""" 236 pather_class = FisherShipPather 237 health_bar_y = -50 238 239 in_ship_map = False # (#1023) 240
241 - def _update_buoy(self):
242 pass # no buoy for the fisher
243