Imported Upstream version 2008.1+svn1553
[opeanno-debian-packaging.git] / game / world / building / buildable.py
blob2b647074f7d586178be26cb61d5946b47c6a55a5
1 # ###################################################
2 # Copyright (C) 2008 The OpenAnno Team
3 # team@openanno.org
4 # This file is part of OpenAnno.
6 # OpenAnno 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.
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.
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 # ###################################################
22 import game.main
23 import math
24 from game.util import Point, Rect
26 class BuildableSingle(object):
27 @classmethod
28 def areBuildRequirementsSatisfied(cls, x, y, before = None, **kwargs):
29 state = {'x' : x, 'y' : y}
30 state.update(kwargs)
31 for check in (cls.isIslandBuildRequirementSatisfied, cls.isSettlementBuildRequirementSatisfied, cls.isGroundBuildRequirementSatisfied, cls.isBuildingBuildRequirementSatisfied, cls.isUnitBuildRequirementSatisfied):
32 update = check(**state)
33 if update is None:
34 return None
35 else:
36 state.update(update)
37 if not update.get('buildable', True):
38 return state
39 if before is not None:
40 update = cls.isMultiBuildRequirementSatisfied(*before, **state)
41 if update is None:
42 return None
43 else:
44 state.update(update)
45 if not update.get('buildable', True):
46 return state
47 return state
49 @classmethod
50 def isMultiBuildRequirementSatisfied(cls, *before, **state):
51 for i in before:
52 if not i.get('buildable', True):
53 continue
54 if i['island'] != state['island']:
55 return {'buildable' : False}
56 return {}
58 @classmethod
59 def isIslandBuildRequirementSatisfied(cls, x, y, **state):
60 island = game.main.session.world.get_island(x, y)
61 if island is None:
62 return {'buildable' : False}
63 p = Point(0,0)
64 for p.x, p.y in ((xx,yy) for xx in xrange(x, x + cls.size[0]) for yy in xrange(y, y + cls.size[1])):
65 if island.get_tile(p) is None:
66 return {'buildable' : False}
67 return {'island' : island}
69 @classmethod
70 def isSettlementBuildRequirementSatisfied(cls, x, y, island, **state):
71 settlements = island.get_settlements(Rect(x, y, x + cls.size[0] - 1, y + cls.size[1] - 1))
72 if len(settlements) != 1:
73 return {'buildable' : False}
74 return {'settlement' : settlements.pop()}
76 @classmethod
77 def isGroundBuildRequirementSatisfied(cls, x, y, island, **state):
78 p = Point(0,0)
79 for p.x, p.y in ((xx,yy) for xx in xrange(x, x + cls.size[0]) for yy in xrange(y, y + cls.size[1])):
80 tile_classes = island.get_tile(p).__class__.classes
81 if 'constructible' not in tile_classes:
82 return {'buildable' : False}
83 return {}
85 @classmethod
86 def isBuildingBuildRequirementSatisfied(cls, x, y, island, **state):
87 from nature import GrowingBuilding
88 from path import Path
89 tear = []
90 p = Point(0,0)
91 for p.x, p.y in [ (xx,yy) for xx in xrange(x, x + cls.size[0]) for yy in xrange(y, y + cls.size[1]) ]:
92 obj = island.get_tile(p).object
93 if obj is not None:
94 if isinstance(obj, GrowingBuilding):
95 if obj.__class__ is cls:
96 return None
97 tear.append(obj.getId())
98 else:
99 return {'buildable' : False}
100 return {} if len(tear) == 0 else {'tear' : tear}
102 @classmethod
103 def isUnitBuildRequirementSatisfied(cls, x, y, island, **state):
104 return {}
106 @classmethod
107 def getBuildList(cls, point1, point2, **kwargs):
108 x = int(round(point2[0])) - (cls.size[0] - 1) / 2 if (cls.size[0] % 2) == 1 else int(math.ceil(point2[0])) - (cls.size[0]) / 2
109 y = int(round(point2[1])) - (cls.size[1] - 1) / 2 if (cls.size[1] % 2) == 1 else int(math.ceil(point2[1])) - (cls.size[1]) / 2
110 building = cls.areBuildRequirementsSatisfied(x, y, **kwargs)
111 if building is None:
112 return []
113 else:
114 return [building]
116 class BuildableRect(BuildableSingle):
117 @classmethod
118 def getBuildList(cls, point1, point2, **kwargs):
119 buildings = []
120 for x, y in [ (x, y) for x in xrange(int(min(round(point1[0]), round(point2[0]))), 1 + int(max(round(point1[0]), round(point2[0])))) for y in xrange(int(min(round(point1[1]), round(point2[1]))), 1 + int(max(round(point1[1]), round(point2[1])))) ]:
121 building = cls.areBuildRequirementsSatisfied(x, y, buildings, **kwargs)
122 if building is not None:
123 buildings.append(building)
125 return buildings
127 class BuildableLine(BuildableSingle):
128 @classmethod
129 def getBuildList(cls, point1, point2, **kwargs):
131 @param point1:
132 @param point2:
134 buildings = []
135 kwargs['rotation'] = 45
136 y = int(round(point1[1]))
137 for x in xrange(int(round(point1[0])), int(round(point2[0])), (1 if int(round(point2[0])) > int(round(point1[0])) else -1)):
138 building = cls.areBuildRequirementsSatisfied(x, y, buildings, **kwargs)
139 if building is not None:
140 building.update({'action' : ('d' if int(round(point2[0])) < int(round(point1[0])) else 'b') if len(buildings) == 0 else 'bd'})
141 buildings.append(building)
142 x = int(round(point2[0]))
143 is_first = True
144 for y in xrange(int(round(point1[1])), int(round(point2[1])) + (1 if int(round(point2[1])) > int(round(point1[1])) else -1), (1 if int(round(point2[1])) > int(round(point1[1])) else -1)):
145 if len(buildings) == 0: #first tile
146 if y == int(round(point2[1])): #only tile
147 action = 'default'
148 else:
149 action = 'c' if int(round(point2[1])) > int(round(point1[1])) else 'a'
150 elif y == int(round(point2[1])): #last tile
151 if int(round(point1[1])) == int(round(point2[1])): #only tile in this loop
152 action = 'd' if int(round(point2[0])) > int(round(point1[0])) else 'b'
153 else:
154 action = 'a' if int(round(point2[1])) > int(round(point1[1])) else 'c'
155 elif y == int(round(point1[1])): #edge
156 if int(round(point2[0])) > int(round(point1[0])):
157 action = 'cd' if int(round(point2[1])) > int(round(point1[1])) else 'ad'
158 else:
159 action = 'bc' if int(round(point2[1])) > int(round(point1[1])) else 'ab'
160 else:
161 action = 'ac'
162 is_first = False
164 building = cls.areBuildRequirementsSatisfied(x, y, buildings, **kwargs)
165 if building is not None:
166 building.update({'action' : action})
167 buildings.append(building)
168 return buildings
170 class BuildableSingleWithSurrounding(BuildableSingle):
171 @classmethod
172 def getBuildList(cls, point1, point2, **kwargs):
173 x = int(round(point2[0])) - (cls.size[0] - 1) / 2 if (cls.size[0] % 2) == 1 else int(math.ceil(point2[0])) - (cls.size[0]) / 2
174 y = int(round(point2[1])) - (cls.size[1] - 1) / 2 if (cls.size[1] % 2) == 1 else int(math.ceil(point2[1])) - (cls.size[1]) / 2
175 building = cls.areBuildRequirementsSatisfied(x, y, **kwargs)
176 if building is None:
177 return []
178 buildings = [building]
179 for xx in xrange(x - cls.radius, x + cls.size[0] + cls.radius):
180 for yy in xrange(y - cls.radius, y + cls.size[1] + cls.radius):
181 if ((xx < x or xx >= x + cls.size[0]) or (yy < y or yy >= y + cls.size[1])) and ((max(x - xx, 0, xx - x - cls.size[0] + 1) ** 2) + (max(y - yy, 0, yy - y - cls.size[1] + 1) ** 2)) <= cls.radius ** 2:
182 building = game.main.session.entities.buildings[cls._surroundingBuildingClass].areBuildRequirementsSatisfied(xx, yy, **kwargs)
183 if building is not None:
184 building.update(building = game.main.session.entities.buildings[cls._surroundingBuildingClass], **kwargs)
185 buildings.append(building)
186 return buildings