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/editor_game_base.h"
21 
22 #include <memory>
23 
24 #include "base/i18n.h"
25 #include "base/macros.h"
26 #include "base/scoped_timer.h"
27 #include "base/time_string.h"
28 #include "base/wexception.h"
29 #include "economy/flag.h"
30 #include "economy/road.h"
31 #include "economy/waterway.h"
32 #include "graphic/color.h"
33 #include "graphic/road_segments.h"
34 #include "logic/filesystem_constants.h"
35 #include "logic/game.h"
36 #include "logic/game_data_error.h"
37 #include "logic/map_objects/findimmovable.h"
38 #include "logic/map_objects/map_object.h"
39 #include "logic/map_objects/tribes/battle.h"
40 #include "logic/map_objects/tribes/building.h"
41 #include "logic/map_objects/tribes/constructionsite.h"
42 #include "logic/map_objects/tribes/dismantlesite.h"
43 #include "logic/map_objects/tribes/tribe_descr.h"
44 #include "logic/map_objects/tribes/tribes.h"
45 #include "logic/map_objects/tribes/worker.h"
46 #include "logic/map_objects/world/critter.h"
47 #include "logic/map_objects/world/resource_description.h"
48 #include "logic/map_objects/world/world.h"
49 #include "logic/mapregion.h"
50 #include "logic/player.h"
51 #include "logic/playersmanager.h"
52 #include "map_io/map_saver.h"
53 #include "scripting/logic.h"
54 #include "scripting/lua_table.h"
55 #include "sound/sound_handler.h"
56 #include "ui_basic/progresswindow.h"
57 #include "wui/interactive_base.h"
58 #include "wui/interactive_gamebase.h"
59 
60 namespace Widelands {
61 
62 /*
63 ============
64 EditorGameBase::EditorGameBase()
65 
66 initialization
67 ============
68 */
EditorGameBase(LuaInterface * lua_interface)69 EditorGameBase::EditorGameBase(LuaInterface* lua_interface)
70    : gametime_(0),
71      lua_(lua_interface),
72      player_manager_(new PlayersManager(*this)),
73      ibase_(nullptr),
74      loader_ui_(nullptr),
75      game_tips_(nullptr),
76      tmp_fs_(nullptr) {
77 	if (!lua_)  // TODO(SirVer): this is sooo ugly, I can't say
78 		lua_.reset(new LuaEditorInterface(this));
79 }
80 
~EditorGameBase()81 EditorGameBase::~EditorGameBase() {
82 	delete_tempfile();
83 	if (g_sh != nullptr) {
84 		g_sh->remove_fx_set(SoundType::kAmbient);
85 	}
86 }
87 
88 /**
89  * deletes the temporary file/dir
90  * also resets the map filesystem if it points to the temporary file
91  */
delete_tempfile()92 void EditorGameBase::delete_tempfile() {
93 	if (!tmp_fs_) {
94 		return;
95 	}
96 
97 	std::string fs_filename = tmp_fs_->get_basename();
98 	std::string mapfs_filename = map_.filesystem()->get_basename();
99 	if (mapfs_filename == fs_filename)
100 		map_.reset_filesystem();
101 	tmp_fs_.reset();
102 	try {
103 		g_fs->fs_unlink(fs_filename);
104 	} catch (const std::exception& e) {
105 		// if file deletion fails then we have an abandoned file lying around, but otherwise that's
106 		// unproblematic
107 		log("EditorGameBase::delete_tempfile: deleting temporary file/dir failed: %s\n", e.what());
108 	}
109 }
110 
111 /**
112  * creates a new file/dir, saves the map data, and reassigns the map filesystem
113  * does not delete the former temp file if one exists
114  * throws an exception if something goes wrong
115  */
create_tempfile_and_save_mapdata(FileSystem::Type const type)116 void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) {
117 	if (!map_.filesystem()) {
118 		return;
119 	}
120 
121 	// save map data to temporary file and reassign map fs
122 	try {
123 		g_fs->ensure_directory_exists(kTempFileDir);
124 
125 		std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata";
126 		std::string complete_filename = filename + kTempFileExtension;
127 
128 		// if a file with that name already exists, then try a few name modifications
129 		if (g_fs->file_exists(complete_filename)) {
130 			int suffix;
131 			for (suffix = 0; suffix <= 9; suffix++) {
132 				complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension;
133 				if (!g_fs->file_exists(complete_filename))
134 					break;
135 			}
136 			if (suffix > 9) {
137 				throw wexception(
138 				   "EditorGameBase::create_tempfile_and_save_mapdata(): for all considered "
139 				   "filenames a file already existed");
140 			}
141 		}
142 
143 		// create tmp_fs_
144 		tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type));
145 
146 		// save necessary map data (we actually save the whole map)
147 		std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this));
148 		wms->save();
149 
150 		// swap map fs
151 		std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system("."));
152 		map_.swap_filesystem(mapfs);
153 		mapfs.reset();
154 
155 		// This is just a convenience hack:
156 		// If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently
157 		// implemented -
158 		// the file is still in zip mode right now, which means that the file isn't finalized yet,
159 		// i.e.,
160 		// not even a valid zip file until zip mode ends. To force ending the zip mode (thus
161 		// finalizing
162 		// the file)
163 		// we simply perform a (otherwise useless) filesystem request.
164 		// It's not strictly necessary, but this way we get a valid zip file immediately istead of
165 		// at some unkown later point (when an unzip operation happens or a filesystem object
166 		// destructs).
167 		tmp_fs_->file_exists("binary");
168 	} catch (const WException& e) {
169 		log("EditorGameBase: saving map to temporary file failed: %s", e.what());
170 		throw;
171 	}
172 }
173 
think()174 void EditorGameBase::think() {
175 	// TODO(unknown): Get rid of this; replace by a function that just advances gametime
176 	// by a given number of milliseconds
177 }
178 
world() const179 const World& EditorGameBase::world() const {
180 	// Const casts are evil, but this is essentially lazy evaluation and the
181 	// caller should really not modify this.
182 	return *const_cast<EditorGameBase*>(this)->mutable_world();
183 }
184 
mutable_world()185 World* EditorGameBase::mutable_world() {
186 	if (!world_) {
187 		// Lazy initialization of World. We need to create the pointer to the
188 		// world immediately though, because the lua scripts need to have access
189 		// to world through this method already.
190 		ScopedTimer timer("Loading the world took %ums");
191 		world_.reset(new World());
192 
193 		try {
194 			lua_->run_script("world/init.lua");
195 		} catch (const WException& e) {
196 			log("Could not read world information: %s", e.what());
197 			throw;
198 		}
199 
200 		world_->load_graphics();
201 	}
202 	return world_.get();
203 }
204 
tribes() const205 const Tribes& EditorGameBase::tribes() const {
206 	// Const casts are evil, but this is essentially lazy evaluation and the
207 	// caller should really not modify this.
208 	return *const_cast<EditorGameBase*>(this)->mutable_tribes();
209 }
210 
mutable_tribes()211 Tribes* EditorGameBase::mutable_tribes() {
212 	if (!tribes_) {
213 		// We need to make sure that the world is loaded first for some attribute checks in the worker
214 		// programs.
215 		world();
216 
217 		// Lazy initialization of Tribes. We need to create the pointer to the
218 		// tribe immediately though, because the lua scripts need to have access
219 		// to tribes through this method already.
220 		ScopedTimer timer("Loading the tribes took %ums");
221 		tribes_.reset(new Tribes());
222 
223 		try {
224 			lua_->run_script("tribes/init.lua");
225 		} catch (const WException& e) {
226 			log("Could not read tribes information: %s", e.what());
227 			throw;
228 		}
229 	}
230 	return tribes_.get();
231 }
232 
set_ibase(InteractiveBase * const b)233 void EditorGameBase::set_ibase(InteractiveBase* const b) {
234 	ibase_.reset(b);
235 }
236 
get_igbase()237 InteractiveGameBase* EditorGameBase::get_igbase() {
238 	return dynamic_cast<InteractiveGameBase*>(get_ibase());
239 }
240 
241 /// @see PlayerManager class
remove_player(PlayerNumber plnum)242 void EditorGameBase::remove_player(PlayerNumber plnum) {
243 	player_manager_->remove_player(plnum);
244 }
245 
246 /// @see PlayerManager class
add_player(PlayerNumber const player_number,uint8_t const initialization_index,const std::string & tribe,const std::string & name,TeamNumber team)247 Player* EditorGameBase::add_player(PlayerNumber const player_number,
248                                    uint8_t const initialization_index,
249                                    const std::string& tribe,
250                                    const std::string& name,
251                                    TeamNumber team) {
252 	return player_manager_->add_player(player_number, initialization_index, tribe, name, team);
253 }
254 
get_player(const int32_t n) const255 Player* EditorGameBase::get_player(const int32_t n) const {
256 	return player_manager_->get_player(n);
257 }
258 
player(const int32_t n) const259 const Player& EditorGameBase::player(const int32_t n) const {
260 	return player_manager_->player(n);
261 }
262 
inform_players_about_ownership(MapIndex const i,PlayerNumber const new_owner)263 void EditorGameBase::inform_players_about_ownership(MapIndex const i,
264                                                     PlayerNumber const new_owner) {
265 	iterate_players_existing_const(plnum, kMaxPlayers, *this, p) {
266 		Player::Field& player_field = p->fields_[i];
267 		if (1 < player_field.vision) {
268 			player_field.owner = new_owner;
269 		}
270 	}
271 }
inform_players_about_immovable(MapIndex const i,MapObjectDescr const * const descr)272 void EditorGameBase::inform_players_about_immovable(MapIndex const i,
273                                                     MapObjectDescr const* const descr) {
274 	if (!Road::is_road_descr(descr) && !Waterway::is_waterway_descr(descr))
275 		iterate_players_existing_const(plnum, kMaxPlayers, *this, p) {
276 			Player::Field& player_field = p->fields_[i];
277 			if (1 < player_field.vision) {
278 				player_field.map_object_descr = descr;
279 			}
280 		}
281 }
282 
allocate_player_maps()283 void EditorGameBase::allocate_player_maps() {
284 	iterate_players_existing(plnum, kMaxPlayers, *this, p) {
285 		p->allocate_map();
286 	}
287 }
288 
289 /**
290  * Load and prepare detailed game and map data.
291  * This happens once just after the host has started the game / the editor has started and before
292  * the graphics are loaded.
293  */
postload()294 void EditorGameBase::postload() {
295 	create_tempfile_and_save_mapdata(FileSystem::ZIP);
296 
297 	// Postload tribes and world
298 	step_loader_ui(_("Postloading world and tribes…"));
299 
300 	assert(tribes_);
301 	tribes_->postload();
302 	assert(world_);
303 	world_->postload();
304 
305 	for (DescriptionIndex i = 0; i < tribes_->nrtribes(); i++) {
306 		const TribeDescr* tribe = tribes_->get_tribe_descr(i);
307 		for (DescriptionIndex j = 0; j < world_->get_nr_resources(); j++) {
308 			const ResourceDescription* res = world_->get_resource(j);
309 			if (res->detectable()) {
310 				// This function will throw an exception if this tribe doesn't
311 				// have a high enough resource indicator for this resource
312 				tribe->get_resource_indicator(res, res->max_amount());
313 			}
314 		}
315 		// For the "none" indicator
316 		tribe->get_resource_indicator(nullptr, 0);
317 	}
318 
319 	// TODO(unknown): postload players? (maybe)
320 }
321 
322 /**
323  * Load all graphics.
324  * This function needs to be called once at startup when the graphics system is ready.
325  * If the graphics system is to be replaced at runtime, the function must be called after that has
326  * happened.
327  */
load_graphics()328 void EditorGameBase::load_graphics() {
329 	assert(tribes_);
330 	assert(has_loader_ui());
331 	step_loader_ui(_("Loading graphics"));
332 	tribes_->load_graphics();
333 }
334 
create_loader_ui(const std::vector<std::string> & tipstexts,bool show_game_tips,const std::string & background)335 UI::ProgressWindow& EditorGameBase::create_loader_ui(const std::vector<std::string>& tipstexts,
336                                                      bool show_game_tips,
337                                                      const std::string& background) {
338 	assert(!has_loader_ui());
339 	loader_ui_.reset(new UI::ProgressWindow(background));
340 	registered_game_tips_ = tipstexts;
341 	if (show_game_tips) {
342 		game_tips_.reset(registered_game_tips_.empty() ?
343 		                    nullptr :
344 		                    new GameTips(*loader_ui_, registered_game_tips_));
345 	}
346 	return *loader_ui_.get();
347 }
change_loader_ui_background(const std::string & background)348 void EditorGameBase::change_loader_ui_background(const std::string& background) {
349 	assert(has_loader_ui());
350 	assert(game_tips_ == nullptr);
351 	if (background.empty()) {
352 		game_tips_.reset(registered_game_tips_.empty() ?
353 		                    nullptr :
354 		                    new GameTips(*loader_ui_, registered_game_tips_));
355 	} else {
356 		loader_ui_->set_background(background);
357 	}
358 }
step_loader_ui(const std::string & text) const359 void EditorGameBase::step_loader_ui(const std::string& text) const {
360 	if (loader_ui_ != nullptr) {
361 		loader_ui_->step(text);
362 	}
363 }
remove_loader_ui()364 void EditorGameBase::remove_loader_ui() {
365 	assert(loader_ui_ != nullptr);
366 	loader_ui_.reset(nullptr);
367 	game_tips_.reset(nullptr);
368 	registered_game_tips_.clear();
369 }
370 
371 /**
372  * Instantly create a building at the given x/y location. There is no build time.
373  * \li owner  is the player number of the building's owner.
374  * \li idx is the building type index.
375  * \li former_buildings is the list of former buildings
376  */
warp_building(const Coords & c,PlayerNumber const owner,DescriptionIndex const idx,FormerBuildings former_buildings)377 Building& EditorGameBase::warp_building(const Coords& c,
378                                         PlayerNumber const owner,
379                                         DescriptionIndex const idx,
380                                         FormerBuildings former_buildings) {
381 	Player* plr = get_player(owner);
382 	const TribeDescr& tribe = plr->tribe();
383 	return tribe.get_building_descr(idx)->create(*this, plr, c, false, true, former_buildings);
384 }
385 
386 /**
387  * Create a building site at the given x/y location for the given building type.
388  *
389  * \li idx : the building index of the building in construction
390  * \li former_buildings : the former buildings. If it is not empty, this is
391  * an enhancement.
392  */
393 Building&
warp_constructionsite(const Coords & c,PlayerNumber const owner,DescriptionIndex idx,bool loading,FormerBuildings former_buildings,const BuildingSettings * settings,std::map<DescriptionIndex,Quantity> preserved_wares)394 EditorGameBase::warp_constructionsite(const Coords& c,
395                                       PlayerNumber const owner,
396                                       DescriptionIndex idx,
397                                       bool loading,
398                                       FormerBuildings former_buildings,
399                                       const BuildingSettings* settings,
400                                       std::map<DescriptionIndex, Quantity> preserved_wares) {
401 	Player* plr = get_player(owner);
402 	const TribeDescr& tribe = plr->tribe();
403 	ConstructionSite& b = dynamic_cast<ConstructionSite&>(
404 	   tribe.get_building_descr(idx)->create(*this, plr, c, true, loading, former_buildings));
405 	if (settings) {
406 		b.apply_settings(*settings);
407 	}
408 	b.add_dropout_wares(preserved_wares);
409 	return b;
410 }
411 
412 /**
413  * Create a dismantle site
414  * \li former_buildings : the former buildings list. This should not be empty,
415  * except during loading.
416  */
warp_dismantlesite(const Coords & c,PlayerNumber const owner,bool loading,FormerBuildings former_buildings,std::map<DescriptionIndex,Quantity> preserved_wares)417 Building& EditorGameBase::warp_dismantlesite(const Coords& c,
418                                              PlayerNumber const owner,
419                                              bool loading,
420                                              FormerBuildings former_buildings,
421                                              std::map<DescriptionIndex, Quantity> preserved_wares) {
422 	Player* plr = get_player(owner);
423 	const TribeDescr& tribe = plr->tribe();
424 
425 	BuildingDescr const* const descr =
426 	   tribe.get_building_descr(tribe.safe_building_index("dismantlesite"));
427 
428 	upcast(const DismantleSiteDescr, ds_descr, descr);
429 
430 	return *new DismantleSite(*ds_descr, *this, c, plr, loading, former_buildings, preserved_wares);
431 }
432 
433 /**
434  * Instantly create a bob at the given x/y location.
435  */
create_bob(Coords c,const BobDescr & descr,Player * owner)436 Bob& EditorGameBase::create_bob(Coords c, const BobDescr& descr, Player* owner) {
437 	return descr.create(*this, owner, c);
438 }
439 
440 /**
441  * Instantly create a critter at the given x/y location.
442  *
443  */
444 
create_critter(const Coords & c,DescriptionIndex const bob_type_idx,Player * owner)445 Bob& EditorGameBase::create_critter(const Coords& c,
446                                     DescriptionIndex const bob_type_idx,
447                                     Player* owner) {
448 	const BobDescr* descr = dynamic_cast<const BobDescr*>(world().get_critter_descr(bob_type_idx));
449 	return create_bob(c, *descr, owner);
450 }
451 
create_critter(const Coords & c,const std::string & name,Player * owner)452 Bob& EditorGameBase::create_critter(const Coords& c, const std::string& name, Player* owner) {
453 	const BobDescr* descr = dynamic_cast<const BobDescr*>(world().get_critter_descr(name));
454 	if (descr == nullptr)
455 		throw GameDataError("create_critter(%i,%i,%s,%s): critter not found", c.x, c.y, name.c_str(),
456 		                    owner->get_name().c_str());
457 	return create_bob(c, *descr, owner);
458 }
459 
460 /*
461 ===============
462 Create an immovable at the given location.
463 If tribe is not zero, create a immovable of a player (not a PlayerImmovable
464 but an immovable defined by the players tribe)
465 Does not perform any placeability checks.
466 If this immovable was created by a building, 'former_building' can be set in order to display
467 information about it.
468 ===============
469 */
create_immovable(const Coords & c,DescriptionIndex const idx,MapObjectDescr::OwnerType type,Player * owner)470 Immovable& EditorGameBase::create_immovable(const Coords& c,
471                                             DescriptionIndex const idx,
472                                             MapObjectDescr::OwnerType type,
473                                             Player* owner) {
474 	return do_create_immovable(c, idx, type, owner, nullptr);
475 }
476 
create_immovable_with_name(const Coords & c,const std::string & name,MapObjectDescr::OwnerType type,Player * owner,const BuildingDescr * former_building_descr)477 Immovable& EditorGameBase::create_immovable_with_name(const Coords& c,
478                                                       const std::string& name,
479                                                       MapObjectDescr::OwnerType type,
480                                                       Player* owner,
481                                                       const BuildingDescr* former_building_descr) {
482 	DescriptionIndex idx;
483 	if (type == MapObjectDescr::OwnerType::kTribe) {
484 		idx = tribes().immovable_index(name.c_str());
485 		if (!tribes().immovable_exists(idx)) {
486 			throw wexception(
487 			   "EditorGameBase::create_immovable_with_name(%i, %i): %s is not defined for the tribes",
488 			   c.x, c.y, name.c_str());
489 		}
490 	} else {
491 		idx = world().get_immovable_index(name.c_str());
492 		if (idx == INVALID_INDEX) {
493 			throw wexception(
494 			   "EditorGameBase::create_immovable_with_name(%i, %i): %s is not defined for the world",
495 			   c.x, c.y, name.c_str());
496 		}
497 	}
498 	return do_create_immovable(c, idx, type, owner, former_building_descr);
499 }
500 
do_create_immovable(const Coords & c,DescriptionIndex const idx,MapObjectDescr::OwnerType type,Player * owner,const BuildingDescr * former_building_descr)501 Immovable& EditorGameBase::do_create_immovable(const Coords& c,
502                                                DescriptionIndex const idx,
503                                                MapObjectDescr::OwnerType type,
504                                                Player* owner,
505                                                const BuildingDescr* former_building_descr) {
506 	const ImmovableDescr& descr =
507 	   *(type == MapObjectDescr::OwnerType::kTribe ? tribes().get_immovable_descr(idx) :
508 	                                                 world().get_immovable_descr(idx));
509 	assert(&descr);
510 	inform_players_about_immovable(Map::get_index(c, map().get_width()), &descr);
511 	Immovable& immovable = descr.create(*this, c, former_building_descr);
512 	if (owner != nullptr) {
513 		immovable.set_owner(owner);
514 	}
515 	return immovable;
516 }
517 
518 /**
519  * Instantly create a ship at the given x/y location.
520  *
521  * idx is the bob type.
522  */
523 
create_ship(const Coords & c,DescriptionIndex const ship_type_idx,Player * owner)524 Bob& EditorGameBase::create_ship(const Coords& c,
525                                  DescriptionIndex const ship_type_idx,
526                                  Player* owner) {
527 	const BobDescr* descr = dynamic_cast<const BobDescr*>(tribes().get_ship_descr(ship_type_idx));
528 	return create_bob(c, *descr, owner);
529 }
530 
create_ship(const Coords & c,const std::string & name,Player * owner)531 Bob& EditorGameBase::create_ship(const Coords& c, const std::string& name, Player* owner) {
532 	try {
533 		return create_ship(c, tribes().safe_ship_index(name), owner);
534 	} catch (const GameDataError& e) {
535 		throw GameDataError("create_ship(%i,%i,%s,%s): ship not found: %s", c.x, c.y, name.c_str(),
536 		                    owner->get_name().c_str(), e.what());
537 	}
538 }
539 
create_ferry(const Coords & c,Player * owner)540 Bob& EditorGameBase::create_ferry(const Coords& c, Player* owner) {
541 	const BobDescr* descr =
542 	   dynamic_cast<const BobDescr*>(tribes().get_worker_descr(owner->tribe().ferry()));
543 	return create_bob(c, *descr, owner);
544 }
545 
546 /*
547 ================
548 Returns the correct player, creates it
549 with the scenario data when he is not yet created
550 This should only happen in the editor.
551 In the game, this is the same as get_player(). If it returns
552 zero it means that this player is disabled in the game.
553 ================
554 */
get_safe_player(PlayerNumber const n)555 Player* EditorGameBase::get_safe_player(PlayerNumber const n) {
556 	return get_player(n);
557 }
558 
559 /**
560  * Cleanup for load
561  *
562  * make this object ready to load new data
563  */
cleanup_for_load()564 void EditorGameBase::cleanup_for_load() {
565 	step_loader_ui(_("Cleaning up for loading: Map objects (1/3)"));
566 	cleanup_objects();  /// Clean all the stuff up, so we can load.
567 
568 	step_loader_ui(_("Cleaning up for loading: Players (2/3)"));
569 	player_manager_->cleanup();
570 
571 	step_loader_ui(_("Cleaning up for loading: Map (3/3)"));
572 	map_.cleanup();
573 
574 	delete_tempfile();
575 }
576 
set_road(const FCoords & f,uint8_t const direction,RoadSegment const roadtype)577 void EditorGameBase::set_road(const FCoords& f,
578                               uint8_t const direction,
579                               RoadSegment const roadtype) {
580 	const Map& m = map();
581 	const Field& first_field = m[0];
582 	assert(0 <= f.x);
583 	assert(f.x < m.get_width());
584 	assert(0 <= f.y);
585 	assert(f.y < m.get_height());
586 	assert(&first_field <= f.field);
587 	assert(f.field < &first_field + m.max_index());
588 	assert(direction == WALK_SW || direction == WALK_SE || direction == WALK_E);
589 
590 	if (f.field->get_road(direction) == roadtype) {
591 		return;
592 	}
593 	f.field->set_road(direction, roadtype);
594 
595 	FCoords neighbour;
596 	switch (direction) {
597 	case WALK_SW:
598 		neighbour = m.bl_n(f);
599 		break;
600 	case WALK_SE:
601 		neighbour = m.br_n(f);
602 		break;
603 	case WALK_E:
604 		neighbour = m.r_n(f);
605 		break;
606 	default:
607 		NEVER_HERE();
608 	}
609 	MapIndex const i = f.field - &first_field;
610 	MapIndex const neighbour_i = neighbour.field - &first_field;
611 	iterate_players_existing_const(plnum, kMaxPlayers, *this, p) {
612 		Player::Field& first_player_field = *p->fields_.get();
613 		Player::Field& player_field = (&first_player_field)[i];
614 		if (1 < player_field.vision || 1 < (&first_player_field)[neighbour_i].vision) {
615 			switch (direction) {
616 			case WALK_SE:
617 				player_field.r_se = roadtype;
618 				break;
619 			case WALK_SW:
620 				player_field.r_sw = roadtype;
621 				break;
622 			case WALK_E:
623 				player_field.r_e = roadtype;
624 				break;
625 			default:
626 				NEVER_HERE();
627 			}
628 		}
629 	}
630 }
631 
632 /// This unconquers an area. This is only possible, when there is a building
633 /// placed on this node.
unconquer_area(PlayerArea<Area<FCoords>> player_area,PlayerNumber const destroying_player)634 void EditorGameBase::unconquer_area(PlayerArea<Area<FCoords>> player_area,
635                                     PlayerNumber const destroying_player) {
636 	assert(0 <= player_area.x);
637 	assert(player_area.x < map().get_width());
638 	assert(0 <= player_area.y);
639 	assert(player_area.y < map().get_height());
640 	assert(&map()[0] <= player_area.field);
641 	assert(player_area.field < &map()[map().max_index()]);
642 	assert(0 < player_area.player_number);
643 	assert(player_area.player_number <= map().get_nrplayers());
644 
645 	//  Here must be a building.
646 	assert(
647 	   dynamic_cast<const Building&>(*map().get_immovable(player_area)).owner().player_number() ==
648 	   player_area.player_number);
649 
650 	//  step 1: unconquer area of this building
651 	do_conquer_area(player_area, false, destroying_player);
652 }
653 
654 /// This conquers a given area because of a new (military) building that is set
655 /// there.
conquer_area(PlayerArea<Area<FCoords>> player_area,bool conquer_guarded_location)656 void EditorGameBase::conquer_area(PlayerArea<Area<FCoords>> player_area,
657                                   bool conquer_guarded_location) {
658 	assert(0 <= player_area.x);
659 	assert(player_area.x < map().get_width());
660 	assert(0 <= player_area.y);
661 	assert(player_area.y < map().get_height());
662 	assert(&map()[0] <= player_area.field);
663 	assert(player_area.field < &map()[map().max_index()]);
664 	assert(0 < player_area.player_number);
665 	assert(player_area.player_number <= map().get_nrplayers());
666 
667 	do_conquer_area(player_area, true, 0, conquer_guarded_location);
668 }
669 
change_field_owner(const FCoords & fc,PlayerNumber const new_owner)670 void EditorGameBase::change_field_owner(const FCoords& fc, PlayerNumber const new_owner) {
671 	const Field& first_field = map()[0];
672 
673 	PlayerNumber const old_owner = fc.field->get_owned_by();
674 	if (old_owner == new_owner) {
675 		return;
676 	}
677 
678 	if (old_owner) {
679 		Notifications::publish(
680 		   NoteFieldPossession(fc, NoteFieldPossession::Ownership::LOST, get_player(old_owner)));
681 	}
682 
683 	fc.field->set_owned_by(new_owner);
684 
685 	// TODO(unknown): the player should do this when it gets the NoteFieldPossession.
686 	// This means also sending a note when new_player = 0, i.e. the field is no
687 	// longer owned.
688 	inform_players_about_ownership(fc.field - &first_field, new_owner);
689 
690 	if (new_owner) {
691 		Notifications::publish(
692 		   NoteFieldPossession(fc, NoteFieldPossession::Ownership::GAINED, get_player(new_owner)));
693 	}
694 }
695 
conquer_area_no_building(PlayerArea<Area<FCoords>> player_area)696 void EditorGameBase::conquer_area_no_building(PlayerArea<Area<FCoords>> player_area) {
697 	assert(0 <= player_area.x);
698 	assert(player_area.x < map().get_width());
699 	assert(0 <= player_area.y);
700 	assert(player_area.y < map().get_height());
701 	assert(&map()[0] <= player_area.field);
702 	assert(player_area.field < &map()[0] + map().max_index());
703 	assert(0 < player_area.player_number);
704 	assert(player_area.player_number <= map().get_nrplayers());
705 	MapRegion<Area<FCoords>> mr(map(), player_area);
706 	do {
707 		change_field_owner(mr.location(), player_area.player_number);
708 	} while (mr.advance(map()));
709 
710 	//  This must reach two steps beyond the conquered area to adjust the borders
711 	//  of neighbour players.
712 	player_area.radius += 2;
713 	map_.recalc_for_field_area(*this, player_area);
714 }
715 
716 /// Conquers the given area for that player; does the actual work.
717 /// Additionally, it updates the visible area for that player.
718 // TODO(unknown): this needs a more fine grained refactoring
719 // for example scripts will want to (un)conquer area of non oval shape
720 // or give area back to the neutral player (this is very important for the Lua
721 // testsuite).
do_conquer_area(PlayerArea<Area<FCoords>> player_area,bool const conquer,PlayerNumber const preferred_player,bool const conquer_guarded_location_by_superior_influence,bool const neutral_when_no_influence,bool const neutral_when_competing_influence)722 void EditorGameBase::do_conquer_area(PlayerArea<Area<FCoords>> player_area,
723                                      bool const conquer,
724                                      PlayerNumber const preferred_player,
725                                      bool const conquer_guarded_location_by_superior_influence,
726                                      bool const neutral_when_no_influence,
727                                      bool const neutral_when_competing_influence) {
728 	assert(0 <= player_area.x);
729 	assert(player_area.x < map().get_width());
730 	assert(0 <= player_area.y);
731 	assert(player_area.y < map().get_height());
732 	const Field& first_field = map()[0];
733 	assert(&first_field <= player_area.field);
734 	assert(player_area.field < &first_field + map().max_index());
735 	assert(0 < player_area.player_number);
736 	assert(player_area.player_number <= map().get_nrplayers());
737 	assert(preferred_player <= map().get_nrplayers());
738 	assert(!conquer || !preferred_player);
739 	Player* conquering_player = get_player(player_area.player_number);
740 	MapRegion<Area<FCoords>> mr(map(), player_area);
741 	do {
742 		MapIndex const index = mr.location().field - &first_field;
743 		MilitaryInfluence const influence =
744 		   map().calc_influence(mr.location(), Area<>(player_area, player_area.radius));
745 
746 		PlayerNumber const owner = mr.location().field->get_owned_by();
747 		if (conquer) {
748 			//  adds the influence
749 			MilitaryInfluence new_influence_modified = conquering_player->military_influence(index) +=
750 			   influence;
751 			if (owner && !conquer_guarded_location_by_superior_influence)
752 				new_influence_modified = 1;
753 			if (!owner || player(owner).military_influence(index) < new_influence_modified) {
754 				change_field_owner(mr.location(), player_area.player_number);
755 			}
756 		} else if (!(conquering_player->military_influence(index) -= influence) &&
757 		           owner == player_area.player_number) {
758 			//  The player completely lost influence over the location, which he
759 			//  owned. Now we must see if some other player has influence and if
760 			//  so, transfer the ownership to that player.
761 			PlayerNumber best_player;
762 			if (preferred_player && player(preferred_player).military_influence(index))
763 				best_player = preferred_player;
764 			else {
765 				best_player = neutral_when_no_influence ? 0 : player_area.player_number;
766 				MilitaryInfluence highest_military_influence = 0;
767 				PlayerNumber const nr_players = map().get_nrplayers();
768 				iterate_players_existing_const(p, nr_players, *this, plr) {
769 					if (MilitaryInfluence const value = plr->military_influence(index)) {
770 						if (value > highest_military_influence) {
771 							highest_military_influence = value;
772 							best_player = p;
773 						} else if (value == highest_military_influence) {
774 							best_player = neutral_when_competing_influence ? 0 : player_area.player_number;
775 						}
776 					}
777 				}
778 			}
779 			if (best_player != player_area.player_number) {
780 				change_field_owner(mr.location(), best_player);
781 			}
782 		}
783 	} while (mr.advance(map()));
784 
785 	// This must reach two steps beyond the conquered area to adjust the borders
786 	// of neighbour players.
787 	player_area.radius += 2;
788 	map_.recalc_for_field_area(*this, player_area);
789 
790 	//  Deal with player immovables in the lost area
791 	//  Players are not allowed to have their immovables on their borders.
792 	//  Therefore the area must be enlarged before calling
793 	//  cleanup_playerimmovables_area, so that those new border locations are
794 	//  covered.
795 	// TODO(SirVer): In the editor, no buildings should burn down when a military
796 	// building is removed. Check this again though
797 	if (is_a(Game, this)) {
798 		cleanup_playerimmovables_area(player_area);
799 	}
800 }
801 
802 /// Makes sure that buildings cannot exist outside their owner's territory.
cleanup_playerimmovables_area(PlayerArea<Area<FCoords>> const area)803 void EditorGameBase::cleanup_playerimmovables_area(PlayerArea<Area<FCoords>> const area) {
804 	std::vector<ImmovableFound> immovables;
805 	std::vector<PlayerImmovable*> burnlist;
806 
807 	//  find all immovables that need fixing
808 	map_.find_immovables(*this, area, &immovables, FindImmovablePlayerImmovable());
809 
810 	for (const ImmovableFound& temp_imm : immovables) {
811 		upcast(PlayerImmovable, imm, temp_imm.object);
812 		if (!map_[temp_imm.coords].is_interior(imm->owner().player_number())) {
813 			if (std::find(burnlist.begin(), burnlist.end(), imm) == burnlist.end()) {
814 				burnlist.push_back(imm);
815 			}
816 		}
817 	}
818 
819 	//  fix all immovables
820 	upcast(Game, game, this);
821 	for (PlayerImmovable* temp_imm : burnlist) {
822 		if (upcast(Building, building, temp_imm))
823 			building->set_defeating_player(area.player_number);
824 		else if (upcast(Flag, flag, temp_imm))
825 			if (Building* const flag_building = flag->get_building())
826 				flag_building->set_defeating_player(area.player_number);
827 		if (game)
828 			temp_imm->schedule_destroy(*game);
829 		else
830 			temp_imm->remove(*this);
831 	}
832 }
833 }  // namespace Widelands
834