* Better Patch for Configfile handling
[opeanno-debian-packaging.git] / game / world / units / collector.py
blob2ea24480264c7eb6c56a37793ad6e0fe39f0bab5
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 from game.world.units.unit import Unit
23 from game.world.storageholder import StorageHolder
24 from game.util import Rect, Point
25 from game.world.pathfinding import Movement
26 from game.world.production import PrimaryProducer
27 import game.main
28 import operator
29 import weakref
32 class BuildingCollector(StorageHolder, Unit):
33 movement = Movement.CARRIAGE_MOVEMENT
34 """
35 How does this class work ?
36 Timeline:
37 init
38 |-search_job()
39 |- get_job - he found a job ( it's best )
42 """
43 def __init__(self, home_building, slots = 1, size = 6, start_hidden=True, **kwargs):
44 super(BuildingCollector, self).__init__(x=home_building.position.origin.x,
45 y=home_building.position.origin.y,
46 slots = slots,
47 size = size,
48 **kwargs)
49 #print 'carriage beeing inited'
50 self.home_building = weakref.ref(home_building)
51 self.inventory.limit = size;
52 #for res in home_building.get_consumed_res(): # NOTE: this does not work for multiple production lines yet.
53 # if not self.inventory.hasSlot(res):
54 # self.inventory.addSlot(res, size)
56 self.start_hidden = start_hidden
57 if self.start_hidden:
58 self.hide()
60 # start searching jobs just when construction (of subclass) is completed
61 game.main.session.scheduler.add_new_object(self.search_job, self, 1)
63 def save(self, db):
64 super(BuildingCollector, self).save(db)
65 db("UPDATE unit SET owner = ? WHERE rowid = ?", self.home_building().getId(), self.getId())
66 #print 'savin job', (self.job is not None)
67 if self.job is not None:
68 db("INSERT INTO collector_job(rowid, object, resource, amount) VALUES(?, ?, ?, ?)", self.getId(), self.job.object.getId(), self.job.res, self.job.amount)
70 # TODO:
71 # state of current job
73 def search_job(self):
74 """Search for a job, only called if the collector does not have a job."""
75 self.job = self.get_job()
76 if self.job is None:
77 #print self.id, 'JOB NONE'
78 game.main.session.scheduler.add_new_object(self.search_job, self, 32)
79 else:
80 #print self.id, 'EXECUTE JOB'
81 self.begin_current_job()
83 def get_job(self):
84 """Returns the next job or None"""
86 if self.home_building() is None:
87 return None
89 #print self.id, 'GET JOB'
90 collectable_res = self.get_collectable_res()
91 if len(collectable_res) == 0:
92 return None
93 jobs = []
94 for building in self.get_buildings_in_range():
95 #if isinstance(building, self.home_building().__class__): # Continue if building is of the same class as the home building, to prevent e.g. weaver picking up from weaver.
96 # continue
97 for res in collectable_res:
98 if isinstance(building, PrimaryProducer) and building.active_production_line is not None and building.production[building.active_production_line].production.get(res,1) < 0:
99 break
100 res_amount = building.inventory[res]
101 if res_amount > 0:
102 # get sum of picked up resources by other collectors for res
103 total_pickup_amount = sum([ carriage.job.amount for carriage in building._Provider__collectors if carriage.job.res == res ])
104 # check how much will be delivered
105 total_registered_amount_consumer = sum([ carriage.job.amount for carriage in self.home_building()._Consumer__collectors if carriage.job.res == res ])
106 # check if there are resources left to pickup
107 max_consumer_res_free = self.home_building().inventory.get_limit(res)-(total_registered_amount_consumer+self.home_building().inventory[res])
108 if res_amount > total_pickup_amount and max_consumer_res_free > 0:
109 # add a new job
110 jobs.append(Job(building, res, min(res_amount - total_pickup_amount, self.inventory.get_limit(res), max_consumer_res_free)))
112 # sort job list
113 jobs.sort(key=operator.attrgetter('rating') )
114 jobs.reverse()
116 for job in jobs:
117 if self.check_move(job.object.position):
118 return job
119 return None
121 def setup_new_job(self):
122 """Executes the necessary actions to begin a new job"""
123 self.job.object._Provider__collectors.append(self)
124 self.home_building()._Consumer__collectors.append(self)
126 def begin_current_job(self):
127 """Executes the current job"""
128 #print self.id, 'BEGIN CURRENT JOB'
129 self.setup_new_job()
130 self.show()
131 self.move(self.job.object.position, self.begin_working)
133 def begin_working(self):
134 """Pretends that the collector works by waiting some time"""
135 # uncomment the following line when all collectors have a "stopped" animation
136 #self._instance.act("stopped", self._instance.getFacingLocation(), True)
137 if self.job.object:
138 #print self.getId(), 'BEGIN WORKING'
139 game.main.session.scheduler.add_new_object(self.finish_working, self, 16)
140 else:
141 self.reroute()
143 def finish_working(self):
144 if self.job.object:
145 #print self.getId(), 'FINISH WORKING'
146 self.act("default", self._instance.getFacingLocation(), True)
147 # transfer res
148 self.transfer_res()
149 # deregister at the target we're at
150 self.job.object._Provider__collectors.remove(self)
151 # move back to home
152 self.move_home(callback=self.reached_home)
153 else:
154 self.reroute()
156 def reroute(self):
157 #print self.getId(), 'Rerouting from', self.position
158 # Get a new job
159 #print "Old job %s" % self.job
160 job = self.get_job()
161 # Check if there is a new job
162 if job:
163 # There is a new job!
164 self.job = job
165 #print "New job %s" % self.job
166 self.begin_current_job()
167 else:
168 # There is no new job...
169 # Return home and end job
170 self.move_home(callback=self.reached_home)
172 def reached_home(self):
173 """ we finished now our complete work. Let's do it again in 32 ticks
174 you can use this as event as after work
176 #print self.id, 'FINISHED WORK'
178 if self.home_building() is not None:
179 remnant = self.home_building().inventory.alter(self.job.res, self.job.amount)
180 #assert(remnant == 0, "Home building could not take all ressources from carriage.")
181 remnant = self.inventory.alter(self.job.res, -self.job.amount)
182 #assert(remnant == 0, "Carriage did not pick up amount of ressources specified by the job.")
183 self.home_building()._Consumer__collectors.remove(self)
184 self.end_job()
186 def end_job(self):
187 # he finished the job now
188 # before the new job can begin this will be executed
189 if self.start_hidden:
190 self.hide()
191 game.main.session.scheduler.add_new_object(self.search_job , self, 32)
193 def transfer_res(self):
194 #print self.id, 'TRANSFER PICKUP'
195 res_amount = self.job.object.pickup_resources(self.job.res, self.job.amount)
196 # should not to be. register_collector function at the building should prevent it
197 #print self.id, 'TRANSFERED res:', self.job.res,' amount: ', res_amount,' we should :', self.job.amount
198 assert(res_amount == self.job.amount, "Carriage could not pickup amount of ressources, that was planned for the current job.")
199 self.inventory.alter(self.job.res, res_amount)
201 def get_collectable_res(self):
202 """Gets all resources the Collector can collect"""
203 #print self.id, 'GET COLLECTABLE RES'
204 # find needed res (only res that we have free room for) - Building function
205 return self.home_building().get_needed_res()
207 def get_buildings_in_range(self):
208 #print self.id, 'GET BUILDINGS IN RANGE'
209 """returns all buildings in range
210 Overwrite in subclasses that need ranges arroung the pickup."""
211 from game.world.provider import Provider
212 return [building for building in self.home_building().get_buildings_in_range() if isinstance(building, Provider)]
214 def move_home(self, callback=None):
215 self.move(self.home_building().position, callback=callback, destination_in_building=True)
218 class StorageCollector(BuildingCollector):
219 """ Same as BuildingCollector, except that it moves on roads.
220 Used in storage facilities.
222 movement = Movement.STORAGE_CARRIAGE_MOVEMENT
224 def begin_current_job(self):
225 """Declare target of StorageCollector as building, because it always is"""
226 super(StorageCollector, self).begin_current_job()
227 self.move(self.job.object.position, self.begin_working, destination_in_building = True)
229 class AnimalCollector(BuildingCollector):
230 """ Collector that gets resources from animals """
232 def begin_current_job(self):
233 """Tell the animal to stop. First step of a job"""
234 #print self.id, 'BEGIN CURRENT JOB'
235 self.setup_new_job()
236 self.stop_animal()
238 def pickup_animal(self):
239 """Moves collector to animal. Called by animal when it actually stopped"""
240 #print self.id, 'PICKUP ANIMAL'
241 self.show()
242 self.move(self.job.object.position, self.begin_working)
244 def finish_working(self):
245 """Transfer res and such. Called when collector arrives at the animal"""
246 super(AnimalCollector, self).finish_working()
247 self.get_animal()
249 def reached_home(self):
250 """Transfer res to home building and such. Called when collector arrives at it's home"""
251 super(AnimalCollector, self).reached_home()
252 # sheep and herder are inside the building now, pretending to work.
253 self.release_animal()
255 def get_buildings_in_range(self):
256 # This is only a small workarround
257 # as long we have no Collector class
258 return self.get_animals_in_range()
260 def get_animals_in_range(self):
261 # TODO: use the Collector class instead of BuildCollector
262 #print self.id, 'GET ANIMALS IN RANGE'
263 """returns all buildings in range
264 Overwrite in subclasses that need ranges arroung the pickup."""
265 return self.home_building().animals
267 def stop_animal(self):
268 """Tell animal to stop at the next occasion"""
269 #print self.id, 'STOP ANIMAL', self.job.object.id
270 self.job.object.stop_after_job(self)
272 def get_animal(self):
273 """Sends animal to collectors home building"""
274 #print self.id, 'GET ANIMAL'
275 self.job.object.move(self.home_building().position, destination_in_building = True)
277 def release_animal(self):
278 """Let animal free after shearing"""
279 #print self.id, 'RELEASE ANIMAL', self.job.object.getId()
280 game.main.session.scheduler.add_new_object(self.job.object.search_job, self.job.object, 16)
283 class Job(object):
284 def __init__(self, object, res, amount):
285 self._object = weakref.ref(object)
286 self.res = res
287 self.amount = amount
289 # this is rather a dummy
290 self.rating = amount
292 @property
293 def object(self):
294 return self._object()