1 /*
2 * Copyright (C) 2004-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "economy/road.h"
21
22 #include "base/macros.h"
23 #include "economy/economy.h"
24 #include "economy/flag.h"
25 #include "economy/request.h"
26 #include "logic/editor_game_base.h"
27 #include "logic/game.h"
28 #include "logic/map_objects/map_object.h"
29 #include "logic/map_objects/tribes/carrier.h"
30 #include "logic/map_objects/tribes/tribe_descr.h"
31 #include "logic/map_objects/world/terrain_description.h"
32 #include "logic/map_objects/world/world.h"
33 #include "logic/player.h"
34
35 namespace Widelands {
36
37 // dummy instance because MapObject needs a description
38 namespace {
39 const RoadDescr g_road_descr("road", "Road");
40 }
41
is_road_descr(MapObjectDescr const * const descr)42 bool Road::is_road_descr(MapObjectDescr const* const descr) {
43 return descr == &g_road_descr;
44 }
45
CarrierSlot()46 Road::CarrierSlot::CarrierSlot()
47 : carrier(nullptr), carrier_request(nullptr), second_carrier(false) {
48 }
49
50 /**
51 * Most of the actual work is done in init.
52 */
Road()53 Road::Road() : RoadBase(g_road_descr), busy_(false), wallet_(0), last_wallet_charge_(0) {
54 CarrierSlot slot;
55 carrier_slots_.push_back(slot);
56 carrier_slots_.push_back(slot);
57 carrier_slots_[0].second_carrier = false;
58 carrier_slots_[1].second_carrier = true;
59 }
60
61 /**
62 * Most of the actual work is done in cleanup.
63 */
~Road()64 Road::~Road() {
65 for (CarrierSlot& slot : carrier_slots_) {
66 delete slot.carrier_request;
67 }
68 }
69
70 /**
71 * Create a road between the given flags, using the given path.
72 */
create(EditorGameBase & egbase,Flag & start,Flag & end,const Path & path)73 Road& Road::create(EditorGameBase& egbase, Flag& start, Flag& end, const Path& path) {
74 assert(start.get_position() == path.get_start());
75 assert(end.get_position() == path.get_end());
76 assert(start.get_owner() == end.get_owner());
77
78 Road& road = *new Road();
79 road.set_owner(start.get_owner());
80 road.busy_ = false;
81 road.flags_[FlagStart] = &start;
82 road.flags_[FlagEnd] = &end;
83 // flagidx_ is set when attach_road() is called, i.e. in init()
84 road.set_path(egbase, path);
85
86 road.init(egbase);
87
88 return road;
89 }
90
road_type_for_drawing() const91 RoadSegment Road::road_type_for_drawing() const {
92 return busy_ ? RoadSegment::kBusy : RoadSegment::kNormal;
93 }
94
cleanup(EditorGameBase & egbase)95 void Road::cleanup(EditorGameBase& egbase) {
96 Economy::check_split(*flags_[FlagStart], *flags_[FlagEnd], wwWARE);
97 Economy::check_split(*flags_[FlagStart], *flags_[FlagEnd], wwWORKER);
98 for (CarrierSlot& slot : carrier_slots_) {
99 delete slot.carrier_request;
100 slot.carrier_request = nullptr;
101
102 // carrier will be released via PlayerImmovable::cleanup
103 slot.carrier = nullptr;
104 }
105 RoadBase::cleanup(egbase);
106 }
107
link_into_flags(EditorGameBase & egbase,bool)108 void Road::link_into_flags(EditorGameBase& egbase, bool) {
109 RoadBase::link_into_flags(egbase);
110 Economy::check_merge(*flags_[FlagStart], *flags_[FlagEnd], wwWARE);
111 Economy::check_merge(*flags_[FlagStart], *flags_[FlagEnd], wwWORKER);
112 if (upcast(Game, game, &egbase)) {
113 for (CarrierSlot& slot : carrier_slots_) {
114 if (Carrier* const carrier = slot.carrier.get(*game)) {
115 // This happens after a road split. Tell the carrier what's going on.
116 carrier->set_location(this);
117 carrier->update_task_road(*game);
118 } else if (!slot.carrier_request && (!slot.second_carrier || busy_)) {
119 // Normal carriers are requested at once, second carriers only for busy roads
120 request_carrier(slot);
121 }
122 }
123 }
124 }
125
set_busy(EditorGameBase & e,bool b)126 void Road::set_busy(EditorGameBase& e, bool b) {
127 unmark_map(e);
128 wallet_ = b ? kRoadMaxWallet : 0;
129 busy_ = b;
130 mark_map(e);
131 }
132
set_economy(Economy * const e,WareWorker type)133 void Road::set_economy(Economy* const e, WareWorker type) {
134 RoadBase::set_economy(e, type);
135 if (type == wwWORKER) {
136 for (CarrierSlot& slot : carrier_slots_) {
137 if (slot.carrier_request) {
138 slot.carrier_request->set_economy(e);
139 }
140 }
141 }
142 }
143
144 /**
145 * Request a new carrier.
146 *
147 * Only call this if the road can handle a new carrier, and if no request has
148 * been issued.
149 */
request_carrier(CarrierSlot & slot)150 void Road::request_carrier(CarrierSlot& slot) {
151 slot.carrier_request = new Request(
152 *this, slot.second_carrier ? owner().tribe().carrier2() : owner().tribe().carrier(),
153 request_carrier_callback, wwWORKER);
154 }
155
156 /**
157 * The carrier has arrived successfully.
158 */
request_carrier_callback(Game & game,Request & rq,DescriptionIndex,Worker * const w,PlayerImmovable & target)159 void Road::request_carrier_callback(
160 Game& game, Request& rq, DescriptionIndex, Worker* const w, PlayerImmovable& target) {
161 assert(w);
162
163 Road& road = dynamic_cast<Road&>(target);
164
165 for (CarrierSlot& slot : road.carrier_slots_) {
166 if (slot.carrier_request == &rq) {
167 Carrier& carrier = dynamic_cast<Carrier&>(*w);
168 slot.carrier_request = nullptr;
169 slot.carrier = &carrier;
170
171 carrier.start_task_road(game);
172 delete &rq;
173 return;
174 }
175 }
176
177 /*
178 * Oops! We got a request_callback but don't have the request.
179 * Try to send him home.
180 */
181 log("Road(%u): got a request_callback but do not have the request\n", road.serial());
182 delete &rq;
183 w->start_task_gowarehouse(game);
184 }
carriers_count() const185 uint8_t Road::carriers_count() const {
186 return (carrier_slots_[1].carrier == nullptr) ? 1 : 2;
187 }
188
189 /**
190 * If we lost our carrier, re-request it.
191 */
remove_worker(Worker & w)192 void Road::remove_worker(Worker& w) {
193 EditorGameBase& egbase = get_owner()->egbase();
194
195 for (CarrierSlot& slot : carrier_slots_) {
196 Carrier const* carrier = slot.carrier.get(egbase);
197
198 if (carrier == &w) {
199 slot.carrier = nullptr;
200 carrier = nullptr;
201 request_carrier(slot);
202 }
203 }
204
205 PlayerImmovable::remove_worker(w);
206 }
207
208 /**
209 * A carrier was created by someone else (e.g. Scripting Engine)
210 * and should now be assigned to this road.
211 */
assign_carrier(Carrier & c,uint8_t slot)212 void Road::assign_carrier(Carrier& c, uint8_t slot) {
213 assert(slot <= 1);
214
215 // Send the worker home if it occupies our slot
216 CarrierSlot& s = carrier_slots_[slot];
217
218 delete s.carrier_request;
219 s.carrier_request = nullptr;
220 if (Carrier* const current_carrier = s.carrier.get(owner().egbase()))
221 current_carrier->set_location(nullptr);
222
223 carrier_slots_[slot].carrier = &c;
224 carrier_slots_[slot].carrier_request = nullptr;
225 }
226
227 /**
228 * The flag that splits this road has been initialized. Perform the actual
229 * splitting.
230 *
231 * After the split, this road will span [start...new flag]. A new road will
232 * be created to span [new flag...end]
233 */
234 // TODO(SirVer): This needs to take an EditorGameBase as well.
postsplit(Game & game,Flag & flag)235 void Road::postsplit(Game& game, Flag& flag) {
236 Flag& oldend = *flags_[FlagEnd];
237
238 // detach from end
239 oldend.detach_road(flagidx_[FlagEnd]);
240
241 // build our new path and the new road's path
242 const Map& map = game.map();
243 CoordPath path(map, path_);
244 CoordPath secondpath(path);
245 int32_t const index = path.get_index(flag.get_position());
246
247 assert(index > 0);
248 assert(static_cast<uint32_t>(index) < path.get_nsteps() - 1);
249
250 path.truncate(index);
251 secondpath.trim_start(index);
252
253 molog("splitting road: first part:\n");
254 for (const Coords& coords : path.get_coords()) {
255 molog("* (%i, %i)\n", coords.x, coords.y);
256 }
257 molog(" second part:\n");
258 for (const Coords& coords : secondpath.get_coords()) {
259 molog("* (%i, %i)\n", coords.x, coords.y);
260 }
261
262 // change road size and reattach
263 flags_[FlagEnd] = &flag;
264 set_path(game, path);
265
266 const Direction dir = get_reverse_dir(path_[path_.get_nsteps() - 1]);
267 flags_[FlagEnd]->attach_road(dir, this);
268 flagidx_[FlagEnd] = dir;
269
270 // recreate road markings
271 mark_map(game);
272
273 // create the new road
274 Road& newroad = *new Road();
275 newroad.set_owner(get_owner());
276 newroad.busy_ = busy_;
277 newroad.flags_[FlagStart] = &flag; // flagidx will be set on init()
278 newroad.flags_[FlagEnd] = &oldend;
279 newroad.set_path(game, secondpath);
280
281 // Find workers on this road that need to be reassigned
282 // The algorithm is pretty simplistic, and has a bias towards keeping
283 // the worker around; there's obviously nothing wrong with that.
284
285 std::vector<Worker*> const workers = get_workers();
286 std::vector<Worker*> reassigned_workers;
287
288 for (Worker* w : workers) {
289 int32_t idx = path.get_index(w->get_position());
290
291 // Careful! If the worker is currently inside the building at our
292 // starting flag, we *must not* reassign him.
293 // If he is in the building at our end flag or at the other road's
294 // end flag, he can be reassigned to the other road.
295 if (idx < 0) {
296 if (dynamic_cast<Building const*>(map.get_immovable(w->get_position()))) {
297 Coords pos;
298 map.get_brn(w->get_position(), &pos);
299 if (pos == path.get_start())
300 idx = 0;
301 }
302 }
303
304 if (idx < 0) {
305 reassigned_workers.push_back(w);
306
307 /*
308 * The current worker is not on this road. Search him
309 * in this road and remove him. Than add him to the new road
310 */
311 for (CarrierSlot& old_slot : carrier_slots_) {
312 Carrier const* const carrier = old_slot.carrier.get(game);
313
314 if (carrier == w) {
315 old_slot.carrier = nullptr;
316 for (CarrierSlot& new_slot : newroad.carrier_slots_) {
317 if (!new_slot.carrier.get(game) && !new_slot.carrier_request &&
318 new_slot.second_carrier == old_slot.second_carrier) {
319 upcast(Carrier, new_carrier, w);
320 new_slot.carrier = new_carrier;
321 break;
322 }
323 }
324 }
325 }
326 }
327
328 // Cause a worker update in any case
329 w->send_signal(game, "road");
330 }
331
332 // Initialize the new road
333 newroad.init(game);
334 newroad.wallet_ = wallet_;
335
336 // Actually reassign workers after the new road has initialized,
337 // so that the reassignment is safe
338 for (Worker*& w : reassigned_workers) {
339 w->set_location(&newroad);
340 }
341
342 // Request a new carrier for this road if necessary. This must be done
343 // _after_ the new road initializes, otherwise request routing might not
344 // work correctly
345 for (CarrierSlot& slot : carrier_slots_) {
346 if (!slot.carrier.get(game) && !slot.carrier_request && (!slot.second_carrier || busy_)) {
347 request_carrier(slot);
348 }
349 }
350
351 // Make sure wares waiting on the original endpoint flags are dealt with.
352 flags_[FlagStart]->update_wares(game, &oldend);
353 oldend.update_wares(game, flags_[FlagStart]);
354 }
355
356 /**
357 * Try to pick up a ware from the given flag.
358 * \return true if a carrier has been sent on its way, false otherwise.
359 */
notify_ware(Game & game,FlagId const flagid)360 bool Road::notify_ware(Game& game, FlagId const flagid) {
361 // Iterate over all carriers and try to find one which will take the ware
362 for (CarrierSlot& slot : carrier_slots_) {
363 if (Carrier* const carrier = slot.carrier.get(game)) {
364 if (carrier->notify_ware(game, flagid)) {
365 // The carrier took the ware, so we're done
366 return true;
367 }
368 }
369 }
370 // No carrier took the ware
371 return false;
372 }
373
374 // This returns true if and only if this is road covers the specified edge and
375 // both triangles adjacent to that edge are unwalkable
is_bridge(const EditorGameBase & egbase,const FCoords & field,uint8_t dir) const376 bool Road::is_bridge(const EditorGameBase& egbase, const FCoords& field, uint8_t dir) const {
377 const Map& map = egbase.map();
378
379 FCoords iterate = map.get_fcoords(path_.get_start());
380 const Path::StepVector::size_type nr_steps = path_.get_nsteps();
381 bool found = false;
382 for (Path::StepVector::size_type i = 0; i <= nr_steps; ++i) {
383 if (iterate == field) {
384 if ((i < nr_steps && path_[i] == dir) || (i > 0 && path_[i - 1] == get_reverse_dir(dir))) {
385 found = true;
386 break;
387 }
388 return false;
389 }
390 if (i < nr_steps) {
391 map.get_neighbour(iterate, path_[i], &iterate);
392 }
393 }
394 if (!found) {
395 return false;
396 }
397
398 FCoords fr, fd;
399 switch (dir) {
400 case WALK_SW:
401 fd = field;
402 map.get_ln(field, &fr);
403 break;
404 case WALK_SE:
405 fd = field;
406 fr = field;
407 break;
408 case WALK_NW:
409 map.get_tln(field, &fd);
410 fr = fd;
411 break;
412 case WALK_NE:
413 map.get_trn(field, &fd);
414 map.get_tln(field, &fr);
415 break;
416 case WALK_W:
417 map.get_tln(field, &fd);
418 map.get_ln(field, &fr);
419 break;
420 case WALK_E:
421 map.get_trn(field, &fd);
422 fr = field;
423 break;
424 default:
425 NEVER_HERE();
426 }
427 return (egbase.world().terrain_descr(fd.field->terrain_d()).get_is() &
428 TerrainDescription::Is::kUnwalkable) &&
429 (egbase.world().terrain_descr(fr.field->terrain_r()).get_is() &
430 TerrainDescription::Is::kUnwalkable);
431 }
432
433 /**
434 * Update last_wallet_charge_ with the current gametime.
435 */
update_wallet_chargetime(Game & game)436 void Road::update_wallet_chargetime(Game& game) {
437 last_wallet_charge_ = game.get_gametime();
438 }
439
440 /**
441 * Subtract maintenance cost, and check for demotion.
442 */
charge_wallet(Game & game)443 void Road::charge_wallet(Game& game) {
444 const uint32_t current_gametime = game.get_gametime();
445 assert(last_wallet_charge_ <= current_gametime);
446
447 wallet_ -= carriers_count() * (current_gametime - last_wallet_charge_) / 1000;
448 last_wallet_charge_ = current_gametime;
449
450 if (wallet_ < 0) {
451 wallet_ = 0;
452 if (busy_) {
453 // Demote the road
454 Carrier* const second_carrier = carrier_slots_[1].carrier.get(game);
455 if (second_carrier && second_carrier->top_state().task == &Carrier::taskRoad) {
456 second_carrier->send_signal(game, "cancel");
457 // This signal is not handled in any special way. It will simply pop the task off the
458 // stack. The string "cancel" has been used to clarify the final goal we want to
459 // achieve, ie: cancelling the current task.
460 carrier_slots_[1].carrier = nullptr;
461 carrier_slots_[1].carrier_request = nullptr;
462 busy_ = false;
463 mark_map(game);
464 }
465 }
466 }
467 }
468
wallet() const469 int32_t Road::wallet() const {
470 return wallet_;
471 }
472
add_to_wallet(int32_t sum)473 void Road::add_to_wallet(int32_t sum) {
474 wallet_ += sum;
475 }
476
477 /**
478 * Add carrying payment, and check for promotion.
479 */
pay_for_road(Game & game,uint8_t queue_length)480 void Road::pay_for_road(Game& game, uint8_t queue_length) {
481 wallet_ += 2 * (carriers_count() + 1) * (4 * queue_length + path_.get_nsteps());
482 charge_wallet(game);
483
484 if (!busy_ && wallet_ > 1.5 * kRoadAnimalPrice) {
485 // Promote the road
486 wallet_ -= kRoadAnimalPrice;
487 busy_ = true;
488 flags_[0]->propagate_promoted_road(this);
489 flags_[1]->propagate_promoted_road(this);
490 mark_map(game);
491 for (CarrierSlot& slot : carrier_slots_) {
492 if (!slot.carrier.get(game) && !slot.carrier_request && slot.second_carrier) {
493 request_carrier(slot);
494 }
495 }
496 }
497 wallet_ = std::min(wallet_, kRoadMaxWallet);
498 }
499
500 /**
501 * Add extra coins for street-segment at building.
502 */
pay_for_building()503 void Road::pay_for_building() {
504 wallet_ += 2 * (carriers_count() + 1);
505 // Don't bother with checks here, since the next ware will cause them anyway
506 }
507
log_general_info(const EditorGameBase & egbase) const508 void Road::log_general_info(const EditorGameBase& egbase) const {
509 PlayerImmovable::log_general_info(egbase);
510 molog("wallet: %i\n", wallet_);
511 }
512 } // namespace Widelands
513