Package horizons :: Package ai :: Package aiplayer :: Module settlementmanager
[hide private]
[frames] | no frames]

Source Code for Module horizons.ai.aiplayer.settlementmanager

  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 logging 
 23   
 24  from horizons.ai.aiplayer.goal.boatbuilder import BoatBuilderGoal 
 25  from horizons.ai.aiplayer.goal.combatship import CombatShipGoal 
 26  from horizons.ai.aiplayer.goal.depositcoverage import ( 
 27          ClayDepositCoverageGoal, MountainCoverageGoal, StoneDepositCoverageGoal) 
 28  from horizons.ai.aiplayer.goal.doctor import DoctorGoal 
 29  from horizons.ai.aiplayer.goal.enlargecollectorarea import EnlargeCollectorAreaGoal 
 30  from horizons.ai.aiplayer.goal.feederchaingoal import ( 
 31          FeederFoodGoal, FeederLiquorGoal, FeederMedicalProductsGoal, FeederSaltGoal, FeederTextileGoal, 
 32          FeederTobaccoProductsGoal) 
 33  from horizons.ai.aiplayer.goal.firestation import FireStationGoal 
 34  from horizons.ai.aiplayer.goal.foundfeederisland import FoundFeederIslandGoal 
 35  from horizons.ai.aiplayer.goal.improvecollectorcoverage import ImproveCollectorCoverageGoal 
 36  from horizons.ai.aiplayer.goal.productionchaingoal import ( 
 37          BoardsGoal, BricksGoal, CommunityGoal, EducationGoal, FaithGoal, FoodGoal, GetTogetherGoal, 
 38          MedicalHerbsProductsGoal, SaltGoal, TextileGoal, TobaccoProductsGoal, ToolsGoal) 
 39  from horizons.ai.aiplayer.goal.signalfire import SignalFireGoal 
 40  from horizons.ai.aiplayer.goal.storagespace import StorageSpaceGoal 
 41  from horizons.ai.aiplayer.goal.tent import TentGoal 
 42  from horizons.ai.aiplayer.goal.tradingship import TradingShipGoal 
 43  from horizons.ai.aiplayer.productionbuilder import ProductionBuilder 
 44  from horizons.ai.aiplayer.productionchain import ProductionChain 
 45  from horizons.ai.aiplayer.resourcemanager import ResourceManager 
 46  from horizons.ai.aiplayer.trademanager import TradeManager 
 47  from horizons.ai.aiplayer.villagebuilder import VillageBuilder 
 48  from horizons.command.building import Tear 
 49  from horizons.command.production import ToggleActive 
 50  from horizons.command.uioptions import SetSettlementUpgradePermissions, SetTaxSetting 
 51  from horizons.component.namedcomponent import NamedComponent 
 52  from horizons.component.storagecomponent import StorageComponent 
 53  from horizons.constants import BUILDINGS, GAME_SPEED, RES, TIER 
 54  from horizons.entities import Entities 
 55  from horizons.scheduler import Scheduler 
 56  from horizons.util.worldobject import WorldObject 
 57  from horizons.world.disaster.buildinginfluencingdisaster import BuildingInfluencingDisaster 
 58  from horizons.world.production.producer import Producer 
59 60 61 -class SettlementManager(WorldObject):
62 """ 63 This is the main settlement control class. 64 65 Important attributes: 66 * feeder_island: boolean showing whether the island is a feeder island (feeder islands have no village area) 67 * island: Island instance 68 * settlement: Settlement instance 69 * land_manager: LandManager instance 70 * production_chain: dictionary where the key is a resource id and the value is the ProductionChain instance 71 * production_builder: ProductionBuilder instance 72 * village_builder: VillageBuilder instance 73 * resource_manager: ResourceManager instance 74 * trade_manager: TradeManager instance 75 """ 76 77 log = logging.getLogger("ai.aiplayer") 78
79 - def __init__(self, owner, land_manager):
80 super().__init__() 81 self.owner = owner 82 self.resource_manager = ResourceManager(self) 83 self.trade_manager = TradeManager(self) 84 self.__init(land_manager) 85 86 self.village_builder = VillageBuilder(self) 87 self.production_builder = ProductionBuilder(self) 88 self.village_builder.display() 89 self.production_builder.display() 90 91 self.__init_goals() 92 93 if not self.feeder_island: 94 self._set_taxes_and_permissions(self.personality.initial_sailor_taxes, self.personality.initial_pioneer_taxes, self.personality.initial_settler_taxes, 95 self.personality.initial_citizen_taxes, self.personality.initial_merchants_taxes, self.personality.initial_sailor_upgrades, 96 self.personality.initial_pioneer_upgrades, self.personality.initial_settler_upgrades, self.personality.initial_citizen_upgrades)
97
98 - def __init(self, land_manager):
99 self.owner = land_manager.owner 100 self.session = self.owner.session 101 self.land_manager = land_manager 102 self.island = self.land_manager.island 103 self.settlement = self.land_manager.settlement 104 self.feeder_island = land_manager.feeder_island 105 self.personality = self.owner.personality_manager.get('SettlementManager') 106 107 # create a production chain for every building material, settler consumed resource, and resources that have to be imported from feeder islands 108 self.production_chain = {} 109 for resource_id in [RES.COMMUNITY, RES.BOARDS, RES.FOOD, RES.TEXTILE, RES.FAITH, 110 RES.EDUCATION, RES.GET_TOGETHER, RES.BRICKS, RES.TOOLS, RES.LIQUOR, 111 RES.TOBACCO_PRODUCTS, RES.SALT, RES.MEDICAL_HERBS]: 112 self.production_chain[resource_id] = ProductionChain.create(self, resource_id) 113 114 # initialize caches 115 self.__resident_resource_usage_cache = {}
116
117 - def __init_goals(self):
118 """Initialize the list of all the goals the settlement can use.""" 119 self._goals = [] # [SettlementGoal, ...] 120 self._goals.append(BoardsGoal(self)) 121 self._goals.append(SignalFireGoal(self)) 122 self._goals.append(EnlargeCollectorAreaGoal(self)) 123 self._goals.append(ImproveCollectorCoverageGoal(self)) 124 self._goals.append(BricksGoal(self)) 125 if self.feeder_island: 126 self._goals.append(StorageSpaceGoal(self)) 127 self._goals.append(FeederFoodGoal(self)) 128 self._goals.append(FeederTextileGoal(self)) 129 self._goals.append(FeederLiquorGoal(self)) 130 self._goals.append(FeederSaltGoal(self)) 131 self._goals.append(FeederTobaccoProductsGoal(self)) 132 self._goals.append(FeederMedicalProductsGoal(self)) 133 else: 134 self._goals.append(BoatBuilderGoal(self)) 135 self._goals.append(ClayDepositCoverageGoal(self)) 136 self._goals.append(StoneDepositCoverageGoal(self)) 137 self._goals.append(FoundFeederIslandGoal(self)) 138 self._goals.append(MountainCoverageGoal(self)) 139 self._goals.append(FoodGoal(self)) 140 self._goals.append(CommunityGoal(self)) 141 self._goals.append(FaithGoal(self)) 142 self._goals.append(TextileGoal(self)) 143 self._goals.append(EducationGoal(self)) 144 self._goals.append(GetTogetherGoal(self)) 145 self._goals.append(SaltGoal(self)) 146 self._goals.append(TobaccoProductsGoal(self)) 147 self._goals.append(ToolsGoal(self)) 148 self._goals.append(TentGoal(self)) 149 self._goals.append(TradingShipGoal(self)) 150 self._goals.append(CombatShipGoal(self)) 151 self._goals.append(FireStationGoal(self)) 152 self._goals.append(DoctorGoal(self)) 153 self._goals.append(MedicalHerbsProductsGoal(self))
154
155 - def save(self, db):
156 super().save(db) 157 db("INSERT INTO ai_settlement_manager(rowid, land_manager) VALUES(?, ?)", 158 self.worldid, self.land_manager.worldid) 159 160 self.village_builder.save(db) 161 self.production_builder.save(db) 162 self.resource_manager.save(db) 163 self.trade_manager.save(db)
164 165 @classmethod
166 - def load(cls, db, owner, worldid):
167 self = cls.__new__(cls) 168 self._load(db, owner, worldid) 169 return self
170
171 - def _load(self, db, owner, worldid):
172 self.owner = owner 173 super().load(db, worldid) 174 175 # load the main part 176 land_manager_id = db("SELECT land_manager FROM ai_settlement_manager WHERE rowid = ?", worldid)[0][0] 177 land_manager = WorldObject.get_object_by_id(land_manager_id) 178 179 # find the settlement 180 for settlement in self.owner.session.world.settlements: 181 if settlement.owner == self.owner and settlement.island == land_manager.island: 182 land_manager.settlement = settlement 183 break 184 assert land_manager.settlement 185 self.resource_manager = ResourceManager.load(db, self) 186 self.trade_manager = TradeManager.load(db, self) 187 self.__init(land_manager) 188 189 # load the master builders 190 self.village_builder = VillageBuilder.load(db, self) 191 self.production_builder = ProductionBuilder.load(db, self) 192 self.village_builder.display() 193 self.production_builder.display() 194 195 self.__init_goals() 196 197 # the add_building events happen before the settlement manager is loaded so they have to be repeated here 198 for building in self.settlement.buildings: 199 self.add_building(building)
200
201 - def _set_taxes_and_permissions(self, sailor_taxes, pioneer_taxes, settler_taxes, citizen_taxes, merchants_taxes, sailor_upgrades, pioneer_upgrades, settler_upgrades, citizen_upgrades):
202 """Set new tax settings and building permissions.""" 203 if abs(self.settlement.tax_settings[TIER.SAILORS] - sailor_taxes) > 1e-9: 204 self.log.info("%s set sailors' taxes from %.1f to %.1f", self, self.settlement.tax_settings[TIER.SAILORS], sailor_taxes) 205 SetTaxSetting(self.settlement, TIER.SAILORS, sailor_taxes).execute(self.land_manager.session) 206 if abs(self.settlement.tax_settings[TIER.PIONEERS] - pioneer_taxes) > 1e-9: 207 self.log.info("%s set pioneers' taxes from %.1f to %.1f", self, self.settlement.tax_settings[TIER.PIONEERS], pioneer_taxes) 208 SetTaxSetting(self.settlement, TIER.PIONEERS, pioneer_taxes).execute(self.land_manager.session) 209 if abs(self.settlement.tax_settings[TIER.SETTLERS] - settler_taxes) > 1e-9: 210 self.log.info("%s set settlers' taxes from %.1f to %.1f", self, self.settlement.tax_settings[TIER.SETTLERS], settler_taxes) 211 SetTaxSetting(self.settlement, TIER.SETTLERS, settler_taxes).execute(self.land_manager.session) 212 if abs(self.settlement.tax_settings[TIER.CITIZENS] - citizen_taxes) > 1e-9: 213 self.log.info("%s set citizens' taxes from %.1f to %.1f", self, self.settlement.tax_settings[TIER.CITIZENS], citizen_taxes) 214 SetTaxSetting(self.settlement, TIER.CITIZENS, citizen_taxes).execute(self.land_manager.session) 215 if abs(self.settlement.tax_settings[TIER.MERCHANTS] - merchants_taxes) > 1e-9: 216 self.log.info("%s set merchants' taxes from %.1f to %.1f", self, self.settlement.tax_settings[TIER.MERCHANTS], merchants_taxes) 217 SetTaxSetting(self.settlement, TIER.MERCHANTS, merchants_taxes).execute(self.land_manager.session) 218 if self.settlement.upgrade_permissions[TIER.SAILORS] != sailor_upgrades: 219 self.log.info('%s set sailor upgrade permissions to %s', self, sailor_upgrades) 220 SetSettlementUpgradePermissions(self.settlement, TIER.SAILORS, sailor_upgrades).execute(self.land_manager.session) 221 if self.settlement.upgrade_permissions[TIER.PIONEERS] != pioneer_upgrades: 222 self.log.info('%s set pioneer upgrade permissions to %s', self, pioneer_upgrades) 223 SetSettlementUpgradePermissions(self.settlement, TIER.PIONEERS, pioneer_upgrades).execute(self.land_manager.session) 224 if self.settlement.upgrade_permissions[TIER.SETTLERS] != settler_upgrades: 225 self.log.info('%s set settler upgrade permissions to %s', self, settler_upgrades) 226 SetSettlementUpgradePermissions(self.settlement, TIER.SETTLERS, settler_upgrades).execute(self.land_manager.session) 227 if self.settlement.upgrade_permissions[TIER.CITIZENS] != citizen_upgrades: 228 self.log.info('%s set citizen upgrade permissions to %s', self, citizen_upgrades) 229 SetSettlementUpgradePermissions(self.settlement, TIER.CITIZENS, citizen_upgrades).execute(self.land_manager.session)
230
231 - def _set_taxes_and_permissions_prefix(self, prefix):
232 """Set new tax settings and building permissions according to the prefix used in the personality file.""" 233 sailor_taxes = getattr(self.personality, '{}_sailor_taxes'.format(prefix)) 234 pioneer_taxes = getattr(self.personality, '{}_pioneer_taxes'.format(prefix)) 235 settler_taxes = getattr(self.personality, '{}_settler_taxes'.format(prefix)) 236 citizen_taxes = getattr(self.personality, '{}_citizen_taxes'.format(prefix)) 237 merchants_taxes = getattr(self.personality, '{}_merchants_taxes'.format(prefix)) 238 sailor_upgrades = getattr(self.personality, '{}_sailor_upgrades'.format(prefix)) 239 pioneer_upgrades = getattr(self.personality, '{}_pioneer_upgrades'.format(prefix)) 240 settler_upgrades = getattr(self.personality, '{}_settler_upgrades'.format(prefix)) 241 citizen_upgrades = getattr(self.personality, '{}_citizen_upgrades'.format(prefix)) 242 self._set_taxes_and_permissions(sailor_taxes, pioneer_taxes, settler_taxes, citizen_taxes, merchants_taxes, 243 sailor_upgrades, pioneer_upgrades, settler_upgrades, citizen_upgrades)
244
245 - def can_provide_resources(self):
246 """Return a boolean showing whether this settlement is complete enough to concentrate on building a new settlement.""" 247 if self.village_builder.tent_queue: 248 return False 249 settler_houses = 0 250 residences = self.settlement.buildings_by_id.get(BUILDINGS.RESIDENTIAL, []) 251 for building in residences: 252 if building.level >= TIER.SETTLERS: 253 settler_houses += 1 254 if settler_houses > len(residences) * self.personality.new_settlement_settler_ratio: 255 return True 256 return False
257
258 - def get_resource_production(self, resource_id):
259 """Return the current production capacity (including import) per tick of the given resource.""" 260 # as long as there are enough collectors it is correct to calculate it this way 261 if resource_id == RES.LIQUOR and not self.feeder_island: 262 # normal settlements go straight for get-together so their separate liquor production is zero. 263 # feeder islands have to produce liquor because get-together is not tradable 264 return self.get_resource_production(RES.GET_TOGETHER) * self.production_chain[RES.GET_TOGETHER].get_ratio(RES.LIQUOR) 265 else: 266 return self.production_chain[resource_id].get_final_production_level()
267
268 - def get_resource_production_requirement(self, resource_id):
269 """Return the amount of resource per tick the settlement needs.""" 270 if resource_id not in self.__resident_resource_usage_cache or self.__resident_resource_usage_cache[resource_id][0] != Scheduler().cur_tick: 271 total = 0 272 if resource_id == RES.BRICKS: 273 total = self.personality.dummy_bricks_requirement if self.owner.settler_level > 0 else 0 # dummy value to cause bricks production to be built 274 elif resource_id == RES.BOARDS: 275 total = self.personality.dummy_boards_requirement # dummy value to cause boards production to be built 276 elif resource_id == RES.TOOLS: 277 total = self.personality.dummy_tools_requirement if self.owner.settler_level > 1 else 0 # dummy value to cause tools production to be built 278 elif resource_id == RES.LIQUOR: 279 total = self.production_chain[RES.GET_TOGETHER].get_ratio(RES.LIQUOR) * self.get_resource_production_requirement(RES.GET_TOGETHER) 280 else: 281 for residence in self.settlement.buildings_by_id.get(BUILDINGS.RESIDENTIAL, []): 282 for production in residence.get_component(Producer).get_productions(): 283 production_line = production._prod_line 284 if resource_id in production_line.consumed_res: 285 # subtract because the amount will be negative 286 total -= float(production_line.consumed_res[resource_id]) / production_line.time / GAME_SPEED.TICKS_PER_SECOND 287 288 self.__resident_resource_usage_cache[resource_id] = (Scheduler().cur_tick, total) 289 return self.__resident_resource_usage_cache[resource_id][1]
290
291 - def _manual_upgrade(self, level, limit):
292 """ 293 Manually allow settlers to upgrade. If more then the set limit are already upgrading then don't stop them. 294 295 @param level: the initial settler level from which to upgrade 296 @param limit: the maximum number of residences of the specified level upgrading at the same time 297 @return: boolean showing whether we gave any new residences the right to upgrade 298 """ 299 300 num_upgrading = 0 301 for building in self.settlement.buildings_by_id.get(BUILDINGS.RESIDENTIAL, []): 302 if building.level == level: 303 upgrade_production = building._upgrade_production 304 if upgrade_production is not None and not upgrade_production.is_paused(): 305 num_upgrading += 1 306 if num_upgrading >= limit: 307 return False 308 309 upgraded_any = False 310 for building in self.settlement.buildings_by_id.get(BUILDINGS.RESIDENTIAL, []): 311 if building.level == level: 312 upgrade_production = building._upgrade_production 313 if upgrade_production is not None and upgrade_production.is_paused(): 314 ToggleActive(building.get_component(Producer), upgrade_production).execute(self.land_manager.session) 315 num_upgrading += 1 316 upgraded_any = True 317 if num_upgrading >= limit: 318 return True 319 return upgraded_any
320
321 - def get_ideal_production_level(self, resource_id):
322 """ 323 Return the amount of resource per tick the settlement should produce. 324 325 This is the amount that should be produced to satisfy the people in this settlement, 326 keep up the current export rate, and fix the player's global deficit. This means 327 that different (feeder) islands will have different ideal production levels. 328 """ 329 330 total = 0.0 331 for settlement_manager in self.owner.settlement_managers: 332 usage = settlement_manager.get_resource_production_requirement(resource_id) * self.personality.production_level_multiplier 333 production = settlement_manager.get_resource_production(resource_id) 334 resource_import = settlement_manager.trade_manager.get_total_import(resource_id) 335 resource_export = settlement_manager.resource_manager.get_total_export(resource_id) 336 total += usage 337 if settlement_manager is not self: 338 total -= production + resource_export - resource_import 339 return max(0.0, total)
340
341 - def _start_feeder_tick(self):
342 self.log.info('%s food requirement %.5f', self, self.get_ideal_production_level(RES.FOOD)) 343 self.log.info('%s textile requirement %.5f', self, self.get_ideal_production_level(RES.TEXTILE)) 344 self.log.info('%s liquor requirement %.5f', self, self.get_ideal_production_level(RES.LIQUOR)) 345 self.log.info('%s salt requirement %.5f', self, self.get_ideal_production_level(RES.SALT)) 346 self.log.info('%s tobacco products requirement %.5f', self, self.get_ideal_production_level(RES.TOBACCO_PRODUCTS)) 347 self.log.info('%s medical herbs requirement %.5f', self, self.get_ideal_production_level(RES.MEDICAL_HERBS)) 348 self.production_builder.manage_production() 349 self.resource_manager.refresh()
350
351 - def _end_feeder_tick(self):
352 self.resource_manager.replay_deep_low_priority_requests() 353 self.resource_manager.record_expected_exportable_production(self.owner.tick_interval) 354 self.resource_manager.manager_buysell() 355 self.resource_manager.finish_tick()
356
357 - def _start_general_tick(self):
358 self.log.info('%s food production %.5f / %.5f', self, self.get_resource_production(RES.FOOD), 359 self.get_resource_production_requirement(RES.FOOD)) 360 self.log.info('%s textile production %.5f / %.5f', self, self.get_resource_production(RES.TEXTILE), 361 self.get_resource_production_requirement(RES.TEXTILE)) 362 self.log.info('%s get-together production %.5f / %.5f', self, self.get_resource_production(RES.GET_TOGETHER), 363 self.get_resource_production_requirement(RES.GET_TOGETHER)) 364 self.log.info('%s salt production %.5f / %.5f', self, self.get_resource_production(RES.SALT), 365 self.get_resource_production_requirement(RES.SALT)) 366 self.log.info('%s tobacco products production %.5f / %.5f', self, self.get_resource_production(RES.TOBACCO_PRODUCTS), 367 self.get_resource_production_requirement(RES.TOBACCO_PRODUCTS)) 368 self.log.info('%s medical herbs production %.5f / %.5f', self, self.get_resource_production(RES.MEDICAL_HERBS), 369 self.get_resource_production_requirement(RES.MEDICAL_HERBS)) 370 self.production_builder.manage_production() 371 self.trade_manager.refresh() 372 self.resource_manager.refresh() 373 self.need_materials = False
374
376 # TODO: use a better system for managing settler upgrades and taxes 377 if self.land_manager.owner.settler_level == 0: 378 # if we are on level 0 and there is a house that can be upgraded then do it. 379 if self._manual_upgrade(0, 1): 380 self._set_taxes_and_permissions_prefix('early') 381 elif self.get_resource_production(RES.BRICKS) > 1e-9 and not self.settlement.count_buildings(BUILDINGS.VILLAGE_SCHOOL): 382 # if we just need the school then upgrade sailors manually 383 free_boards = self.settlement.get_component(StorageComponent).inventory[RES.BOARDS] 384 free_boards -= Entities.buildings[BUILDINGS.VILLAGE_SCHOOL].costs[RES.BOARDS] 385 free_boards /= 2 # TODO: load this from upgrade resources 386 if free_boards > 0: 387 self._manual_upgrade(0, free_boards) 388 self._set_taxes_and_permissions_prefix('no_school') 389 elif self.settlement.count_buildings(BUILDINGS.VILLAGE_SCHOOL): 390 if self.need_materials: 391 self._set_taxes_and_permissions_prefix('school') 392 else: 393 self._set_taxes_and_permissions_prefix('final')
394
395 - def _end_general_tick(self):
396 self.trade_manager.finalize_requests() 397 self.trade_manager.organize_shipping() 398 self.resource_manager.record_expected_exportable_production(self.owner.tick_interval) 399 self.resource_manager.manager_buysell() 400 self.resource_manager.finish_tick()
401
402 - def _add_goals(self, goals):
403 """Add the settlement's goals that can be activated to the goals list.""" 404 for goal in self._goals: 405 if goal.can_be_activated: 406 goal.update() 407 goals.append(goal)
408
409 - def tick(self, goals):
410 """Refresh the settlement info and add its goals to the player's goal list.""" 411 if self.feeder_island: 412 self._start_feeder_tick() 413 self._add_goals(goals) 414 self._end_feeder_tick() 415 else: 416 self._start_general_tick() 417 self._add_goals(goals) 418 self._end_general_tick()
419
420 - def add_building(self, building):
421 """Called when a new building is added to the settlement (the building already exists during the call).""" 422 coords = building.position.origin.to_tuple() 423 if coords in self.village_builder.plan: 424 self.village_builder.add_building(building) 425 else: 426 self.production_builder.add_building(building)
427
428 - def remove_building(self, building):
429 """Called when a building is removed from the settlement (the building still exists during the call).""" 430 coords = building.position.origin.to_tuple() 431 if coords in self.village_builder.plan: 432 self.village_builder.remove_building(building) 433 else: 434 self.production_builder.remove_building(building)
435
436 - def handle_lost_area(self, coords_list):
437 """ 438 Handle losing the potential land in the given coordinates list. 439 440 Take the following actions: 441 * remove the lost area from the village, production, and road areas 442 * remove village sections with impossible main squares 443 * remove all planned buildings that are now impossible from the village area 444 * remove planned fields that are now impossible 445 * remove fields that can no longer be serviced by a farm 446 * TODO: if the village area takes too much of the total area then remove / reduce the remaining sections 447 """ 448 449 self.land_manager.handle_lost_area(coords_list) 450 self.village_builder.handle_lost_area(coords_list) 451 self.production_builder.handle_lost_area(coords_list) 452 self.production_builder.handle_new_area() # some of the village area may have been repurposed as production area 453 454 self.village_builder.display() 455 self.production_builder.display()
456
457 - def handle_disaster(self, message):
458 position = message.building.position 459 if issubclass(message.disaster_class, BuildingInfluencingDisaster): 460 rescue = message.disaster_class.RESCUE_BUILDING_TYPE 461 else: 462 self.log.info('%s ignoring unknown disaster of type %s', self, message.disaster_class.__name__) 463 return 464 465 rescue_radius = Entities.buildings[rescue].radius 466 handled = False 467 468 for b in self.settlement.buildings_by_id[rescue]: 469 if b.position.distance(position) > rescue_radius: 470 continue 471 # TODO: check whether the building and the doctor/fire station are connected by road 472 self.log.info('%s ignoring %s at %s because %s should be able to handle it', 473 self, message.disaster_class.__name__, message.building, rescue) 474 handled = True 475 break 476 477 if not handled: 478 self.log.info('%s removing %s because of %s', self, message.building, message.disaster_class.__name__) 479 Tear(message.building).execute(self.session)
480
481 - def __str__(self):
482 return '{}.SM({}/{})'.format( 483 self.owner, 484 self.settlement.get_component(NamedComponent).name if hasattr( 485 self, 'settlement') else 'unknown', 486 self.worldid if hasattr(self, 'worldid') else 'none')
487