← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1678598-expedition-no-eco-crash into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1678598-expedition-no-eco-crash into lp:widelands.

Commit message:
Only cancel expedition if there is a reachable portdock.
- Reset the expedition state to "Waiting" if no portdock could be found
- Show a warning message to owner
- AI will continue exploring

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1678598 in widelands: "END OF EXPEDITION due to time-out schip.cc assertion failed "
  https://bugs.launchpad.net/widelands/+bug/1678598

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1678598-expedition-no-eco-crash/+merge/323004
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1678598-expedition-no-eco-crash into lp:widelands.
=== modified file 'src/ai/defaultai_seafaring.cc'
--- src/ai/defaultai_seafaring.cc	2017-01-28 14:53:28 +0000
+++ src/ai/defaultai_seafaring.cc	2017-04-23 12:22:45 +0000
@@ -19,6 +19,8 @@
 
 #include "ai/defaultai.h"
 
+#include "economy/fleet.h"
+
 using namespace Widelands;
 
 // this scores spot for potential colony
@@ -358,6 +360,14 @@
 		log("%d: %s at %3dx%3d: END OF EXPEDITION due to time-out\n", pn,
 		    so.ship->get_shipname().c_str(), so.ship->get_position().x, so.ship->get_position().y);
 
+		// In case there is no port left to get back to, continue exploring
+		if (!so.ship->get_fleet() || !so.ship->get_fleet()->has_ports()) {
+			log("%d: %s at %3dx%3d: END OF EXPEDITION without port, continue exploring\n", pn,
+				 so.ship->get_shipname().c_str(), so.ship->get_position().x, so.ship->get_position().y);
+			persistent_data->expedition_start_time = gametime;
+			return;
+		}
+
 		// For known and running expedition
 	} else {
 		// set persistent_data->colony_scan_area based on elapsed expedition time

=== modified file 'src/economy/flag.cc'
--- src/economy/flag.cc	2017-01-25 18:55:59 +0000
+++ src/economy/flag.cc	2017-04-23 12:22:45 +0000
@@ -652,12 +652,13 @@
 	always_call_for_flag_ = nullptr;
 }
 
-void Flag::init(EditorGameBase& egbase) {
+bool Flag::init(EditorGameBase& egbase) {
 	PlayerImmovable::init(egbase);
 
 	set_position(egbase, position_);
 
 	animstart_ = egbase.get_gametime();
+	return true;
 }
 
 /**

=== modified file 'src/economy/flag.h'
--- src/economy/flag.h	2017-01-25 18:55:59 +0000
+++ src/economy/flag.h	2017-04-23 12:22:45 +0000
@@ -142,7 +142,7 @@
 	void log_general_info(const EditorGameBase&) override;
 
 protected:
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	void draw(uint32_t gametime,

=== modified file 'src/economy/fleet.cc'
--- src/economy/fleet.cc	2017-01-25 18:55:59 +0000
+++ src/economy/fleet.cc	2017-04-23 12:22:45 +0000
@@ -93,19 +93,22 @@
  * Initialize the fleet, including a search through the map
  * to rejoin with the next other fleet we can find.
  */
-void Fleet::init(EditorGameBase& egbase) {
+bool Fleet::init(EditorGameBase& egbase) {
 	MapObject::init(egbase);
 
 	if (ships_.empty() && ports_.empty()) {
 		molog("Empty fleet initialized; disband immediately\n");
 		remove(egbase);
-		return;
+		return false;
 	}
 
 	find_other_fleet(egbase);
 
-	if (active())
+	if (active()) {
 		update(egbase);
+		return true;
+	}
+	return false;
 }
 
 struct StepEvalFindFleet {
@@ -564,6 +567,10 @@
 	}
 }
 
+bool Fleet::has_ports() {
+	return !ports_.empty();
+}
+
 /**
  * Search among the docks of the fleet for the one that has the given flag as its base.
  *

=== modified file 'src/economy/fleet.h'
--- src/economy/fleet.h	2017-01-25 18:55:59 +0000
+++ src/economy/fleet.h	2017-04-23 12:22:45 +0000
@@ -91,7 +91,7 @@
 
 	bool active() const;
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void update(EditorGameBase&);
 
@@ -99,6 +99,7 @@
 	void remove_ship(EditorGameBase& egbase, Ship* ship);
 	void add_port(EditorGameBase& egbase, PortDock* port);
 	void remove_port(EditorGameBase& egbase, PortDock* port);
+	bool has_ports();
 
 	void log_general_info(const EditorGameBase&) override;
 

=== modified file 'src/economy/portdock.cc'
--- src/economy/portdock.cc	2017-02-22 20:23:49 +0000
+++ src/economy/portdock.cc	2017-04-23 12:22:45 +0000
@@ -144,7 +144,7 @@
 	// do nothing
 }
 
-void PortDock::init(EditorGameBase& egbase) {
+bool PortDock::init(EditorGameBase& egbase) {
 	PlayerImmovable::init(egbase);
 
 	for (const Coords& coords : dockpoints_) {
@@ -152,6 +152,7 @@
 	}
 
 	init_fleet(egbase);
+	return true;
 }
 
 /**

=== modified file 'src/economy/portdock.h'
--- src/economy/portdock.h	2017-01-25 18:55:59 +0000
+++ src/economy/portdock.h	2017-04-23 12:22:45 +0000
@@ -102,7 +102,7 @@
 	          float scale,
 	          RenderTarget* dst) override;
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	void add_neighbours(std::vector<RoutingNodeNeighbour>& neighbours);

=== modified file 'src/economy/road.cc'
--- src/economy/road.cc	2017-01-25 18:55:59 +0000
+++ src/economy/road.cc	2017-04-23 12:22:45 +0000
@@ -222,11 +222,12 @@
 /**
  * Initialize the road.
 */
-void Road::init(EditorGameBase& egbase) {
+bool Road::init(EditorGameBase& egbase) {
 	PlayerImmovable::init(egbase);
 
 	if (2 <= path_.get_nsteps())
 		link_into_flags(egbase);
+	return true;
 }
 
 /**

=== modified file 'src/economy/road.h'
--- src/economy/road.h	2017-01-25 18:55:59 +0000
+++ src/economy/road.h	2017-04-23 12:22:45 +0000
@@ -115,7 +115,7 @@
 	void log_general_info(const EditorGameBase&) override;
 
 protected:
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	void draw(uint32_t gametime,

=== modified file 'src/economy/ware_instance.cc'
--- src/economy/ware_instance.cc	2017-01-25 18:55:59 +0000
+++ src/economy/ware_instance.cc	2017-04-23 12:22:45 +0000
@@ -192,8 +192,8 @@
 	}
 }
 
-void WareInstance::init(EditorGameBase& egbase) {
-	MapObject::init(egbase);
+bool WareInstance::init(EditorGameBase& egbase) {
+	return MapObject::init(egbase);
 }
 
 void WareInstance::cleanup(EditorGameBase& egbase) {

=== modified file 'src/economy/ware_instance.h'
--- src/economy/ware_instance.h	2017-01-25 18:55:59 +0000
+++ src/economy/ware_instance.h	2017-04-23 12:22:45 +0000
@@ -72,7 +72,7 @@
 		return descr_index_;
 	}
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 	void update(Game&);

=== modified file 'src/logic/map_objects/bob.cc'
--- src/logic/map_objects/bob.cc	2017-02-25 20:03:40 +0000
+++ src/logic/map_objects/bob.cc	2017-04-23 12:22:45 +0000
@@ -118,7 +118,7 @@
  *
  * \note Make sure you call this from derived classes!
  */
-void Bob::init(EditorGameBase& egbase) {
+bool Bob::init(EditorGameBase& egbase) {
 	MapObject::init(egbase);
 
 	if (upcast(Game, game, &egbase))
@@ -126,6 +126,7 @@
 	else
 		// In editor: play idle task forever
 		set_animation(egbase, descr().get_animation("idle"));
+	return true;
 }
 
 /**

=== modified file 'src/logic/map_objects/bob.h'
--- src/logic/map_objects/bob.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/bob.h	2017-04-23 12:22:45 +0000
@@ -235,7 +235,7 @@
 		return animstart_;
 	}
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 	void schedule_destroy(Game&);

=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc	2017-03-02 12:21:57 +0000
+++ src/logic/map_objects/immovable.cc	2017-04-23 12:22:45 +0000
@@ -395,7 +395,7 @@
 /**
  * Actually initialize the immovable.
 */
-void Immovable::init(EditorGameBase& egbase) {
+bool Immovable::init(EditorGameBase& egbase) {
 	BaseImmovable::init(egbase);
 
 	set_position(egbase, position_);
@@ -412,6 +412,7 @@
 	if (upcast(Game, game, &egbase)) {
 		switch_program(*game, "program");
 	}
+	return true;
 }
 
 /**
@@ -1289,8 +1290,8 @@
 /**
  * Initialize the immovable.
 */
-void PlayerImmovable::init(EditorGameBase& egbase) {
-	BaseImmovable::init(egbase);
+bool PlayerImmovable::init(EditorGameBase& egbase) {
+	return BaseImmovable::init(egbase);
 }
 
 /**

=== modified file 'src/logic/map_objects/immovable.h'
--- src/logic/map_objects/immovable.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/immovable.h	2017-04-23 12:22:45 +0000
@@ -224,7 +224,7 @@
 		increment_program_pointer();
 	}
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 	void draw(uint32_t gametime,
@@ -383,7 +383,7 @@
 	void set_owner(Player*) override;
 
 protected:
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 private:

=== modified file 'src/logic/map_objects/map_object.cc'
--- src/logic/map_objects/map_object.cc	2017-02-23 19:38:51 +0000
+++ src/logic/map_objects/map_object.cc	2017-04-23 12:22:45 +0000
@@ -439,8 +439,9 @@
  *
  * \warning Make sure you call this from derived classes!
  */
-void MapObject::init(EditorGameBase& egbase) {
+bool MapObject::init(EditorGameBase& egbase) {
 	egbase.objects().insert(this);
+	return true;
 }
 
 /**

=== modified file 'src/logic/map_objects/map_object.h'
--- src/logic/map_objects/map_object.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/map_object.h	2017-04-23 12:22:45 +0000
@@ -401,7 +401,7 @@
 	/// Called only when the oject is logically created in the simulation. If
 	/// called again, such as when the object is loaded from a savegame, it will
 	/// cause bugs.
-	virtual void init(EditorGameBase&);
+	virtual bool init(EditorGameBase&);
 
 	virtual void cleanup(EditorGameBase&);
 

=== modified file 'src/logic/map_objects/tribes/battle.cc'
--- src/logic/map_objects/tribes/battle.cc	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/battle.cc	2017-04-23 12:22:45 +0000
@@ -74,7 +74,7 @@
 	init(game);
 }
 
-void Battle::init(EditorGameBase& egbase) {
+bool Battle::init(EditorGameBase& egbase) {
 	MapObject::init(egbase);
 
 	creationtime_ = egbase.get_gametime();
@@ -89,6 +89,7 @@
 		battle->cancel(game, *second_);
 	}
 	second_->set_battle(game, this);
+	return true;
 }
 
 void Battle::cleanup(EditorGameBase& egbase) {

=== modified file 'src/logic/map_objects/tribes/battle.h'
--- src/logic/map_objects/tribes/battle.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/battle.h	2017-04-23 12:22:45 +0000
@@ -52,7 +52,7 @@
 	Battle(Game&, Soldier&, Soldier&);  //  to create a new battle in the game
 
 	// Implements MapObject.
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	bool has_new_save_support() override {
 		return true;

=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc	2017-03-02 12:21:57 +0000
+++ src/logic/map_objects/tribes/building.cc	2017-04-23 12:22:45 +0000
@@ -313,7 +313,7 @@
 derived class' init.
 ===============
 */
-void Building::init(EditorGameBase& egbase) {
+bool Building::init(EditorGameBase& egbase) {
 	PlayerImmovable::init(egbase);
 
 	// Set the building onto the map
@@ -351,6 +351,7 @@
 		start_animation(egbase, descr().get_animation("idle"));
 
 	leave_time_ = egbase.get_gametime();
+	return true;
 }
 
 void Building::cleanup(EditorGameBase& egbase) {

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2017-02-14 21:49:40 +0000
+++ src/logic/map_objects/tribes/building.h	2017-04-23 12:22:45 +0000
@@ -311,7 +311,7 @@
 
 	void start_animation(EditorGameBase&, uint32_t anim);
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 

=== modified file 'src/logic/map_objects/tribes/constructionsite.cc'
--- src/logic/map_objects/tribes/constructionsite.cc	2017-03-02 12:21:57 +0000
+++ src/logic/map_objects/tribes/constructionsite.cc	2017-04-23 12:22:45 +0000
@@ -110,7 +110,7 @@
 Initialize the construction site by starting orders
 ===============
 */
-void ConstructionSite::init(EditorGameBase& egbase) {
+bool ConstructionSite::init(EditorGameBase& egbase) {
 	PartiallyFinishedBuilding::init(egbase);
 
 	const std::map<DescriptionIndex, uint8_t>* buildcost;
@@ -139,6 +139,7 @@
 
 		work_steps_ += it->second;
 	}
+	return true;
 }
 
 /*

=== modified file 'src/logic/map_objects/tribes/constructionsite.h'
--- src/logic/map_objects/tribes/constructionsite.h	2017-02-10 15:41:31 +0000
+++ src/logic/map_objects/tribes/constructionsite.h	2017-04-23 12:22:45 +0000
@@ -96,7 +96,7 @@
 		return *building_;
 	}
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	bool burn_on_destroy() override;

=== modified file 'src/logic/map_objects/tribes/dismantlesite.cc'
--- src/logic/map_objects/tribes/dismantlesite.cc	2017-02-28 12:59:39 +0000
+++ src/logic/map_objects/tribes/dismantlesite.cc	2017-04-23 12:22:45 +0000
@@ -106,7 +106,7 @@
 Initialize the construction site by starting orders
 ===============
 */
-void DismantleSite::init(EditorGameBase& egbase) {
+bool DismantleSite::init(EditorGameBase& egbase) {
 	PartiallyFinishedBuilding::init(egbase);
 
 	for (const auto& ware : count_returned_wares(this)) {
@@ -115,6 +115,7 @@
 		wares_.push_back(wq);
 		work_steps_ += ware.second;
 	}
+	return true;
 }
 
 /*

=== modified file 'src/logic/map_objects/tribes/dismantlesite.h'
--- src/logic/map_objects/tribes/dismantlesite.h	2017-02-10 15:41:31 +0000
+++ src/logic/map_objects/tribes/dismantlesite.h	2017-04-23 12:22:45 +0000
@@ -74,7 +74,7 @@
 	              Building::FormerBuildings& former_buildings);
 
 	bool burn_on_destroy() override;
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 
 	bool get_building_work(Game&, Worker&, bool success) override;
 

=== modified file 'src/logic/map_objects/tribes/militarysite.cc'
--- src/logic/map_objects/tribes/militarysite.cc	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/militarysite.cc	2017-04-23 12:22:45 +0000
@@ -148,7 +148,7 @@
 	        .str();
 }
 
-void MilitarySite::init(EditorGameBase& egbase) {
+bool MilitarySite::init(EditorGameBase& egbase) {
 	Building::init(egbase);
 
 	upcast(Game, game, &egbase);
@@ -167,6 +167,7 @@
 	nexthealtime_ = egbase.get_gametime() + 1000;
 	if (game)
 		schedule_act(*game, 1000);
+	return true;
 }
 
 /**

=== modified file 'src/logic/map_objects/tribes/militarysite.h'
--- src/logic/map_objects/tribes/militarysite.h	2017-02-10 15:41:31 +0000
+++ src/logic/map_objects/tribes/militarysite.h	2017-04-23 12:22:45 +0000
@@ -84,7 +84,7 @@
 	MilitarySite(const MilitarySiteDescr&);
 	virtual ~MilitarySite();
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 	void remove_worker(Worker&) override;

=== modified file 'src/logic/map_objects/tribes/partially_finished_building.cc'
--- src/logic/map_objects/tribes/partially_finished_building.cc	2017-02-28 12:59:39 +0000
+++ src/logic/map_objects/tribes/partially_finished_building.cc	2017-04-23 12:22:45 +0000
@@ -66,13 +66,14 @@
 	Building::cleanup(egbase);
 }
 
-void PartiallyFinishedBuilding::init(EditorGameBase& egbase) {
+bool PartiallyFinishedBuilding::init(EditorGameBase& egbase) {
 	Building::init(egbase);
 
 	if (upcast(Game, game, &egbase))
 		request_builder(*game);
 
 	Notifications::publish(NoteSound("create_construction_site", position_, 255));
+	return true;
 }
 
 /*

=== modified file 'src/logic/map_objects/tribes/partially_finished_building.h'
--- src/logic/map_objects/tribes/partially_finished_building.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/partially_finished_building.h	2017-04-23 12:22:45 +0000
@@ -47,7 +47,7 @@
 	uint32_t get_playercaps() const override;
 	const Image* representative_image() const override;
 	void cleanup(EditorGameBase&) override;
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void set_economy(Economy*) override;
 
 	uint32_t get_nrwaresqueues() {

=== modified file 'src/logic/map_objects/tribes/productionsite.cc'
--- src/logic/map_objects/tribes/productionsite.cc	2017-02-22 20:23:49 +0000
+++ src/logic/map_objects/tribes/productionsite.cc	2017-04-23 12:22:45 +0000
@@ -411,7 +411,7 @@
 /**
  * Initialize the production site.
  */
-void ProductionSite::init(EditorGameBase& egbase) {
+bool ProductionSite::init(EditorGameBase& egbase) {
 	Building::init(egbase);
 
 	const BillOfMaterials& input_wares = descr().input_wares();
@@ -440,6 +440,7 @@
 
 	if (upcast(Game, game, &egbase))
 		try_start_working(*game);
+	return true;
 }
 
 /**

=== modified file 'src/logic/map_objects/tribes/productionsite.h'
--- src/logic/map_objects/tribes/productionsite.h	2017-02-14 20:24:36 +0000
+++ src/logic/map_objects/tribes/productionsite.h	2017-04-23 12:22:45 +0000
@@ -205,7 +205,7 @@
 
 	InputQueue& inputqueue(DescriptionIndex, WareWorker) override;
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 

=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc	2017-02-22 20:23:49 +0000
+++ src/logic/map_objects/tribes/ship.cc	2017-04-23 12:22:45 +0000
@@ -152,7 +152,7 @@
 	start_task_ship(game);
 }
 
-void Ship::init(EditorGameBase& egbase) {
+bool Ship::init(EditorGameBase& egbase) {
 	Bob::init(egbase);
 	init_fleet(egbase);
 	Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kGained));
@@ -161,6 +161,7 @@
 	// Assigning a ship name
 	shipname_ = get_owner()->pick_shipname();
 	molog("New ship: %s\n", shipname_.c_str());
+	return true;
 }
 
 /**
@@ -168,11 +169,11 @@
  * The fleet code will automatically merge us into a larger
  * fleet, if one is reachable.
  */
-void Ship::init_fleet(EditorGameBase& egbase) {
+bool Ship::init_fleet(EditorGameBase& egbase) {
 	assert(get_owner() != nullptr);
 	Fleet* fleet = new Fleet(*get_owner());
 	fleet->add_ship(this);
-	fleet->init(egbase);
+	return fleet->init(egbase);
 	// fleet calls the set_fleet function appropriately
 }
 
@@ -898,7 +899,6 @@
 
 	if ((ship_state_ == ShipStates::kExpeditionColonizing) || !state_is_expedition())
 		return;
-	send_signal(game, "cancel_expedition");
 
 	// The workers were hold in an idle state so that they did not try
 	// to become fugitive or run to the next warehouse. But now, we
@@ -920,8 +920,27 @@
 	// Bring us back into a fleet and a economy.
 	set_economy(game, nullptr);
 	init_fleet(game);
+	if (!get_fleet() || !get_fleet()->has_ports()) {
+		// We lost our last reachable port, so we reset the expedition's state
+		ship_state_ = ShipStates::kExpeditionWaiting;
+		set_economy(game, expedition_->economy.get());
+
+		worker = nullptr;
+		for (ShippingItem& item : items_) {
+			item.get(game, nullptr, &worker);
+			if (worker) {
+				worker->reset_tasks(game);
+				worker->start_task_idle(game, 0, -1);
+			}
+		}
+
+		Notifications::publish(NoteShipWindow(serial(), NoteShipWindow::Action::kNoPortLeft));
+		return;
+	}
 	assert(get_economy() && get_economy() != expedition_->economy.get());
 
+	send_signal(game, "cancel_expedition");
+
 	// Delete the expedition and the economy it created.
 	expedition_.reset(nullptr);
 

=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h	2017-02-22 20:23:49 +0000
+++ src/logic/map_objects/tribes/ship.h	2017-04-23 12:22:45 +0000
@@ -59,7 +59,7 @@
 
 	Serial serial;
 
-	enum class Action { kRefresh, kClose };
+	enum class Action { kRefresh, kClose, kNoPortLeft };
 	const Action action;
 
 	NoteShipWindow(Serial init_serial, const Action& init_action)
@@ -120,7 +120,7 @@
 
 	void init_auto_task(Game&) override;
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	void start_task_ship(Game&);
@@ -264,7 +264,7 @@
 	void ship_update_expedition(Game&, State&);
 	void ship_update_idle(Game&, State&);
 
-	void init_fleet(EditorGameBase&);
+	bool init_fleet(EditorGameBase&);
 	void set_fleet(Fleet* fleet);
 
 	void send_message(Game& game,

=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc	2017-01-31 07:18:19 +0000
+++ src/logic/map_objects/tribes/soldier.cc	2017-04-23 12:22:45 +0000
@@ -234,7 +234,7 @@
 	combat_walkend_ = 0;
 }
 
-void Soldier::init(EditorGameBase& egbase) {
+bool Soldier::init(EditorGameBase& egbase) {
 	health_level_ = 0;
 	attack_level_ = 0;
 	defense_level_ = 0;
@@ -247,7 +247,7 @@
 	combat_walkstart_ = 0;
 	combat_walkend_ = 0;
 
-	Worker::init(egbase);
+	return Worker::init(egbase);
 }
 
 void Soldier::cleanup(EditorGameBase& egbase) {

=== modified file 'src/logic/map_objects/tribes/soldier.h'
--- src/logic/map_objects/tribes/soldier.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/soldier.h	2017-04-23 12:22:45 +0000
@@ -175,7 +175,7 @@
 public:
 	Soldier(const SoldierDescr&);
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	void set_level(uint32_t health, uint32_t attack, uint32_t defense, uint32_t evade);

=== modified file 'src/logic/map_objects/tribes/trainingsite.cc'
--- src/logic/map_objects/tribes/trainingsite.cc	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/trainingsite.cc	2017-04-23 12:22:45 +0000
@@ -218,7 +218,7 @@
 /**
  * Setup the building and request soldiers
  */
-void TrainingSite::init(EditorGameBase& egbase) {
+bool TrainingSite::init(EditorGameBase& egbase) {
 	ProductionSite::init(egbase);
 
 	upcast(Game, game, &egbase);
@@ -232,6 +232,7 @@
 		}
 	}
 	update_soldier_request();
+	return true;
 }
 
 /**

=== modified file 'src/logic/map_objects/tribes/trainingsite.h'
--- src/logic/map_objects/tribes/trainingsite.h	2017-02-10 15:41:31 +0000
+++ src/logic/map_objects/tribes/trainingsite.h	2017-04-23 12:22:45 +0000
@@ -169,7 +169,7 @@
 public:
 	TrainingSite(const TrainingSiteDescr&);
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 

=== modified file 'src/logic/map_objects/tribes/warehouse.cc'
--- src/logic/map_objects/tribes/warehouse.cc	2017-02-18 23:30:16 +0000
+++ src/logic/map_objects/tribes/warehouse.cc	2017-04-23 12:22:45 +0000
@@ -383,7 +383,7 @@
 	}
 }
 
-void Warehouse::init(EditorGameBase& egbase) {
+bool Warehouse::init(EditorGameBase& egbase) {
 	Building::init(egbase);
 
 	Player& player = owner();
@@ -448,6 +448,7 @@
 		}
 	}
 	cleanup_in_progress_ = false;
+	return true;
 }
 
 void Warehouse::init_containers(Player& player) {

=== modified file 'src/logic/map_objects/tribes/warehouse.h'
--- src/logic/map_objects/tribes/warehouse.h	2017-02-22 20:23:49 +0000
+++ src/logic/map_objects/tribes/warehouse.h	2017-04-23 12:22:45 +0000
@@ -147,7 +147,7 @@
 	/// * Conquers land if the the warehouse type is configured to do that.
 	/// * Sends a message to the player about the creation of this warehouse.
 	/// * Sets up @ref PortDock for ports
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 
 	void cleanup(EditorGameBase&) override;
 

=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc	2017-02-28 12:59:39 +0000
+++ src/logic/map_objects/tribes/worker.cc	2017-04-23 12:22:45 +0000
@@ -1071,7 +1071,7 @@
 /**
  * Initialize the worker
  */
-void Worker::init(EditorGameBase& egbase) {
+bool Worker::init(EditorGameBase& egbase) {
 	Bob::init(egbase);
 
 	// a worker should always start out at a fixed location
@@ -1081,6 +1081,7 @@
 
 	if (upcast(Game, game, &egbase))
 		create_needed_experience(*game);
+	return true;
 }
 
 /**

=== modified file 'src/logic/map_objects/tribes/worker.h'
--- src/logic/map_objects/tribes/worker.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/worker.h	2017-04-23 12:22:45 +0000
@@ -115,7 +115,7 @@
 	void schedule_incorporate(Game&);
 	void incorporate(Game&);
 
-	void init(EditorGameBase&) override;
+	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
 	bool wakeup_flag_capacity(Game&, Flag&);

=== modified file 'src/wui/shipwindow.cc'
--- src/wui/shipwindow.cc	2017-02-25 13:27:40 +0000
+++ src/wui/shipwindow.cc	2017-04-23 12:22:45 +0000
@@ -27,6 +27,7 @@
 #include "logic/map_objects/tribes/worker.h"
 #include "logic/player.h"
 #include "ui_basic/box.h"
+#include "ui_basic/messagebox.h"
 #include "wui/actionconfirm.h"
 #include "wui/game_debug_ui.h"
 #include "wui/interactive_player.h"
@@ -57,6 +58,20 @@
 	   [this](const Widelands::NoteShipWindow& note) {
 		   if (note.serial == ship_.serial()) {
 			   switch (note.action) {
+			   // Unable to cancel the expedition
+			   case Widelands::NoteShipWindow::Action::kNoPortLeft:
+				if (upcast(InteractiveGameBase, igamebase, ship_.get_owner()->egbase().get_ibase())) {
+					if (igamebase->can_act(ship_.get_owner()->player_number())) {
+						UI::WLMessageBox messagebox(
+						   get_parent(),
+						   /** TRANSLATORS: Window label when an expedition can't be canceled */
+						   _("Cancel expedition"), _("This expedition can’t be canceled, because the "
+						                             "ship has no port to return to."),
+						   UI::WLMessageBox::MBoxType::kOk);
+						messagebox.run<UI::Panel::Returncodes>();
+					}
+				}
+				break;
 			   // The ship state has changed, e.g. expedition canceled
 			   case Widelands::NoteShipWindow::Action::kRefresh:
 				   init(true);


Follow ups