Merge lp:~widelands-dev/widelands/remove-anti-congestion-algorithm into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 8936
Proposed branch: lp:~widelands-dev/widelands/remove-anti-congestion-algorithm
Merge into: lp:widelands
Diff against target: 1225 lines (+414/-446)
8 files modified
src/economy/flag.cc (+126/-255)
src/economy/flag.h (+11/-20)
src/economy/road.cc (+1/-2)
src/economy/road.h (+3/-3)
src/logic/map_objects/tribes/carrier.cc (+233/-134)
src/logic/map_objects/tribes/carrier.h (+11/-9)
src/logic/map_objects/tribes/worker.cc (+28/-22)
src/logic/map_objects/tribes/worker.h (+1/-1)
To merge this branch: bzr merge lp:~widelands-dev/widelands/remove-anti-congestion-algorithm
Reviewer Review Type Date Requested Status
Widelands Developers Pending
Review via email: mp+359294@code.launchpad.net

Commit message

Remove the anti-congestion algorithm introduced in r8775 because it has too many bugs in it.

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4272. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/458903460.
Appveyor build 4066. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_remove_anti_congestion_algorithm-4066.

8933. By GunChleoc

Improved savegame compatibility

8934. By GunChleoc

Added some {}

Revision history for this message
GunChleoc (gunchleoc) wrote :

We can't start working on the desyncs without this branch, so I'm gonna merge.

@bunnybot merge

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4286. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/459339126.
Appveyor build 4080. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_remove_anti_congestion_algorithm-4080.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.

Travis build 4286. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/459339126.

8935. By GunChleoc

Fix linker error

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4293. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/459617030.
Appveyor build 4086. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_remove_anti_congestion_algorithm-4086.

Revision history for this message
GunChleoc (gunchleoc) wrote :

@bunnybot merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/economy/flag.cc'
2--- src/economy/flag.cc 2018-09-04 15:48:47 +0000
3+++ src/economy/flag.cc 2018-11-26 06:43:43 +0000
4@@ -337,30 +337,7 @@
5 }
6
7 /**
8- * Called by workers wanting to drop a ware to their building's flag.
9- * \return true/allow on low congestion-risk.
10- */
11-bool Flag::has_capacity_for_ware(WareInstance& ware) const {
12- // avoid iteration for the easy cases
13- if (ware_filled_ < ware_capacity_ - 2) {
14- return true; // more than two free slots, allow
15- }
16- if (ware_filled_ >= ware_capacity_) {
17- return false; // all slots filled, no room
18- }
19-
20- DescriptionIndex const descr_index = ware.descr_index();
21- for (int i = 0; i < ware_filled_; ++i) {
22- if (wares_[i].ware->descr_index() == descr_index) {
23- return false; // ware of this type already present, leave room for other types
24- }
25- }
26- return true; // ware of this type not present, allow
27-}
28-
29-/**
30- * \return true/allow if the flag can hold more wares of any type.
31- * has_capacity_for_ware() also checks ware's type to prevent congestion.
32+ * Returns true if the flag can hold more wares.
33 */
34 bool Flag::has_capacity() const {
35 return (ware_filled_ < ware_capacity_);
36@@ -386,24 +363,13 @@
37 capacity_wait_.erase(it);
38 }
39
40-/**
41- * Adds given ware to this flag.
42- * Please check has_capacity() or better has_capacity_for_ware() before.
43- */
44 void Flag::add_ware(EditorGameBase& egbase, WareInstance& ware) {
45+
46 assert(ware_filled_ < ware_capacity_);
47- init_ware(egbase, ware, wares_[ware_filled_++]);
48- if (upcast(Game, game, &egbase)) {
49- ware.update(*game); // will call call_carrier() if necessary
50- }
51-}
52
53-/**
54- * Properly assigns given ware instance to given pending ware.
55- */
56-void Flag::init_ware(EditorGameBase& egbase, WareInstance& ware, PendingWare& pi) {
57+ PendingWare& pi = wares_[ware_filled_++];
58 pi.ware = &ware;
59- pi.pending = true;
60+ pi.pending = false;
61 pi.nextstep = nullptr;
62 pi.priority = 0;
63
64@@ -423,24 +389,32 @@
65 }
66
67 ware.set_location(egbase, this);
68+
69+ if (upcast(Game, game, &egbase)) {
70+ ware.update(*game); // will call call_carrier() if necessary
71+ }
72 }
73
74 /**
75- * \return a ware currently waiting for a carrier to the given destflag.
76+ * \return true if a ware is currently waiting for a carrier to the given Flag.
77 *
78 * \note Due to fetch_from_flag() semantics, this function makes no sense
79 * for a building destination.
80 */
81-Flag::PendingWare* Flag::get_ware_for_flag(Flag& destflag, bool const pending_only) {
82+bool Flag::has_pending_ware(Game&, Flag& dest) {
83 for (int32_t i = 0; i < ware_filled_; ++i) {
84- PendingWare* pw = &wares_[i];
85- if ((!pending_only || pw->pending) && pw->nextstep == &destflag &&
86- destflag.allow_ware_from_flag(*pw->ware, *this)) {
87- return pw;
88- }
89+ if (!wares_[i].pending) {
90+ continue;
91+ }
92+
93+ if (wares_[i].nextstep != &dest) {
94+ continue;
95+ }
96+
97+ return true;
98 }
99
100- return nullptr;
101+ return false;
102 }
103
104 /**
105@@ -450,124 +424,109 @@
106 #define MAX_TRANSFER_PRIORITY 16
107
108 /**
109- * Called by the carriercode when the carrier is called away from his job
110- * but has acknowledged a ware before. This ware is then freed again
111- * to be picked by another carrier. Returns true if a ware was indeed
112- * made pending again.
113+ * Called by carrier code to indicate that the carrier is moving to pick up an
114+ * ware. Ware with highest transfer priority is chosen.
115+ * \return true if an ware is actually waiting for the carrier.
116 */
117-bool Flag::cancel_pickup(Game& game, Flag& destflag) {
118+bool Flag::ack_pickup(Game&, Flag& destflag) {
119+ int32_t highest_pri = -1;
120+ int32_t i_pri = -1;
121+
122 for (int32_t i = 0; i < ware_filled_; ++i) {
123- PendingWare& pw = wares_[i];
124- if (!pw.pending && pw.nextstep == &destflag) {
125- pw.pending = true;
126- pw.ware->update(game); // will call call_carrier() if necessary
127- return true;
128- }
129+ if (!wares_[i].pending) {
130+ continue;
131+ }
132+
133+ if (wares_[i].nextstep != &destflag) {
134+ continue;
135+ }
136+
137+ if (wares_[i].priority > highest_pri) {
138+ highest_pri = wares_[i].priority;
139+ i_pri = i;
140+
141+ // Increase ware priority, it matters only if the ware has to wait.
142+ if (wares_[i].priority < MAX_TRANSFER_PRIORITY) {
143+ wares_[i].priority++;
144+ }
145+ }
146+ }
147+
148+ if (i_pri >= 0) {
149+ wares_[i_pri].pending = false;
150+ return true;
151 }
152
153 return false;
154 }
155-
156 /**
157 * Called by carrier code to find the best among the wares on this flag
158 * that are meant for the provided dest.
159 * \return index of found ware (carrier will take it)
160 * or kNotFoundAppropriate (carrier will leave empty-handed)
161 */
162-int32_t Flag::find_pending_ware(PlayerImmovable& dest) {
163- int32_t highest_pri = -1;
164- int32_t best_index = kNotFoundAppropriate;
165- bool ware_pended = false;
166-
167- for (int32_t i = 0; i < ware_filled_; ++i) {
168- PendingWare& pw = wares_[i];
169- if (pw.nextstep != &dest) {
170- continue;
171- }
172-
173- if (pw.priority < MAX_TRANSFER_PRIORITY) {
174- pw.priority++;
175- }
176- // Release promised pickup, in case we find a preferable ware
177- if (!ware_pended && !pw.pending) {
178- ware_pended = pw.pending = true;
179- }
180-
181- // If dest is flag, we exclude wares that can stress it
182- if (&dest != building_ && !dynamic_cast<Flag&>(dest).allow_ware_from_flag(*pw.ware, *this)) {
183- continue;
184- }
185-
186- if (pw.priority > highest_pri) {
187- highest_pri = pw.priority;
188- best_index = i;
189- }
190- }
191-
192- return best_index;
193-}
194-
195-/**
196- * Like find_pending_ware() above, but for carriers who have a ware to drop on this flag.
197- * \return same as find_pending_ware() above, plus kDenyDrop (carrier will wait)
198- */
199-int32_t Flag::find_swappable_ware(WareInstance& ware, Flag& destflag) {
200- DescriptionIndex const descr_index = ware.descr_index();
201- int32_t highest_pri = -1;
202- int32_t best_index = kNotFoundAppropriate;
203- bool has_same_ware = false;
204- bool has_allowed = false;
205- bool ware_pended = false;
206-
207- for (int32_t i = 0; i < ware_filled_; ++i) {
208- PendingWare& pw = wares_[i];
209- if (pw.nextstep != &destflag) {
210- if (pw.ware->descr_index() == descr_index) {
211- has_same_ware = true;
212- }
213- continue;
214- }
215-
216- if (pw.priority < MAX_TRANSFER_PRIORITY) {
217- pw.priority++;
218- }
219- // Release promised pickup, in case we find a preferable ware
220- if (!ware_pended && !pw.pending) {
221- ware_pended = pw.pending = true;
222- }
223-
224- // We prefer to retrieve wares that won't stress the destflag
225- if (destflag.allow_ware_from_flag(*pw.ware, *this)) {
226- if (!has_allowed) {
227- has_allowed = true;
228- highest_pri = -1;
229- }
230- } else {
231- if (has_allowed) {
232- continue;
233- }
234- }
235-
236- if (pw.priority > highest_pri) {
237- highest_pri = pw.priority;
238- best_index = i;
239- }
240- }
241-
242- if (best_index > kNotFoundAppropriate) {
243- return (ware_filled_ > ware_capacity_ - 3 || has_allowed) ? best_index : kNotFoundAppropriate;
244- } else {
245- return (ware_filled_ < ware_capacity_ - 2 ||
246- (ware_filled_ < ware_capacity_ && !has_same_ware)) ?
247- kNotFoundAppropriate :
248- kDenyDrop;
249- }
250-}
251-
252-/**
253- * Called by carrier code to retrieve a ware found by the previous methods.
254- */
255-WareInstance* Flag::fetch_pending_ware(Game& game, int32_t best_index) {
256+bool Flag::cancel_pickup(Game& game, Flag& destflag) {
257+ int32_t lowest_prio = MAX_TRANSFER_PRIORITY + 1;
258+ int32_t i_pri = -1;
259+
260+ for (int32_t i = 0; i < ware_filled_; ++i) {
261+ if (wares_[i].pending) {
262+ continue;
263+ }
264+
265+ if (wares_[i].nextstep != &destflag) {
266+ continue;
267+ }
268+
269+ if (wares_[i].priority < lowest_prio) {
270+ lowest_prio = wares_[i].priority;
271+ i_pri = i;
272+ }
273+ }
274+
275+ if (i_pri >= 0) {
276+ wares_[i_pri].pending = true;
277+ wares_[i_pri].ware->update(game); // will call call_carrier() if necessary
278+ return true;
279+ }
280+
281+ return false;
282+}
283+
284+/**
285+ * Wake one sleeper from the capacity queue.
286+*/
287+void Flag::wake_up_capacity_queue(Game& game) {
288+ while (!capacity_wait_.empty()) {
289+ Worker* const w = capacity_wait_.front().get(game);
290+ capacity_wait_.pop_front();
291+ if (w && w->wakeup_flag_capacity(game, *this)) {
292+ break;
293+ }
294+ }
295+}
296+
297+/**
298+ * Called by carrier code to retrieve one of the wares on the flag that is meant
299+ * for that carrier.
300+ *
301+ * This function may return 0 even if \ref ack_pickup() has already been
302+ * called successfully.
303+*/
304+WareInstance* Flag::fetch_pending_ware(Game& game, PlayerImmovable& dest) {
305+ int32_t best_index = -1;
306+
307+ for (int32_t i = 0; i < ware_filled_; ++i) {
308+ if (wares_[i].nextstep != &dest) {
309+ continue;
310+ }
311+
312+ // We prefer to retrieve wares that have already been acked
313+ if (best_index < 0 || !wares_[i].pending) {
314+ best_index = i;
315+ }
316+ }
317+
318 if (best_index < 0) {
319 return nullptr;
320 }
321@@ -579,43 +538,14 @@
322 sizeof(wares_[0]) * (ware_filled_ - best_index));
323
324 ware->set_location(game, nullptr);
325+
326+ // wake up capacity wait queue
327+ wake_up_capacity_queue(game);
328+
329 return ware;
330 }
331
332 /**
333- * Called by carrier code to notify waiting carriers
334- * which may be interested in the new state of this flag.
335- */
336-void Flag::ware_departing(Game& game) {
337- // Wake up one sleeper from the capacity queue.
338- while (!capacity_wait_.empty()) {
339- Worker* const w = capacity_wait_.front().get(game);
340- capacity_wait_.erase(capacity_wait_.begin());
341- if (w && w->wakeup_flag_capacity(game, *this)) {
342- return;
343- }
344- }
345-
346- // Consider pending wares of neighboring flags.
347- for (int32_t dir = 1; dir <= WalkingDir::LAST_DIRECTION; ++dir) {
348- Road* const road = get_road(dir);
349- if (!road) {
350- continue;
351- }
352-
353- Flag* other = &road->get_flag(Road::FlagEnd);
354- if (other == this) {
355- other = &road->get_flag(Road::FlagStart);
356- }
357-
358- PendingWare* pw = other->get_ware_for_flag(*this, kPendingOnly);
359- if (pw && road->notify_ware(game, *other)) {
360- pw->pending = false;
361- }
362- }
363-}
364-
365-/**
366 * Accelerate potential promotion of roads adjacent to a newly promoted road.
367 */
368 void Flag::propagate_promoted_road(Road* const promoted_road) {
369@@ -684,7 +614,7 @@
370 memmove(&wares_[i], &wares_[i + 1], sizeof(wares_[0]) * (ware_filled_ - i));
371
372 if (upcast(Game, game, &egbase)) {
373- ware_departing(*game);
374+ wake_up_capacity_queue(*game);
375 }
376
377 return;
378@@ -755,20 +685,27 @@
379
380 for (int32_t dir = 1; dir <= 6; ++dir) {
381 Road* const road = get_road(dir);
382+ Flag* other;
383+ Road::FlagId flagid;
384+
385 if (!road) {
386 continue;
387 }
388
389- Flag* other = &road->get_flag(Road::FlagEnd);
390- if (other == this) {
391+ if (&road->get_flag(Road::FlagStart) == this) {
392+ flagid = Road::FlagStart;
393+ other = &road->get_flag(Road::FlagEnd);
394+ } else {
395+ flagid = Road::FlagEnd;
396 other = &road->get_flag(Road::FlagStart);
397 }
398+
399 if (other != &nextflag) {
400 continue;
401 }
402
403 // Yes, this is the road we want; inform it
404- if (other->update_ware_from_flag(game, wares_[i], *road, *this)) {
405+ if (road->notify_ware(game, flagid)) {
406 return;
407 }
408
409@@ -782,72 +719,6 @@
410 }
411
412 /**
413- * Called by neighboring flags, before agreeing for a carrier
414- * to take one of their wares heading to this flag.
415- * \return true/allow on low congestion-risk.
416- */
417-bool Flag::allow_ware_from_flag(WareInstance& ware, Flag& flag) {
418- // avoid iteration for the easy cases
419- if (ware_filled_ < ware_capacity_ - 2) {
420- return true;
421- }
422-
423- DescriptionIndex const descr_index = ware.descr_index();
424- bool has_swappable = false;
425- for (int i = 0; i < ware_filled_; ++i) {
426- PendingWare& pw = wares_[i];
427- if (pw.pending && pw.nextstep == &flag) {
428- has_swappable = true;
429- } else if (pw.ware->descr_index() == descr_index) {
430- return false;
431- }
432- }
433- return ware_filled_ < ware_capacity_ || has_swappable;
434-}
435-
436-/**
437- * Called when a ware is trying to reach this flag through the provided road,
438- * having just arrived to the provided flag.
439- * Swaps pending wares if possible. Otherwise,
440- * asks road for carrier on low congestion-risk.
441- * \return false if the ware is not immediately served.
442- */
443-bool Flag::update_ware_from_flag(Game& game, PendingWare& pw1, Road& road, Flag& flag) {
444- WareInstance& w1 = *pw1.ware;
445- DescriptionIndex const w1_descr_index = w1.descr_index();
446- bool has_same_ware = false;
447- bool has_swappable = false;
448- for (int i = 0; i < ware_filled_; ++i) {
449- PendingWare& pw2 = wares_[i];
450- WareInstance& w2 = *pw2.ware;
451- if (w2.descr_index() == w1_descr_index) {
452- if (pw2.nextstep == &flag) {
453- // swap pending wares remotely
454- init_ware(game, w1, pw2);
455- flag.init_ware(game, w2, pw1);
456- w1.update(game);
457- w2.update(game);
458- return true;
459- }
460-
461- has_same_ware = true;
462- } else if (pw2.pending && pw2.nextstep == &flag) {
463- has_swappable = true;
464- }
465- }
466-
467- // ask road for carrier on low congestion-risk
468- if (ware_filled_ < ware_capacity_ - 2 ||
469- (!has_same_ware && (ware_filled_ < ware_capacity_ || has_swappable))) {
470- if (road.notify_ware(game, flag)) {
471- pw1.pending = false;
472- return true;
473- }
474- }
475- return false;
476-}
477-
478-/**
479 * Called whenever a road gets broken or split.
480 * Make sure all wares on this flag are rerouted if necessary.
481 *
482
483=== modified file 'src/economy/flag.h'
484--- src/economy/flag.h 2018-09-25 06:32:35 +0000
485+++ src/economy/flag.h 2018-11-26 06:43:43 +0000
486@@ -47,10 +47,6 @@
487 DISALLOW_COPY_AND_ASSIGN(FlagDescr);
488 };
489
490-constexpr bool kPendingOnly = true; // ignore non-pending wares
491-constexpr int32_t kNotFoundAppropriate = -1; // no ware appropiate for carrying
492-constexpr int32_t kDenyDrop = -2; // flag is full and no ware appropiate for swapping
493-
494 /**
495 * Flag represents a flag as you see it on the map.
496 *
497@@ -125,15 +121,7 @@
498
499 bool is_dead_end() const;
500
501- struct PendingWare {
502- WareInstance* ware; ///< the ware itself
503- bool pending; ///< if the ware is pending
504- int32_t priority; ///< carrier prefers the ware with highest priority
505- OPtr<PlayerImmovable> nextstep; ///< next step that this ware is sent to
506- };
507-
508 bool has_capacity() const;
509- bool has_capacity_for_ware(WareInstance&) const;
510 uint32_t total_capacity() {
511 return ware_capacity_;
512 }
513@@ -143,14 +131,10 @@
514 void wait_for_capacity(Game&, Worker&);
515 void skip_wait_for_capacity(Game&, Worker&);
516 void add_ware(EditorGameBase&, WareInstance&);
517- void init_ware(EditorGameBase&, WareInstance&, PendingWare&);
518- PendingWare* get_ware_for_flag(Flag&, bool pending_only = false);
519+ bool has_pending_ware(Game&, Flag& destflag);
520+ bool ack_pickup(Game&, Flag& destflag);
521 bool cancel_pickup(Game&, Flag& destflag);
522- void ware_departing(Game&);
523- bool allow_ware_from_flag(WareInstance&, Flag&);
524- int32_t find_swappable_ware(WareInstance&, Flag&);
525- int32_t find_pending_ware(PlayerImmovable&);
526- WareInstance* fetch_pending_ware(Game&, int32_t);
527+ WareInstance* fetch_pending_ware(Game&, PlayerImmovable& dest);
528 void propagate_promoted_road(Road* promoted_road);
529 Wares get_wares();
530 uint8_t count_wares_in_queue(PlayerImmovable& dest) const;
531@@ -171,13 +155,20 @@
532 void
533 draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
534
535+ void wake_up_capacity_queue(Game&);
536+
537 static void
538 flag_job_request_callback(Game&, Request&, DescriptionIndex, Worker*, PlayerImmovable&);
539
540 void set_flag_position(Coords coords);
541
542 private:
543- bool update_ware_from_flag(Game&, PendingWare&, Road&, Flag&);
544+ struct PendingWare {
545+ WareInstance* ware; ///< the ware itself
546+ bool pending; ///< if the ware is pending
547+ int32_t priority; ///< carrier prefers the ware with highest priority
548+ OPtr<PlayerImmovable> nextstep; ///< next step that this ware is sent to
549+ };
550
551 struct FlagJob {
552 Request* request;
553
554=== modified file 'src/economy/road.cc'
555--- src/economy/road.cc 2018-09-04 15:48:47 +0000
556+++ src/economy/road.cc 2018-11-26 06:43:43 +0000
557@@ -542,8 +542,7 @@
558 * Try to pick up a ware from the given flag.
559 * \return true if a carrier has been sent on its way, false otherwise.
560 */
561-bool Road::notify_ware(Game& game, Flag& flag) {
562- FlagId flagid = &flag == flags_[Road::FlagEnd] ? Road::FlagEnd : Road::FlagStart;
563+bool Road::notify_ware(Game& game, FlagId const flagid) {
564 // Iterate over all carriers and try to find one which will take the ware
565 for (CarrierSlot& slot : carrier_slots_) {
566 if (Carrier* const carrier = slot.carrier.get(game)) {
567
568=== modified file 'src/economy/road.h'
569--- src/economy/road.h 2018-09-25 06:32:35 +0000
570+++ src/economy/road.h 2018-11-26 06:43:43 +0000
571@@ -113,7 +113,7 @@
572 void presplit(Game&, Coords split);
573 void postsplit(Game&, Flag&);
574
575- bool notify_ware(Game& game, Flag& flag);
576+ bool notify_ware(Game& game, FlagId flagid);
577 void update_wallet_chargetime(Game& game);
578 void charge_wallet(Game& game);
579 int32_t wallet() const;
580@@ -148,8 +148,8 @@
581 uint8_t carriers_count() const;
582
583 private:
584- /// Counter that is incremented for every ware served by this road
585- /// according to the delay of service and decremented over time.
586+ /// Counter that is incremented when a ware does not get a carrier for this
587+ /// road immediately and decremented over time.
588 int32_t wallet_;
589
590 /// holds the gametime when wallet_ was last charged
591
592=== modified file 'src/logic/map_objects/tribes/carrier.cc'
593--- src/logic/map_objects/tribes/carrier.cc 2018-10-30 18:40:57 +0000
594+++ src/logic/map_objects/tribes/carrier.cc 2018-11-26 06:43:43 +0000
595@@ -51,7 +51,7 @@
596
597 top_state().ivar1 = 0;
598
599- operation_ = INIT;
600+ promised_pickup_to_ = NOONE;
601 }
602
603 /**
604@@ -78,14 +78,15 @@
605 return pop_task(game);
606 }
607
608- if (operation_ == INIT) {
609- operation_ = find_source_flag(game);
610+ // Check for pending wares
611+ if (promised_pickup_to_ == NOONE) {
612+ find_pending_ware(game);
613 }
614
615- if (operation_ > NO_OPERATION) {
616+ if (promised_pickup_to_ != NOONE) {
617 if (state.ivar1) {
618 state.ivar1 = 0;
619- return start_task_transport(game, operation_);
620+ return start_task_transport(game, promised_pickup_to_);
621 } else {
622 // Short delay before we move to pick up
623 state.ivar1 = 1;
624@@ -119,10 +120,10 @@
625 * a ware there, we have to make sure that they do not count on us anymore.
626 */
627 void Carrier::road_pop(Game& game, State& /* state */) {
628- if (operation_ > NO_OPERATION && get_location(game)) {
629+ if (promised_pickup_to_ != NOONE && get_location(game)) {
630 Road& road = dynamic_cast<Road&>(*get_location(game));
631- Flag& flag = road.get_flag(static_cast<Road::FlagId>(operation_));
632- Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(operation_ ^ 1));
633+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(promised_pickup_to_));
634+ Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(promised_pickup_to_ ^ 1));
635
636 flag.cancel_pickup(game, otherflag);
637 }
638@@ -160,99 +161,35 @@
639 return pop_task(game);
640 }
641
642- int32_t const ivar1 = state.ivar1;
643- if (ivar1 == -1) {
644+ if (state.ivar1 == -1) {
645 // If we're "in" the target building, special code applies
646- return deliver_to_building(game, state);
647- }
648-
649- WareInstance* ware = get_carried_ware(game);
650- if (ware) {
651- assert(ware->get_location(game) == this);
652- }
653-
654- Road& road = dynamic_cast<Road&>(*get_location(game));
655- int32_t const dest = ware ? ivar1 ^ 1 : ivar1;
656- Flag& flag = road.get_flag(static_cast<Road::FlagId>(dest));
657-
658- if (ware) {
659- // If the ware should go to the building attached to our flag,
660- // walk directly into said building
661+ deliver_to_building(game, state);
662+ } else if (!does_carry_ware()) {
663+ // If we don't carry something, walk to the flag
664+ pickup_from_flag(game, state);
665+ } else {
666+ Road& road = dynamic_cast<Road&>(*get_location(game));
667+ // If the ware should go to the building attached to our flag, walk
668+ // directly into said building
669+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(state.ivar1 ^ 1));
670+
671+ WareInstance& ware = *get_carried_ware(game);
672+ assert(ware.get_location(game) == this);
673+
674 // A sanity check is necessary, in case the building has been destroyed
675+ PlayerImmovable* const next = ware.get_next_move_step(game);
676
677- PlayerImmovable* const next = ware->get_next_move_step(game);
678 if (next && next != &flag && &next->base_flag() == &flag) {
679 // pay some coins before entering the building,
680 // to compensate for the time to be spent in its street-segment
681 road.pay_for_building();
682-
683- if (!start_task_walktoflag(game, dest)) {
684- // Enter building
685- state.ivar1 = -1;
686- start_task_move(game, WALK_NW, descr().get_right_walk_anims(does_carry_ware()), true);
687- }
688- return;
689- }
690- }
691-
692- if (!start_task_walktoflag(game, dest, operation_ == WAIT)) {
693- // If the flag is overloaded we are allowed to drop wares,
694- // as long as we can pick another up. Otherwise we have to wait.
695-
696- Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(dest ^ 1));
697- int32_t otherware_idx =
698- ware ? flag.find_swappable_ware(*ware, otherflag) : flag.find_pending_ware(otherflag);
699- if (operation_ == WAIT) {
700- if (otherware_idx < kNotFoundAppropriate) {
701- return start_task_waitforcapacity(game, flag); // join flag's wait queue
702- } else {
703- operation_ = dest ^ 1; // resume transport without joining flag's wait queue
704- set_animation(game, descr().get_animation("idle"));
705- return schedule_act(game, 20);
706- }
707- } else if (otherware_idx < kNotFoundAppropriate) {
708- operation_ = WAIT; // move one node away
709- set_animation(game, descr().get_animation("idle"));
710- return schedule_act(game, 20);
711- }
712-
713- WareInstance* otherware = flag.fetch_pending_ware(game, otherware_idx);
714-
715- if (ware) {
716- const bool ware_astray = (ware->get_next_move_step(game) == nullptr);
717- // Drop our ware
718- flag.add_ware(game, *fetch_carried_ware(game));
719- // If the destination of the dropped ware changed while carrying it and we don't have
720- // anything else we should carry, we might pick it up again immediately, so check again
721- if (ware_astray && otherware_idx == kNotFoundAppropriate) {
722- otherware_idx = flag.find_pending_ware(otherflag);
723- otherware = flag.fetch_pending_ware(game, otherware_idx);
724- }
725- }
726-
727- // Pick up new load, if any
728- if (otherware) {
729- // pay before getting the ware, while checking for road promotion
730- road.pay_for_road(game, flag.count_wares_in_queue(otherflag));
731-
732- set_carried_ware(game, otherware);
733- flag.ware_departing(game);
734-
735- operation_ = state.ivar1 = dest;
736- set_animation(game, descr().get_animation("idle"));
737- schedule_act(game, 20);
738- } else {
739- Flag::PendingWare* pw = otherflag.get_ware_for_flag(flag, kPendingOnly);
740- if (pw) {
741- pw->pending = false;
742-
743- operation_ = state.ivar1 = dest ^ 1;
744- set_animation(game, descr().get_animation("idle"));
745- schedule_act(game, 20);
746- } else {
747- operation_ = NO_OPERATION;
748- pop_task(game);
749- }
750+ enter_building(game, state);
751+ } else if ((flag.has_capacity() || !swap_or_wait(game, state)) &&
752+ !start_task_walktoflag(game, state.ivar1 ^ 1)) {
753+ // If the flag is overloaded we are allowed to drop wares as
754+ // long as we can pick another up. Otherwise we have to wait.
755+ // Drop the ware, possible exchanging it with another one
756+ drop_ware(game, state);
757 }
758 }
759 }
760@@ -268,7 +205,6 @@
761 BaseImmovable* const pos = game.map()[get_position()].get_immovable();
762
763 if (dynamic_cast<Flag const*>(pos)) {
764- operation_ = INIT;
765 return pop_task(game); // we are done
766 } else if (upcast(Building, building, pos)) {
767 // Drop all wares addressed to this building
768@@ -301,51 +237,212 @@
769 }
770
771 /**
772- * Called by road code to indicate that the given flag
773- * (0 = start, 1 = end) has a ware ready for transfer.
774+ * Walks to the queued flag and picks up one acked ware
775+ *
776+ * \param g Game the carrier lives on
777+ * \param s Flags sent to the task
778+ */
779+void Carrier::pickup_from_flag(Game& game, State& state) {
780+ int32_t const ivar1 = state.ivar1;
781+ if (!start_task_walktoflag(game, ivar1)) {
782+
783+ promised_pickup_to_ = NOONE;
784+
785+ Road& road = dynamic_cast<Road&>(*get_location(game));
786+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(ivar1));
787+ Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(ivar1 ^ 1));
788+
789+ // Are there wares to move between our flags?
790+ if (WareInstance* const ware = flag.fetch_pending_ware(game, otherflag)) {
791+ // pay before getting the ware, while checking for road promotion
792+ road.pay_for_road(game, flag.count_wares_in_queue(otherflag));
793+ set_carried_ware(game, ware);
794+
795+ set_animation(game, descr().get_animation("idle"));
796+ return schedule_act(game, 20);
797+ } else {
798+ molog("[Carrier]: Nothing suitable on flag.\n");
799+ return pop_task(game);
800+ }
801+ }
802+}
803+
804+/**
805+ * Drop one ware in a flag, and pick up a new one if we acked it
806+ *
807+ * \param g Game the carrier lives on.
808+ * \param s Flags sent to the task
809+ */
810+void Carrier::drop_ware(Game& game, State& state) {
811+ WareInstance* other = nullptr;
812+ Road& road = dynamic_cast<Road&>(*get_location(game));
813+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(state.ivar1 ^ 1));
814+ Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(state.ivar1));
815+
816+ if (promised_pickup_to_ == (state.ivar1 ^ 1)) {
817+ // If there's a ware we acked, we can drop ours even if the flag is
818+ // flooded
819+ other = flag.fetch_pending_ware(game, otherflag);
820+
821+ if (!other && !flag.has_capacity()) {
822+ molog("[Carrier]: strange: acked ware from busy flag no longer "
823+ "present.\n");
824+
825+ promised_pickup_to_ = NOONE;
826+ set_animation(game, descr().get_animation("idle"));
827+ return schedule_act(game, 20);
828+ }
829+
830+ state.ivar1 = promised_pickup_to_;
831+ promised_pickup_to_ = NOONE;
832+ }
833+
834+ // Drop our ware
835+ flag.add_ware(game, *fetch_carried_ware(game));
836+
837+ // Pick up new load, if any
838+ if (other) {
839+ // pay before getting the ware, while checking for road promotion
840+ road.pay_for_road(game, flag.count_wares_in_queue(otherflag));
841+ set_carried_ware(game, other);
842+
843+ set_animation(game, descr().get_animation("idle"));
844+ return schedule_act(game, 20);
845+ } else {
846+ return pop_task(game);
847+ }
848+}
849+
850+/**
851+ * When picking up wares, if some of them is targeted to the building attached
852+ * to target flag walk straight into it and deliver.
853+ *
854+ * \param g Game the carrier lives on.
855+ * \param s Flags sent to the task.
856+ */
857+void Carrier::enter_building(Game& game, State& state) {
858+ if (!start_task_walktoflag(game, state.ivar1 ^ 1)) {
859+ state.ivar1 = -1;
860+ return start_task_move(game, WALK_NW, descr().get_right_walk_anims(does_carry_ware()), true);
861+ }
862+}
863+
864+/**
865+ * Swaps wares from an overloaded flag for as long as the carrier can pick
866+ * up new wares from it. Otherwise, changes the carrier state to wait.
867+ *
868+ * \param g Game the carrier lives on.
869+ * \param s Flags sent to the task.
870+ *
871+ * \return true if the carrier must wait before delivering his wares.
872+ */
873+bool Carrier::swap_or_wait(Game& game, State& state) {
874+ // Road that employs us
875+ Road& road = dynamic_cast<Road&>(*get_location(game));
876+ // Flag we are delivering to
877+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(state.ivar1 ^ 1));
878+ // The other flag of our road
879+ Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(state.ivar1));
880+
881+ if (promised_pickup_to_ == (state.ivar1 ^ 1)) {
882+ // All is well, we already acked a ware that we can pick up
883+ // from this flag
884+ return false;
885+ } else if (flag.has_pending_ware(game, otherflag)) {
886+ if (!flag.ack_pickup(game, otherflag)) {
887+ throw wexception(
888+ "MO(%u): transport: overload exchange: flag %u is fucked up", serial(), flag.serial());
889+ }
890+
891+ promised_pickup_to_ = state.ivar1 ^ 1;
892+ return false;
893+ } else if (!start_task_walktoflag(game, state.ivar1 ^ 1, true)) {
894+ start_task_waitforcapacity(game, flag); // wait one node away
895+ }
896+
897+ return true;
898+}
899+
900+/**
901+ * Called by Road code to indicate that a new ware has arrived on a flag
902+ * (0 = start, 1 = end).
903 * \return true if the carrier is going to fetch it.
904 */
905 bool Carrier::notify_ware(Game& game, int32_t const flag) {
906 State& state = top_state();
907
908- if (operation_ == WAIT) {
909- if (state.objvar1.get(game) ==
910- &dynamic_cast<Road&>(*get_location(game)).get_flag(static_cast<Road::FlagId>(flag))) {
911- operation_ = flag;
912- send_signal(game, "wakeup");
913- return true;
914- }
915- } else if (operation_ == NO_OPERATION) {
916- operation_ = flag;
917+ // Check if we've already acked something
918+ if (promised_pickup_to_ != NOONE) {
919+ return false;
920+ }
921+
922+ // If we are currently in a transport.
923+ // Explanation:
924+ // a) a different carrier / road may be better suited for this ware
925+ // (the transport code does not have priorities for the actual
926+ // carrier that is notified)
927+ // b) the transport task has logic that allows it to
928+ // drop a ware on an overloaded flag iff it can pick up a ware
929+ // at the same time.
930+ // We should ack said ware to avoid more confusion before we move
931+ // onto the flag, but we can't do that if we have already acked
932+ // something.
933+ // c) we might ack for a flag that we are actually moving away from;
934+ // this will get us into trouble if wares have arrived on the other
935+ // flag while we couldn't ack them.
936+ //
937+ // (Maybe the need for this lengthy explanation is proof that the
938+ // ack system needs to be reworked.)
939+ if (State const* const transport = get_state(taskTransport))
940+ if ((transport->ivar1 == -1 && find_closest_flag(game) != flag) || flag == transport->ivar1)
941+ return false;
942+
943+ // Ack it if we haven't
944+ promised_pickup_to_ = flag;
945+
946+ if (state.task == &taskRoad) {
947 send_signal(game, "ware");
948- return true;
949+ } else if (state.task == &taskWaitforcapacity) {
950+ send_signal(game, "wakeup");
951 }
952-
953- return false;
954+ return true;
955 }
956
957 /**
958- * Find a pending ware meant for our road,
959- * remove its pending status, and
960- * \return the flag it is on.
961+ * Find a pending ware on one of the road's flags, ack it and set promised_pickup_to_
962+ * accordingly.
963 */
964-int32_t Carrier::find_source_flag(Game& game) {
965- assert(operation_ == INIT);
966-
967+void Carrier::find_pending_ware(Game& game) {
968 Road& road = dynamic_cast<Road&>(*get_location(game));
969- int32_t near = find_closest_flag(game);
970- Flag& nearflag = road.get_flag(static_cast<Road::FlagId>(near));
971- Flag& farflag = road.get_flag(static_cast<Road::FlagId>(near ^ 1));
972-
973- Flag::PendingWare* pw;
974- if ((pw = nearflag.get_ware_for_flag(farflag))) {
975- pw->pending = false;
976- return near;
977- } else if ((pw = farflag.get_ware_for_flag(nearflag, kPendingOnly))) {
978- pw->pending = false;
979- return near ^ 1;
980- } else {
981- return NO_OPERATION;
982+ uint32_t havewarebits = 0;
983+
984+ assert(promised_pickup_to_ == NOONE);
985+
986+ if (road.get_flag(Road::FlagStart).has_pending_ware(game, road.get_flag(Road::FlagEnd))) {
987+ havewarebits |= 1;
988+ }
989+
990+ if (road.get_flag(Road::FlagEnd).has_pending_ware(game, road.get_flag(Road::FlagStart))) {
991+ havewarebits |= 2;
992+ }
993+
994+ // If both flags have a ware, we pick the one closer to us.
995+ if (havewarebits == 3) {
996+ havewarebits = 1 << find_closest_flag(game);
997+ }
998+
999+ // Ack our decision
1000+ if (havewarebits == 1) {
1001+ promised_pickup_to_ = START_FLAG;
1002+ if (!road.get_flag(Road::FlagStart).ack_pickup(game, road.get_flag(Road::FlagEnd))) {
1003+ throw wexception("Carrier::find_pending_ware: start flag is messed up");
1004+ }
1005+
1006+ } else if (havewarebits == 2) {
1007+ promised_pickup_to_ = END_FLAG;
1008+ if (!road.get_flag(Road::FlagEnd).ack_pickup(game, road.get_flag(Road::FlagStart))) {
1009+ throw wexception("Carrier::find_pending_ware: end flag is messed up");
1010+ }
1011 }
1012 }
1013
1014@@ -418,7 +515,7 @@
1015
1016 Worker::log_general_info(egbase);
1017
1018- molog("operation = %i\n", operation_);
1019+ molog("promised_pickup_to = %i\n", promised_pickup_to_);
1020 }
1021
1022 /*
1023@@ -428,7 +525,7 @@
1024
1025 ==============================
1026 */
1027-constexpr uint8_t kCurrentPacketVersion = 2;
1028+constexpr uint8_t kCurrentPacketVersion = 3;
1029
1030 Carrier::Loader::Loader() {
1031 }
1032@@ -437,10 +534,12 @@
1033 Worker::Loader::load(fr);
1034
1035 try {
1036- uint8_t packet_version = fr.unsigned_8();
1037- if (packet_version == kCurrentPacketVersion) {
1038+ const uint8_t packet_version = fr.unsigned_8();
1039+ // TODO(GunChleoc): Remove savegame compatibility after Build 21.
1040+ if (packet_version <= kCurrentPacketVersion && packet_version >= 1) {
1041 Carrier& carrier = get<Carrier>();
1042- carrier.operation_ = fr.signed_32();
1043+ // TODO(GunChleoc): std::min is for savegame compatibility. Remove after Build 21.
1044+ carrier.promised_pickup_to_ = std::min(-1, fr.signed_32());
1045 } else {
1046 throw UnhandledVersionError("Carrier", packet_version, kCurrentPacketVersion);
1047 }
1048@@ -465,7 +564,7 @@
1049 Worker::do_save(egbase, mos, fw);
1050
1051 fw.unsigned_8(kCurrentPacketVersion);
1052- fw.signed_32(operation_);
1053+ fw.signed_32(promised_pickup_to_);
1054 }
1055
1056 CarrierDescr::CarrierDescr(const std::string& init_descname,
1057
1058=== modified file 'src/logic/map_objects/tribes/carrier.h'
1059--- src/logic/map_objects/tribes/carrier.h 2018-09-04 15:48:47 +0000
1060+++ src/logic/map_objects/tribes/carrier.h 2018-11-26 06:43:43 +0000
1061@@ -24,7 +24,6 @@
1062 #include "logic/map_objects/tribes/worker.h"
1063
1064 namespace Widelands {
1065-class PendingWare;
1066
1067 class CarrierDescr : public WorkerDescr {
1068 public:
1069@@ -50,7 +49,7 @@
1070 MO_DESCR(CarrierDescr)
1071
1072 explicit Carrier(const CarrierDescr& carrier_descr)
1073- : Worker(carrier_descr), operation_(NO_OPERATION) {
1074+ : Worker(carrier_descr), promised_pickup_to_(NOONE) {
1075 }
1076 ~Carrier() override {
1077 }
1078@@ -67,7 +66,7 @@
1079 static Task const taskRoad;
1080
1081 private:
1082- int32_t find_source_flag(Game&);
1083+ void find_pending_ware(Game&);
1084 int32_t find_closest_flag(Game&);
1085
1086 // internal task stuff
1087@@ -78,14 +77,17 @@
1088 static Task const taskTransport;
1089
1090 void deliver_to_building(Game&, State&);
1091+ void pickup_from_flag(Game&, State&);
1092+ void drop_ware(Game&, State&);
1093+ void enter_building(Game&, State&);
1094+ bool swap_or_wait(Game&, State&);
1095
1096+ /// -1: no ware acked; 0/1: acked ware for start/end flag of road
1097 // This should be an enum, but this clutters the code with too many casts
1098- static const int32_t INIT = -3; // ready to undertake or resume operations
1099- static const int32_t WAIT = -2; // waiting for flag capacity
1100- static const int32_t NO_OPERATION = -1; // idling
1101- static const int32_t START_FLAG = 0; // serving start flag of road
1102- static const int32_t END_FLAG = 1; // serving end flag of road
1103- int32_t operation_;
1104+ static const int32_t NOONE = -1;
1105+ static const int32_t START_FLAG = 0;
1106+ static const int32_t END_FLAG = 1;
1107+ int32_t promised_pickup_to_;
1108
1109 // saving and loading
1110 protected:
1111
1112=== modified file 'src/logic/map_objects/tribes/worker.cc'
1113--- src/logic/map_objects/tribes/worker.cc 2018-11-23 21:26:24 +0000
1114+++ src/logic/map_objects/tribes/worker.cc 2018-11-26 06:43:43 +0000
1115@@ -137,11 +137,10 @@
1116 totalres += amount;
1117 totalchance += 8 * amount;
1118
1119- // Add penalty for fields that are running out
1120- // Except for totally depleted fields or wrong ressource fields
1121- // if we already know there is no ressource (left) we won't mine there
1122+ // Add penalty for fields that are running out
1123 if (amount == 0)
1124- totalchance += 0;
1125+ // we already know it's completely empty, so punish is less
1126+ totalchance += 1;
1127 else if (amount <= 2)
1128 totalchance += 6;
1129 else if (amount <= 4)
1130@@ -1820,11 +1819,12 @@
1131 if (upcast(Flag, flag, pos)) {
1132 // Is this "our" flag?
1133 if (flag->get_building() == location) {
1134- WareInstance* const ware = get_carried_ware(game);
1135- if (state.ivar1 && ware && flag->has_capacity_for_ware(*ware)) {
1136- flag->add_ware(game, *fetch_carried_ware(game));
1137- set_animation(game, descr().get_animation("idle"));
1138- return schedule_act(game, 20); // rest a while
1139+ if (state.ivar1 && flag->has_capacity()) {
1140+ if (WareInstance* const ware = fetch_carried_ware(game)) {
1141+ flag->add_ware(game, *ware);
1142+ set_animation(game, descr().get_animation("idle"));
1143+ return schedule_act(game, 20); // rest a while
1144+ }
1145 }
1146
1147 // Don't try to enter building if it is a dismantle site
1148@@ -2057,18 +2057,16 @@
1149 if (ware) {
1150 // We're in the building, walk onto the flag
1151 if (upcast(Building, building, location)) {
1152- Flag& baseflag = building->base_flag();
1153- if (baseflag.has_capacity_for_ware(*ware)) {
1154- start_task_leavebuilding(game, false); // exit throttle
1155- } else {
1156- start_task_waitforcapacity(game, baseflag);
1157+ if (start_task_waitforcapacity(game, building->base_flag())) {
1158+ return;
1159 }
1160- return;
1161+
1162+ return start_task_leavebuilding(game, false); // exit throttle
1163 }
1164
1165 // We're on the flag, drop the ware and pause a little
1166 if (upcast(Flag, flag, location)) {
1167- if (flag->has_capacity_for_ware(*ware)) {
1168+ if (flag->has_capacity()) {
1169 flag->add_ware(game, *fetch_carried_ware(game));
1170
1171 set_animation(game, descr().get_animation("idle"));
1172@@ -2148,11 +2146,9 @@
1173
1174 // The ware has decided that it doesn't want to go to us after all
1175 // In order to return to the warehouse, we're switching to State_DropOff
1176- Flag& flag = dynamic_cast<Flag&>(*location);
1177 if (WareInstance* const ware =
1178- flag.fetch_pending_ware(game, flag.find_pending_ware(employer))) {
1179+ dynamic_cast<Flag&>(*location).fetch_pending_ware(game, employer)) {
1180 set_carried_ware(game, ware);
1181- flag.ware_departing(game);
1182 }
1183
1184 set_animation(game, descr().get_animation("idle"));
1185@@ -2215,15 +2211,25 @@
1186 static_cast<Bob::Ptr>(&Worker::waitforcapacity_pop), true};
1187
1188 /**
1189- * Pushes a wait task and
1190- * adds the worker to the flag's wait queue.
1191+ * Checks the capacity of the flag.
1192+ *
1193+ * If there is none, a wait task is pushed, and the worker is added to the
1194+ * flag's wait queue. The function returns true in this case.
1195+ * If the flag still has capacity, the function returns false and doesn't
1196+ * act at all.
1197 */
1198-void Worker::start_task_waitforcapacity(Game& game, Flag& flag) {
1199+bool Worker::start_task_waitforcapacity(Game& game, Flag& flag) {
1200+ if (flag.has_capacity()) {
1201+ return false;
1202+ }
1203+
1204 push_task(game, taskWaitforcapacity);
1205
1206 top_state().objvar1 = &flag;
1207
1208 flag.wait_for_capacity(game, *this);
1209+
1210+ return true;
1211 }
1212
1213 void Worker::waitforcapacity_update(Game& game, State&) {
1214
1215=== modified file 'src/logic/map_objects/tribes/worker.h'
1216--- src/logic/map_objects/tribes/worker.h 2018-09-14 08:46:36 +0000
1217+++ src/logic/map_objects/tribes/worker.h 2018-11-26 06:43:43 +0000
1218@@ -163,7 +163,7 @@
1219 void start_task_releaserecruit(Game&, Worker&);
1220 void start_task_fetchfromflag(Game&);
1221
1222- void start_task_waitforcapacity(Game&, Flag&);
1223+ bool start_task_waitforcapacity(Game&, Flag&);
1224 void start_task_leavebuilding(Game&, bool changelocation);
1225 void start_task_fugitive(Game&);
1226

Subscribers

People subscribed via source and target branches

to status/vote changes: