Package horizons :: Package world :: Package disaster :: Module buildinginfluencingdisaster
[hide private]
[frames] | no frames]

Source Code for Module horizons.world.disaster.buildinginfluencingdisaster

  1  # ################################################### 
  2  # Copyright (C) 2013-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  from typing import TYPE_CHECKING, Type 
 23   
 24  from horizons.constants import BUILDINGS, GAME_SPEED, TIER 
 25  from horizons.messaging import AddStatusIcon, NewDisaster, RemoveStatusIcon 
 26  from horizons.scheduler import Scheduler 
 27  from horizons.util.python.callback import Callback 
 28  from horizons.util.worldobject import WorldObject 
 29  from horizons.world.disaster import Disaster 
 30   
 31  if TYPE_CHECKING: 
 32          from horizons.world.status import StatusIcon 
33 34 35 -class BuildingInfluencingDisaster(Disaster):
36 """Simulates a building influencing disaster. 37 38 Starts at a certain building and will spread out over time. 39 40 """ 41 42 # Defines the building type that should be influenced, by default it infects the residents of a settlement 43 BUILDING_TYPE = BUILDINGS.RESIDENTIAL 44 45 # Defines the minimum tier a settlement needs before this disaster can break out, by default its the PIONEER tier 46 MIN_BREAKOUT_TIER = TIER.PIONEERS 47 48 # Defines the minimum number of pioneer or higher residences that need to be in a 49 # settlement before this disaster can break loose 50 MIN_INHABITANTS_FOR_BREAKOUT = 5 51 52 # Defines the status icon for the influenced BUILDING_TYPE 53 STATUS_ICON = None # type: Type[StatusIcon] 54 55 # Defines building type that consumes resources of type DISASTER_RES 56 RESCUE_BUILDING_TYPE = None # type: int 57 58 # In a range of how many tiles can the disaster spread? 59 EXPANSION_RADIUS = 0 60 61 TIME_BEFORE_HAVOC = GAME_SPEED.TICKS_PER_SECOND * 30 62 # By default, try twice before disasterying 63 EXPANSION_TIME = (TIME_BEFORE_HAVOC // 2) - 1 64
65 - def __init__(self, settlement, manager):
66 super().__init__(settlement, manager) 67 self._affected_buildings = []
68
69 - def save(self, db):
70 super().save(db) 71 for building in self._affected_buildings: 72 ticks = Scheduler().get_remaining_ticks(self, Callback(self.wreak_havoc, building), True) 73 db("INSERT INTO building_influencing_disaster(disaster, building, remaining_ticks_havoc) VALUES(?, ?, ?)", 74 self.worldid, building.worldid, ticks)
75
76 - def load(self, db, worldid):
77 super().load(db, worldid) 78 for building_id, ticks in db("SELECT building, remaining_ticks_havoc FROM building_influencing_disaster WHERE disaster = ?", worldid): 79 # do half of infect() 80 building = WorldObject.get_object_by_id(building_id) 81 self.log.debug("%s loading disaster %s", self, building) 82 self.infect(building, load=(db, worldid))
83
84 - def breakout(self):
85 assert self.can_breakout(self._settlement) 86 super().breakout() 87 possible_buildings = self._settlement.buildings_by_id[self.BUILDING_TYPE] 88 building = self._settlement.session.random.choice(possible_buildings) 89 self.infect(building) 90 self.log.debug("%s breakout out on %s at %s", self, building, building.position)
91 92 @classmethod
93 - def can_breakout(cls, settlement):
94 return settlement.owner.settler_level >= cls.MIN_BREAKOUT_TIER and \ 95 settlement.count_buildings(cls.BUILDING_TYPE) > cls.MIN_INHABITANTS_FOR_BREAKOUT
96
97 - def expand(self):
98 if not self.evaluate(): 99 self._manager.end_disaster(self._settlement) 100 self.log.debug("%s ending", self) 101 # We are done here, time to leave 102 return 103 self.log.debug("%s still active, expanding..", self) 104 for building in self._affected_buildings: 105 for tile in self._settlement.get_tiles_in_radius(building.position, self.EXPANSION_RADIUS, False): 106 if tile.object is None or tile.object.id != self.BUILDING_TYPE: 107 continue 108 if tile.object in self._affected_buildings: 109 continue 110 if self._settlement.session.random.random() <= self.SEED_CHANCE: 111 self.infect(tile.object) 112 return
113
114 - def end(self):
116
117 - def infect(self, building, load=None):
118 """@load: (db, disaster_worldid), set on restoring infected state of savegame""" 119 super().infect(building, load=load) 120 self._affected_buildings.append(building) 121 havoc_time = self.TIME_BEFORE_HAVOC 122 # keep in sync with load() 123 if load: 124 db, worldid = load 125 havoc_time = db("SELECT remaining_ticks_havoc FROM building_influencing_disaster WHERE disaster = ? AND building = ?", worldid, building.worldid)[0][0] 126 Scheduler().add_new_object(Callback(self.wreak_havoc, building), self, run_in=havoc_time) 127 AddStatusIcon.broadcast(building, self.STATUS_ICON(building)) 128 NewDisaster.broadcast(building.owner, building, self.__class__, self)
129
130 - def recover(self, building):
131 super().recover(building) 132 RemoveStatusIcon.broadcast(self, building, self.STATUS_ICON) 133 callback = Callback(self.wreak_havoc, building) 134 Scheduler().rem_call(self, callback) 135 self._affected_buildings.remove(building)
136
137 - def evaluate(self):
138 return len(self._affected_buildings) > 0
139
140 - def wreak_havoc(self, building):
141 super().wreak_havoc(building) 142 self._affected_buildings.remove(building)
143