← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/00_soldier_control into lp:widelands

 

SirVer has proposed merging lp:~widelands-dev/widelands/00_soldier_control into lp:widelands.

Commit message:
Refactor SoldierControl to use composition instead of inheritance.

The interface is still weird, but at least this gets rid of some DynamicCasts. It could potentially modify behavior of MilitarySite, Warehouses (+ Headquarters and ports) or Trainingsites, though nothing of this would be intentional.


Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/00_soldier_control/+merge/325902
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/00_soldier_control into lp:widelands.
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2017-01-28 14:53:28 +0000
+++ src/ai/defaultai.cc	2017-06-19 07:06:15 +0000
@@ -1306,11 +1306,10 @@
 				const int32_t radius = militarysite->descr().get_conquers() + 4;
 
 				if (radius > dist) {
-
-					field.area_military_capacity += militarysite->max_soldier_capacity();
-					field.area_military_presence += militarysite->stationed_soldiers().size();
-
-					if (militarysite->stationed_soldiers().empty()) {
+					field.area_military_capacity += militarysite->soldier_control()->max_soldier_capacity();
+					field.area_military_presence += militarysite->soldier_control()->stationed_soldiers().size();
+
+					if (militarysite->soldier_control()->stationed_soldiers().empty()) {
 						field.military_unstationed += 1;
 					} else {
 						field.military_stationed += 1;
@@ -2811,7 +2810,7 @@
 		bool occupied_military_ = false;
 		Building* b = flag.get_building();
 		if (upcast(MilitarySite, militb, b)) {
-			if (militb->stationed_soldiers().size() > 0) {
+			if (militb->soldier_control()->stationed_soldiers().size() > 0) {
 				occupied_military_ = true;
 			}
 		}

=== modified file 'src/ai/defaultai_warfare.cc'
--- src/ai/defaultai_warfare.cc	2017-05-20 22:42:49 +0000
+++ src/ai/defaultai_warfare.cc	2017-06-19 07:06:15 +0000
@@ -225,7 +225,7 @@
 		if (upcast(MilitarySite, bld, f.field->get_immovable())) {
 			if (player_->is_hostile(bld->owner())) {
 				std::vector<Soldier*> defenders;
-				defenders = bld->present_soldiers();
+				defenders = bld->soldier_control()->present_soldiers();
 				defenders_strength = calculate_strength(defenders);
 
 				flag = &bld->base_flag();
@@ -239,7 +239,7 @@
 			if (player_->is_hostile(Wh->owner())) {
 
 				std::vector<Soldier*> defenders;
-				defenders = Wh->present_soldiers();
+				defenders = Wh->soldier_control()->present_soldiers();
 				defenders_strength = calculate_strength(defenders);
 
 				flag = &Wh->base_flag();
@@ -434,12 +434,13 @@
 	// counting vacant positions
 	vacant_mil_positions_ = 0;
 	for (TrainingSiteObserver tso : trainingsites) {
-		vacant_mil_positions_ +=
-		   10 * (tso.site->soldier_capacity() - tso.site->stationed_soldiers().size());
+		vacant_mil_positions_ += 10 * (tso.site->soldier_control()->soldier_capacity() -
+		                               tso.site->soldier_control()->stationed_soldiers().size());
 		vacant_mil_positions_ += (tso.site->can_start_working()) ? 0 : 10;
 	}
 	for (MilitarySiteObserver mso : militarysites) {
-		vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
+		vacant_mil_positions_ += mso.site->soldier_control()->soldier_capacity() -
+		                         mso.site->soldier_control()->stationed_soldiers().size();
 	}
 }
 
@@ -470,8 +471,8 @@
 	}
 
 	// changing capacity to 0 - this will happen only once.....
-	if (tso.site->soldier_capacity() > 1) {
-		game().send_player_change_soldier_capacity(*ts, -tso.site->soldier_capacity());
+	if (tso.site->soldier_control()->soldier_capacity() > 1) {
+		game().send_player_change_soldier_capacity(*ts, -tso.site->soldier_control()->soldier_capacity());
 		return true;
 	}
 
@@ -512,7 +513,7 @@
 
 	// if soldier capacity is set to 0, we need to find out if the site is
 	// supplied enough to incrase the capacity to 1
-	if (tso.site->soldier_capacity() == 0) {
+	if (tso.site->soldier_control()->soldier_capacity() == 0) {
 
 		// First subsitute wares
 		int32_t filled = 0;
@@ -610,7 +611,7 @@
 		// same economy where the thrown out soldiers can go to.
 
 		if (ms->economy().warehouses().size()) {
-			uint32_t const j = ms->soldier_capacity();
+			uint32_t const j = ms->soldier_control()->soldier_capacity();
 
 			if (MilitarySite::kPrefersRookies != ms->get_soldier_preference()) {
 				game().send_player_militarysite_set_soldier_preference(
@@ -665,8 +666,8 @@
 		if (other_player_accessible(
 		       vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kEnemy)) {
 
-			Quantity const total_capacity = ms->max_soldier_capacity();
-			Quantity const target_capacity = ms->soldier_capacity();
+			Quantity const total_capacity = ms->soldier_control()->max_soldier_capacity();
+			Quantity const target_capacity = ms->soldier_control()->soldier_capacity();
 
 			game().send_player_change_soldier_capacity(*ms, total_capacity - target_capacity);
 			changed = true;
@@ -681,7 +682,7 @@
 			mso.enemies_nearby = true;
 			enemy_last_seen_ = gametime;
 		} else {  // otherwise decrease soldiers
-			uint32_t const j = ms->soldier_capacity();
+			uint32_t const j = ms->soldier_control()->soldier_capacity();
 
 			if (MilitarySite::kPrefersRookies != ms->get_soldier_preference()) {
 				game().send_player_militarysite_set_soldier_preference(
@@ -785,9 +786,9 @@
 
 	for (TrainingSiteObserver& trainingsite_obs : trainingsites) {
 		if (trainingsite_obs.site == &site) {
-			if (trainingsite_obs.site->soldier_capacity() > 0) {
+			if (trainingsite_obs.site->soldier_control()->soldier_capacity() > 0) {
 				game().send_player_change_soldier_capacity(
-				   *trainingsite_obs.site, -trainingsite_obs.site->soldier_capacity());
+				   *trainingsite_obs.site, -trainingsite_obs.site->soldier_control()->soldier_capacity());
 			}
 			return;
 		}

=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc	2017-05-25 13:00:12 +0000
+++ src/logic/map_objects/tribes/building.cc	2017-06-19 07:06:15 +0000
@@ -244,7 +244,8 @@
      leave_time_(0),
      defeating_player_(0),
      seeing_(false),
-     attack_target_(nullptr) {
+     attack_target_(nullptr),
+     soldier_control_(nullptr) {
 }
 
 void Building::load_finish(EditorGameBase& egbase) {
@@ -706,6 +707,11 @@
 	attack_target_ = new_attack_target;
 }
 
+void Building::set_soldier_control(SoldierControl* new_soldier_control) {
+	assert(soldier_control_ == nullptr);
+	soldier_control_ = new_soldier_control;
+}
+
 /**
  * Change whether this building sees its vision range based on workers
  * inside the building.

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2017-05-25 12:31:33 +0000
+++ src/logic/map_objects/tribes/building.h	2017-06-19 07:06:15 +0000
@@ -32,6 +32,7 @@
 #include "logic/map_objects/immovable.h"
 #include "logic/map_objects/tribes/attack_target.h"
 #include "logic/map_objects/tribes/bill_of_materials.h"
+#include "logic/map_objects/tribes/soldiercontrol.h"
 #include "logic/map_objects/tribes/wareworker.h"
 #include "logic/map_objects/tribes/workarea_info.h"
 #include "logic/message.h"
@@ -293,13 +294,21 @@
 	void add_worker(Worker&) override;
 	void remove_worker(Worker&) override;
 
-	// Returns the AttackTarget object associated with this building. If the
-	// building can never be attacked (for example productionsites) this will be
-	// nullptr.
+	// AttackTarget object associated with this building. If the building can
+	// never be attacked (for example productionsites) this will be nullptr.
 	const AttackTarget* attack_target() const {
 		return attack_target_;
 	}
 
+	// SoldierControl object associated with this building. If the building can
+	// not house soldiers (for example productionsites) this will be nullptr.
+	const SoldierControl* soldier_control() const {
+		return soldier_control_;
+	}
+	SoldierControl* mutable_soldier_control() {
+		return soldier_control_;
+	}
+
 	void send_message(Game& game,
 	                  const Message::Type msgtype,
 	                  const std::string& title,
@@ -332,6 +341,7 @@
 
 	void set_seeing(bool see);
 	void set_attack_target(AttackTarget* new_attack_target);
+	void set_soldier_control(SoldierControl* new_soldier_control);
 
 	Coords position_;
 	Flag* flag_;
@@ -359,6 +369,7 @@
 private:
 	std::string statistics_string_;
 	AttackTarget* attack_target_;  // owned by the base classes, set by 'set_attack_target'.
+	SoldierControl* soldier_control_; // owned by the base classes, set by 'set_soldier_control'.
 };
 }
 

=== modified file 'src/logic/map_objects/tribes/militarysite.cc'
--- src/logic/map_objects/tribes/militarysite.cc	2017-06-06 05:26:58 +0000
+++ src/logic/map_objects/tribes/militarysite.cc	2017-06-19 07:06:15 +0000
@@ -43,6 +43,114 @@
 
 namespace Widelands {
 
+// TODO(sirver): This method should probably return a const reference.
+std::vector<Soldier*> MilitarySite::SoldierControl::present_soldiers() const {
+	std::vector<Soldier*> soldiers;
+
+	for (Worker* worker : military_site_->get_workers()) {
+		if (upcast(Soldier, soldier, worker)) {
+			if (military_site_->is_present(*soldier)) {
+				soldiers.push_back(soldier);
+			}
+		}
+	}
+	return soldiers;
+}
+
+// TODO(sirver): This method should probably return a const reference.
+std::vector<Soldier*> MilitarySite::SoldierControl::stationed_soldiers() const {
+	std::vector<Soldier*> soldiers;
+
+	for (Worker* worker : military_site_->get_workers()) {
+		if (upcast(Soldier, soldier, worker)) {
+			soldiers.push_back(soldier);
+		}
+	}
+	return soldiers;
+}
+
+Quantity MilitarySite::SoldierControl::min_soldier_capacity() const {
+	return 1;
+}
+
+Quantity MilitarySite::SoldierControl::max_soldier_capacity() const {
+	return military_site_->descr().get_max_number_of_soldiers();
+}
+
+Quantity MilitarySite::SoldierControl::soldier_capacity() const {
+	return military_site_->capacity_;
+}
+
+void MilitarySite::SoldierControl::set_soldier_capacity(uint32_t const capacity) {
+	assert(min_soldier_capacity() <= capacity);
+	assert(capacity <= max_soldier_capacity());
+	assert(military_site_->capacity_ != capacity);
+	military_site_->capacity_ = capacity;
+	military_site_->update_soldier_request();
+}
+
+void MilitarySite::SoldierControl::drop_soldier(Soldier& soldier) {
+	Game& game = dynamic_cast<Game&>(military_site_->owner().egbase());
+
+	if (!military_site_->is_present(soldier)) {
+		// This can happen when the "drop soldier" player command is delayed
+		// by network delay or a client has bugs.
+		military_site_->molog("MilitarySite::drop_soldier(%u): not present\n", soldier.serial());
+		return;
+	}
+	if (present_soldiers().size() <= min_soldier_capacity()) {
+		military_site_->molog("cannot drop last soldier(s)\n");
+		return;
+	}
+
+	soldier.reset_tasks(game);
+	soldier.start_task_leavebuilding(game, true);
+
+	military_site_->update_soldier_request();
+}
+
+int MilitarySite::SoldierControl::incorporate_soldier(EditorGameBase& egbase, Soldier& s) {
+	if (s.get_location(egbase) != military_site_) {
+		s.set_location(military_site_);
+	}
+
+	// Soldier upgrade is done once the site is full. In soldier upgrade, we
+	// request one new soldier who is better suited than the existing ones.
+	// Normally, I kick out one existing soldier as soon as a new guy starts walking
+	// towards here. However, since that is done via infrequent polling, a new soldier
+	// can sometimes reach the site before such kick-out happens. In those cases, we
+	// should either drop one of the existing soldiers or reject the new guy, to
+	// avoid overstocking this site.
+
+	if (stationed_soldiers().size() > military_site_->descr().get_max_number_of_soldiers()) {
+		return military_site_->incorporate_upgraded_soldier(egbase, s) ? 0 : -1;
+	}
+
+	if (!military_site_->didconquer_) {
+		military_site_->conquer_area(egbase);
+		// Building is now occupied - idle animation should be played
+		military_site_->start_animation(egbase, military_site_->descr().get_animation("idle"));
+
+		if (upcast(Game, game, &egbase)) {
+			military_site_->send_message(
+			   *game, Message::Type::kEconomySiteOccupied, military_site_->descr().descname(),
+			   military_site_->descr().icon_filename(), military_site_->descr().descname(),
+			   military_site_->descr().occupied_str_, true);
+		}
+	}
+
+	if (upcast(Game, game, &egbase)) {
+		// Bind the worker into this house, hide him on the map
+		s.reset_tasks(*game);
+		s.start_task_buildingwork(*game);
+	}
+
+	// Make sure the request count is reduced or the request is deleted.
+	military_site_->update_soldier_request(true);
+
+	return 0;
+}
+
 bool MilitarySite::AttackTarget::can_be_attacked() const {
 	return military_site_->didconquer_;
 }
@@ -63,7 +171,7 @@
 	// We're dealing with a soldier that we might want to keep busy
 	// Now would be the time to implement some player-definable
 	// policy as to how many soldiers are allowed to leave as defenders
-	std::vector<Soldier*> present = military_site_->present_soldiers();
+	std::vector<Soldier*> present = military_site_->soldier_control_.present_soldiers();
 
 	if (1 < present.size()) {
 		for (Soldier* temp_soldier : present) {
@@ -87,7 +195,7 @@
 AttackTarget::AttackResult MilitarySite::AttackTarget::attack(Soldier* enemy) const {
 	Game& game = dynamic_cast<Game&>(military_site_->owner().egbase());
 
-	std::vector<Soldier*> present = military_site_->present_soldiers();
+	std::vector<Soldier*> present = military_site_->soldier_control_.present_soldiers();
 	Soldier* defender = nullptr;
 
 	if (!present.empty()) {
@@ -102,7 +210,7 @@
 	} else {
 		// If one of our stationed soldiers is currently walking into the
 		// building, give us another chance.
-		std::vector<Soldier*> stationed = military_site_->stationed_soldiers();
+		std::vector<Soldier*> stationed = military_site_->soldier_control_.stationed_soldiers();
 		for (Soldier* temp_soldier : stationed) {
 			if (temp_soldier->get_position() == military_site_->get_position()) {
 				defender = temp_soldier;
@@ -233,6 +341,7 @@
 MilitarySite::MilitarySite(const MilitarySiteDescr& ms_descr)
    : Building(ms_descr),
      attack_target_(this),
+	  soldier_control_(this),
      didconquer_(false),
      capacity_(ms_descr.get_max_number_of_soldiers()),
      nexthealtime_(0),
@@ -241,6 +350,7 @@
      doing_upgrade_request_(false) {
 	next_swap_soldiers_time_ = 0;
 	set_attack_target(&attack_target_);
+	set_soldier_control(&soldier_control_);
 }
 
 MilitarySite::~MilitarySite() {
@@ -255,8 +365,8 @@
 */
 void MilitarySite::update_statistics_string(std::string* s) {
 	s->clear();
-	Quantity present = present_soldiers().size();
-	Quantity stationed = stationed_soldiers().size();
+	Quantity present = soldier_control_.present_soldiers().size();
+	Quantity stationed = soldier_control_.stationed_soldiers().size();
 
 	if (present == stationed) {
 		if (capacity_ > stationed) {
@@ -356,60 +466,12 @@
 }
 
 /*
-===============
-Takes one soldier and adds him to ours
-
-returns 0 on succes, -1 if there was no room for this soldier
-===============
-*/
-int MilitarySite::incorporate_soldier(EditorGameBase& egbase, Soldier& s) {
-
-	if (s.get_location(egbase) != this) {
-		s.set_location(this);
-	}
-
-	// Soldier upgrade is done once the site is full. In soldier upgrade, we
-	// request one new soldier who is better suited than the existing ones.
-	// Normally, I kick out one existing soldier as soon as a new guy starts walking
-	// towards here. However, since that is done via infrequent polling, a new soldier
-	// can sometimes reach the site before such kick-out happens. In those cases, we
-	// should either drop one of the existing soldiers or reject the new guy, to
-	// avoid overstocking this site.
-
-	if (stationed_soldiers().size() > descr().get_max_number_of_soldiers()) {
-		return incorporate_upgraded_soldier(egbase, s) ? 0 : -1;
-	}
-
-	if (!didconquer_) {
-		conquer_area(egbase);
-		// Building is now occupied - idle animation should be played
-		start_animation(egbase, descr().get_animation("idle"));
-
-		if (upcast(Game, game, &egbase)) {
-			send_message(*game, Message::Type::kEconomySiteOccupied, descr().descname(),
-			             descr().icon_filename(), descr().descname(), descr().occupied_str_, true);
-		}
-	}
-
-	if (upcast(Game, game, &egbase)) {
-		// Bind the worker into this house, hide him on the map
-		s.reset_tasks(*game);
-		s.start_task_buildingwork(*game);
-	}
-
-	// Make sure the request count is reduced or the request is deleted.
-	update_soldier_request(true);
-
-	return 0;
-}
-
-/*
  * Returns the least wanted soldier -- If player prefers zero-level guys,
  * the most trained soldier is the "weakest guy".
  */
 
 Soldier* MilitarySite::find_least_suited_soldier() {
-	const std::vector<Soldier*> present = present_soldiers();
+	const std::vector<Soldier*> present = soldier_control_.present_soldiers();
 	const int32_t multiplier = kPrefersHeroes == soldier_preference_ ? -1 : 1;
 	int worst_soldier_level = INT_MIN;
 	Soldier* worst_soldier = nullptr;
@@ -439,7 +501,7 @@
  */
 
 bool MilitarySite::drop_least_suited_soldier(bool new_soldier_has_arrived, Soldier* newguy) {
-	const std::vector<Soldier*> present = present_soldiers();
+	const std::vector<Soldier*> present = soldier_control_.present_soldiers();
 
 	// If I have only one soldier, and the  new guy is not here yet, I can't release.
 	if (new_soldier_has_arrived || 1 < present.size()) {
@@ -474,7 +536,7 @@
  */
 bool MilitarySite::incorporate_upgraded_soldier(EditorGameBase& egbase, Soldier& s) {
 	// Call to drop_least routine has side effects: it tries to drop a soldier. Order is important!
-	if (stationed_soldiers().size() < capacity_ || drop_least_suited_soldier(true, &s)) {
+	if (soldier_control_.stationed_soldiers().size() < capacity_ || drop_least_suited_soldier(true, &s)) {
 		Game& game = dynamic_cast<Game&>(egbase);
 		s.set_location(this);
 		s.reset_tasks(game);
@@ -494,7 +556,7 @@
 	MilitarySite& msite = dynamic_cast<MilitarySite&>(target);
 	Soldier& s = dynamic_cast<Soldier&>(*w);
 
-	msite.incorporate_soldier(game, s);
+	msite.soldier_control_.incorporate_soldier(game, s);
 }
 
 /**
@@ -502,8 +564,8 @@
  * as appropriate.
  */
 void MilitarySite::update_normal_soldier_request() {
-	std::vector<Soldier*> present = present_soldiers();
-	Quantity const stationed = stationed_soldiers().size();
+	std::vector<Soldier*> present = soldier_control_.present_soldiers();
+	Quantity const stationed = soldier_control_.stationed_soldiers().size();
 
 	if (stationed < capacity_) {
 		if (!normal_soldier_request_) {
@@ -582,8 +644,8 @@
  */
 
 void MilitarySite::update_soldier_request(bool incd) {
-	const uint32_t capacity = soldier_capacity();
-	const uint32_t stationed = stationed_soldiers().size();
+	const uint32_t capacity = soldier_control_.soldier_capacity();
+	const uint32_t stationed = soldier_control_.stationed_soldiers().size();
 
 	if (doing_upgrade_request_) {
 		if (incd && upgrade_soldier_request_)  // update requests always ask for one soldier at time!
@@ -625,7 +687,7 @@
 			update_normal_soldier_request();
 
 		if ((capacity == stationed) && (!normal_soldier_request_)) {
-			if (present_soldiers().size() == capacity) {
+			if (soldier_control_.present_soldiers().size() == capacity) {
 				doing_upgrade_request_ = true;
 				update_upgrade_soldier_request();
 			}
@@ -668,7 +730,7 @@
 
 	if (nexthealtime_ <= timeofgame) {
 		uint32_t total_heal = descr().get_heal_per_second();
-		std::vector<Soldier*> soldiers = present_soldiers();
+		std::vector<Soldier*> soldiers = soldier_control_.present_soldiers();
 		uint32_t max_total_level = 0;
 		float max_health = 0;
 		Soldier* soldier_to_heal = 0;
@@ -720,7 +782,7 @@
 bool MilitarySite::get_building_work(Game& game, Worker& worker, bool) {
 	if (upcast(Soldier, soldier, &worker)) {
 		// Evict soldiers that have returned home if the capacity is too low
-		if (capacity_ < present_soldiers().size()) {
+		if (capacity_ < soldier_control_.present_soldiers().size()) {
 			worker.reset_tasks(game);
 			worker.start_task_leavebuilding(game, true);
 			return true;
@@ -755,70 +817,6 @@
 	       soldier.get_position() == get_position();
 }
 
-// TODO(sirver): This method should probably return a const reference.
-std::vector<Soldier*> MilitarySite::present_soldiers() const {
-	std::vector<Soldier*> soldiers;
-
-	for (Worker* worker : get_workers()) {
-		if (upcast(Soldier, soldier, worker)) {
-			if (is_present(*soldier)) {
-				soldiers.push_back(soldier);
-			}
-		}
-	}
-	return soldiers;
-}
-
-// TODO(sirver): This method should probably return a const reference.
-std::vector<Soldier*> MilitarySite::stationed_soldiers() const {
-	std::vector<Soldier*> soldiers;
-
-	for (Worker* worker : get_workers()) {
-		if (upcast(Soldier, soldier, worker)) {
-			soldiers.push_back(soldier);
-		}
-	}
-	return soldiers;
-}
-
-Quantity MilitarySite::min_soldier_capacity() const {
-	return 1;
-}
-Quantity MilitarySite::max_soldier_capacity() const {
-	return descr().get_max_number_of_soldiers();
-}
-Quantity MilitarySite::soldier_capacity() const {
-	return capacity_;
-}
-
-void MilitarySite::set_soldier_capacity(uint32_t const capacity) {
-	assert(min_soldier_capacity() <= capacity);
-	assert(capacity <= max_soldier_capacity());
-	assert(capacity_ != capacity);
-	capacity_ = capacity;
-	update_soldier_request();
-}
-
-void MilitarySite::drop_soldier(Soldier& soldier) {
-	Game& game = dynamic_cast<Game&>(owner().egbase());
-
-	if (!is_present(soldier)) {
-		// This can happen when the "drop soldier" player command is delayed
-		// by network delay or a client has bugs.
-		molog("MilitarySite::drop_soldier(%u): not present\n", soldier.serial());
-		return;
-	}
-	if (present_soldiers().size() <= min_soldier_capacity()) {
-		molog("cannot drop last soldier(s)\n");
-		return;
-	}
-
-	soldier.reset_tasks(game);
-	soldier.start_task_leavebuilding(game, true);
-
-	update_soldier_request();
-}
-
 void MilitarySite::conquer_area(EditorGameBase& egbase) {
 	assert(!didconquer_);
 	egbase.conquer_area(PlayerArea<Area<FCoords>>(

=== modified file 'src/logic/map_objects/tribes/militarysite.h'
--- src/logic/map_objects/tribes/militarysite.h	2017-05-20 22:42:49 +0000
+++ src/logic/map_objects/tribes/militarysite.h	2017-06-19 07:06:15 +0000
@@ -68,7 +68,7 @@
 	DISALLOW_COPY_AND_ASSIGN(MilitarySiteDescr);
 };
 
-class MilitarySite : public Building, public SoldierControl {
+class MilitarySite : public Building {
 	friend class MapBuildingdataPacket;
 	MO_DESCR(MilitarySiteDescr)
 
@@ -91,16 +91,6 @@
 	void set_economy(Economy*) override;
 	bool get_building_work(Game&, Worker&, bool success) override;
 
-	// Begin implementation of SoldierControl
-	std::vector<Soldier*> present_soldiers() const override;
-	std::vector<Soldier*> stationed_soldiers() const override;
-	Quantity min_soldier_capacity() const override;
-	Quantity max_soldier_capacity() const override;
-	Quantity soldier_capacity() const override;
-	void set_soldier_capacity(Quantity capacity) override;
-	void drop_soldier(Soldier&) override;
-	int incorporate_soldier(EditorGameBase& game, Soldier& s) override;
-
 	/// Launch the given soldier on an attack towards the given
 	/// target building.
 	void send_attacker(Soldier&, Building&);
@@ -157,7 +147,26 @@
 		MilitarySite* const military_site_;
 	};
 
+	class SoldierControl : public Widelands::SoldierControl {
+	public:
+		explicit SoldierControl(MilitarySite* military_site) : military_site_(military_site) {
+		}
+
+		std::vector<Soldier*> present_soldiers() const override;
+		std::vector<Soldier*> stationed_soldiers() const override;
+		Quantity min_soldier_capacity() const override;
+		Quantity max_soldier_capacity() const override;
+		Quantity soldier_capacity() const override;
+		void set_soldier_capacity(Quantity capacity) override;
+		void drop_soldier(Soldier&) override;
+		int incorporate_soldier(EditorGameBase& game, Soldier& s) override;
+
+	private:
+		MilitarySite* const military_site_;
+	};
+
 	AttackTarget attack_target_;
+	SoldierControl soldier_control_;
 	Requirements soldier_requirements_;  // This is used to grab a bunch of soldiers: Anything goes
 	RequireAttribute soldier_upgrade_requirements_;     // This is used when exchanging soldiers.
 	std::unique_ptr<Request> normal_soldier_request_;   // filling the site

=== modified file 'src/logic/map_objects/tribes/production_program.cc'
--- src/logic/map_objects/tribes/production_program.cc	2017-06-15 22:00:08 +0000
+++ src/logic/map_objects/tribes/production_program.cc	2017-06-19 07:06:15 +0000
@@ -1276,8 +1276,9 @@
 }
 
 void ProductionProgram::ActCheckSoldier::execute(Game& game, ProductionSite& ps) const {
-	SoldierControl& ctrl = dynamic_cast<SoldierControl&>(ps);
-	const std::vector<Soldier*> soldiers = ctrl.present_soldiers();
+	const SoldierControl* ctrl = ps.soldier_control();
+	assert(ctrl != nullptr);
+	const std::vector<Soldier*> soldiers = ctrl->present_soldiers();
 	if (soldiers.empty()) {
 		ps.set_production_result(_("No soldier to train!"));
 		return ps.program_end(game, Skipped);
@@ -1355,8 +1356,8 @@
 }
 
 void ProductionProgram::ActTrain::execute(Game& game, ProductionSite& ps) const {
-	SoldierControl& ctrl = dynamic_cast<SoldierControl&>(ps);
-	const std::vector<Soldier*> soldiers = ctrl.present_soldiers();
+	const SoldierControl* ctrl = ps.soldier_control();;
+	const std::vector<Soldier*> soldiers = ctrl->present_soldiers();
 	const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();
 	std::vector<Soldier*>::const_iterator it = soldiers.begin();
 

=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc	2017-05-21 22:18:02 +0000
+++ src/logic/map_objects/tribes/soldier.cc	2017-06-19 07:06:15 +0000
@@ -832,8 +832,8 @@
 
 	// Count remaining defenders
 	if (enemy) {
-		if (upcast(MilitarySite, ms, enemy)) {
-			defenders = ms->present_soldiers().size();
+		if (enemy->soldier_control() != nullptr) {
+			defenders = enemy->soldier_control()->present_soldiers().size();
 		}
 		if (upcast(Warehouse, wh, enemy)) {
 			Requirements noreq;
@@ -865,18 +865,18 @@
 			BaseImmovable* const newimm = game.map()[state.coords].get_immovable();
 			upcast(MilitarySite, newsite, newimm);
 			if (newsite && (&newsite->owner() == &owner())) {
-				if (upcast(SoldierControl, ctrl, newsite)) {
-					state.objvar1 = nullptr;
-					// We may also have our location destroyed in between
-					if (ctrl->stationed_soldiers().size() < ctrl->soldier_capacity() &&
-					    (!location ||
-					     location->base_flag().get_position() != newsite->base_flag().get_position())) {
-						molog("[attack] enemy belongs to us now, move in\n");
-						pop_task(game);
-						set_location(newsite);
-						newsite->update_soldier_request();
-						return schedule_act(game, 10);
-					}
+				const SoldierControl* soldier_control = newsite->soldier_control();
+				state.objvar1 = nullptr;
+				// We may also have our location destroyed in between
+				if (soldier_control->stationed_soldiers().size() <
+				       soldier_control->soldier_capacity() &&
+				    (!location ||
+				     location->base_flag().get_position() != newsite->base_flag().get_position())) {
+					molog("[attack] enemy belongs to us now, move in\n");
+					pop_task(game);
+					set_location(newsite);
+					newsite->update_soldier_request();
+					return schedule_act(game, 10);
 				}
 			}
 		}

=== modified file 'src/logic/map_objects/tribes/soldiercontrol.h'
--- src/logic/map_objects/tribes/soldiercontrol.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/soldiercontrol.h	2017-06-19 07:06:15 +0000
@@ -41,7 +41,8 @@
  * the two concepts are equal. However, they're different for a MilitarySite,
  * where soldiers can be outside in combat.
  */
-struct SoldierControl {
+class SoldierControl {
+public:
 	/**
 	 * \return a list of soldiers that are currently present in the building.
 	 */
@@ -78,16 +79,6 @@
 	 */
 	virtual void set_soldier_capacity(Quantity capacity) = 0;
 
-	void changeSoldierCapacity(int32_t const difference) {
-		Widelands::Quantity const old_capacity = soldier_capacity();
-		Widelands::Quantity const new_capacity = std::min(
-		   static_cast<Widelands::Quantity>(std::max(static_cast<int32_t>(old_capacity) + difference,
-		                                             static_cast<int32_t>(min_soldier_capacity()))),
-		   max_soldier_capacity());
-		if (old_capacity != new_capacity)
-			set_soldier_capacity(new_capacity);
-	}
-
 	/**
 	 * Evict the given soldier from the building immediately,
 	 * without changing the building's capacity.
@@ -108,7 +99,7 @@
 	 * informed by the soldier when it is removed, but WareHouses for example
 	 * will not.
 	 */
-	virtual int outcorporate_soldier(EditorGameBase&, Soldier&) {
+	virtual int outcorporate_soldier(Soldier&) {
 		return 0;
 	}
 
@@ -116,6 +107,7 @@
 	virtual ~SoldierControl() {
 	}
 };
+
 }
 
 #endif  // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_SOLDIERCONTROL_H

=== modified file 'src/logic/map_objects/tribes/trainingsite.cc'
--- src/logic/map_objects/tribes/trainingsite.cc	2017-04-23 12:11:19 +0000
+++ src/logic/map_objects/tribes/trainingsite.cc	2017-06-19 07:06:15 +0000
@@ -178,6 +178,78 @@
 	}
 }
 
+std::vector<Soldier*> TrainingSite::SoldierControl::present_soldiers() const {
+	return training_site_->soldiers_;
+}
+
+std::vector<Soldier*> TrainingSite::SoldierControl::stationed_soldiers() const {
+	return training_site_->soldiers_;
+}
+
+Quantity TrainingSite::SoldierControl::min_soldier_capacity() const {
+	return 0;
+}
+Quantity TrainingSite::SoldierControl::max_soldier_capacity() const {
+	return training_site_->descr().get_max_number_of_soldiers();
+}
+Quantity TrainingSite::SoldierControl::soldier_capacity() const {
+	return training_site_->capacity_;
+}
+
+void TrainingSite::SoldierControl::set_soldier_capacity(Quantity const capacity) {
+	assert(min_soldier_capacity() <= capacity);
+	assert(capacity <= max_soldier_capacity());
+	assert(training_site_->capacity_ != capacity);
+	training_site_->capacity_ = capacity;
+	training_site_->update_soldier_request();
+}
+
+/**
+ * Drop a given soldier.
+ *
+ * 'Dropping' means releasing the soldier from the site. The soldier then
+ * becomes available to the economy.
+ *
+ * \note This is called from player commands, so we need to verify that the
+ * soldier is actually stationed here, without breaking anything if he isn't.
+ */
+void TrainingSite::SoldierControl::drop_soldier(Soldier& soldier) {
+	Game& game = dynamic_cast<Game&>(training_site_->owner().egbase());
+
+	std::vector<Soldier*>::iterator it = std::find(training_site_->soldiers_.begin(), training_site_->soldiers_.end(), &soldier);
+	if (it == training_site_->soldiers_.end()) {
+		training_site_->molog("TrainingSite::SoldierControl::drop_soldier: soldier not in training site");
+		return;
+	}
+
+	training_site_->soldiers_.erase(it);
+
+	soldier.reset_tasks(game);
+	soldier.start_task_leavebuilding(game, true);
+
+	// Schedule, so that we can call new soldiers on next act()
+	training_site_->schedule_act(game, 100);
+	Notifications::publish(NoteTrainingSiteSoldierTrained(training_site_, training_site_->get_owner()));
+}
+
+int TrainingSite::SoldierControl::incorporate_soldier(EditorGameBase& egbase, Soldier& s) {
+	if (s.get_location(egbase) != training_site_) {
+		if (stationed_soldiers().size() + 1 > training_site_->descr().get_max_number_of_soldiers())
+			return -1;
+
+		s.set_location(training_site_);
+	}
+
+	// Bind the worker into this house, hide him on the map
+	if (upcast(Game, game, &egbase))
+		s.start_task_idle(*game, 0, -1);
+
+	// Make sure the request count is reduced or the request is deleted.
+	training_site_->update_soldier_request();
+
+	return 0;
+}
+
 /*
 =============================
 
@@ -188,10 +260,13 @@
 
 TrainingSite::TrainingSite(const TrainingSiteDescr& d)
    : ProductionSite(d),
+soldier_control_(this),
      soldier_request_(nullptr),
      capacity_(descr().get_max_number_of_soldiers()),
      build_heroes_(false),
      result_(Failed) {
+	set_soldier_control(&soldier_control_);
+
 	// Initialize this in the constructor so that loading code may
 	// overwrite priorities.
 	calc_upgrades();
@@ -329,7 +404,7 @@
 		soldier_request_ = nullptr;
 
 		while (soldiers_.size() > capacity_) {
-			drop_soldier(**soldiers_.rbegin());
+			soldier_control_.drop_soldier(**soldiers_.rbegin());
 		}
 	}
 }
@@ -353,86 +428,7 @@
 	assert(s.get_location(game) == &tsite);
 	assert(tsite.soldier_request_ == &rq);
 
-	tsite.incorporate_soldier(game, s);
-}
-
-/*
-===============
-Takes one soldier and adds him to ours
-
-returns 0 on succes, -1 if there was no room for this soldier
-===============
-*/
-int TrainingSite::incorporate_soldier(EditorGameBase& egbase, Soldier& s) {
-	if (s.get_location(egbase) != this) {
-		if (stationed_soldiers().size() + 1 > descr().get_max_number_of_soldiers())
-			return -1;
-
-		s.set_location(this);
-	}
-
-	// Bind the worker into this house, hide him on the map
-	if (upcast(Game, game, &egbase))
-		s.start_task_idle(*game, 0, -1);
-
-	// Make sure the request count is reduced or the request is deleted.
-	update_soldier_request();
-
-	return 0;
-}
-
-std::vector<Soldier*> TrainingSite::present_soldiers() const {
-	return soldiers_;
-}
-
-std::vector<Soldier*> TrainingSite::stationed_soldiers() const {
-	return soldiers_;
-}
-
-Quantity TrainingSite::min_soldier_capacity() const {
-	return 0;
-}
-Quantity TrainingSite::max_soldier_capacity() const {
-	return descr().get_max_number_of_soldiers();
-}
-Quantity TrainingSite::soldier_capacity() const {
-	return capacity_;
-}
-
-void TrainingSite::set_soldier_capacity(Quantity const capacity) {
-	assert(min_soldier_capacity() <= capacity);
-	assert(capacity <= max_soldier_capacity());
-	assert(capacity_ != capacity);
-	capacity_ = capacity;
-	update_soldier_request();
-}
-
-/**
- * Drop a given soldier.
- *
- * 'Dropping' means releasing the soldier from the site. The soldier then
- * becomes available to the economy.
- *
- * \note This is called from player commands, so we need to verify that the
- * soldier is actually stationed here, without breaking anything if he isn't.
- */
-void TrainingSite::drop_soldier(Soldier& soldier) {
-	Game& game = dynamic_cast<Game&>(owner().egbase());
-
-	std::vector<Soldier*>::iterator it = std::find(soldiers_.begin(), soldiers_.end(), &soldier);
-	if (it == soldiers_.end()) {
-		molog("TrainingSite::drop_soldier: soldier not in training site");
-		return;
-	}
-
-	soldiers_.erase(it);
-
-	soldier.reset_tasks(game);
-	soldier.start_task_leavebuilding(game, true);
-
-	// Schedule, so that we can call new soldiers on next act()
-	schedule_act(game, 100);
-	Notifications::publish(NoteTrainingSiteSoldierTrained(this, get_owner()));
+	tsite.soldier_control_.incorporate_soldier(game, s);
 }
 
 /**
@@ -456,7 +452,7 @@
 	// Drop soldiers only now, so that changes in the soldiers array don't
 	// mess things up
 	for (Soldier* soldier : droplist) {
-		drop_soldier(*soldier);
+		soldier_control_.drop_soldier(*soldier);
 	}
 }
 
@@ -518,7 +514,7 @@
 	// Finally drop the soldier.
 	if (nullptr != soldier_to_drop) {
 		log("TrainingSite::drop_stalled_soldiers: Kicking somebody out.\n");
-		drop_soldier(*soldier_to_drop);
+		soldier_control_.drop_soldier(*soldier_to_drop);
 	}
 }
 

=== modified file 'src/logic/map_objects/tribes/trainingsite.h'
--- src/logic/map_objects/tribes/trainingsite.h	2017-04-23 12:11:19 +0000
+++ src/logic/map_objects/tribes/trainingsite.h	2017-06-19 07:06:15 +0000
@@ -148,7 +148,7 @@
  *        surrounding strongholds, the training site will burn even if it
  *        contains soldiers!
  */
-class TrainingSite : public ProductionSite, public SoldierControl {
+class TrainingSite : public ProductionSite {
 	friend class MapBuildingdataPacket;
 	MO_DESCR(TrainingSiteDescr)
 	friend struct ::TrainingSiteWindow;
@@ -189,17 +189,6 @@
 
 	void set_economy(Economy* e) override;
 
-	// Begin implementation of SoldierControl
-	std::vector<Soldier*> present_soldiers() const override;
-	std::vector<Soldier*> stationed_soldiers() const override;
-	Quantity min_soldier_capacity() const override;
-	Quantity max_soldier_capacity() const override;
-	Quantity soldier_capacity() const override;
-	void set_soldier_capacity(Quantity capacity) override;
-	void drop_soldier(Soldier&) override;
-	int incorporate_soldier(EditorGameBase&, Soldier&) override;
-	// End implementation of SoldierControl
-
 	int32_t get_pri(enum TrainingAttribute atr);
 	void set_pri(enum TrainingAttribute atr, int32_t prio);
 
@@ -212,6 +201,24 @@
 	void program_end(Game&, ProgramResult) override;
 
 private:
+	class SoldierControl : public Widelands::SoldierControl {
+	public:
+		explicit SoldierControl(TrainingSite* training_site) : training_site_(training_site) {
+		}
+
+		std::vector<Soldier*> present_soldiers() const override;
+		std::vector<Soldier*> stationed_soldiers() const override;
+		Quantity min_soldier_capacity() const override;
+		Quantity max_soldier_capacity() const override;
+		Quantity soldier_capacity() const override;
+		void set_soldier_capacity(Quantity capacity) override;
+		void drop_soldier(Soldier&) override;
+		int incorporate_soldier(EditorGameBase& game, Soldier& s) override;
+
+	private:
+		TrainingSite* const training_site_;
+	};
+
 	void update_soldier_request();
 	static void
 	request_soldier_callback(Game&, Request&, DescriptionIndex, Worker*, PlayerImmovable&);
@@ -225,6 +232,7 @@
 	void drop_stalled_soldiers(Game&);
 	Upgrade* get_upgrade(TrainingAttribute);
 
+	SoldierControl soldier_control_;
 	/// Open requests for soldiers. The soldiers can be under way or unavailable
 	Request* soldier_request_;
 

=== modified file 'src/logic/map_objects/tribes/warehouse.cc'
--- src/logic/map_objects/tribes/warehouse.cc	2017-05-20 22:42:49 +0000
+++ src/logic/map_objects/tribes/warehouse.cc	2017-06-19 07:06:15 +0000
@@ -303,21 +303,77 @@
 	}
 }
 
-/*
-==============================
-IMPLEMENTATION
-==============================
-*/
+std::vector<Soldier*> Warehouse::SoldierControl::present_soldiers() const {
+	std::vector<Soldier*> rv;
+	DescriptionIndex const soldier_index = warehouse_->owner().tribe().soldier();
+	IncorporatedWorkers::const_iterator sidx = warehouse_->incorporated_workers_.find(soldier_index);
+
+	if (sidx != warehouse_->incorporated_workers_.end()) {
+		const WorkerList& soldiers = sidx->second;
+		for (Worker* temp_soldier : soldiers) {
+			rv.push_back(static_cast<Soldier*>(temp_soldier));
+		}
+	}
+	return rv;
+}
+
+std::vector<Soldier*> Warehouse::SoldierControl::stationed_soldiers() const {
+	return present_soldiers();
+}
+
+Quantity Warehouse::SoldierControl::min_soldier_capacity() const {
+	return 0;
+}
+
+Quantity Warehouse::SoldierControl::max_soldier_capacity() const {
+	return 4294967295U;
+}
+
+Quantity Warehouse::SoldierControl::soldier_capacity() const {
+	return max_soldier_capacity();
+}
+
+void Warehouse::SoldierControl::set_soldier_capacity(Quantity /* capacity */) {
+	throw wexception("Not implemented for a Warehouse!");
+}
+
+void Warehouse::SoldierControl::drop_soldier(Soldier&) {
+	throw wexception("Not implemented for a Warehouse!");
+}
+
+int Warehouse::SoldierControl::outcorporate_soldier(Soldier& soldier) {
+	DescriptionIndex const soldier_index = warehouse_->owner().tribe().soldier();
+	if (warehouse_->incorporated_workers_.count(soldier_index)) {
+		WorkerList& soldiers = warehouse_->incorporated_workers_[soldier_index];
+
+		WorkerList::iterator i = std::find(soldiers.begin(), soldiers.end(), &soldier);
+
+		soldiers.erase(i);
+		warehouse_->supply_->remove_workers(soldier_index, 1);
+	}
+#ifndef NDEBUG
+	else
+		throw wexception("outcorporate_soldier: soldier not in this warehouse!");
+#endif
+	return 0;
+}
+
+int Warehouse::SoldierControl::incorporate_soldier(EditorGameBase& egbase, Soldier& soldier) {
+	warehouse_->incorporate_worker(egbase, &soldier);
+	return 0;
+}
 
 Warehouse::Warehouse(const WarehouseDescr& warehouse_descr)
    : Building(warehouse_descr),
      attack_target_(this),
+	  soldier_control_(this),
      supply_(new WarehouseSupply(this)),
      next_military_act_(0),
      portdock_(nullptr) {
 	next_stock_remove_act_ = 0;
 	cleanup_in_progress_ = false;
 	set_attack_target(&attack_target_);
+	set_soldier_control(&soldier_control_);
 }
 
 Warehouse::~Warehouse() {
@@ -1287,49 +1343,6 @@
 	return portdock_->expedition_bootstrap()->inputqueue(index, type);
 }
 
-/*
- * SoldierControl implementations
- */
-std::vector<Soldier*> Warehouse::present_soldiers() const {
-	std::vector<Soldier*> rv;
-
-	DescriptionIndex const soldier_index = owner().tribe().soldier();
-	IncorporatedWorkers::const_iterator sidx = incorporated_workers_.find(soldier_index);
-
-	if (sidx != incorporated_workers_.end()) {
-		const WorkerList& soldiers = sidx->second;
-
-		for (Worker* temp_soldier : soldiers) {
-			rv.push_back(static_cast<Soldier*>(temp_soldier));
-		}
-	}
-
-	return rv;
-}
-int Warehouse::incorporate_soldier(EditorGameBase& egbase, Soldier& soldier) {
-	incorporate_worker(egbase, &soldier);
-	return 0;
-}
-
-int Warehouse::outcorporate_soldier(EditorGameBase& /* egbase */, Soldier& soldier) {
-
-	DescriptionIndex const soldier_index = owner().tribe().soldier();
-	if (incorporated_workers_.count(soldier_index)) {
-		WorkerList& soldiers = incorporated_workers_[soldier_index];
-
-		WorkerList::iterator i = std::find(soldiers.begin(), soldiers.end(), &soldier);
-
-		soldiers.erase(i);
-		supply_->remove_workers(soldier_index, 1);
-	}
-#ifndef NDEBUG
-	else
-		throw wexception("outcorporate_soldier: soldier not in this warehouse!");
-#endif
-
-	return 0;
-}
-
 void Warehouse::log_general_info(const EditorGameBase& egbase) {
 	Building::log_general_info(egbase);
 

=== modified file 'src/logic/map_objects/tribes/warehouse.h'
--- src/logic/map_objects/tribes/warehouse.h	2017-05-20 22:42:49 +0000
+++ src/logic/map_objects/tribes/warehouse.h	2017-06-19 07:06:15 +0000
@@ -70,7 +70,7 @@
 	DISALLOW_COPY_AND_ASSIGN(WarehouseDescr);
 };
 
-class Warehouse : public Building, public SoldierControl {
+class Warehouse : public Building {
 	friend class PortDock;
 	friend class MapBuildingdataPacket;
 
@@ -172,29 +172,6 @@
 	void insert_workers(DescriptionIndex, Quantity count);
 	void remove_workers(DescriptionIndex, Quantity count);
 
-	/* SoldierControl implementation */
-	std::vector<Soldier*> present_soldiers() const override;
-	std::vector<Soldier*> stationed_soldiers() const override {
-		return present_soldiers();
-	}
-	Quantity min_soldier_capacity() const override {
-		return 0;
-	}
-	Quantity max_soldier_capacity() const override {
-		return 4294967295U;
-	}
-	Quantity soldier_capacity() const override {
-		return max_soldier_capacity();
-	}
-	void set_soldier_capacity(Quantity /* capacity */) override {
-		throw wexception("Not implemented for a Warehouse!");
-	}
-	void drop_soldier(Soldier&) override {
-		throw wexception("Not implemented for a Warehouse!");
-	}
-	int outcorporate_soldier(EditorGameBase&, Soldier&) override;
-	int incorporate_soldier(EditorGameBase&, Soldier& soldier) override;
-
 	bool fetch_from_flag(Game&) override;
 
 	Quantity count_workers(const Game&, DescriptionIndex worker, const Requirements&, Match);
@@ -241,6 +218,25 @@
 	void log_general_info(const EditorGameBase&) override;
 
 private:
+	class SoldierControl : public Widelands::SoldierControl {
+	public:
+		explicit SoldierControl(Warehouse* warehouse) : warehouse_(warehouse) {
+		}
+
+		std::vector<Soldier*> present_soldiers() const override;
+		std::vector<Soldier*> stationed_soldiers() const override;
+		Quantity min_soldier_capacity() const override;
+		Quantity max_soldier_capacity() const override;
+		Quantity soldier_capacity() const override;
+		void set_soldier_capacity(Quantity capacity) override;
+		void drop_soldier(Soldier&) override;
+		int incorporate_soldier(EditorGameBase& game, Soldier& s) override;
+		int outcorporate_soldier(Soldier&) override;
+
+	private:
+		Warehouse* const warehouse_;
+	};
+
 	// A warehouse that conquers space can also be attacked.
 	class AttackTarget : public Widelands::AttackTarget {
 	public:
@@ -286,6 +282,7 @@
 	void update_all_planned_workers(Game&);
 
 	AttackTarget attack_target_;
+	SoldierControl soldier_control_;
 	WarehouseSupply* supply_;
 
 	std::vector<StockPolicy> ware_policy_;

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2017-05-25 12:58:01 +0000
+++ src/logic/player.cc	2017-06-19 07:06:15 +0000
@@ -19,6 +19,7 @@
 
 #include "logic/player.h"
 
+#include <cassert>
 #include <memory>
 
 #include <boost/bind.hpp>
@@ -806,8 +807,12 @@
 		return;
 	if (soldier.descr().type() != MapObjectType::SOLDIER)
 		return;
-	if (upcast(SoldierControl, ctrl, &imm))
-		ctrl->drop_soldier(soldier);
+	if (upcast(Building, building, &imm)) {
+		SoldierControl* soldier_control = building->mutable_soldier_control();
+		if (soldier_control != nullptr) {
+			soldier_control->drop_soldier(soldier);
+		}
+	}
 }
 
 /*
@@ -842,9 +847,10 @@
 
 	for (BaseImmovable* temp_flag : flags) {
 		upcast(Flag, attackerflag, temp_flag);
-		upcast(MilitarySite, ms, attackerflag->get_building());
-		std::vector<Soldier*> const present = ms->present_soldiers();
-		uint32_t const nr_staying = ms->min_soldier_capacity();
+		const SoldierControl* soldier_control = attackerflag->get_building()->soldier_control();
+		assert(soldier_control != nullptr);
+		std::vector<Soldier*> const present = soldier_control->present_soldiers();
+		uint32_t const nr_staying = soldier_control->min_soldier_capacity();
 		uint32_t const nr_present = present.size();
 		if (nr_staying < nr_present) {
 			uint32_t const nr_taken = std::min(nr_wanted, nr_present - nr_staying);

=== modified file 'src/logic/playercommand.cc'
--- src/logic/playercommand.cc	2017-02-12 09:10:57 +0000
+++ src/logic/playercommand.cc	2017-06-19 07:06:15 +0000
@@ -1514,10 +1514,20 @@
 }
 
 void CmdChangeSoldierCapacity::execute(Game& game) {
-	if (upcast(Building, building, game.objects().get_object(serial)))
-		if (&building->owner() == game.get_player(sender()))
-			if (upcast(SoldierControl, ctrl, building))
-				ctrl->changeSoldierCapacity(val);
+	if (upcast(Building, building, game.objects().get_object(serial))) {
+		if (&building->owner() == game.get_player(sender()) &&
+		    building->soldier_control() != nullptr) {
+			SoldierControl* soldier_control = building->mutable_soldier_control();
+			Widelands::Quantity const old_capacity = soldier_control->soldier_capacity();
+			Widelands::Quantity const new_capacity =
+			   std::min(static_cast<Widelands::Quantity>(
+			               std::max(static_cast<int32_t>(old_capacity) + val,
+			                        static_cast<int32_t>(soldier_control->min_soldier_capacity()))),
+			            soldier_control->max_soldier_capacity());
+			if (old_capacity != new_capacity)
+				soldier_control->set_soldier_capacity(new_capacity);
+		}
+	}
 }
 
 void CmdChangeSoldierCapacity::serialize(StreamWrite& ser) {

=== modified file 'src/map_io/map_buildingdata_packet.cc'
--- src/map_io/map_buildingdata_packet.cc	2017-02-12 09:10:57 +0000
+++ src/map_io/map_buildingdata_packet.cc	2017-06-19 07:06:15 +0000
@@ -525,20 +525,20 @@
 		//  Cmd_ChangeSoldierCapacity to the beginning of the game's command
 		//  queue. But that would not work because the command queue is not read
 		//  yet and will be cleared before it is read.
-		if (militarysite.capacity_ < militarysite.min_soldier_capacity()) {
+		if (militarysite.capacity_ < militarysite.soldier_control()->min_soldier_capacity()) {
 			log("WARNING: militarysite %u of player %u at (%i, %i) has capacity "
 			    "set to %u but it must be at least %u. Changing to that value.\n",
 			    militarysite.serial(), militarysite.owner().player_number(),
 			    militarysite.get_position().x, militarysite.get_position().y, militarysite.capacity_,
-			    militarysite.min_soldier_capacity());
-			militarysite.capacity_ = militarysite.min_soldier_capacity();
-		} else if (militarysite.max_soldier_capacity() < militarysite.capacity_) {
+			    militarysite.soldier_control()->min_soldier_capacity());
+			militarysite.capacity_ = militarysite.soldier_control()->min_soldier_capacity();
+		} else if (militarysite.soldier_control()->max_soldier_capacity() < militarysite.capacity_) {
 			log("WARNING: militarysite %u of player %u at (%i, %i) has capacity "
 			    "set to %u but it can be at most %u. Changing to that value.\n",
 			    militarysite.serial(), militarysite.owner().player_number(),
 			    militarysite.get_position().x, militarysite.get_position().y, militarysite.capacity_,
-			    militarysite.max_soldier_capacity());
-			militarysite.capacity_ = militarysite.max_soldier_capacity();
+			    militarysite.soldier_control()->max_soldier_capacity());
+			militarysite.capacity_ = militarysite.soldier_control()->max_soldier_capacity();
 		}
 	} catch (const WException& e) {
 		throw GameDataError("militarysite: %s", e.what());
@@ -800,20 +800,20 @@
 		//  Cmd_ChangeSoldierCapacity to the beginning of the game's command
 		//  queue. But that would not work because the command queue is not read
 		//  yet and will be cleared before it is read.
-		if (trainingsite.capacity_ < trainingsite.min_soldier_capacity()) {
+		if (trainingsite.capacity_ < trainingsite.soldier_control()->min_soldier_capacity()) {
 			log("WARNING: trainingsite %u of player %u at (%i, %i) has capacity "
 			    "set to %u but it must be at least %u. Changing to that value.\n",
 			    trainingsite.serial(), trainingsite.owner().player_number(),
 			    trainingsite.get_position().x, trainingsite.get_position().y, trainingsite.capacity_,
-			    trainingsite.min_soldier_capacity());
-			trainingsite.capacity_ = trainingsite.min_soldier_capacity();
-		} else if (trainingsite.max_soldier_capacity() < trainingsite.capacity_) {
+			    trainingsite.soldier_control()->min_soldier_capacity());
+			trainingsite.capacity_ = trainingsite.soldier_control()->min_soldier_capacity();
+		} else if (trainingsite.soldier_control()->max_soldier_capacity() < trainingsite.capacity_) {
 			log("WARNING: trainingsite %u of player %u at (%i, %i) has capacity "
 			    "set to %u but it can be at most %u. Changing to that value.\n",
 			    trainingsite.serial(), trainingsite.owner().player_number(),
 			    trainingsite.get_position().x, trainingsite.get_position().y, trainingsite.capacity_,
-			    trainingsite.max_soldier_capacity());
-			trainingsite.capacity_ = trainingsite.max_soldier_capacity();
+			    trainingsite.soldier_control()->max_soldier_capacity());
+			trainingsite.capacity_ = trainingsite.soldier_control()->max_soldier_capacity();
 		}
 	} catch (const WException& e) {
 		throw GameDataError("trainingsite: %s", e.what());

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2017-04-05 14:19:14 +0000
+++ src/scripting/lua_map.cc	2017-06-19 07:06:15 +0000
@@ -573,7 +573,7 @@
 					                   s->get_defense_level(), s->get_evade_level());
 
 					if (is == sp.first) {
-						sc->outcorporate_soldier(egbase, *s);
+						sc->outcorporate_soldier(*s);
 						s->remove(egbase);
 						++d;
 						break;
@@ -4654,13 +4654,13 @@
 // documented in parent class
 int LuaWarehouse::get_soldiers(lua_State* L) {
 	Warehouse* wh = get(L, get_egbase(L));
-	return do_get_soldiers(L, *wh, wh->owner().tribe());
+	return do_get_soldiers(L, *wh->soldier_control(), wh->owner().tribe());
 }
 
 // documented in parent class
 int LuaWarehouse::set_soldiers(lua_State* L) {
 	Warehouse* wh = get(L, get_egbase(L));
-	return do_set_soldiers(L, wh->get_position(), wh, wh->get_owner());
+	return do_set_soldiers(L, wh->get_position(), wh->mutable_soldier_control(), wh->get_owner());
 }
 
 /* RST
@@ -4923,7 +4923,7 @@
 
 // documented in parent class
 int LuaMilitarySite::get_max_soldiers(lua_State* L) {
-	lua_pushuint32(L, get(L, get_egbase(L))->soldier_capacity());
+	lua_pushuint32(L, get(L, get_egbase(L))->soldier_control()->soldier_capacity());
 	return 1;
 }
 
@@ -4936,13 +4936,13 @@
 // documented in parent class
 int LuaMilitarySite::get_soldiers(lua_State* L) {
 	MilitarySite* ms = get(L, get_egbase(L));
-	return do_get_soldiers(L, *ms, ms->owner().tribe());
+	return do_get_soldiers(L, *ms->soldier_control(), ms->owner().tribe());
 }
 
 // documented in parent class
 int LuaMilitarySite::set_soldiers(lua_State* L) {
 	MilitarySite* ms = get(L, get_egbase(L));
-	return do_set_soldiers(L, ms->get_position(), ms, ms->get_owner());
+	return do_set_soldiers(L, ms->get_position(), ms->mutable_soldier_control(), ms->get_owner());
 }
 
 /*
@@ -4977,7 +4977,7 @@
 
 // documented in parent class
 int LuaTrainingSite::get_max_soldiers(lua_State* L) {
-	lua_pushuint32(L, get(L, get_egbase(L))->soldier_capacity());
+	lua_pushuint32(L, get(L, get_egbase(L))->soldier_control()->soldier_capacity());
 	return 1;
 }
 
@@ -4990,13 +4990,13 @@
 // documented in parent class
 int LuaTrainingSite::get_soldiers(lua_State* L) {
 	TrainingSite* ts = get(L, get_egbase(L));
-	return do_get_soldiers(L, *ts, ts->owner().tribe());
+	return do_get_soldiers(L, *ts->soldier_control(), ts->owner().tribe());
 }
 
 // documented in parent class
 int LuaTrainingSite::set_soldiers(lua_State* L) {
 	TrainingSite* ts = get(L, get_egbase(L));
-	return do_set_soldiers(L, ts->get_position(), ts, ts->get_owner());
+	return do_set_soldiers(L, ts->get_position(), ts->mutable_soldier_control(), ts->get_owner());
 }
 
 /*

=== modified file 'src/wui/building_statistics_menu.cc'
--- src/wui/building_statistics_menu.cc	2017-04-03 17:29:50 +0000
+++ src/wui/building_statistics_menu.cc	2017-06-19 07:06:15 +0000
@@ -422,8 +422,9 @@
 				if (!stats_vector[last_building_index_].is_constructionsite) {
 					if (upcast(MilitarySite, militarysite,
 					           map[stats_vector[last_building_index_].pos].get_immovable())) {
-						if (militarysite->stationed_soldiers().size() <
-						    militarysite->soldier_capacity()) {
+						auto* soldier_control = militarysite->soldier_control();
+						if (soldier_control->stationed_soldiers().size() <
+						    soldier_control->soldier_capacity()) {
 							found = true;
 							break;
 						}
@@ -442,8 +443,10 @@
 				if (!stats_vector[last_building_index_].is_constructionsite) {
 					if (upcast(MilitarySite, militarysite,
 					           map[stats_vector[last_building_index_].pos].get_immovable())) {
-						if (militarysite->stationed_soldiers().size() <
-						    militarysite->soldier_capacity()) {
+						auto* soldier_control = militarysite->soldier_control();
+						assert(soldier_control != nullptr);
+						if (soldier_control->stationed_soldiers().size() <
+						    soldier_control->soldier_capacity()) {
 							found = true;
 							break;
 						}
@@ -461,7 +464,9 @@
 		if (!found) {  // Now look at the old
 			if (upcast(MilitarySite, militarysite,
 			           map[stats_vector[last_building_index_].pos].get_immovable())) {
-				if (militarysite->stationed_soldiers().size() < militarysite->soldier_capacity()) {
+				auto* soldier_control = militarysite->soldier_control();
+				assert(soldier_control != nullptr);
+				if (soldier_control->stationed_soldiers().size() < soldier_control->soldier_capacity()) {
 					found = true;
 				}
 			} else if (upcast(ProductionSite, productionsite,
@@ -585,9 +590,10 @@
 						++nr_unproductive;
 					}
 				} else if (building.type() == MapObjectType::MILITARYSITE) {
-					MilitarySite& militarysite = dynamic_cast<MilitarySite&>(immovable);
-					total_soldier_capacity += militarysite.soldier_capacity();
-					total_stationed_soldiers += militarysite.stationed_soldiers().size();
+					const SoldierControl* soldier_control = dynamic_cast<Building&>(immovable).soldier_control();
+					assert(soldier_control != nullptr);
+					total_soldier_capacity += soldier_control->soldier_capacity();
+					total_stationed_soldiers += soldier_control->stationed_soldiers().size();
 					if (total_stationed_soldiers < total_soldier_capacity) {
 						++nr_unproductive;
 					}

=== modified file 'src/wui/soldiercapacitycontrol.cc'
--- src/wui/soldiercapacitycontrol.cc	2017-02-25 13:27:40 +0000
+++ src/wui/soldiercapacitycontrol.cc	2017-06-19 07:06:15 +0000
@@ -97,9 +97,8 @@
 }
 
 void SoldierCapacityControl::think() {
-
-	SoldierControl* soldiers = dynamic_cast<SoldierControl*>(&building_);
-
+	const SoldierControl* soldiers = building_.soldier_control();
+	assert(soldiers != nullptr);
 	uint32_t const capacity = soldiers->soldier_capacity();
 	value_.set_text(boost::lexical_cast<std::string>(capacity));
 

=== modified file 'src/wui/soldierlist.cc'
--- src/wui/soldierlist.cc	2017-05-18 21:26:29 +0000
+++ src/wui/soldierlist.cc	2017-06-19 07:06:15 +0000
@@ -94,7 +94,7 @@
 	};
 
 	Widelands::EditorGameBase& egbase_;
-	SoldierControl& soldiers_;
+	const SoldierControl* soldier_control_;
 
 	SoldierFn mouseover_fn_;
 	SoldierFn click_fn_;
@@ -115,13 +115,14 @@
                            Widelands::Building& building)
    : Panel(&parent, 0, 0, 0, 0),
      egbase_(gegbase),
-     soldiers_(*dynamic_cast<SoldierControl*>(&building)),
+     soldier_control_(building.soldier_control()),
      last_animate_time_(0) {
+		  assert(soldier_control_ != nullptr);
 	Soldier::calc_info_icon_size(building.owner().tribe(), icon_width_, icon_height_);
 	icon_width_ += 2 * kIconBorder;
 	icon_height_ += 2 * kIconBorder;
 
-	Widelands::Quantity maxcapacity = soldiers_.max_soldier_capacity();
+	Widelands::Quantity maxcapacity = soldier_control_->max_soldier_capacity();
 	if (maxcapacity <= kMaxColumns) {
 		cols_ = maxcapacity;
 		rows_ = 1;
@@ -137,7 +138,7 @@
 	// Initialize the icons
 	uint32_t row = 0;
 	uint32_t col = 0;
-	for (Soldier* soldier : soldiers_.present_soldiers()) {
+	for (Soldier* soldier : soldier_control_->present_soldiers()) {
 		Icon icon;
 		icon.soldier = soldier;
 		icon.row = row;
@@ -168,10 +169,10 @@
 
 void SoldierPanel::think() {
 	bool changes = false;
-	uint32_t capacity = soldiers_.soldier_capacity();
+	uint32_t capacity = soldier_control_->soldier_capacity();
 
 	// Update soldier list and target row/col:
-	std::vector<Soldier*> soldierlist = soldiers_.present_soldiers();
+	std::vector<Soldier*> soldierlist = soldier_control_->present_soldiers();
 	std::vector<uint32_t> row_occupancy;
 	row_occupancy.resize(rows_);
 
@@ -276,7 +277,7 @@
 
 void SoldierPanel::draw(RenderTarget& dst) {
 	// Fill a region matching the current site capacity with black
-	uint32_t capacity = soldiers_.soldier_capacity();
+	uint32_t capacity = soldier_control_->soldier_capacity();
 	uint32_t fullrows = capacity / kMaxColumns;
 
 	if (fullrows) {
@@ -347,7 +348,7 @@
 struct SoldierList : UI::Box {
 	SoldierList(UI::Panel& parent, InteractiveGameBase& igb, Widelands::Building& building);
 
-	SoldierControl& soldiers() const;
+	const SoldierControl* soldiers() const;
 
 private:
 	void mouseover(const Soldier* soldier);
@@ -424,8 +425,8 @@
 	add(buttons, UI::Box::Resizing::kFullSize);
 }
 
-SoldierControl& SoldierList::soldiers() const {
-	return *dynamic_cast<SoldierControl*>(&building_);
+const SoldierControl* SoldierList::soldiers() const {
+	return building_.soldier_control();
 }
 
 void SoldierList::think() {
@@ -464,9 +465,9 @@
 }
 
 void SoldierList::eject(const Soldier* soldier) {
-	uint32_t const capacity_min = soldiers().min_soldier_capacity();
+	uint32_t const capacity_min = soldiers()->min_soldier_capacity();
 	bool can_act = igbase_.can_act(building_.owner().player_number());
-	bool over_min = capacity_min < soldiers().present_soldiers().size();
+	bool over_min = capacity_min < soldiers()->present_soldiers().size();
 
 	if (can_act && over_min)
 		igbase_.game().send_player_drop_soldier(building_, soldier->serial());


References