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/ware_instance.h"
21 
22 #include <memory>
23 
24 #include "base/macros.h"
25 #include "base/wexception.h"
26 #include "economy/economy.h"
27 #include "economy/flag.h"
28 #include "economy/portdock.h"
29 #include "economy/request.h"
30 #include "economy/ship_fleet.h"
31 #include "economy/transfer.h"
32 #include "io/fileread.h"
33 #include "io/filewrite.h"
34 #include "logic/game.h"
35 #include "logic/map_objects/tribes/ship.h"
36 #include "logic/map_objects/tribes/tribe_descr.h"
37 #include "logic/map_objects/tribes/warehouse.h"
38 #include "logic/map_objects/tribes/worker.h"
39 #include "map_io/map_object_loader.h"
40 #include "map_io/map_object_saver.h"
41 
42 namespace Widelands {
43 
44 /**
45  * Whenever a WareInstance is idle, it issues an IdleWareSupply.
46  */
47 // TODO(unknown): This maybe shouldn't be here.
48 struct IdleWareSupply : public Supply {
49 	explicit IdleWareSupply(WareInstance&);
50 	~IdleWareSupply() override;
51 
52 	void set_economy(Economy*);
53 
54 	//  implementation of Supply
55 	PlayerImmovable* get_position(Game&) override;
56 	bool is_active() const override;
57 	SupplyProviders provider_type(Game*) const override;
58 	bool has_storage() const override;
59 	void get_ware_type(WareWorker& type, DescriptionIndex& ware) const override;
60 	void send_to_storage(Game&, Warehouse* wh) override;
61 
62 	uint32_t nr_supplies(const Game&, const Request&) const override;
63 	WareInstance& launch_ware(Game&, const Request&) override;
64 	Worker& launch_worker(Game&, const Request&) override;
65 
66 private:
67 	WareInstance& ware_;
68 	Economy* economy_;
69 };
70 
71 /**
72  * Initialize the Supply and update the economy.
73  */
IdleWareSupply(WareInstance & ware)74 IdleWareSupply::IdleWareSupply(WareInstance& ware) : ware_(ware), economy_(nullptr) {
75 	set_economy(ware.get_economy());
76 }
77 
78 /**
79  * Cleanup.
80  */
~IdleWareSupply()81 IdleWareSupply::~IdleWareSupply() {
82 	set_economy(nullptr);
83 }
84 
85 /**
86  * Add/remove self from economies as necessary.
87  */
set_economy(Economy * const e)88 void IdleWareSupply::set_economy(Economy* const e) {
89 	if (economy_ != e) {
90 		if (economy_)
91 			economy_->remove_supply(*this);
92 		if ((economy_ = e))
93 			economy_->add_supply(*this);
94 	}
95 }
96 
97 /**
98  * Figure out the player immovable that this ware belongs to.
99  */
get_position(Game & game)100 PlayerImmovable* IdleWareSupply::get_position(Game& game) {
101 	MapObject* const loc = ware_.get_location(game);
102 
103 	if (upcast(PlayerImmovable, playerimmovable, loc))
104 		return playerimmovable;
105 
106 	if (upcast(Worker, worker, loc))
107 		return worker->get_location(game);
108 
109 	if (upcast(Ship, ship, loc)) {
110 		if (PortDock* pd = ship->get_destination()) {
111 			return pd;
112 		}
113 		return ship->get_fleet()->get_arbitrary_dock();
114 	}
115 
116 	return nullptr;
117 }
118 
is_active() const119 bool IdleWareSupply::is_active() const {
120 	return true;
121 }
122 
provider_type(Game * game) const123 SupplyProviders IdleWareSupply::provider_type(Game* game) const {
124 	MapObject* const loc = ware_.get_location(*game);
125 	if (is_a(Ship, loc)) {
126 		return SupplyProviders::kShip;
127 	}
128 	if (upcast(Worker, worker, loc)) {
129 		if (worker->is_shipping()) {
130 			return SupplyProviders::kShip;
131 		}
132 	}
133 	return SupplyProviders::kFlagOrRoad;
134 }
135 
has_storage() const136 bool IdleWareSupply::has_storage() const {
137 	return ware_.is_moving();
138 }
139 
get_ware_type(WareWorker & type,DescriptionIndex & ware) const140 void IdleWareSupply::get_ware_type(WareWorker& type, DescriptionIndex& ware) const {
141 	type = wwWARE;
142 	ware = ware_.descr_index();
143 }
144 
nr_supplies(const Game &,const Request & req) const145 uint32_t IdleWareSupply::nr_supplies(const Game&, const Request& req) const {
146 	if (req.get_type() == wwWARE && req.get_index() == ware_.descr_index())
147 		return 1;
148 
149 	return 0;
150 }
151 
152 /**
153  * The ware is already "launched", so we only need to return it.
154  */
launch_ware(Game &,const Request & req)155 WareInstance& IdleWareSupply::launch_ware(Game&, const Request& req) {
156 	if (req.get_type() != wwWARE)
157 		throw wexception("IdleWareSupply::launch_ware : called for non-ware request");
158 	if (req.get_index() != ware_.descr_index())
159 		throw wexception("IdleWareSupply: ware(%u) (type = %i) requested for %i", ware_.serial(),
160 		                 ware_.descr_index(), req.get_index());
161 
162 	return ware_;
163 }
164 
launch_worker(Game &,const Request &)165 Worker& IdleWareSupply::launch_worker(Game&, const Request&) {
166 	throw wexception("IdleWareSupply::launch_worker makes no sense");
167 }
168 
send_to_storage(Game & game,Warehouse * wh)169 void IdleWareSupply::send_to_storage(Game& game, Warehouse* wh) {
170 	assert(!has_storage());
171 
172 	Transfer* t = new Transfer(game, ware_);
173 	t->set_destination(*wh);
174 	ware_.set_transfer(game, *t);
175 }
176 
177 /*************************************************************************/
178 /*                     Ware Instance Implementation                      */
179 /*************************************************************************/
WareInstance(DescriptionIndex const i,const WareDescr * const ware_descr)180 WareInstance::WareInstance(DescriptionIndex const i, const WareDescr* const ware_descr)
181    : MapObject(ware_descr),
182      economy_(nullptr),
183      descr_index_(i),
184      supply_(nullptr),
185      transfer_(nullptr) {
186 }
187 
~WareInstance()188 WareInstance::~WareInstance() {
189 	if (supply_) {
190 		FORMAT_WARNINGS_OFF
191 		molog("Ware %u still has supply %p\n", descr_index_, supply_);
192 		FORMAT_WARNINGS_ON
193 		delete supply_;
194 	}
195 }
196 
init(EditorGameBase & egbase)197 bool WareInstance::init(EditorGameBase& egbase) {
198 	return MapObject::init(egbase);
199 }
200 
cleanup(EditorGameBase & egbase)201 void WareInstance::cleanup(EditorGameBase& egbase) {
202 	// Unlink from our current location, if necessary
203 	if (upcast(Flag, flag, location_.get(egbase)))
204 		flag->remove_ware(egbase, this);
205 
206 	delete supply_;
207 	supply_ = nullptr;
208 
209 	cancel_moving();
210 	set_location(egbase, nullptr);
211 
212 	MapObject::cleanup(egbase);
213 }
214 
215 /**
216  * Ware accounting
217  */
set_economy(Economy * const e)218 void WareInstance::set_economy(Economy* const e) {
219 	assert(!e || e->type() == wwWARE);
220 	if (descr_index_ == INVALID_INDEX || economy_ == e)
221 		return;
222 
223 	if (economy_)
224 		economy_->remove_wares_or_workers(descr_index_, 1);
225 
226 	economy_ = e;
227 	if (supply_)
228 		supply_->set_economy(e);
229 
230 	if (economy_)
231 		economy_->add_wares_or_workers(descr_index_, 1);
232 }
233 
234 /**
235  * Change the current location.
236  * Once you've assigned a ware to its new location, you usually have to call
237  * \ref update() as well.
238  */
set_location(EditorGameBase & egbase,MapObject * const location)239 void WareInstance::set_location(EditorGameBase& egbase, MapObject* const location) {
240 	MapObject* const oldlocation = location_.get(egbase);
241 
242 	if (oldlocation == location)
243 		return;
244 
245 	location_ = location;
246 
247 	if (location) {
248 		Economy* eco = nullptr;
249 
250 		if (upcast(Flag const, flag, location))
251 			eco = flag->get_economy(wwWARE);
252 		else if (upcast(Worker const, worker, location))
253 			eco = worker->get_economy(wwWARE);
254 		else if (upcast(PortDock const, portdock, location))
255 			eco = portdock->get_economy(wwWARE);
256 		else if (upcast(Ship const, ship, location))
257 			eco = ship->get_economy(wwWARE);
258 		else
259 			throw wexception("WareInstance delivered to bad location %u", location->serial());
260 
261 		if (oldlocation && get_economy()) {
262 			if (get_economy() != eco)
263 				throw wexception("WareInstance::set_location() implies change of economy");
264 		} else {
265 			set_economy(eco);
266 		}
267 	} else {
268 		set_economy(nullptr);
269 	}
270 }
271 
272 /**
273  * Handle delayed updates.
274  */
act(Game & game,uint32_t)275 void WareInstance::act(Game& game, uint32_t) {
276 	update(game);
277 }
278 
279 /**
280  * Performs the state updates necessary for the current location:
281  * - if it's a building, acknowledge the Request or incorporate into warehouse
282  * - if it's a flag and we have no request, start the return to warehouse timer
283  * and issue a Supply
284  *
285  * \note \ref update() may result in the deletion of this object.
286  * \note It is important that this function is idempotent, i.e. calling
287  *       \ref update() twice in a row should have the same effect as calling
288  *       it only once, \em unless the instance is deleted as a side-effect of
289  *       \ref update().
290  */
update(Game & game)291 void WareInstance::update(Game& game) {
292 	if (!descr_)  // Upsy, we're not even initialized. Happens on load
293 		return;
294 
295 	MapObject* const loc = location_.get(game);
296 
297 	// Reset our state if we're not on location or outside an economy
298 	if (!get_economy()) {
299 		cancel_moving();
300 		return;
301 	}
302 
303 	if (!loc) {
304 		// Before dying, output as much information as we can.
305 		log_general_info(game);
306 
307 		// If our location gets lost, our owner is supposed to destroy us
308 		throw wexception("WARE(%u): WareInstance::update has no location\n", serial());
309 	}
310 
311 	// Update whether we have a Supply or not
312 	if (!transfer_ || !transfer_->get_request()) {
313 		if (!supply_) {
314 			supply_ = new IdleWareSupply(*this);
315 		}
316 	} else {
317 		delete supply_;
318 		supply_ = nullptr;
319 	}
320 
321 	// Deal with transfers
322 	if (transfer_) {
323 		upcast(PlayerImmovable, location, loc);
324 
325 		if (!location) {
326 			return;  // wait
327 		}
328 
329 		bool success;
330 		PlayerImmovable* const nextstep = transfer_->get_next_step(location, success);
331 		transfer_nextstep_ = nextstep;
332 
333 		if (!nextstep) {
334 			if (upcast(Flag, flag, location))
335 				flag->call_carrier(game, *this, nullptr);
336 
337 			Transfer* const t = transfer_;
338 
339 			transfer_ = nullptr;
340 			transfer_nextstep_ = nullptr;
341 
342 			if (success) {
343 				t->has_finished();
344 				return;
345 			} else {
346 				t->has_failed();
347 
348 				cancel_moving();
349 				update(game);
350 				return;
351 			}
352 		}
353 
354 		if (upcast(Flag, flag, location)) {
355 			flag->call_carrier(game, *this, dynamic_cast<Building const*>(nextstep) &&
356 			                                      &nextstep->base_flag() != location ?
357 			                                   &nextstep->base_flag() :
358 			                                   nextstep);
359 		} else if (upcast(PortDock, pd, location)) {
360 			pd->update_shippingitem(game, *this);
361 		} else {
362 			throw wexception(
363 			   "Ware_Instance::update in bad type of PlayerImmovable %u", location->serial());
364 		}
365 	}
366 }
367 
368 /**
369  * Called by a worker when it carries the ware into the given building.
370  */
enter_building(Game & game,Building & building)371 void WareInstance::enter_building(Game& game, Building& building) {
372 	if (transfer_) {
373 		if (transfer_->get_destination(game) == &building) {
374 			Transfer* t = transfer_;
375 
376 			transfer_ = nullptr;
377 			transfer_nextstep_ = nullptr;
378 
379 			t->has_finished();
380 			return;
381 		}
382 
383 		bool success;
384 		PlayerImmovable* const nextstep = transfer_->get_next_step(&building, success);
385 		transfer_nextstep_ = nextstep;
386 
387 		if (success) {
388 			assert(nextstep);
389 
390 			if (upcast(PortDock, pd, nextstep)) {
391 				pd->add_shippingitem(game, *this);
392 				return;
393 			}
394 
395 			// There are some situations where we might end up in a warehouse
396 			// as part of a requested route, and we need to move out of it
397 			// again, e.g.:
398 			//  - we were requested just when we were being carried into the
399 			//    warehouse
400 			//  - we were carried into a harbour/warehouse to be
401 			//    shipped across the sea, but a better, land-based route has been
402 			//    found
403 			if (upcast(Warehouse, warehouse, &building)) {
404 				warehouse->do_launch_ware(game, *this);
405 				return;
406 			}
407 
408 			throw wexception(
409 			   "MO(%u): ware(%s): do not know how to move from building %u (%s at (%u,%u)) "
410 			   "to %u (%s) -> not a warehouse!",
411 			   serial(), descr_->name().c_str(), building.serial(), building.descr().name().c_str(),
412 			   building.get_position().x, building.get_position().y, nextstep->serial(),
413 			   nextstep->descr().name().c_str());
414 		} else {
415 			Transfer* t = transfer_;
416 
417 			transfer_ = nullptr;
418 			transfer_nextstep_ = nullptr;
419 
420 			t->has_failed();
421 			cancel_moving();
422 
423 			if (is_a(Warehouse, &building)) {
424 				building.receive_ware(game, descr_index_);
425 				remove(game);
426 			} else {
427 				update(game);
428 			}
429 			return;
430 		}
431 	} else {
432 		// We don't have a transfer, so just enter the building
433 		building.receive_ware(game, descr_index_);
434 		remove(game);
435 	}
436 }
437 
438 /**
439  * Set ware state so that it follows the given transfer.
440  *
441  * \param t the new transfer (non-zero; use \ref cancel_transfer to stop a
442  *          transfer).
443  */
set_transfer(Game & game,Transfer & t)444 void WareInstance::set_transfer(Game& game, Transfer& t) {
445 	transfer_nextstep_ = nullptr;
446 
447 	// Reset current transfer
448 	if (transfer_) {
449 		transfer_->has_failed();
450 		transfer_ = nullptr;
451 	}
452 
453 	// Set transfer state
454 	transfer_ = &t;
455 
456 	delete supply_;
457 	supply_ = nullptr;
458 
459 	// Schedule an update.
460 	// Do not update immediately, because update() could try to reference
461 	// the Transfer object in a way that is not valid yet (note that this
462 	// function is called in the Transfer constructor before the Transfer
463 	// is linked to the corresponding Request).
464 	schedule_act(game, 1, 0);
465 }
466 
467 /**
468  * The transfer has been cancelled, just stop moving.
469  */
cancel_transfer(Game & game)470 void WareInstance::cancel_transfer(Game& game) {
471 	transfer_ = nullptr;
472 	transfer_nextstep_ = nullptr;
473 
474 	update(game);
475 }
476 
477 /**
478  * We are moving when there's a transfer, it's that simple.
479  */
is_moving() const480 bool WareInstance::is_moving() const {
481 	return transfer_;
482 }
483 
484 /**
485  * Call this function if movement + potential request need to be cancelled for
486  * whatever reason.
487  */
cancel_moving()488 void WareInstance::cancel_moving() {
489 	molog("cancel_moving\n");
490 
491 	if (transfer_) {
492 		transfer_->has_failed();
493 		transfer_ = nullptr;
494 		transfer_nextstep_ = nullptr;
495 	}
496 }
497 
498 /**
499  * Return the next flag we should be moving to, or the final target if the route
500  * has been completed successfully.
501  */
get_next_move_step(Game & game)502 PlayerImmovable* WareInstance::get_next_move_step(Game& game) {
503 	return transfer_ ? dynamic_cast<PlayerImmovable*>(transfer_nextstep_.get(game)) : nullptr;
504 }
505 
log_general_info(const EditorGameBase & egbase) const506 void WareInstance::log_general_info(const EditorGameBase& egbase) const {
507 	MapObject::log_general_info(egbase);
508 
509 	molog("Ware: %s\n", descr().name().c_str());
510 	molog("Location: %u\n", location_.serial());
511 }
512 
513 /*
514 ==============================
515 
516 Load/save support
517 
518 ==============================
519 */
520 
521 constexpr uint8_t kCurrentPacketVersion = 2;
522 
load(FileRead & fr)523 void WareInstance::Loader::load(FileRead& fr) {
524 	MapObject::Loader::load(fr);
525 
526 	WareInstance& ware = get<WareInstance>();
527 	location_ = fr.unsigned_32();
528 	transfer_nextstep_ = fr.unsigned_32();
529 	if (fr.unsigned_8()) {
530 		ware.transfer_ = new Transfer(dynamic_cast<Game&>(egbase()), ware);
531 		ware.transfer_->read(fr, transfer_);
532 	}
533 }
534 
load_pointers()535 void WareInstance::Loader::load_pointers() {
536 	MapObject::Loader::load_pointers();
537 
538 	WareInstance& ware = get<WareInstance>();
539 
540 	// There is a race condition where a ware may lose its location and be scheduled
541 	// for removal via the update callback, but the game is saved just before the
542 	// removal. This is why we allow a null location on load.
543 	if (location_)
544 		ware.set_location(egbase(), &mol().get<MapObject>(location_));
545 	if (transfer_nextstep_)
546 		ware.transfer_nextstep_ = &mol().get<MapObject>(transfer_nextstep_);
547 	if (ware.transfer_)
548 		ware.transfer_->read_pointers(mol(), transfer_);
549 }
550 
load_finish()551 void WareInstance::Loader::load_finish() {
552 	MapObject::Loader::load_finish();
553 
554 	WareInstance& ware = get<WareInstance>();
555 	if (!ware.transfer_ || !ware.transfer_->get_request()) {
556 		if (!ware.supply_)
557 			ware.supply_ = new IdleWareSupply(ware);
558 	}
559 }
560 
save(EditorGameBase & egbase,MapObjectSaver & mos,FileWrite & fw)561 void WareInstance::save(EditorGameBase& egbase, MapObjectSaver& mos, FileWrite& fw) {
562 	fw.unsigned_8(HeaderWareInstance);
563 	fw.unsigned_8(kCurrentPacketVersion);
564 	fw.c_string(descr().name());
565 
566 	MapObject::save(egbase, mos, fw);
567 
568 	fw.unsigned_32(mos.get_object_file_index_or_zero(location_.get(egbase)));
569 	fw.unsigned_32(mos.get_object_file_index_or_zero(transfer_nextstep_.get(egbase)));
570 	if (transfer_) {
571 		fw.unsigned_8(1);
572 		transfer_->write(mos, fw);
573 	} else {
574 		fw.unsigned_8(0);
575 	}
576 }
577 
load(EditorGameBase & egbase,MapObjectLoader & mol,FileRead & fr,const TribesLegacyLookupTable & lookup_table)578 MapObject::Loader* WareInstance::load(EditorGameBase& egbase,
579                                       MapObjectLoader& mol,
580                                       FileRead& fr,
581                                       const TribesLegacyLookupTable& lookup_table) {
582 	try {
583 		uint8_t packet_version = fr.unsigned_8();
584 
585 		// Some maps may contain ware info, so we need compatibility here.
586 		if (1 <= packet_version && packet_version <= kCurrentPacketVersion) {
587 			if (packet_version == 1) {
588 				fr.c_string();  // Consume tribe name
589 			}
590 			const std::string warename = lookup_table.lookup_ware(fr.c_string());
591 
592 			DescriptionIndex wareindex = egbase.tribes().ware_index(warename);
593 			const WareDescr* descr = egbase.tribes().get_ware_descr(wareindex);
594 
595 			std::unique_ptr<Loader> loader(new Loader);
596 			loader->init(egbase, mol, *new WareInstance(wareindex, descr));
597 			loader->load(fr);
598 
599 			return loader.release();
600 		} else {
601 			throw UnhandledVersionError("WareInstance", packet_version, kCurrentPacketVersion);
602 		}
603 	} catch (const std::exception& e) {
604 		throw wexception("WareInstance: %s", e.what());
605 	}
606 	NEVER_HERE();
607 }
608 }  // namespace Widelands
609