1 /*
2  * Copyright (C) 2002-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 "logic/player.h"
21 
22 #include <cassert>
23 #include <memory>
24 
25 #include <boost/algorithm/string.hpp>
26 
27 #include "base/i18n.h"
28 #include "base/log.h"
29 #include "base/macros.h"
30 #include "base/warning.h"
31 #include "base/wexception.h"
32 #include "economy/economy.h"
33 #include "economy/expedition_bootstrap.h"
34 #include "economy/flag.h"
35 #include "economy/road.h"
36 #include "economy/waterway.h"
37 #include "io/fileread.h"
38 #include "io/filewrite.h"
39 #include "logic/cmd_delete_message.h"
40 #include "logic/cmd_luacoroutine.h"
41 #include "logic/game.h"
42 #include "logic/game_data_error.h"
43 #include "logic/map_objects/checkstep.h"
44 #include "logic/map_objects/findimmovable.h"
45 #include "logic/map_objects/tribes/building.h"
46 #include "logic/map_objects/tribes/constructionsite.h"
47 #include "logic/map_objects/tribes/militarysite.h"
48 #include "logic/map_objects/tribes/soldier.h"
49 #include "logic/map_objects/tribes/soldiercontrol.h"
50 #include "logic/map_objects/tribes/trainingsite.h"
51 #include "logic/map_objects/tribes/tribe_basic_info.h"
52 #include "logic/map_objects/tribes/warehouse.h"
53 #include "logic/playercommand.h"
54 #include "scripting/lua_table.h"
55 #include "sound/note_sound.h"
56 #include "sound/sound_handler.h"
57 #include "wui/interactive_player.h"
58 
59 namespace {
60 
terraform_for_building(Widelands::EditorGameBase & egbase,const Widelands::PlayerNumber player_number,const Widelands::Coords & location,const Widelands::BuildingDescr * descr)61 void terraform_for_building(Widelands::EditorGameBase& egbase,
62                             const Widelands::PlayerNumber player_number,
63                             const Widelands::Coords& location,
64                             const Widelands::BuildingDescr* descr) {
65 	const Widelands::Map& map = egbase.map();
66 	Widelands::FCoords c[4];  //  Big buildings occupy 4 locations.
67 	c[0] = map.get_fcoords(location);
68 	map.get_brn(c[0], &c[1]);
69 	if (Widelands::BaseImmovable* const immovable = c[0].field->get_immovable())
70 		immovable->remove(egbase);
71 	{
72 		size_t nr_locations = 1;
73 		if ((descr->get_size() & Widelands::BUILDCAPS_SIZEMASK) == Widelands::BUILDCAPS_BIG) {
74 			nr_locations = 4;
75 			map.get_trn(c[0], &c[1]);
76 			map.get_tln(c[0], &c[2]);
77 			map.get_ln(c[0], &c[3]);
78 		}
79 		for (size_t i = 0; i < nr_locations; ++i) {
80 			//  Make sure that the player owns the area around.
81 			egbase.conquer_area_no_building(Widelands::PlayerArea<Widelands::Area<Widelands::FCoords>>(
82 			   player_number, Widelands::Area<Widelands::FCoords>(c[i], 1)));
83 
84 			if (Widelands::BaseImmovable* const immovable = c[i].field->get_immovable())
85 				immovable->remove(egbase);
86 		}
87 	}
88 }
89 }  // namespace
90 
91 namespace Widelands {
92 
93 /**
94  * Find the longest possible enhancement chain leading to the given
95  * building descr. The FormerBuildings given in reference must be empty and will be
96  * filled with the BuildingDescr.
97  */
find_former_buildings(const Tribes & tribes,const Widelands::DescriptionIndex bi,Widelands::FormerBuildings * former_buildings)98 void find_former_buildings(const Tribes& tribes,
99                            const Widelands::DescriptionIndex bi,
100                            Widelands::FormerBuildings* former_buildings) {
101 	assert(former_buildings && former_buildings->empty());
102 	former_buildings->push_back(std::make_pair(bi, ""));
103 
104 	for (;;) {
105 		Widelands::DescriptionIndex oldest_idx = former_buildings->front().first;
106 		const Widelands::BuildingDescr* oldest = tribes.get_building_descr(oldest_idx);
107 		if (!oldest->is_enhanced()) {
108 			break;
109 		}
110 		for (DescriptionIndex i = 0; i < tribes.nrbuildings(); ++i) {
111 			const BuildingDescr* building_descr = tribes.get_building_descr(i);
112 			if (building_descr->enhancement() == oldest_idx) {
113 				former_buildings->insert(former_buildings->begin(), std::make_pair(i, ""));
114 				break;
115 			}
116 		}
117 	}
118 }
119 
Player(EditorGameBase & the_egbase,PlayerNumber const plnum,uint8_t const initialization_index,const TribeDescr & tribe_descr,const std::string & name)120 Player::Player(EditorGameBase& the_egbase,
121                PlayerNumber const plnum,
122                uint8_t const initialization_index,
123                const TribeDescr& tribe_descr,
124                const std::string& name)
125    : egbase_(the_egbase),
126      initialization_index_(initialization_index),
127      team_number_(0),
128      team_player_uptodate_(false),
129      see_all_(false),
130      player_number_(plnum),
131      tribe_(tribe_descr),
132      casualties_(0),
133      kills_(0),
134      msites_lost_(0),
135      msites_defeated_(0),
136      civil_blds_lost_(0),
137      civil_blds_defeated_(0),
138      ship_name_counter_(0),
139      fields_(nullptr),
140      allowed_worker_types_(the_egbase.tribes().nrworkers(), true),
141      allowed_building_types_(the_egbase.tribes().nrbuildings(), true),
142      current_produced_statistics_(the_egbase.tribes().nrwares()),
143      current_consumed_statistics_(the_egbase.tribes().nrwares()),
144      ware_productions_(the_egbase.tribes().nrwares()),
145      ware_consumptions_(the_egbase.tribes().nrwares()),
146      ware_stocks_(the_egbase.tribes().nrwares()),
147      message_fx_(SoundHandler::register_fx(SoundType::kMessage, "sound/message")),
148      attack_fx_(SoundHandler::register_fx(SoundType::kMessage, "sound/military/under_attack")),
149      occupied_fx_(SoundHandler::register_fx(SoundType::kMessage, "sound/military/site_occupied")) {
150 	set_name(name);
151 
152 	// Disallow workers that the player's tribe doesn't have.
153 	for (size_t worker_index = 0; worker_index < allowed_worker_types_.size(); ++worker_index) {
154 		if (!tribe().has_worker(static_cast<DescriptionIndex>(worker_index))) {
155 			allowed_worker_types_[worker_index] = false;
156 		}
157 	}
158 
159 	// Disallow buildings that the player's tribe doesn't have and
160 	// that aren't militarysites that the tribe could conquer.
161 	for (size_t i = 0; i < allowed_building_types_.size(); ++i) {
162 		const DescriptionIndex& building_index = static_cast<DescriptionIndex>(i);
163 		const BuildingDescr& descr = *tribe().get_building_descr(building_index);
164 		if (!tribe().has_building(building_index) && descr.type() != MapObjectType::MILITARYSITE) {
165 			allowed_building_types_[i] = false;
166 		}
167 	}
168 
169 	// Subscribe to NoteImmovables.
170 	immovable_subscriber_ =
171 	   Notifications::subscribe<NoteImmovable>([this](const NoteImmovable& note) {
172 		   if (note.pi->owner().player_number() == player_number()) {
173 			   if (upcast(Building, building, note.pi))
174 				   update_building_statistics(*building, note.ownership);
175 		   }
176 		});
177 
178 	// Subscribe to NoteFieldTerrainChanged.
179 	field_terrain_changed_subscriber_ = Notifications::subscribe<NoteFieldTerrainChanged>(
180 	   [this](const NoteFieldTerrainChanged& note) {
181 		   if (vision(note.map_index) > 1) {
182 			   rediscover_node(egbase().map(), note.fc);
183 		   }
184 		});
185 
186 	// Populating remaining_shipnames vector
187 	for (const auto& shipname : tribe_descr.get_ship_names()) {
188 		remaining_shipnames_.insert(shipname);
189 	}
190 }
191 
~Player()192 Player::~Player() {
193 }
194 
create_default_infrastructure()195 void Player::create_default_infrastructure() {
196 	const Map& map = egbase().map();
197 	if (map.get_starting_pos(player_number_)) {
198 		const Widelands::TribeBasicInfo::Initialization& initialization =
199 		   tribe().initialization(initialization_index_);
200 
201 		Game& game = dynamic_cast<Game&>(egbase());
202 
203 		// Run the corresponding script
204 		std::unique_ptr<LuaTable> table(game.lua().run_script(initialization.script));
205 		table->do_not_warn_about_unaccessed_keys();
206 		std::unique_ptr<LuaCoroutine> cr = table->get_coroutine("func");
207 		cr->push_arg(this);
208 		game.enqueue_command(new CmdLuaCoroutine(game.get_gametime(), std::move(cr)));
209 
210 		// Check if other starting positions are shared in and initialize them as well
211 		for (uint8_t n = 0; n < further_shared_in_player_.size(); ++n) {
212 			Coords const further_pos = map.get_starting_pos(further_shared_in_player_.at(n));
213 
214 			// Run the corresponding script
215 			std::unique_ptr<LuaCoroutine> ncr =
216 			   game.lua()
217 			      .run_script(tribe().initialization(further_initializations_.at(n)).script)
218 			      ->get_coroutine("func");
219 			ncr->push_arg(this);
220 			ncr->push_arg(further_pos);
221 			game.enqueue_command(new CmdLuaCoroutine(game.get_gametime(), std::move(ncr)));
222 		}
223 	} else
224 		throw WLWarning(_("Missing starting position"),
225 		                _("Widelands could not start the game, because player %u has "
226 		                  "no starting position.\n"
227 		                  "You can manually add a starting position with the Widelands "
228 		                  "Editor to fix this problem."),
229 		                static_cast<unsigned int>(player_number_));
230 }
231 
232 /**
233  * Allocate the fields array that contains player-specific field information.
234  */
allocate_map()235 void Player::allocate_map() {
236 	const Map& map = egbase().map();
237 	assert(map.get_width());
238 	assert(map.get_height());
239 	fields_.reset(new Field[map.max_index()]);
240 }
241 
242 /**
243  * Assign the player the given team number.
244  */
set_team_number(TeamNumber team)245 void Player::set_team_number(TeamNumber team) {
246 	team_number_ = team;
247 	team_player_uptodate_ = false;
248 }
249 
250 /**
251  * Returns whether this player and the given other player can attack
252  * each other.
253  */
is_hostile(const Player & other) const254 bool Player::is_hostile(const Player& other) const {
255 	return &other != this && (!team_number_ || team_number_ != other.team_number_) &&
256 	       !is_attack_forbidden(other.player_number());
257 }
258 
is_defeated() const259 bool Player::is_defeated() const {
260 	for (const auto& economy : economies()) {
261 		if (!economy.second->warehouses().empty()) {
262 			return false;
263 		}
264 	}
265 	return true;
266 }
267 
initialize()268 void Player::AiPersistentState::initialize() {
269 	colony_scan_area = kColonyScanStartArea;
270 	trees_around_cutters = 0;
271 	last_attacked_player = std::numeric_limits<int16_t>::max();
272 	expedition_start_time = kNoExpedition;
273 	ships_utilization = 200;
274 	no_more_expeditions = false;
275 	target_military_score = 100;
276 	least_military_score = 0;
277 	ai_productionsites_ratio = std::rand() % 5 + 7;
278 	ai_personality_mil_upper_limit = 100;
279 
280 	// all zeroes
281 	assert(neuron_weights.size() == Widelands::Player::AiPersistentState::kNeuronPoolSize);
282 	assert(neuron_functs.size() == Widelands::Player::AiPersistentState::kNeuronPoolSize);
283 	assert(f_neurons.size() == Widelands::Player::AiPersistentState::kFNeuronPoolSize);
284 	assert(magic_numbers.size() == Widelands::Player::AiPersistentState::kMagicNumbersSize);
285 	for (size_t i = 0; i < neuron_weights.size(); ++i) {
286 		neuron_weights.at(i) = 0;
287 	}
288 	for (size_t i = 0; i < neuron_functs.size(); ++i) {
289 		neuron_functs.at(i) = 0;
290 	}
291 	for (size_t i = 0; i < f_neurons.size(); ++i) {
292 		f_neurons.at(i) = 0;
293 	}
294 	for (size_t i = 0; i < magic_numbers.size(); ++i) {
295 		magic_numbers.at(i) = 0;
296 	}
297 	remaining_basic_buildings.clear();
298 
299 	initialized = true;
300 }
301 
302 /**
303  * Updates the vector containing allied players
304  */
update_team_players()305 void Player::update_team_players() {
306 	team_player_.clear();
307 	team_player_uptodate_ = true;
308 
309 	if (!team_number_)
310 		return;
311 
312 	for (PlayerNumber i = 1; i <= kMaxPlayers; ++i) {
313 		Player* other = egbase().get_player(i);
314 		if (!other)
315 			continue;
316 		if (other == this)
317 			continue;
318 		if (team_number_ == other->team_number_)
319 			team_player_.push_back(other);
320 	}
321 }
322 
323 /*
324  * Plays the corresponding sound when a message is received and if sound is
325  * enabled.
326  */
play_message_sound(const Message * message)327 void Player::play_message_sound(const Message* message) {
328 	if (g_sh->is_sound_enabled(SoundType::kMessage)) {
329 		FxId fx;
330 		switch (message->type()) {
331 		case Message::Type::kEconomySiteOccupied:
332 			fx = occupied_fx_;
333 			break;
334 		case Message::Type::kWarfareUnderAttack:
335 			fx = attack_fx_;
336 			break;
337 		default:
338 			fx = message_fx_;
339 		}
340 		Notifications::publish(
341 		   NoteSound(SoundType::kMessage, fx, message->position(), kFxPriorityAlwaysPlay));
342 	}
343 }
344 
add_message(Game & game,std::unique_ptr<Message> new_message,bool const popup)345 MessageId Player::add_message(Game& game, std::unique_ptr<Message> new_message, bool const popup) {
346 	MessageId id = get_messages()->add_message(std::move(new_message));
347 	const Message* message = messages()[id];
348 
349 	// MapObject connection
350 	if (message->serial() > 0) {
351 		MapObject* mo = egbase().objects().get_object(message->serial());
352 		mo->removed.connect([this, id](unsigned) { message_object_removed(id); });
353 	}
354 
355 	// Sound & popup
356 	if (InteractivePlayer* const iplayer = game.get_ipl()) {
357 		if (&iplayer->player() == this) {
358 			play_message_sound(message);
359 			if (popup)
360 				iplayer->popup_message(id, *message);
361 		}
362 	}
363 
364 	return id;
365 }
366 
add_message_with_timeout(Game & game,std::unique_ptr<Message> message,uint32_t const timeout,uint32_t const radius)367 MessageId Player::add_message_with_timeout(Game& game,
368                                            std::unique_ptr<Message> message,
369                                            uint32_t const timeout,
370                                            uint32_t const radius) {
371 	const Map& map = game.map();
372 	uint32_t const gametime = game.get_gametime();
373 	Coords const position = message->position();
374 	for (const auto& tmp_message : messages()) {
375 		if (tmp_message.second->type() == message->type() &&
376 		    tmp_message.second->sub_type() == message->sub_type() &&
377 		    gametime < tmp_message.second->sent() + timeout &&
378 		    map.calc_distance(tmp_message.second->position(), position) <= radius) {
379 			return MessageId::null();
380 		}
381 	}
382 	return add_message(game, std::move(message));
383 }
384 
message_object_removed(MessageId message_id) const385 void Player::message_object_removed(MessageId message_id) const {
386 	// Send delete command
387 	upcast(Game, game, &egbase_);
388 	if (!game) {
389 		return;
390 	}
391 
392 	game->cmdqueue().enqueue(new CmdDeleteMessage(game->get_gametime(), player_number_, message_id));
393 }
394 
ships() const395 const std::set<Serial>& Player::ships() const {
396 	return ships_;
397 }
add_ship(Serial ship)398 void Player::add_ship(Serial ship) {
399 	ships_.insert(ship);
400 }
remove_ship(Serial ship)401 void Player::remove_ship(Serial ship) {
402 	auto it = ships_.find(ship);
403 	if (it != ships_.end()) {
404 		ships_.erase(it);
405 	}
406 }
407 
408 /*
409 ===============
410 Return filtered buildcaps that take the player's territory into account.
411 ===============
412 */
get_buildcaps(const FCoords & fc) const413 NodeCaps Player::get_buildcaps(const FCoords& fc) const {
414 	const Map& map = egbase().map();
415 	uint8_t buildcaps = fc.field->nodecaps();
416 
417 	if (!fc.field->is_interior(player_number_))
418 		buildcaps = 0;
419 
420 	// Check if a building's flag can't be build due to ownership
421 	else if (buildcaps & BUILDCAPS_BUILDINGMASK) {
422 		FCoords flagcoords;
423 		map.get_brn(fc, &flagcoords);
424 		if (!flagcoords.field->is_interior(player_number_))
425 			buildcaps &= ~BUILDCAPS_BUILDINGMASK;
426 
427 		//  Prevent big buildings that would swell over borders.
428 		if ((buildcaps & BUILDCAPS_BIG) == BUILDCAPS_BIG &&
429 		    (!map.tr_n(fc).field->is_interior(player_number_) ||
430 		     !map.tl_n(fc).field->is_interior(player_number_) ||
431 		     !map.l_n(fc).field->is_interior(player_number_)))
432 			buildcaps &= ~BUILDCAPS_SMALL;
433 	}
434 
435 	return static_cast<NodeCaps>(buildcaps);
436 }
437 
438 /**
439  * Build a flag, checking that it's legal to do so. Returns
440  * the flag in case of success, else returns 0;
441  */
build_flag(const Coords & c)442 Flag* Player::build_flag(const Coords& c) {
443 	int32_t buildcaps = get_buildcaps(egbase().map().get_fcoords(c));
444 
445 	if (buildcaps & BUILDCAPS_FLAG)
446 		return new Flag(egbase(), this, c);
447 	return nullptr;
448 }
449 
force_flag(const FCoords & c)450 Flag& Player::force_flag(const FCoords& c) {
451 	log("Forcing flag at (%i, %i)\n", c.x, c.y);
452 	const Map& map = egbase().map();
453 	if (BaseImmovable* const immovable = c.field->get_immovable()) {
454 		if (upcast(Flag, existing_flag, immovable)) {
455 			if (existing_flag->get_owner() == this)
456 				return *existing_flag;
457 		} else if (!dynamic_cast<RoadBase const*>(immovable))  //  A road or waterway is OK
458 			immovable->remove(egbase());                        //  Make room for the flag.
459 	}
460 	MapRegion<Area<FCoords>> mr(map, Area<FCoords>(c, 1));
461 	do
462 		if (upcast(Flag, flag, mr.location().field->get_immovable()))
463 			flag->remove(egbase());  //  Remove all flags that are too close.
464 	while (mr.advance(map));
465 
466 	//  Make sure that the player owns the area around.
467 	egbase().conquer_area_no_building(
468 	   PlayerArea<Area<FCoords>>(player_number(), Area<FCoords>(c, 1)));
469 	return *new Flag(egbase(), this, c);
470 }
471 
472 /*
473 ===============
474 Build a road along the given path.
475 Perform sanity checks (ownership, flags).
476 
477 Note: the diagnostic log messages aren't exactly errors. They might happen
478 in some situations over the network.
479 ===============
480 */
build_road(const Path & path)481 Road* Player::build_road(const Path& path) {
482 	const Map& map = egbase().map();
483 	FCoords fc = map.get_fcoords(path.get_start());
484 	if (upcast(Flag, start, fc.field->get_immovable())) {
485 		if (upcast(Flag, end, map.get_immovable(path.get_end()))) {
486 
487 			//  Verify ownership of the path.
488 			const int32_t laststep = path.get_nsteps() - 1;
489 			for (int32_t i = 0; i < laststep; ++i) {
490 				fc = map.get_neighbour(fc, path[i]);
491 
492 				if (BaseImmovable* const imm = fc.field->get_immovable())
493 					if (imm->get_size() >= BaseImmovable::SMALL) {
494 						return nullptr;
495 					}
496 				if (!(get_buildcaps(fc) & MOVECAPS_WALK)) {
497 					log("%i: building road, unwalkable\n", player_number());
498 					return nullptr;
499 				}
500 			}
501 			return &Road::create(egbase(), *start, *end, path);
502 		} else
503 			log("%i: building road, missed end flag\n", player_number());
504 	} else
505 		log("%i: building road, missed start flag\n", player_number());
506 
507 	return nullptr;
508 }
509 
force_road(const Path & path)510 Road& Player::force_road(const Path& path) {
511 	const Map& map = egbase().map();
512 	FCoords c = map.get_fcoords(path.get_start());
513 	Flag& start = force_flag(c);
514 	Flag& end = force_flag(map.get_fcoords(path.get_end()));
515 
516 	Path::StepVector::size_type const laststep = path.get_nsteps() - 1;
517 	for (Path::StepVector::size_type i = 0; i < laststep; ++i) {
518 		c = map.get_neighbour(c, path[i]);
519 		log("Clearing for road at (%i, %i)\n", c.x, c.y);
520 
521 		//  Make sure that the player owns the area around.
522 		dynamic_cast<Game&>(egbase()).conquer_area_no_building(
523 		   PlayerArea<Area<FCoords>>(player_number(), Area<FCoords>(c, 1)));
524 
525 		if (BaseImmovable* const immovable = c.field->get_immovable()) {
526 			assert(immovable != &start);
527 			assert(immovable != &end);
528 			immovable->remove(egbase());
529 		}
530 	}
531 	return Road::create(egbase(), start, end, path);
532 }
533 
build_waterway(const Path & path)534 Waterway* Player::build_waterway(const Path& path) {
535 	const Map& map = egbase().map();
536 
537 	if (path.get_nsteps() > map.get_waterway_max_length()) {
538 		log("%d: Refused to build a waterway because it is too long. Permitted length %d, actual "
539 		    "length %" PRIuS ".",
540 		    static_cast<unsigned int>(player_number()), map.get_waterway_max_length(),
541 		    path.get_nsteps());
542 		return nullptr;
543 	}
544 
545 	FCoords fc = map.get_fcoords(path.get_start());
546 	if (upcast(Flag, start, fc.field->get_immovable())) {
547 		if (upcast(Flag, end, map.get_immovable(path.get_end()))) {
548 			//  Verify ownership of the path.
549 			const int32_t laststep = path.get_nsteps() - 1;
550 			for (int32_t i = 0; i < laststep; ++i) {
551 				fc = map.get_neighbour(fc, path[i]);
552 
553 				if (BaseImmovable* const imm = fc.field->get_immovable()) {
554 					if (imm->get_size() >= BaseImmovable::SMALL) {
555 						return nullptr;
556 					}
557 				}
558 				if (!CheckStepFerry(egbase()).reachable_dest(map, fc)) {
559 					log("%i: building waterway aborted, unreachable for ferries\n",
560 					    static_cast<unsigned int>(player_number()));
561 					return nullptr;
562 				}
563 			}
564 			return &Waterway::create(egbase(), *start, *end, path);
565 		} else {
566 			log("%i: building waterway aborted, missing end flag\n",
567 			    static_cast<unsigned int>(player_number()));
568 		}
569 	} else {
570 		log("%i: building waterway aborted, missing start flag\n",
571 		    static_cast<unsigned int>(player_number()));
572 	}
573 	return nullptr;
574 }
575 
force_waterway(const Path & path)576 Waterway& Player::force_waterway(const Path& path) {
577 	const Map& map = egbase().map();
578 	FCoords c = map.get_fcoords(path.get_start());
579 	Flag& start = force_flag(c);
580 	Flag& end = force_flag(map.get_fcoords(path.get_end()));
581 
582 	Path::StepVector::size_type const laststep = path.get_nsteps() - 1;
583 	for (Path::StepVector::size_type i = 0; i < laststep; ++i) {
584 		c = map.get_neighbour(c, path[i]);
585 		log("Clearing for waterway at (%i, %i)\n", c.x, c.y);
586 
587 		//  Make sure that the player owns the area around.
588 		dynamic_cast<Game&>(egbase()).conquer_area_no_building(
589 		   PlayerArea<Area<FCoords>>(player_number(), Area<FCoords>(c, 1)));
590 
591 		if (BaseImmovable* const immovable = c.field->get_immovable()) {
592 			assert(immovable != &start);
593 			assert(immovable != &end);
594 			immovable->remove(egbase());
595 		}
596 	}
597 	return Waterway::create(egbase(), start, end, path);
598 }
599 
force_building(Coords const location,const FormerBuildings & former_buildings)600 Building& Player::force_building(Coords const location, const FormerBuildings& former_buildings) {
601 	const Map& map = egbase().map();
602 	DescriptionIndex idx = former_buildings.back().first;
603 	const BuildingDescr* descr = egbase().tribes().get_building_descr(idx);
604 	terraform_for_building(egbase(), player_number(), location, descr);
605 	FCoords flag_loc;
606 	map.get_brn(map.get_fcoords(location), &flag_loc);
607 	force_flag(flag_loc);
608 
609 	return descr->create(egbase(), this, map.get_fcoords(location), false, false, former_buildings);
610 }
611 
force_csite(Coords const location,DescriptionIndex b_idx,const FormerBuildings & former_buildings)612 Building& Player::force_csite(Coords const location,
613                               DescriptionIndex b_idx,
614                               const FormerBuildings& former_buildings) {
615 	EditorGameBase& eg = egbase();
616 	const Map& map = eg.map();
617 	const Tribes& tribes = eg.tribes();
618 	const PlayerNumber pn = player_number();
619 
620 	if (!former_buildings.empty()) {
621 		DescriptionIndex idx = former_buildings.back().first;
622 		const BuildingDescr* descr = tribes.get_building_descr(idx);
623 		terraform_for_building(eg, pn, location, descr);
624 	}
625 	FCoords flag_loc;
626 	map.get_brn(map.get_fcoords(location), &flag_loc);
627 	force_flag(flag_loc);
628 
629 	terraform_for_building(eg, pn, location, tribes.get_building_descr(b_idx));
630 
631 	return eg.warp_constructionsite(
632 	   map.get_fcoords(location), player_number_, b_idx, false, former_buildings);
633 }
634 
635 /*
636 ===============
637 Place a construction site or building, checking that it's legal to do so.
638 ===============
639 */
build(Coords c,DescriptionIndex const idx,bool constructionsite,FormerBuildings & former_buildings)640 Building* Player::build(Coords c,
641                         DescriptionIndex const idx,
642                         bool constructionsite,
643                         FormerBuildings& former_buildings) {
644 	// Validate building type
645 	if (!tribe().has_building(idx)) {
646 		return nullptr;
647 	}
648 
649 	const BuildingDescr* descr = egbase().tribes().get_building_descr(idx);
650 
651 	if (!descr->is_buildable()) {
652 		return nullptr;
653 	}
654 
655 	// Validate build position
656 	const Map& map = egbase().map();
657 	map.normalize_coords(c);
658 	const FCoords fc = map.get_fcoords(c);
659 	const FCoords brn = map.br_n(fc);
660 	if (!fc.field->is_interior(player_number()) || !brn.field->is_interior(player_number())) {
661 		return nullptr;
662 	}
663 	if (descr->get_size() >= BaseImmovable::BIG &&
664 	    !((map.l_n(fc).field->is_interior(player_number())) &&
665 	      (map.tr_n(fc).field->is_interior(player_number())) &&
666 	      (map.tl_n(fc).field->is_interior(player_number())))) {
667 		return nullptr;
668 	}
669 
670 	if (descr->get_built_over_immovable() != INVALID_INDEX &&
671 	    !(fc.field->get_immovable() &&
672 	      fc.field->get_immovable()->has_attribute(descr->get_built_over_immovable()))) {
673 		return nullptr;
674 	}
675 
676 	const NodeCaps buildcaps = descr->get_built_over_immovable() == INVALID_INDEX ?
677 	                              get_buildcaps(fc) :
678 	                              map.get_max_nodecaps(egbase(), fc);
679 	if (descr->get_ismine()) {
680 		if (!(buildcaps & BUILDCAPS_MINE)) {
681 			return nullptr;
682 		}
683 	} else {
684 		if ((buildcaps & BUILDCAPS_SIZEMASK) < descr->get_size() - BaseImmovable::SMALL + 1) {
685 			return nullptr;
686 		}
687 		if (descr->get_isport() && !(buildcaps & BUILDCAPS_PORT)) {
688 			return nullptr;
689 		}
690 	}
691 	if (!(brn.field->get_immovable() &&
692 	      brn.field->get_immovable()->descr().type() == MapObjectType::FLAG) &&
693 	    !(get_buildcaps(brn) & BUILDCAPS_FLAG)) {
694 		return nullptr;
695 	}
696 
697 	if (constructionsite) {
698 		return &egbase().warp_constructionsite(c, player_number_, idx, false, former_buildings);
699 	} else {
700 		return &descr->create(egbase(), this, c, false, false, former_buildings);
701 	}
702 }
703 
704 /*
705 ===============
706 Bulldoze the given road, waterway, flag or building.
707 ===============
708 */
bulldoze(PlayerImmovable & imm,bool const recurse)709 void Player::bulldoze(PlayerImmovable& imm, bool const recurse) {
710 	std::vector<OPtr<PlayerImmovable>> bulldozelist;
711 	bulldozelist.push_back(&imm);
712 
713 	while (!bulldozelist.empty()) {
714 		PlayerImmovable* immovable = bulldozelist.back().get(egbase());
715 		bulldozelist.pop_back();
716 		if (!immovable)
717 			continue;
718 
719 		// General security check
720 		if (immovable->get_owner() != this)
721 			return;
722 
723 		// Destroy, after extended security check
724 		if (upcast(Building, building, immovable)) {
725 			if (!(building->get_playercaps() & Building::PCap_Bulldoze))
726 				return;
727 
728 			Flag& flag = building->base_flag();
729 			building->destroy(egbase());
730 			//  Now imm and building are dangling reference/pointer! Do not use!
731 
732 			if (recurse && flag.is_dead_end())
733 				bulldozelist.push_back(&flag);
734 		} else if (upcast(Flag, flag, immovable)) {
735 			if (Building* const flagbuilding = flag->get_building())
736 				if (!(flagbuilding->get_playercaps() & Building::PCap_Bulldoze)) {
737 					log("Player trying to rip flag (%u) with undestroyable "
738 					    "building (%u)\n",
739 					    flag->serial(), flagbuilding->serial());
740 					return;
741 				}
742 
743 			OPtr<Flag> flagcopy = flag;
744 			if (recurse) {
745 				for (uint8_t primary_road_id = 6; primary_road_id; --primary_road_id) {
746 					// Recursive bulldoze calls may cause flag to disappear
747 					if (!flagcopy.get(egbase()))
748 						return;
749 
750 					if (RoadBase* const primary_road = flag->get_roadbase(primary_road_id)) {
751 						Flag& primary_start = primary_road->get_flag(RoadBase::FlagStart);
752 						Flag& primary_other = flag == &primary_start ?
753 						                         primary_road->get_flag(RoadBase::FlagEnd) :
754 						                         primary_start;
755 						primary_road->destroy(egbase());
756 						log("destroying road/waterway from (%i, %i) going in dir %u\n",
757 						    flag->get_position().x, flag->get_position().y, primary_road_id);
758 						//  The primary road is gone. Now see if the flag at the other
759 						//  end of it is a dead-end.
760 						if (primary_other.is_dead_end())
761 							bulldozelist.push_back(&primary_other);
762 					}
763 				}
764 			}
765 
766 			// Recursive bulldoze calls may cause flag to disappear
767 			if (flagcopy.get(egbase()))
768 				flag->destroy(egbase());
769 		} else if (upcast(RoadBase, road, immovable)) {
770 			Flag& start = road->get_flag(RoadBase::FlagStart);
771 			Flag& end = road->get_flag(RoadBase::FlagEnd);
772 
773 			road->destroy(egbase());
774 			//  Now imm and road are dangling reference/pointer! Do not use!
775 
776 			if (recurse) {
777 				// Destroy all roads and waterways between the flags, not just selected
778 				while (RoadBase* const r = start.get_roadbase(end))
779 					r->destroy(egbase());
780 
781 				OPtr<Flag> endcopy = &end;
782 				if (start.is_dead_end())
783 					bulldozelist.push_back(&start);
784 				// At this point, end may have become dangling
785 				if (Flag* pend = endcopy.get(egbase())) {
786 					if (pend->is_dead_end())
787 						bulldozelist.push_back(&end);
788 				}
789 			}
790 		} else
791 			throw wexception("Player::bulldoze(%u): bad immovable type", immovable->serial());
792 	}
793 }
794 
start_stop_building(PlayerImmovable & imm)795 void Player::start_stop_building(PlayerImmovable& imm) {
796 	if (imm.get_owner() == this)
797 		if (upcast(ProductionSite, productionsite, &imm))
798 			productionsite->set_stopped(!productionsite->is_stopped());
799 }
800 
start_or_cancel_expedition(Warehouse & wh)801 void Player::start_or_cancel_expedition(Warehouse& wh) {
802 	if (wh.get_owner() == this)
803 		if (PortDock* pd = wh.get_portdock()) {
804 			if (pd->expedition_started()) {
805 				upcast(Game, game, &egbase());
806 				pd->cancel_expedition(*game);
807 			} else
808 				pd->start_expedition();
809 		}
810 }
811 
military_site_set_soldier_preference(PlayerImmovable & imm,SoldierPreference soldier_preference)812 void Player::military_site_set_soldier_preference(PlayerImmovable& imm,
813                                                   SoldierPreference soldier_preference) {
814 	if (imm.get_owner() == this)
815 		if (upcast(MilitarySite, milsite, &imm))
816 			milsite->set_soldier_preference(soldier_preference);
817 }
818 
819 /*
820  * enhance this building, remove it, but give the constructionsite
821  * an idea of enhancing
822  */
enhance_building(Building * building,DescriptionIndex const index_of_new_building,bool keep_wares)823 void Player::enhance_building(Building* building,
824                               DescriptionIndex const index_of_new_building,
825                               bool keep_wares) {
826 	enhance_or_dismantle(building, index_of_new_building, keep_wares);
827 }
828 
829 /*
830  * rip this building down, but slowly: a builder will take it gradually
831  * apart.
832  */
dismantle_building(Building * building,bool keep_wares)833 void Player::dismantle_building(Building* building, bool keep_wares) {
834 	enhance_or_dismantle(building, INVALID_INDEX, keep_wares);
835 }
enhance_or_dismantle(Building * building,DescriptionIndex const index_of_new_building,bool keep_wares)836 void Player::enhance_or_dismantle(Building* building,
837                                   DescriptionIndex const index_of_new_building,
838                                   bool keep_wares) {
839 	if (building->get_owner() == this &&
840 	    (index_of_new_building == INVALID_INDEX ||
841 	     building->descr().enhancement() == index_of_new_building)) {
842 		FormerBuildings former_buildings = building->get_former_buildings();
843 		const Coords position = building->get_position();
844 
845 		//  Get wares, workers, and soldiers
846 		//  Make copies of the vectors, because the originals are destroyed with
847 		//  the building.
848 		std::vector<Worker*> workers;
849 		std::map<DescriptionIndex, Quantity> wares;
850 		auto add_to_wares = [&wares](DescriptionIndex di, Quantity add) {
851 			if (add == 0) {
852 				return;
853 			}
854 			auto it = wares.find(di);
855 			if (it == wares.end()) {
856 				wares[di] = add;
857 			} else {
858 				it->second += add;
859 			}
860 		};
861 
862 		if (upcast(Warehouse, wh, building)) {
863 			workers = wh->get_incorporated_workers();
864 			if (keep_wares) {
865 				for (DescriptionIndex di = wh->get_wares().get_nrwareids(); di; --di) {
866 					wares[di - 1] = wh->get_wares().stock(di - 1);
867 				}
868 				if (PortDock* pd = wh->get_portdock()) {
869 					for (DescriptionIndex di : tribe().wares()) {
870 						add_to_wares(di, pd->count_waiting(wwWARE, di));
871 					}
872 					if (ExpeditionBootstrap* x = pd->expedition_bootstrap()) {
873 						for (const InputQueue* q : x->queues(true)) {
874 							if (q->get_type() == wwWARE) {
875 								add_to_wares(q->get_index(), q->get_filled());
876 							}
877 						}
878 					}
879 				}
880 			}
881 		} else {
882 			workers = building->get_workers();
883 			if (keep_wares) {
884 				// TODO(Nordfriese): Add support for markets?
885 				if (upcast(ProductionSite, ps, building)) {
886 					for (const InputQueue* q : ps->inputqueues()) {
887 						if (q->get_type() == wwWARE) {
888 							auto it = wares.find(q->get_index());
889 							if (it == wares.end()) {
890 								wares[q->get_index()] = q->get_filled();
891 							} else {
892 								it->second += q->get_filled();
893 							}
894 						}
895 					}
896 				}
897 			}
898 		}
899 		assert(keep_wares || wares.empty());
900 
901 		const BuildingSettings* settings = nullptr;
902 		if (index_of_new_building != INVALID_INDEX) {
903 			settings = building->create_building_settings();
904 			// For enhancing, register whether the window was open
905 			Notifications::publish(NoteBuilding(building->serial(), NoteBuilding::Action::kStartWarp));
906 		}
907 
908 		building->remove(egbase());  //  no fire or stuff
909 		//  Hereafter the old building does not exist and building is a dangling
910 		//  pointer.
911 
912 		if (index_of_new_building != INVALID_INDEX) {
913 			building = &egbase().warp_constructionsite(position, player_number_, index_of_new_building,
914 			                                           false, former_buildings, settings, wares);
915 		} else {
916 			building =
917 			   &egbase().warp_dismantlesite(position, player_number_, false, former_buildings, wares);
918 		}
919 
920 		// Open the new building window if needed
921 		Notifications::publish(NoteBuilding(building->serial(), NoteBuilding::Action::kFinishWarp));
922 
923 		//  Hereafter building points to the new building.
924 
925 		// Reassign the workers and soldiers.
926 		// Note that this will make sure they stay within the economy;
927 		// However, they are no longer associated with the building as
928 		// workers of that buiding, which is why they will leave for a
929 		// warehouse.
930 		for (Worker* temp_worker : workers) {
931 			temp_worker->set_location(building);
932 		}
933 	}
934 }
935 
936 /*
937 ===============
938 Perform an action on the given flag.
939 ===============
940 */
flagaction(Flag & flag)941 void Player::flagaction(Flag& flag) {
942 	if (flag.get_owner() == this) {  //  Additional security check.
943 		flag.add_flag_job(dynamic_cast<Game&>(egbase()), tribe().geologist(), "expedition");
944 	}
945 }
946 
allow_worker_type(DescriptionIndex const i,bool const allow)947 void Player::allow_worker_type(DescriptionIndex const i, bool const allow) {
948 	assert(i < static_cast<int>(allowed_worker_types_.size()));
949 	assert(!allow || tribe().get_worker_descr(i)->is_buildable());
950 	allowed_worker_types_[i] = allow;
951 }
952 
953 /*
954  * allow building
955  *
956  * Disable or enable a building for a player
957  */
allow_building_type(DescriptionIndex const i,bool const allow)958 void Player::allow_building_type(DescriptionIndex const i, bool const allow) {
959 	assert(i < allowed_building_types_.size());
960 	allowed_building_types_[i] = allow;
961 }
962 
963 /*
964  * Economy stuff below
965  */
create_economy(WareWorker type)966 Economy* Player::create_economy(WareWorker type) {
967 	std::unique_ptr<Economy> eco(new Economy(*this, type));
968 	const Serial serial = eco->serial();
969 
970 	assert(economies_.count(serial) == 0);
971 	economies_.emplace(std::make_pair(serial, std::move(eco)));
972 	assert(economies_.at(serial)->serial() == serial);
973 	assert(economies_.count(serial) == 1);
974 
975 	return get_economy(serial);
976 }
977 
create_economy(Serial serial,WareWorker type)978 Economy* Player::create_economy(Serial serial, WareWorker type) {
979 	std::unique_ptr<Economy> eco(new Economy(*this, serial, type));
980 
981 	assert(economies_.count(serial) == 0);
982 	economies_.emplace(std::make_pair(serial, std::move(eco)));
983 	assert(economies_.at(serial)->serial() == serial);
984 	assert(economies_.count(serial) == 1);
985 
986 	return get_economy(serial);
987 }
988 
remove_economy(Serial serial)989 void Player::remove_economy(Serial serial) {
990 	assert(has_economy(serial));
991 	economies_.erase(economies_.find(serial));
992 	assert(!has_economy(serial));
993 }
994 
economies() const995 const std::map<Serial, std::unique_ptr<Economy>>& Player::economies() const {
996 	return economies_;
997 }
998 
get_economy(Widelands::Serial serial) const999 Economy* Player::get_economy(Widelands::Serial serial) const {
1000 	if (economies_.count(serial) == 0) {
1001 		return nullptr;
1002 	}
1003 	return economies_.at(serial).get();
1004 }
1005 
has_economy(Widelands::Serial serial) const1006 bool Player::has_economy(Widelands::Serial serial) const {
1007 	return economies_.count(serial) != 0;
1008 }
1009 
1010 /************  Military stuff  **********/
1011 
1012 /*
1013 ==========
1014 Change the training priotity values
1015 ==========
1016 */
change_training_options(TrainingSite & trainingsite,TrainingAttribute attr,int32_t const val)1017 void Player::change_training_options(TrainingSite& trainingsite,
1018                                      TrainingAttribute attr,
1019                                      int32_t const val) {
1020 	if (trainingsite.get_owner() == this) {
1021 		trainingsite.set_pri(attr, trainingsite.get_pri(attr) + val);
1022 	}
1023 }
1024 
1025 /*
1026 ===========
1027 Forces the drop of given soldier at given house
1028 ===========
1029 */
drop_soldier(PlayerImmovable & imm,Soldier & soldier)1030 void Player::drop_soldier(PlayerImmovable& imm, Soldier& soldier) {
1031 	if (imm.get_owner() != this)
1032 		return;
1033 	if (soldier.descr().type() != MapObjectType::SOLDIER)
1034 		return;
1035 	if (upcast(Building, building, &imm)) {
1036 		SoldierControl* soldier_control = building->mutable_soldier_control();
1037 		if (soldier_control != nullptr) {
1038 			soldier_control->drop_soldier(soldier);
1039 		}
1040 	}
1041 }
1042 
1043 /*
1044 ===========
1045 ===========
1046 */
1047 
1048 /**
1049  * Get a list of soldiers that this player can use to attack the
1050  * building at the given flag.
1051  *
1052  * The default attack should just take the first N soldiers of the
1053  * returned array.
1054  */
1055 // TODO(unknown): Perform a meaningful sort on the soldiers array.
1056 uint32_t
find_attack_soldiers(Flag & flag,std::vector<Soldier * > * soldiers,uint32_t nr_wanted)1057 Player::find_attack_soldiers(Flag& flag, std::vector<Soldier*>* soldiers, uint32_t nr_wanted) {
1058 	uint32_t count = 0;
1059 
1060 	if (soldiers)
1061 		soldiers->clear();
1062 
1063 	const Map& map = egbase().map();
1064 	std::vector<BaseImmovable*> flags;
1065 
1066 	map.find_reachable_immovables_unique(
1067 	   egbase(), Area<FCoords>(map.get_fcoords(flag.get_position()), 25), flags,
1068 	   CheckStepDefault(MOVECAPS_WALK), FindFlagOf(FindImmovablePlayerMilitarySite(*this)));
1069 
1070 	if (flags.empty())
1071 		return 0;
1072 
1073 	for (BaseImmovable* temp_flag : flags) {
1074 		upcast(Flag, attackerflag, temp_flag);
1075 		const SoldierControl* soldier_control = attackerflag->get_building()->soldier_control();
1076 		assert(soldier_control != nullptr);
1077 		std::vector<Soldier*> const present = soldier_control->present_soldiers();
1078 		uint32_t const nr_staying = soldier_control->min_soldier_capacity();
1079 		uint32_t const nr_present = present.size();
1080 		if (nr_staying < nr_present) {
1081 			uint32_t const nr_taken = std::min(nr_wanted, nr_present - nr_staying);
1082 			if (soldiers)
1083 				soldiers->insert(soldiers->end(), present.begin(), present.begin() + nr_taken);
1084 			count += nr_taken;
1085 			nr_wanted -= nr_taken;
1086 			if (!nr_wanted)
1087 				break;
1088 		}
1089 	}
1090 
1091 	return count;
1092 }
1093 
1094 // TODO(unknown): Clean this mess up. The only action we really have right now is
1095 // to attack, so pretending we have more types is pointless.
enemyflagaction(Flag & flag,PlayerNumber const attacker,const std::vector<Widelands::Soldier * > & soldiers)1096 void Player::enemyflagaction(Flag& flag,
1097                              PlayerNumber const attacker,
1098                              const std::vector<Widelands::Soldier*>& soldiers) {
1099 	if (attacker != player_number()) {
1100 		log("Player (%d) is not the sender of an attack (%d)\n", attacker, player_number());
1101 	} else if (soldiers.empty()) {
1102 		log("enemyflagaction: no soldiers given\n");
1103 	} else if (is_hostile(flag.owner())) {
1104 		if (Building* const building = flag.get_building()) {
1105 			if (const AttackTarget* attack_target = building->attack_target()) {
1106 				if (attack_target->can_be_attacked()) {
1107 					for (Soldier* temp_attacker : soldiers) {
1108 						assert(temp_attacker);
1109 						assert(temp_attacker->get_owner() == this);
1110 						if (upcast(MilitarySite, ms, temp_attacker->get_location(egbase()))) {
1111 							assert(ms->get_owner() == this);
1112 							ms->send_attacker(*temp_attacker, *building);
1113 						} else {
1114 							// The soldier may not be in a militarysite anymore if he was kicked out
1115 							// in the short delay between sending and executing a playercommand
1116 							log("Player(%u)::enemyflagaction: Not sending soldier %u because he left the "
1117 							    "building\n",
1118 							    player_number(), temp_attacker->serial());
1119 						}
1120 					}
1121 				}
1122 			}
1123 		}
1124 	}
1125 }
1126 
rediscover_node(const Map & map,const FCoords & f)1127 void Player::rediscover_node(const Map& map, const FCoords& f) {
1128 
1129 	assert(0 <= f.x);
1130 	assert(f.x < map.get_width());
1131 	assert(0 <= f.y);
1132 	assert(f.y < map.get_height());
1133 	const Widelands::Field& first_map_field = map[0];
1134 	assert(&first_map_field <= f.field);
1135 	assert(f.field < &first_map_field + map.max_index());
1136 
1137 	Field& field = fields_[f.field - &first_map_field];
1138 
1139 	assert(fields_.get() <= &field);
1140 	assert(&field < fields_.get() + map.max_index());
1141 
1142 	{  // discover everything (above the ground) in this field
1143 		field.terrains = f.field->get_terrains();
1144 		field.r_e = f.field->get_road(WALK_E);
1145 		field.r_se = f.field->get_road(WALK_SE);
1146 		field.r_sw = f.field->get_road(WALK_SW);
1147 		field.owner = f.field->get_owned_by();
1148 
1149 		// Check if this node is part of a border
1150 		int32_t const mapwidth = map.get_width();
1151 		// right neighbour
1152 		FCoords r = map.r_n(f);
1153 		PlayerNumber r_owner_number = r.field->get_owned_by();
1154 		MapIndex r_index = map.get_index(r, mapwidth);
1155 		Vision r_vision = vision(r_index);
1156 		// top right neighbour
1157 		FCoords tr = map.tr_n(f);
1158 		PlayerNumber tr_owner_number = tr.field->get_owned_by();
1159 		// bottom right neighbour
1160 		FCoords br = map.br_n(f);
1161 		PlayerNumber br_owner_number = br.field->get_owned_by();
1162 		MapIndex br_index = map.get_index(br, mapwidth);
1163 		Vision br_vision = vision(br_index);
1164 		// bottom left neighbour
1165 		FCoords bl = map.bl_n(f);
1166 		PlayerNumber bl_owner_number = bl.field->get_owned_by();
1167 		MapIndex bl_index = map.get_index(bl, mapwidth);
1168 		Vision bl_vision = vision(bl_index);
1169 		// left neighbour
1170 		FCoords l = map.l_n(f);
1171 		PlayerNumber l_owner_number = l.field->get_owned_by();
1172 
1173 		field.border = f.field->is_border();
1174 		field.border_r = ((1 | r_vision) && (r_owner_number == field.owner) &&
1175 		                  ((tr_owner_number == field.owner) ^ (br_owner_number == field.owner)));
1176 		field.border_br = ((1 | bl_vision) && (bl_owner_number == field.owner) &&
1177 		                   ((l_owner_number == field.owner) ^ (br_owner_number == field.owner)));
1178 		field.border_bl = ((1 | br_vision) && (br_owner_number == field.owner) &&
1179 		                   ((r_owner_number == field.owner) ^ (bl_owner_number == field.owner)));
1180 
1181 		{
1182 			const MapObjectDescr* map_object_descr;
1183 			field.constructionsite.becomes = nullptr;
1184 			if (const BaseImmovable* base_immovable = f.field->get_immovable()) {
1185 				map_object_descr = &base_immovable->descr();
1186 
1187 				if (Road::is_road_descr(map_object_descr) ||
1188 				    Waterway::is_waterway_descr(map_object_descr))
1189 					map_object_descr = nullptr;
1190 				else if (upcast(Building const, building, base_immovable)) {
1191 					if (building->get_position() != f)
1192 						// This is not the building's main position so we can not see it.
1193 						map_object_descr = nullptr;
1194 					else {
1195 						if (upcast(ConstructionSite const, cs, building)) {
1196 							field.constructionsite = const_cast<ConstructionSite*>(cs)->get_info();
1197 						}
1198 					}
1199 				}
1200 			} else
1201 				map_object_descr = nullptr;
1202 			field.map_object_descr = map_object_descr;
1203 		}
1204 	}
1205 	{  //  discover the D triangle and the SW edge of the top right neighbour
1206 		FCoords tr = map.tr_n(f);
1207 		Field& tr_field = fields_[tr.field - &first_map_field];
1208 		if (tr_field.vision <= 1) {
1209 			tr_field.terrains.d = tr.field->terrain_d();
1210 			tr_field.r_sw = tr.field->get_road(WALK_SW);
1211 			tr_field.owner = tr.field->get_owned_by();
1212 		}
1213 	}
1214 	{  //  discover both triangles and the SE edge of the top left  neighbour
1215 		FCoords tl = map.tl_n(f);
1216 		Field& tl_field = fields_[tl.field - &first_map_field];
1217 		if (tl_field.vision <= 1) {
1218 			tl_field.terrains = tl.field->get_terrains();
1219 			tl_field.r_se = tl.field->get_road(WALK_SE);
1220 			tl_field.owner = tl.field->get_owned_by();
1221 		}
1222 	}
1223 	{  //  discover the R triangle and the  E edge of the     left  neighbour
1224 		FCoords l = map.l_n(f);
1225 		Field& l_field = fields_[l.field - &first_map_field];
1226 		if (l_field.vision <= 1) {
1227 			l_field.terrains.r = l.field->terrain_r();
1228 			l_field.r_e = l.field->get_road(WALK_E);
1229 			l_field.owner = l.field->get_owned_by();
1230 		}
1231 	}
1232 }
1233 
1234 /// Returns the resulting vision.
see_node(const Map & map,const FCoords & f,Time const gametime,bool const forward)1235 Vision Player::see_node(const Map& map, const FCoords& f, Time const gametime, bool const forward) {
1236 	assert(0 <= f.x);
1237 	assert(f.x < map.get_width());
1238 	assert(0 <= f.y);
1239 	assert(f.y < map.get_height());
1240 	const Widelands::Field& first_map_field = map[0];
1241 	assert(&first_map_field <= f.field);
1242 	assert(f.field < &first_map_field + map.max_index());
1243 
1244 	//  If this is not already a forwarded call, we should inform allied players
1245 	//  as well of this change.
1246 	if (!team_player_uptodate_)
1247 		update_team_players();
1248 	if (!forward && !team_player_.empty()) {
1249 		for (uint8_t j = 0; j < team_player_.size(); ++j)
1250 			team_player_[j]->see_node(map, f, gametime, true);
1251 	}
1252 
1253 	Field& field = fields_[f.field - &first_map_field];
1254 	assert(fields_.get() <= &field);
1255 	assert(&field < fields_.get() + map.max_index());
1256 
1257 	if (field.vision == 0) {
1258 		field.vision = 1;
1259 	}
1260 	if (field.vision == 1) {
1261 		rediscover_node(map, f);
1262 	}
1263 	return ++field.vision;
1264 }
1265 
1266 /// If 'mode' = UnseeMode::kUnexplore, fields will be marked as unexplored. Else, player no longer
1267 /// sees what's currently going on. Returns the vision that this node had before it was hidden.
unsee_node(MapIndex const i,Time const gametime,const SeeUnseeNode mode,bool const forward)1268 Vision Player::unsee_node(MapIndex const i,
1269                           Time const gametime,
1270                           const SeeUnseeNode mode,
1271                           bool const forward) {
1272 	Field& field = fields_[i];
1273 	if ((mode == SeeUnseeNode::kUnsee && field.vision <= 1) ||
1274 	    field.vision < 1)  //  Already does not see this
1275 		return field.vision;
1276 
1277 	const Vision original_vision = field.vision;
1278 
1279 	//  If this is not already a forwarded call, we should inform allied players
1280 	//  as well of this change.
1281 	if (!team_player_uptodate_)
1282 		update_team_players();
1283 	if (!forward && !team_player_.empty()) {
1284 		for (uint8_t j = 0; j < team_player_.size(); ++j)
1285 			team_player_[j]->unsee_node(i, gametime, mode, true);
1286 	}
1287 
1288 	if (mode == SeeUnseeNode::kUnexplore) {
1289 		field.vision = 0;
1290 	} else {
1291 		--field.vision;
1292 		assert(1 <= field.vision);
1293 	}
1294 	if (field.vision < 2) {
1295 		field.time_node_last_unseen = gametime;
1296 	}
1297 	return original_vision;
1298 }
1299 
hide_or_reveal_field(const uint32_t gametime,const Coords & coords,SeeUnseeNode mode)1300 void Player::hide_or_reveal_field(const uint32_t gametime,
1301                                   const Coords& coords,
1302                                   SeeUnseeNode mode) {
1303 	const Map& map = egbase().map();
1304 	FCoords fcoords = map.get_fcoords(coords);
1305 	const Widelands::MapIndex index = fcoords.field - &map[0];
1306 
1307 	switch (mode) {
1308 	// Reveal field
1309 	case SeeUnseeNode::kReveal: {
1310 		Widelands::Vision new_vision = see_node(map, fcoords, gametime);
1311 		// If the field was manually hidden, restore the original vision
1312 		if (hidden_fields_.count(index) == 1) {
1313 			auto iter = hidden_fields_.find(index);
1314 			Vision original_vision = iter->second;
1315 			while (new_vision < original_vision) {
1316 				new_vision = see_node(map, fcoords, gametime);
1317 			}
1318 			hidden_fields_.erase(iter);
1319 		}
1320 	} break;
1321 	// Hide field
1322 	case SeeUnseeNode::kUnsee:
1323 	case SeeUnseeNode::kUnexplore: {
1324 		const Widelands::Vision new_vision = unsee_node(index, gametime, mode);
1325 		// Remember the original vision so that we can unhide the fields again
1326 		if (hidden_fields_.count(index) != 1) {
1327 			hidden_fields_.insert(std::make_pair(index, new_vision));
1328 		}
1329 	} break;
1330 	}
1331 }
1332 
1333 /**
1334  * Called by Game::think to sample statistics data in regular intervals.
1335  */
sample_statistics()1336 void Player::sample_statistics() {
1337 	assert(ware_productions_.size() == egbase().tribes().nrwares());
1338 	assert(ware_consumptions_.size() == egbase().tribes().nrwares());
1339 	assert(ware_stocks_.size() == egbase().tribes().nrwares());
1340 
1341 	// Calculate stocks
1342 	std::vector<uint32_t> stocks(egbase().tribes().nrwares());
1343 
1344 	for (const auto& economy : economies()) {
1345 		if (economy.second->type() == wwWARE) {
1346 			for (Widelands::Warehouse* warehouse : economy.second->warehouses()) {
1347 				const Widelands::WareList& wares = warehouse->get_wares();
1348 				for (size_t id = 0; id < stocks.size(); ++id) {
1349 					stocks[id] += wares.stock(DescriptionIndex(id));
1350 				}
1351 			}
1352 		}
1353 	}
1354 
1355 	// Update statistics
1356 	for (uint32_t i = 0; i < ware_productions_.size(); ++i) {
1357 		ware_productions_[i].push_back(current_produced_statistics_[i]);
1358 		current_produced_statistics_[i] = 0;
1359 
1360 		ware_consumptions_[i].push_back(current_consumed_statistics_[i]);
1361 		current_consumed_statistics_[i] = 0;
1362 
1363 		ware_stocks_[i].push_back(stocks[i]);
1364 	}
1365 }
1366 
1367 /**
1368  * A ware was produced. Update the corresponding statistics.
1369  */
ware_produced(DescriptionIndex const wareid)1370 void Player::ware_produced(DescriptionIndex const wareid) {
1371 	assert(ware_productions_.size() == egbase().tribes().nrwares());
1372 	assert(egbase().tribes().ware_exists(wareid));
1373 	++current_produced_statistics_[wareid];
1374 }
1375 
1376 /**
1377  * Return count of produced wares for ware index
1378  */
get_current_produced_statistics(uint8_t const wareid)1379 uint32_t Player::get_current_produced_statistics(uint8_t const wareid) {
1380 	assert(wareid < egbase().tribes().nrwares());
1381 	assert(wareid < ware_productions_.size());
1382 	assert(wareid < current_produced_statistics_.size());
1383 	uint32_t sum = current_produced_statistics_[wareid];
1384 	for (const auto stat : *get_ware_production_statistics(wareid)) {
1385 		sum += stat;
1386 	}
1387 	return sum;
1388 }
1389 
1390 /**
1391  * Some units from one kind of ware were consumed.
1392  * Update the corresponding statistics
1393  *
1394  * \param wareid the ID of the consumed wares
1395  * \param count the number of consumed wares
1396  */
ware_consumed(DescriptionIndex const wareid,uint8_t const count)1397 void Player::ware_consumed(DescriptionIndex const wareid, uint8_t const count) {
1398 	assert(ware_consumptions_.size() == egbase().tribes().nrwares());
1399 	assert(egbase().tribes().ware_exists(wareid));
1400 
1401 	current_consumed_statistics_[wareid] += count;
1402 }
1403 
1404 /**
1405  * Get current ware production statistics
1406  */
1407 const std::vector<uint32_t>*
get_ware_production_statistics(DescriptionIndex const ware) const1408 Player::get_ware_production_statistics(DescriptionIndex const ware) const {
1409 	assert(ware < static_cast<int>(ware_productions_.size()));
1410 	return &ware_productions_[ware];
1411 }
1412 
1413 /**
1414  * Get current ware consumption statistics
1415  */
1416 const std::vector<uint32_t>*
get_ware_consumption_statistics(DescriptionIndex const ware) const1417 Player::get_ware_consumption_statistics(DescriptionIndex const ware) const {
1418 
1419 	assert(ware < static_cast<int>(ware_consumptions_.size()));
1420 
1421 	return &ware_consumptions_[ware];
1422 }
1423 
get_ware_stock_statistics(DescriptionIndex const ware) const1424 const std::vector<uint32_t>* Player::get_ware_stock_statistics(DescriptionIndex const ware) const {
1425 	assert(ware < static_cast<int>(ware_stocks_.size()));
1426 
1427 	return &ware_stocks_[ware];
1428 }
1429 
1430 const Player::BuildingStatsVector&
get_building_statistics(const DescriptionIndex & i) const1431 Player::get_building_statistics(const DescriptionIndex& i) const {
1432 	return *const_cast<Player*>(this)->get_mutable_building_statistics(i);
1433 }
1434 
get_mutable_building_statistics(const DescriptionIndex & i)1435 Player::BuildingStatsVector* Player::get_mutable_building_statistics(const DescriptionIndex& i) {
1436 	DescriptionIndex const nr_buildings = egbase().tribes().nrbuildings();
1437 	if (building_stats_.size() < nr_buildings)
1438 		building_stats_.resize(nr_buildings);
1439 	return &building_stats_[i];
1440 }
1441 
1442 /**
1443  * Add or remove the given building from building statistics.
1444  * Only to be called by \ref receive
1445  */
update_building_statistics(Building & building,NoteImmovable::Ownership ownership)1446 void Player::update_building_statistics(Building& building, NoteImmovable::Ownership ownership) {
1447 	upcast(ConstructionSite const, constructionsite, &building);
1448 	const std::string& building_name =
1449 	   constructionsite ? constructionsite->building().name() : building.descr().name();
1450 
1451 	const size_t nr_buildings = egbase().tribes().nrbuildings();
1452 
1453 	// Get the valid vector for this
1454 	if (building_stats_.size() < nr_buildings)
1455 		building_stats_.resize(nr_buildings);
1456 
1457 	std::vector<BuildingStats>& stat =
1458 	   *get_mutable_building_statistics(egbase().tribes().building_index(building_name.c_str()));
1459 
1460 	if (ownership == NoteImmovable::Ownership::GAINED) {
1461 		BuildingStats new_building;
1462 		new_building.is_constructionsite = constructionsite;
1463 		new_building.pos = building.get_position();
1464 		stat.push_back(new_building);
1465 	} else {
1466 		Coords const building_position = building.get_position();
1467 		for (uint32_t i = 0; i < stat.size(); ++i) {
1468 			if (stat[i].pos == building_position) {
1469 				stat.erase(stat.begin() + i);
1470 				return;
1471 			}
1472 		}
1473 
1474 		throw wexception("InteractivePlayer::loose_immovable(): A building should be "
1475 		                 "removed at (%i, %i), but nothing is known about this building!",
1476 		                 building_position.x, building_position.y);
1477 	}
1478 }
1479 /**
1480  * Functions used by AI to save/read AI data stored in Player class.
1481  */
1482 
set_ai(const std::string & ai)1483 void Player::set_ai(const std::string& ai) {
1484 	ai_ = ai;
1485 }
1486 
get_ai() const1487 const std::string& Player::get_ai() const {
1488 	return ai_;
1489 }
1490 
is_attack_forbidden(PlayerNumber who) const1491 bool Player::is_attack_forbidden(PlayerNumber who) const {
1492 	return forbid_attack_.find(who) != forbid_attack_.end();
1493 }
1494 
set_attack_forbidden(PlayerNumber who,bool forbid)1495 void Player::set_attack_forbidden(PlayerNumber who, bool forbid) {
1496 	const auto it = forbid_attack_.find(who);
1497 	if (forbid ^ (it == forbid_attack_.end())) {
1498 		return;
1499 	} else if (forbid) {
1500 		forbid_attack_.emplace(who);
1501 	} else {
1502 		forbid_attack_.erase(it);
1503 	}
1504 }
1505 
1506 /**
1507  * Pick random name from remaining names (if any)
1508  */
pick_shipname()1509 const std::string Player::pick_shipname() {
1510 	++ship_name_counter_;
1511 
1512 	if (!remaining_shipnames_.empty()) {
1513 		Game& game = dynamic_cast<Game&>(egbase());
1514 		assert(is_a(Game, &egbase()));
1515 		const uint32_t index = game.logic_rand() % remaining_shipnames_.size();
1516 		std::unordered_set<std::string>::iterator it = remaining_shipnames_.begin();
1517 		std::advance(it, index);
1518 		std::string new_name = *it;
1519 		remaining_shipnames_.erase(it);
1520 		return new_name;
1521 	}
1522 	return (boost::format(pgettext("shipname", "Ship %d")) % ship_name_counter_).str();
1523 }
1524 
1525 /**
1526  * Read remaining ship indexes to the give file
1527  *
1528  * \param fr source stream
1529  */
read_remaining_shipnames(FileRead & fr)1530 void Player::read_remaining_shipnames(FileRead& fr) {
1531 	// First get rid of default shipnames
1532 	remaining_shipnames_.clear();
1533 	const uint16_t count = fr.unsigned_16();
1534 	for (uint16_t i = 0; i < count; ++i) {
1535 		remaining_shipnames_.insert(fr.string());
1536 	}
1537 	ship_name_counter_ = fr.unsigned_32();
1538 }
1539 
1540 /**
1541  * Read statistics data from a file.
1542  *
1543  * \param fr source stream
1544  */
read_statistics(FileRead & fr,const uint16_t packet_version,const TribesLegacyLookupTable & lookup_table)1545 void Player::read_statistics(FileRead& fr,
1546                              const uint16_t packet_version,
1547                              const TribesLegacyLookupTable& lookup_table) {
1548 	uint16_t nr_wares = fr.unsigned_16();
1549 	size_t nr_entries = fr.unsigned_16();
1550 
1551 	// Stats are saved as a single string to reduce number of hard disk write operations
1552 	const auto parse_stats = [nr_entries](
1553 	   std::vector<std::vector<uint32_t>>* stats, const DescriptionIndex ware_index,
1554 	   const std::string& stats_string, const std::string& description) {
1555 		if (!stats_string.empty()) {
1556 			std::vector<std::string> stats_vector;
1557 			boost::split(stats_vector, stats_string, boost::is_any_of("|"));
1558 			if (stats_vector.size() != nr_entries) {
1559 				throw GameDataError("wrong number of %s statistics - expected %" PRIuS
1560 				                    " but got %" PRIuS,
1561 				                    description.c_str(), nr_entries, stats_vector.size());
1562 			}
1563 			for (size_t j = 0; j < nr_entries; ++j) {
1564 				stats->at(ware_index)[j] = static_cast<unsigned int>(atoi(stats_vector.at(j).c_str()));
1565 			}
1566 		} else if (nr_entries > 0) {
1567 			throw GameDataError("wrong number of %s statistics - expected %" PRIuS " but got 0",
1568 			                    description.c_str(), nr_entries);
1569 		}
1570 	};
1571 
1572 	for (uint32_t i = 0; i < current_produced_statistics_.size(); ++i)
1573 		ware_productions_[i].resize(nr_entries);
1574 
1575 	for (uint16_t i = 0; i < nr_wares; ++i) {
1576 		const std::string name = lookup_table.lookup_ware(fr.c_string());
1577 		const DescriptionIndex idx = egbase().tribes().ware_index(name);
1578 		if (!egbase().tribes().ware_exists(idx)) {
1579 			log("Player %u statistics: unknown ware name %s", player_number(), name.c_str());
1580 			continue;
1581 		}
1582 
1583 		current_produced_statistics_[idx] = fr.unsigned_32();
1584 		if (packet_version < 22) {
1585 			for (uint32_t j = 0; j < nr_entries; ++j) {
1586 				ware_productions_[idx][j] = fr.unsigned_32();
1587 			}
1588 		} else {
1589 			parse_stats(&ware_productions_, idx, fr.c_string(), "produced");
1590 		}
1591 	}
1592 
1593 	// Read consumption statistics
1594 	nr_wares = fr.unsigned_16();
1595 	nr_entries = fr.unsigned_16();
1596 
1597 	for (uint32_t i = 0; i < current_consumed_statistics_.size(); ++i)
1598 		ware_consumptions_[i].resize(nr_entries);
1599 
1600 	for (uint16_t i = 0; i < nr_wares; ++i) {
1601 		const std::string name = lookup_table.lookup_ware(fr.c_string());
1602 		const DescriptionIndex idx = egbase().tribes().ware_index(name);
1603 		if (!egbase().tribes().ware_exists(idx)) {
1604 			log("Player %u consumption statistics: unknown ware name %s", player_number(),
1605 			    name.c_str());
1606 			continue;
1607 		}
1608 
1609 		current_consumed_statistics_[idx] = fr.unsigned_32();
1610 		// TODO(GunChleoc): Get rid of this savegame compatibility code after build 21
1611 		if (packet_version < 22) {
1612 			for (uint32_t j = 0; j < nr_entries; ++j) {
1613 				ware_consumptions_[idx][j] = fr.unsigned_32();
1614 			}
1615 		} else {
1616 			parse_stats(&ware_consumptions_, idx, fr.c_string(), "consumed");
1617 		}
1618 	}
1619 
1620 	// Read stock statistics
1621 	nr_wares = fr.unsigned_16();
1622 	nr_entries = fr.unsigned_16();
1623 
1624 	for (uint32_t i = 0; i < ware_stocks_.size(); ++i)
1625 		ware_stocks_[i].resize(nr_entries);
1626 
1627 	for (uint16_t i = 0; i < nr_wares; ++i) {
1628 		const std::string name = lookup_table.lookup_ware(fr.c_string());
1629 		const DescriptionIndex idx = egbase().tribes().ware_index(name);
1630 		if (!egbase().tribes().ware_exists(idx)) {
1631 			log("Player %u stock statistics: unknown ware name %s", player_number(), name.c_str());
1632 			continue;
1633 		}
1634 
1635 		if (packet_version < 22) {
1636 			for (uint32_t j = 0; j < nr_entries; ++j) {
1637 				ware_stocks_[idx][j] = fr.unsigned_32();
1638 			}
1639 		} else {
1640 			parse_stats(&ware_stocks_, idx, fr.c_string(), "stock");
1641 		}
1642 	}
1643 
1644 	// All statistics should have the same size
1645 	assert(ware_productions_.size() == ware_consumptions_.size());
1646 	assert(ware_productions_[0].size() == ware_consumptions_[0].size());
1647 
1648 	assert(ware_productions_.size() == ware_stocks_.size());
1649 	assert(ware_productions_[0].size() == ware_stocks_[0].size());
1650 }
1651 
1652 /**
1653  * Write remaining ship indexes to the given file
1654  */
write_remaining_shipnames(FileWrite & fw) const1655 void Player::write_remaining_shipnames(FileWrite& fw) const {
1656 	fw.unsigned_16(remaining_shipnames_.size());
1657 	for (const auto& shipname : remaining_shipnames_) {
1658 		fw.string(shipname);
1659 	}
1660 	fw.unsigned_32(ship_name_counter_);
1661 }
1662 
1663 /**
1664  * Write statistics data to the given file
1665  */
write_statistics(FileWrite & fw) const1666 void Player::write_statistics(FileWrite& fw) const {
1667 	// Save stats as a single string to reduce number of hard disk write operations
1668 	const auto write_stats = [&fw](
1669 	   const std::vector<std::vector<uint32_t>>& stats, const DescriptionIndex ware_index) {
1670 		std::ostringstream oss("");
1671 		const int sizem = stats[ware_index].size() - 1;
1672 		if (sizem >= 0) {
1673 			for (int i = 0; i < sizem; ++i) {
1674 				oss << stats[ware_index][i] << "|";
1675 			}
1676 			oss << stats[ware_index][sizem];
1677 		}
1678 		fw.c_string(oss.str());
1679 	};
1680 
1681 	const Tribes& tribes = egbase().tribes();
1682 	const std::set<DescriptionIndex>& tribe_wares = tribe().wares();
1683 	const size_t nr_wares = tribe_wares.size();
1684 
1685 	// Write produce statistics
1686 	fw.unsigned_16(nr_wares);
1687 	fw.unsigned_16(ware_productions_[0].size());
1688 
1689 	for (const DescriptionIndex ware_index : tribe_wares) {
1690 		fw.c_string(tribes.get_ware_descr(ware_index)->name());
1691 		fw.unsigned_32(current_produced_statistics_[ware_index]);
1692 		write_stats(ware_productions_, ware_index);
1693 	}
1694 
1695 	// Write consume statistics
1696 	fw.unsigned_16(nr_wares);
1697 	fw.unsigned_16(ware_consumptions_[0].size());
1698 
1699 	for (const DescriptionIndex ware_index : tribe_wares) {
1700 		fw.c_string(tribes.get_ware_descr(ware_index)->name());
1701 		fw.unsigned_32(current_consumed_statistics_[ware_index]);
1702 		write_stats(ware_consumptions_, ware_index);
1703 	}
1704 
1705 	// Write stock statistics
1706 	fw.unsigned_16(nr_wares);
1707 	fw.unsigned_16(ware_stocks_[0].size());
1708 
1709 	for (const DescriptionIndex ware_index : tribe_wares) {
1710 		fw.c_string(tribes.get_ware_descr(ware_index)->name());
1711 		write_stats(ware_stocks_, ware_index);
1712 	}
1713 }
1714 }  // namespace Widelands
1715