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/map_objects/bob.h"
21 
22 #include "base/macros.h"
23 #include "base/math.h"
24 #include "base/wexception.h"
25 #include "economy/roadbase.h"
26 #include "economy/route.h"
27 #include "economy/transfer.h"
28 #include "graphic/rendertarget.h"
29 #include "io/fileread.h"
30 #include "io/filewrite.h"
31 #include "logic/game.h"
32 #include "logic/game_data_error.h"
33 #include "logic/map_objects/backtrace.h"
34 #include "logic/map_objects/checkstep.h"
35 #include "logic/map_objects/findbob.h"
36 #include "logic/map_objects/tribes/ship.h"
37 #include "logic/map_objects/tribes/soldier.h"
38 #include "logic/map_objects/tribes/tribe_descr.h"
39 #include "logic/map_objects/world/critter.h"
40 #include "logic/path.h"
41 #include "logic/player.h"
42 #include "logic/widelands_geometry_io.h"
43 #include "map_io/map_object_loader.h"
44 #include "map_io/map_object_saver.h"
45 #include "wui/mapviewpixelconstants.h"
46 
47 namespace Widelands {
48 
BobDescr(const std::string & init_descname,const MapObjectType init_type,MapObjectDescr::OwnerType owner_type,const LuaTable & table)49 BobDescr::BobDescr(const std::string& init_descname,
50                    const MapObjectType init_type,
51                    MapObjectDescr::OwnerType owner_type,
52                    const LuaTable& table)
53    : MapObjectDescr(init_type, table.get_string("name"), init_descname, table),
54      owner_type_(owner_type),
55      // Only tribe bobs have a vision range, since it would be irrelevant for world bobs.
56      vision_range_(owner_type == MapObjectDescr::OwnerType::kTribe ? table.get_int("vision_range") :
57                                                                      0) {
58 	if (!is_animation_known("idle")) {
59 		throw GameDataError("Bob %s has no idle animation", table.get_string("name").c_str());
60 	}
61 }
62 
63 /**
64  * Only tribe bobs have a vision range, since it would be irrelevant
65  * for world bobs.
66  *
67  * \returns radius (in fields) of area that the bob can see
68  */
vision_range() const69 uint32_t BobDescr::vision_range() const {
70 	return vision_range_;
71 }
72 
73 /**
74  * Create a bob of this type
75  */
create(EditorGameBase & egbase,Player * const owner,const Coords & coords) const76 Bob& BobDescr::create(EditorGameBase& egbase, Player* const owner, const Coords& coords) const {
77 	Bob& bob = create_object();
78 	bob.set_owner(owner);
79 	bob.set_position(egbase, coords);
80 	bob.init(egbase);
81 
82 	return bob;
83 }
84 
Bob(const BobDescr & init_descr)85 Bob::Bob(const BobDescr& init_descr)
86    : MapObject(&init_descr),
87      position_(FCoords(Coords(0, 0), nullptr)),  // not linked anywhere
88      linknext_(nullptr),
89      linkpprev_(nullptr),
90      anim_(0),
91      animstart_(0),
92      walking_(IDLE),
93      walkstart_(0),
94      walkend_(0),
95      actid_(0),
96      actscheduled_(false),
97      in_act_(false) {
98 }
99 
100 /**
101  * Cleanup an object. Removes map links
102  */
~Bob()103 Bob::~Bob() {
104 	if (position_.field) {
105 		molog("MapObject::~MapObject: pos_.field != 0, cleanup() not "
106 		      "called!\n");
107 		abort();
108 	}
109 }
110 
111 /**
112  * Initialize the object
113  *
114  * \note Make sure you call this from derived classes!
115  */
init(EditorGameBase & egbase)116 bool Bob::init(EditorGameBase& egbase) {
117 	MapObject::init(egbase);
118 
119 	if (upcast(Game, game, &egbase)) {
120 		schedule_act(*game, 1);
121 	} else {
122 		// In editor: play idle task forever
123 		set_animation(egbase, descr().get_animation("idle", this));
124 	}
125 	return true;
126 }
127 
128 /**
129  * Perform independent cleanup as necessary.
130  */
cleanup(EditorGameBase & egbase)131 void Bob::cleanup(EditorGameBase& egbase) {
132 	while (!stack_.empty()) {  //  bobs in the editor do not have tasks
133 		do_pop_task(dynamic_cast<Game&>(egbase));
134 	}
135 
136 	set_owner(nullptr);  // implicitly remove ourselves from owner's map
137 
138 	if (position_.field) {
139 		position_.field = nullptr;
140 		*linkpprev_ = linknext_;
141 		if (linknext_)
142 			linknext_->linkpprev_ = linkpprev_;
143 	}
144 
145 	MapObject::cleanup(egbase);
146 }
147 
148 /**
149  * Called by Cmd_Queue when a CMD_ACT event is triggered.
150  * Hand the acting over to the task.
151  *
152  * Change to the next task if necessary.
153  */
act(Game & game,uint32_t const data)154 void Bob::act(Game& game, uint32_t const data) {
155 	// Eliminate spurious calls of act().
156 	// These calls are to be expected and perfectly normal, e.g. when a carrier's
157 	// idle task is interrupted by the request to pick up a ware from a flag.
158 	if (data != actid_)
159 		return;
160 
161 	++actid_;
162 	actscheduled_ = false;
163 
164 	if (stack_.empty()) {
165 		signal_ = "";
166 		init_auto_task(game);
167 
168 		if (stack_.empty())
169 			throw wexception("MO(%u): init_auto_task() failed to set a task", serial());
170 		if (!actscheduled_)
171 			throw wexception("MO(%u): init_auto_task() failed to schedule something", serial());
172 
173 		return;
174 	}
175 
176 	do_act(game);
177 }
178 
179 /**
180  * Perform the actual call to update() as appropriate.
181  */
do_act(Game & game)182 void Bob::do_act(Game& game) {
183 	assert(!in_act_);
184 	assert(stack_.size());
185 
186 	in_act_ = true;
187 
188 	const Task& task = *top_state().task;
189 
190 	(this->*task.update)(game, top_state());
191 
192 	if (!actscheduled_)
193 		throw wexception("MO(%u): update[%s] failed to act", serial(), task.name);
194 
195 	in_act_ = false;
196 }
197 
198 /**
199  * Kill self ASAP.
200  */
schedule_destroy(Game & game)201 void Bob::schedule_destroy(Game& game) {
202 	MapObject::schedule_destroy(game);
203 	++actid_;  // to skip over any updates that may be scheduled
204 	actscheduled_ = true;
205 }
206 
207 /**
208  * Schedule a new act for the current task. All other pending acts are
209  * cancelled.
210  */
schedule_act(Game & game,uint32_t tdelta)211 void Bob::schedule_act(Game& game, uint32_t tdelta) {
212 	MapObject::schedule_act(game, tdelta, actid_);
213 	actscheduled_ = true;
214 }
215 
216 /**
217  * Explicitly state that we don't want to act.
218  */
skip_act()219 void Bob::skip_act() {
220 	assert(in_act_);
221 
222 	actscheduled_ = true;
223 }
224 
225 /**
226  * Push a new task onto the stack.
227  *
228  * push_task() itself does not call any functions of the task, so the caller
229  * can fill the state information with parameters for the task.
230  */
push_task(Game & game,const Task & task,uint32_t const tdelta)231 void Bob::push_task(Game& game, const Task& task, uint32_t const tdelta) {
232 	assert(!task.unique || !get_state(task));
233 	assert(in_act_ || stack_.empty());
234 
235 	stack_.push_back(State(&task));
236 	schedule_act(game, tdelta);
237 }
238 
239 /**
240  * Actually pop the top-most task, but don't schedule anything.
241  */
do_pop_task(Game & game)242 void Bob::do_pop_task(Game& game) {
243 	State& state = top_state();
244 
245 	if (state.task->pop)
246 		(this->*state.task->pop)(game, state);
247 
248 	delete state.path;
249 	delete state.route;
250 
251 	stack_.pop_back();
252 }
253 
254 /**
255  * Remove the current task from the stack.
256  *
257  * pop_task() itself does not call any parent task functions, but it ensures
258  * that it will happen.
259  */
pop_task(Game & game)260 void Bob::pop_task(Game& game) {
261 	assert(in_act_);
262 
263 	do_pop_task(game);
264 
265 	schedule_act(game, 10);
266 }
267 
268 /**
269  * Get the bottom-most (usually the only) state of this task from the stack.
270  * \return 0 if this task is not running at all.
271  */
get_state(const Task & task)272 Bob::State* Bob::get_state(const Task& task) {
273 	std::vector<State>::iterator it = stack_.end();
274 
275 	while (it != stack_.begin()) {
276 		--it;
277 
278 		if (it->task == &task)
279 			return &*it;
280 	}
281 
282 	return nullptr;
283 }
284 
get_state(const Task & task) const285 Bob::State const* Bob::get_state(const Task& task) const {
286 	std::vector<State>::const_iterator it = stack_.end();
287 
288 	while (it != stack_.begin()) {
289 		--it;
290 
291 		if (it->task == &task)
292 			return &*it;
293 	}
294 
295 	return nullptr;
296 }
297 
298 /**
299  * Mark the current signal as handled.
300  */
signal_handled()301 void Bob::signal_handled() {
302 	assert(in_act_);
303 
304 	signal_.clear();
305 }
306 
307 /**
308  * Send the given signal to this bob.
309  *
310  * Signal delivery is asynchronous, i.e. this functions guarantees that
311  * the top-most task's update() function is called, but only with a certain
312  * delay.
313  *
314  * This function also calls all tasks' signal_immediate() function immediately.
315  *
316  * \param g the \ref Game object
317  * \param sig the signal string
318  */
send_signal(Game & game,char const * const sig)319 void Bob::send_signal(Game& game, char const* const sig) {
320 	assert(*sig);  //  use set_signal() for signal removal
321 
322 	for (uint32_t i = 0; i < stack_.size(); ++i) {
323 		State& state = stack_[i];
324 
325 		if (state.task->signal_immediate)
326 			(this->*state.task->signal_immediate)(game, state, sig);
327 	}
328 
329 	signal_ = sig;
330 	schedule_act(game, 10);
331 }
332 
333 /**
334  * Force a complete reset of the state stack.
335  *
336  * The state stack is emptied completely, and an init auto task is scheduled
337  * as if the Bob has just been created and initialized.
338  */
reset_tasks(Game & game)339 void Bob::reset_tasks(Game& game) {
340 	while (!stack_.empty())
341 		do_pop_task(game);
342 
343 	signal_.clear();
344 
345 	++actid_;
346 	schedule_act(game, 10);
347 }
348 
349 /**
350  * Wait a time or indefinitely.
351  *
352  * Every signal can interrupt this task.  No signals are caught.
353  */
354 
355 Bob::Task const Bob::taskIdle = {"idle", &Bob::idle_update,
356                                  nullptr,  // signal_immediate
357                                  nullptr, true};
358 
359 /**
360  * Start an idle phase, using the given animation
361  *
362  * If the timeout is a positive value, the idle phase stops after the given
363  * time.
364  *
365  * This task always succeeds unless interrupted.
366  */
start_task_idle(Game & game,uint32_t const anim,int32_t const timeout)367 void Bob::start_task_idle(Game& game, uint32_t const anim, int32_t const timeout) {
368 	assert(timeout < 0 || timeout > 0);
369 
370 	set_animation(game, anim);
371 
372 	push_task(game, taskIdle);
373 
374 	top_state().ivar1 = timeout;
375 }
376 
idle_update(Game & game,State & state)377 void Bob::idle_update(Game& game, State& state) {
378 	if (!state.ivar1 || get_signal().size())
379 		return pop_task(game);
380 
381 	if (state.ivar1 > 0)
382 		schedule_act(game, state.ivar1);
383 	else
384 		skip_act();
385 
386 	state.ivar1 = 0;
387 }
388 
is_idle()389 bool Bob::is_idle() {
390 	return get_state(taskIdle);
391 }
392 
393 /**
394  * Move along a predefined path.
395  * \par ivar1 the step number.
396  * \par ivar2 non-zero if we should force moving onto the final field.
397  * \par ivar3 number of steps to take maximally or -1
398  *
399  * Sets the following signal(s):
400  * "fail" - cannot move along the given path
401  */
402 Bob::Task const Bob::taskMovepath = {"movepath", &Bob::movepath_update,
403                                      nullptr,  // signal_immediate
404                                      nullptr, true};
405 
406 struct BlockedTracker {
407 	struct CoordData {
408 		Coords coord;
409 		int dist;
410 	};
411 	// Distance-based ordering as a heuristic for unblock()
412 	struct CoordOrdering {
operator ()Widelands::BlockedTracker::CoordOrdering413 		bool operator()(const CoordData& a, const CoordData& b) const {
414 			if (a.dist != b.dist)
415 				return a.dist < b.dist;
416 			return std::forward_as_tuple(a.coord.y, a.coord.x) <
417 			       std::forward_as_tuple(b.coord.y, b.coord.x);
418 		}
419 	};
420 	using Cache = std::map<CoordData, bool, CoordOrdering>;
421 
BlockedTrackerWidelands::BlockedTracker422 	BlockedTracker(Game& game, Bob& bob, const Coords& finaldest)
423 	   : game_(game), bob_(bob), map_(game.map()), finaldest_(finaldest) {
424 		nrblocked_ = 0;
425 		disabled_ = false;
426 	}
427 
428 	// This heuristic tries to unblock fields that are close to the destination,
429 	// in the hope that subsequent pathfinding will find a way to bring us
430 	// closer, if not complete to, the destination
unblockWidelands::BlockedTracker431 	void unblock() {
432 		uint32_t origblocked = nrblocked_;
433 		int unblockprob = nrblocked_;
434 
435 		for (Cache::iterator it = nodes_.begin(); it != nodes_.end() && unblockprob > 0; ++it) {
436 			if (it->second) {
437 				if (static_cast<int32_t>(game_.logic_rand() % origblocked) < unblockprob) {
438 					it->second = false;
439 					--nrblocked_;
440 					unblockprob -= 2;
441 				}
442 			}
443 		}
444 	}
445 
is_blockedWidelands::BlockedTracker446 	bool is_blocked(const FCoords& field) {
447 		if (disabled_)
448 			return false;
449 
450 		CoordData cd;
451 		cd.coord = field;
452 		cd.dist = map_.calc_distance(field, finaldest_);
453 
454 		Cache::iterator it = nodes_.find(cd);
455 		if (it != nodes_.end())
456 			return it->second;
457 
458 		bool const blocked = bob_.check_node_blocked(game_, field, false);
459 		nodes_.insert(std::make_pair(cd, blocked));
460 		if (blocked)
461 			++nrblocked_;
462 		return blocked;
463 	}
464 
465 	Game& game_;
466 	Bob& bob_;
467 	const Map& map_;
468 	Coords finaldest_;
469 	Cache nodes_;
470 	int nrblocked_;
471 	bool disabled_;
472 };
473 
474 struct CheckStepBlocked {
CheckStepBlockedWidelands::CheckStepBlocked475 	explicit CheckStepBlocked(BlockedTracker& tracker) : tracker_(tracker) {
476 	}
477 
allowedWidelands::CheckStepBlocked478 	bool allowed(const Map&, FCoords, FCoords end, int32_t, CheckStep::StepId) const {
479 		if (end == tracker_.finaldest_)
480 			return true;
481 
482 		return !tracker_.is_blocked(end);
483 	}
reachable_destWidelands::CheckStepBlocked484 	bool reachable_dest(const Map&, FCoords) const {
485 		return true;
486 	}
487 
488 	BlockedTracker& tracker_;
489 };
490 
491 /**
492  * Start moving to the given destination. persist is the same parameter as
493  * for Map::findpath().
494  *
495  * \return false if no path could be found.
496  *
497  * \note The task finishes once the goal has been reached. It may fail.
498  *
499  * \param only_step defines how many steps should be taken, before this
500  * returns as a success
501  */
start_task_movepath(Game & game,const Coords & dest,int32_t const persist,const DirAnimations & anims,bool const forceonlast,int32_t const only_step,bool const forceall)502 bool Bob::start_task_movepath(Game& game,
503                               const Coords& dest,
504                               int32_t const persist,
505                               const DirAnimations& anims,
506                               bool const forceonlast,
507                               int32_t const only_step,
508                               bool const forceall) {
509 	Path path;
510 	BlockedTracker tracker(game, *this, dest);
511 	CheckStepAnd cstep;
512 
513 	if (forceonlast)
514 		cstep.add(CheckStepWalkOn(descr().movecaps(), true));
515 	else
516 		cstep.add(CheckStepDefault(descr().movecaps()));
517 	cstep.add(CheckStepBlocked(tracker));
518 
519 	if (forceall)
520 		tracker.disabled_ = true;
521 
522 	const Map& map = game.map();
523 	if (map.findpath(position_, dest, persist, path, cstep) < 0) {
524 		if (!tracker.nrblocked_)
525 			return false;
526 
527 		tracker.unblock();
528 		if (map.findpath(position_, dest, persist, path, cstep) < 0) {
529 			if (!tracker.nrblocked_)
530 				return false;
531 
532 			tracker.disabled_ = true;
533 			if (map.findpath(position_, dest, persist, path, cstep) < 0)
534 				return false;
535 		}
536 	}
537 
538 	push_task(game, taskMovepath);
539 	State& state = top_state();
540 	state.path = new Path(path);
541 	state.ivar1 = 0;  // step #
542 	state.ivar2 = forceonlast ? 1 : (forceall ? 2 : 0);
543 	state.ivar3 = only_step;
544 	state.diranims = anims;
545 	return true;
546 }
547 
548 /**
549  * Start moving along the given, precalculated path.
550  */
start_task_movepath(Game & game,const Path & path,const DirAnimations & anims,bool const forceonlast,int32_t const only_step)551 void Bob::start_task_movepath(Game& game,
552                               const Path& path,
553                               const DirAnimations& anims,
554                               bool const forceonlast,
555                               int32_t const only_step) {
556 	assert(path.get_start() == get_position());
557 
558 	push_task(game, taskMovepath);
559 	State& state = top_state();
560 	state.path = new Path(path);
561 	state.ivar1 = 0;
562 	state.ivar2 = forceonlast ? 1 : 0;
563 	state.ivar3 = only_step;
564 	state.diranims = anims;
565 }
566 
567 /**
568  * Move to the given index on the given path. The current position must be on
569  * the given path.
570  *
571  * \return true if a task has been started, or false if we already are on
572  * the given path index.
573  */
start_task_movepath(Game & game,const Path & origpath,int32_t const index,const DirAnimations & anims,bool const forceonlast,int32_t const only_step)574 bool Bob::start_task_movepath(Game& game,
575                               const Path& origpath,
576                               int32_t const index,
577                               const DirAnimations& anims,
578                               bool const forceonlast,
579                               int32_t const only_step) {
580 	CoordPath path(game.map(), origpath);
581 	int32_t const curidx = path.get_index(get_position());
582 
583 	if (curidx == -1) {
584 		molog("ERROR: (%i, %i) is not on the given path:\n", get_position().x, get_position().y);
585 		for (const Coords& coords : path.get_coords()) {
586 			molog("* (%i, %i)\n", coords.x, coords.y);
587 		}
588 		log_general_info(game);
589 		log("%s", get_backtrace().c_str());
590 		throw wexception("MO(%u): start_task_movepath(index): not on path", serial());
591 	}
592 
593 	if (curidx != index) {
594 		if (curidx < index) {
595 			path.truncate(index);
596 			path.trim_start(curidx);
597 		} else {
598 			path.truncate(curidx);
599 			path.trim_start(index);
600 			path.reverse();
601 		}
602 		start_task_movepath(game, path, anims, forceonlast, only_step);
603 		return true;
604 	}
605 
606 	return false;
607 }
608 
movepath_update(Game & game,State & state)609 void Bob::movepath_update(Game& game, State& state) {
610 	if (get_signal().size()) {
611 		return pop_task(game);
612 	}
613 
614 	assert(state.ivar1 >= 0);
615 	Path const* const path = state.path;
616 
617 	if (!path)
618 		// probably success; this can happen when loading a game
619 		// that contains a zero-length path.
620 		return pop_task(game);
621 
622 	if (static_cast<Path::StepVector::size_type>(state.ivar1) >= path->get_nsteps()) {
623 		assert(position_ == path->get_end());
624 		return pop_task(game);  //  success
625 	} else if (state.ivar1 == state.ivar3)
626 		// We have stepped all steps that we were asked for.
627 		// This is some kind of success, though we do not are were we wanted
628 		// to go
629 		return pop_task(game);
630 
631 	Direction const dir = (*path)[state.ivar1];
632 
633 	// Slowing down a ship if two or more on same spot
634 	// Using probability of 1/8 and pausing it for 5, 10 or 15 seconds
635 	if (game.logic_rand() % 8 == 0) {
636 		if (is_a(Ship, this)) {
637 			const uint32_t ships_count = game.map().find_bobs(
638 			   game, Widelands::Area<Widelands::FCoords>(get_position(), 0), nullptr, FindBobShip());
639 			assert(ships_count > 0);
640 			if (ships_count > 1) {
641 				molog("Pausing the ship because %d ships on the same spot\n", ships_count);
642 				return start_task_idle(
643 				   game, state.diranims.get_animation(dir), ((game.logic_rand() % 3) + 1) * 5000);
644 			}
645 		}
646 	}
647 
648 	bool forcemove =
649 	   (state.ivar2 &&
650 	    static_cast<Path::StepVector::size_type>(state.ivar1) + 1 == path->get_nsteps());
651 
652 	++state.ivar1;
653 	return start_task_move(game, dir, state.diranims, state.ivar2 == 2 ? true : forcemove);
654 	// Note: state pointer is invalid beyond this point
655 }
656 
657 /**
658  * Move into one direction for one step.
659  * \li ivar1: direction
660  * \li ivar2: non-zero if the move should be forced
661  */
662 Bob::Task const Bob::taskMove = {"move", &Bob::move_update, nullptr, nullptr, true};
663 
664 /**
665  * Move into the given direction, without passability checks.
666  */
start_task_move(Game & game,int32_t const dir,const DirAnimations & anims,bool const forcemove)667 void Bob::start_task_move(Game& game,
668                           int32_t const dir,
669                           const DirAnimations& anims,
670                           bool const forcemove) {
671 	int32_t const tdelta =
672 	   start_walk(game, static_cast<WalkingDir>(dir), anims.get_animation(dir), forcemove);
673 	if (tdelta < 0)
674 		return send_signal(game, tdelta == -2 ? "blocked" : "fail");
675 	push_task(game, taskMove, tdelta);
676 }
677 
move_update(Game & game,State &)678 void Bob::move_update(Game& game, State&) {
679 	if (static_cast<uint32_t>(walkend_) <= game.get_gametime()) {
680 		end_walk();
681 		return pop_task(game);
682 	} else
683 		//  Only end the task once we've actually completed the step
684 		// Ignore signals until then
685 		return schedule_act(game, walkend_ - game.get_gametime());
686 }
687 
688 // Calculates the actual position to draw on from the base node position. This
689 // function takes walking etc. into account.
690 //
691 // pos is the location, in pixels, of the node position_ on screen with scale
692 // and height taken into account.
calc_drawpos(const EditorGameBase & game,const Vector2f & field_on_dst,const float scale) const693 Vector2f Bob::calc_drawpos(const EditorGameBase& game,
694                            const Vector2f& field_on_dst,
695                            const float scale) const {
696 	const Map& map = game.map();
697 	const FCoords end = position_;
698 	FCoords start;
699 	Vector2f spos = field_on_dst;
700 	Vector2f epos = field_on_dst;
701 
702 	const float triangle_w = kTriangleWidth * scale;
703 	const float triangle_h = kTriangleHeight * scale;
704 
705 	bool bridge = false;
706 	switch (walking_) {
707 	case WALK_NW:
708 		map.get_brn(end, &start);
709 		spos.x += triangle_w / 2.f;
710 		spos.y += triangle_h;
711 		bridge = is_bridge_segment(end.field->road_southeast);
712 		break;
713 	case WALK_NE:
714 		map.get_bln(end, &start);
715 		spos.x -= triangle_w / 2.f;
716 		spos.y += triangle_h;
717 		bridge = is_bridge_segment(end.field->road_southwest);
718 		break;
719 	case WALK_W:
720 		map.get_rn(end, &start);
721 		spos.x += triangle_w;
722 		bridge = is_bridge_segment(end.field->road_east);
723 		break;
724 	case WALK_E:
725 		map.get_ln(end, &start);
726 		spos.x -= triangle_w;
727 		bridge = is_bridge_segment(start.field->road_east);
728 		break;
729 	case WALK_SW:
730 		map.get_trn(end, &start);
731 		spos.x += triangle_w / 2.f;
732 		spos.y -= triangle_h;
733 		bridge = is_bridge_segment(start.field->road_southwest);
734 		break;
735 	case WALK_SE:
736 		map.get_tln(end, &start);
737 		spos.x -= triangle_w / 2.f;
738 		spos.y -= triangle_h;
739 		bridge = is_bridge_segment(start.field->road_southeast);
740 		break;
741 
742 	case IDLE:
743 		start.field = nullptr;
744 		break;
745 	}
746 
747 	if (start.field) {
748 		spos.y += end.field->get_height() * kHeightFactor * scale;
749 		spos.y -= start.field->get_height() * kHeightFactor * scale;
750 
751 		assert(static_cast<uint32_t>(walkstart_) <= game.get_gametime());
752 		assert(walkstart_ < walkend_);
753 		const float f = math::clamp(
754 		   static_cast<float>(game.get_gametime() - walkstart_) / (walkend_ - walkstart_), 0.f, 1.f);
755 		epos.x = f * epos.x + (1.f - f) * spos.x;
756 		epos.y = f * epos.y + (1.f - f) * spos.y;
757 		if (bridge) {
758 			epos.y -= game.player(end.field->get_owned_by()).tribe().bridge_height() * scale *
759 			          (1 - 4 * (f - 0.5f) * (f - 0.5f));
760 		}
761 	}
762 	return epos;
763 }
764 
765 /// It LERPs between start and end position when we are walking.
766 /// Note that the current node is actually the node that we are walking to, not
767 /// the the one that we start from.
draw(const EditorGameBase & egbase,const InfoToDraw &,const Vector2f & field_on_dst,const Widelands::Coords & coords,const float scale,RenderTarget * dst) const768 void Bob::draw(const EditorGameBase& egbase,
769                const InfoToDraw&,
770                const Vector2f& field_on_dst,
771                const Widelands::Coords& coords,
772                const float scale,
773                RenderTarget* dst) const {
774 	if (!anim_) {
775 		return;
776 	}
777 
778 	auto* const bob_owner = get_owner();
779 	const Vector2f point_on_dst = calc_drawpos(egbase, field_on_dst, scale);
780 	dst->blit_animation(point_on_dst, coords, scale, anim_, egbase.get_gametime() - animstart_,
781 	                    (bob_owner == nullptr) ? nullptr : &bob_owner->get_playercolor());
782 }
783 
784 /**
785  * Set a looping animation, starting now.
786  */
set_animation(EditorGameBase & egbase,uint32_t const anim)787 void Bob::set_animation(EditorGameBase& egbase, uint32_t const anim) {
788 	anim_ = anim;
789 	animstart_ = egbase.get_gametime();
790 }
791 
792 /**
793  * Cause the object to walk, honoring passable/unwalkable parts of the map
794  * using movecaps. If force is true, the passability check is skipped.
795  *
796  * \return the number of milliseconds after which the walk has ended. You must
797  * call end_walk() after this time, so schedule a task_act(). Returns -1
798  * if the step is forbidden, and -2 if it is currently blocked.
799  */
start_walk(Game & game,WalkingDir const dir,uint32_t const a,bool const force)800 int32_t Bob::start_walk(Game& game, WalkingDir const dir, uint32_t const a, bool const force) {
801 	FCoords newnode;
802 
803 	const Map& map = game.map();
804 	map.get_neighbour(position_, dir, &newnode);
805 
806 	// Move capability check
807 	if (!force) {
808 		CheckStepDefault cstep(descr().movecaps());
809 
810 		if (!cstep.allowed(map, position_, newnode, dir, CheckStep::stepNormal))
811 			return -1;
812 	}
813 
814 	//  Always call check_node_blocked, because it might communicate with other
815 	//  bobs (as is the case for soldiers on the battlefield).
816 	if (check_node_blocked(game, newnode, true) && !force)
817 		return -2;
818 
819 	// Move is go
820 	int32_t const tdelta = map.calc_cost(position_, dir);
821 	assert(tdelta);
822 
823 	walking_ = dir;
824 	walkstart_ = game.get_gametime();
825 	walkend_ = walkstart_ + tdelta;
826 
827 	set_position(game, newnode);
828 	set_animation(game, a);
829 
830 	return tdelta;  // yep, we were successful
831 }
832 
check_node_blocked(Game & game,const FCoords & field,bool)833 bool Bob::check_node_blocked(Game& game, const FCoords& field, bool) {
834 	// Battles always block movement!
835 	std::vector<Bob*> soldiers;
836 	game.map().find_bobs(game, Area<FCoords>(field, 0), &soldiers, FindBobEnemySoldier(get_owner()));
837 
838 	if (!soldiers.empty()) {
839 		for (Bob* temp_bob : soldiers) {
840 			upcast(Soldier, soldier, temp_bob);
841 			if (soldier->get_battle())
842 				return true;
843 		}
844 	}
845 
846 	return false;
847 }
848 
849 /**
850  * Give the bob a new owner.
851  *
852  * This will update the owner's viewing area.
853  */
set_owner(Player * const player)854 void Bob::set_owner(Player* const player) {
855 	if (owner_ && position_.field)
856 		owner_->unsee_area(Area<FCoords>(get_position(), descr().vision_range()));
857 
858 	owner_ = player;
859 
860 	if (owner_ != nullptr && position_.field)
861 		owner_->see_area(Area<FCoords>(get_position(), descr().vision_range()));
862 }
863 
864 /**
865  * Move the bob to a new position.
866  *
867  * Performs the necessary (un)linking in the \ref Field structures and
868  * updates the owner's viewing area, if the bob has an owner.
869  */
set_position(EditorGameBase & egbase,const Coords & coords)870 void Bob::set_position(EditorGameBase& egbase, const Coords& coords) {
871 	FCoords oldposition = position_;
872 
873 	if (position_.field) {
874 		*linkpprev_ = linknext_;
875 		if (linknext_)
876 			linknext_->linkpprev_ = linkpprev_;
877 	}
878 
879 	position_ = egbase.map().get_fcoords(coords);
880 
881 	linknext_ = position_.field->bobs;
882 	linkpprev_ = &position_.field->bobs;
883 	if (linknext_)
884 		linknext_->linkpprev_ = &linknext_;
885 	*linkpprev_ = this;
886 
887 	if (owner_ != nullptr) {
888 		owner_->see_area(Area<FCoords>(get_position(), descr().vision_range()));
889 
890 		if (oldposition.field)
891 			owner_->unsee_area(Area<FCoords>(oldposition, descr().vision_range()));
892 	}
893 
894 	// Since pretty much everything in Widelands eventually results in the
895 	// movement of a worker (e.g. transporting wares etc.), this should
896 	// help us to find desyncs pretty rapidly.
897 	// In particular, I wanted to add something to set_position because
898 	// it involves coordinates and will thus additionally highlight desyncs
899 	// in pathfinding even when two paths have the same length, and in
900 	// randomly generated movements.
901 	if (upcast(Game, game, &egbase)) {
902 		StreamWrite& ss = game->syncstream();
903 		ss.unsigned_8(SyncEntry::kBobSetPosition);
904 		ss.unsigned_32(serial());
905 		ss.signed_16(coords.x);
906 		ss.signed_16(coords.y);
907 	}
908 }
909 
910 /// Give debug information.
log_general_info(const EditorGameBase & egbase) const911 void Bob::log_general_info(const EditorGameBase& egbase) const {
912 	FORMAT_WARNINGS_OFF
913 	molog("Owner: %p\n", owner_);
914 	FORMAT_WARNINGS_ON
915 	molog("Postition: (%i, %i)\n", position_.x, position_.y);
916 	molog("ActID: %i\n", actid_);
917 	molog("ActScheduled: %s\n", actscheduled_ ? "true" : "false");
918 	molog("Animation: %s\n", anim_ ? descr().get_animation_name(anim_).c_str() : "\\<none\\>");
919 
920 	molog("AnimStart: %i\n", animstart_);
921 	molog("WalkingDir: %i\n", walking_);
922 	molog("WalkingStart: %i\n", walkstart_);
923 	molog("WalkEnd: %i\n", walkend_);
924 
925 	molog("Signal: %s\n", signal_.c_str());
926 
927 	molog("Stack size: %" PRIuS "\n", stack_.size());
928 
929 	for (size_t i = 0; i < stack_.size(); ++i) {
930 		molog("Stack dump %" PRIuS "/%" PRIuS "\n", i + 1, stack_.size());
931 
932 		molog("* task->name: %s\n", stack_[i].task->name);
933 
934 		molog("* ivar1: %i\n", stack_[i].ivar1);
935 		molog("* ivar2: %i\n", stack_[i].ivar2);
936 		molog("* ivar3: %i\n", stack_[i].ivar3);
937 
938 		FORMAT_WARNINGS_OFF
939 		molog("* object pointer: %p\n", stack_[i].objvar1.get(egbase));
940 		FORMAT_WARNINGS_ON
941 		molog("* svar1: %s\n", stack_[i].svar1.c_str());
942 
943 		molog("* coords: (%i, %i)\n", stack_[i].coords.x, stack_[i].coords.y);
944 		molog("* diranims:");
945 		for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) {
946 			molog(" %d", stack_[i].diranims.get_animation(dir));
947 		}
948 		FORMAT_WARNINGS_OFF
949 		molog("\n* path: %p\n", stack_[i].path);
950 		FORMAT_WARNINGS_ON
951 		if (stack_[i].path) {
952 			const Path& path = *stack_[i].path;
953 			molog("** Path length: %" PRIuS "\n", path.get_nsteps());
954 			molog("** Start: (%i, %i)\n", path.get_start().x, path.get_start().y);
955 			molog("** End: (%i, %i)\n", path.get_end().x, path.get_end().y);
956 			// Printing all coordinates of the path
957 			CoordPath coordpath(egbase.map(), path);
958 			for (const Coords& coords : coordpath.get_coords()) {
959 				molog("*  (%i, %i)\n", coords.x, coords.y);
960 			}
961 		}
962 		FORMAT_WARNINGS_OFF
963 		molog("* route: %p\n", stack_[i].route);
964 		molog("* program: %p\n", stack_[i].route);
965 		FORMAT_WARNINGS_ON
966 	}
967 }
968 
969 /*
970 ==============================
971 
972 Load/save support
973 
974 ==============================
975 */
976 
977 constexpr uint8_t kCurrentPacketVersion = 1;
978 
Loader()979 Bob::Loader::Loader() {
980 }
981 
load(FileRead & fr)982 void Bob::Loader::load(FileRead& fr) {
983 	MapObject::Loader::load(fr);
984 
985 	try {
986 		uint8_t packet_version = fr.unsigned_8();
987 		if (packet_version == kCurrentPacketVersion) {
988 
989 			Bob& bob = get<Bob>();
990 
991 			if (PlayerNumber owner_number = fr.unsigned_8()) {
992 				if (owner_number > egbase().map().get_nrplayers())
993 					throw GameDataError("owner number is %u but there are only %u players", owner_number,
994 					                    egbase().map().get_nrplayers());
995 
996 				Player* owner = egbase().get_player(owner_number);
997 				if (!owner)
998 					throw GameDataError("owning player %u does not exist", owner_number);
999 
1000 				bob.set_owner(owner);
1001 			}
1002 
1003 			bob.set_position(egbase(), read_coords_32(&fr));
1004 
1005 			// Animation. If the animation is no longer known, pick the main animation instead.
1006 			std::string animname = fr.c_string();
1007 			if (animname.empty()) {
1008 				bob.anim_ = 0;
1009 			} else if (bob.descr().is_animation_known(animname)) {
1010 				bob.anim_ = bob.descr().get_animation(animname, &bob);
1011 			} else {
1012 				bob.anim_ = bob.descr().main_animation();
1013 				log("Unknown animation '%s' for bob '%s', using main animation instead.\n",
1014 				    animname.c_str(), bob.descr().name().c_str());
1015 			}
1016 
1017 			bob.animstart_ = fr.signed_32();
1018 			bob.walking_ = static_cast<WalkingDir>(read_direction_8_allow_null(&fr));
1019 			if (bob.walking_) {
1020 				bob.walkstart_ = fr.signed_32();
1021 				bob.walkend_ = fr.signed_32();
1022 			}
1023 
1024 			bob.actid_ = fr.unsigned_32();
1025 			bob.signal_ = fr.c_string();
1026 
1027 			uint32_t stacksize = fr.unsigned_32();
1028 			bob.stack_.resize(stacksize);
1029 			states.resize(stacksize);
1030 			for (uint32_t i = 0; i < stacksize; ++i) {
1031 				State& state = bob.stack_[i];
1032 				LoadState& loadstate = states[i];
1033 
1034 				state.task = get_task(fr.c_string());
1035 				state.ivar1 = fr.signed_32();
1036 				state.ivar2 = fr.signed_32();
1037 				state.ivar3 = fr.signed_32();
1038 				loadstate.objvar1 = fr.unsigned_32();
1039 				state.svar1 = fr.c_string();
1040 				state.coords = read_coords_32_allow_null(&fr, egbase().map().extent());
1041 
1042 				if (fr.unsigned_8()) {
1043 					uint32_t anims[6];
1044 					for (int j = 0; j < 6; ++j) {
1045 						std::string dir_animname = fr.c_string();
1046 						if (bob.descr().is_animation_known(dir_animname)) {
1047 							anims[j] = bob.descr().get_animation(dir_animname, &bob);
1048 						} else {
1049 							anims[j] = bob.descr().main_animation();
1050 							log("Unknown directional animation '%s' for bob '%s', using main animation "
1051 							    "instead.\n",
1052 							    dir_animname.c_str(), bob.descr().name().c_str());
1053 						}
1054 					}
1055 					state.diranims =
1056 					   DirAnimations(anims[0], anims[1], anims[2], anims[3], anims[4], anims[5]);
1057 				}
1058 
1059 				if (fr.unsigned_8()) {
1060 					state.path = new Path;
1061 					state.path->load(fr, egbase().map());
1062 				}
1063 
1064 				if (fr.unsigned_8()) {
1065 					state.route = new Route;
1066 					state.route->load(loadstate.route, fr);
1067 				}
1068 
1069 				std::string programname = fr.c_string();
1070 				if (programname.size())
1071 					state.program = get_program(programname);
1072 			}
1073 		} else {
1074 			throw UnhandledVersionError("Bob", packet_version, kCurrentPacketVersion);
1075 		}
1076 	} catch (const WException& e) {
1077 		throw wexception("loading bob: %s", e.what());
1078 	}
1079 }
1080 
load_pointers()1081 void Bob::Loader::load_pointers() {
1082 	MapObject::Loader::load_pointers();
1083 
1084 	Bob& bob = get<Bob>();
1085 	for (uint32_t i = 0; i < bob.stack_.size(); ++i) {
1086 		State& state = bob.stack_[i];
1087 		LoadState& loadstate = states[i];
1088 
1089 		if (loadstate.objvar1)
1090 			state.objvar1 = &mol().get<MapObject>(loadstate.objvar1);
1091 
1092 		if (state.route)
1093 			state.route->load_pointers(loadstate.route, mol());
1094 	}
1095 }
1096 
load_finish()1097 void Bob::Loader::load_finish() {
1098 	MapObject::Loader::load_finish();
1099 
1100 	// Care about new mapobject saving / loading - map objects don't get a task,
1101 	// if created in the editor, so we give them one here.
1102 	//  See bug #537392 for more information:
1103 	//   https://bugs.launchpad.net/widelands/+bug/537392
1104 	Bob& bob = get<Bob>();
1105 	if (bob.stack_.empty() && !egbase().get_gametime())
1106 		if (upcast(Game, game, &egbase())) {
1107 			bob.init_auto_task(*game);
1108 		}
1109 }
1110 
get_task(const std::string & name)1111 const Bob::Task* Bob::Loader::get_task(const std::string& name) {
1112 	if (name == "move")
1113 		return &taskMove;
1114 	if (name == "movepath")
1115 		return &taskMovepath;
1116 	if (name == "idle")
1117 		return &taskIdle;
1118 
1119 	throw GameDataError("unknown bob task '%s'", name.c_str());
1120 }
1121 
get_program(const std::string & name)1122 const MapObjectProgram* Bob::Loader::get_program(const std::string& name) {
1123 	throw GameDataError("unknown map object program '%s'", name.c_str());
1124 }
1125 
save(EditorGameBase & eg,MapObjectSaver & mos,FileWrite & fw)1126 void Bob::save(EditorGameBase& eg, MapObjectSaver& mos, FileWrite& fw) {
1127 	MapObject::save(eg, mos, fw);
1128 
1129 	fw.unsigned_8(kCurrentPacketVersion);
1130 
1131 	fw.unsigned_8(owner_ ? owner_->player_number() : 0);
1132 	write_coords_32(&fw, position_);
1133 
1134 	// linkprev_ and linknext_ are recreated automatically
1135 
1136 	fw.c_string(anim_ ? descr().get_animation_name(anim_) : "");
1137 	fw.signed_32(animstart_);
1138 	write_direction_8_allow_null(&fw, walking_);
1139 	if (walking_) {
1140 		fw.signed_32(walkstart_);
1141 		fw.signed_32(walkend_);
1142 	}
1143 
1144 	fw.unsigned_32(actid_);
1145 	fw.c_string(signal_);
1146 
1147 	fw.unsigned_32(stack_.size());
1148 	for (unsigned int i = 0; i < stack_.size(); ++i) {
1149 		const State& state = stack_[i];
1150 
1151 		fw.c_string(state.task->name);
1152 		fw.signed_32(state.ivar1);
1153 		fw.signed_32(state.ivar2);
1154 		fw.signed_32(state.ivar3);
1155 		if (const MapObject* obj = state.objvar1.get(eg)) {
1156 			fw.unsigned_32(mos.get_object_file_index(*obj));
1157 		} else {
1158 			fw.unsigned_32(0);
1159 		}
1160 		fw.c_string(state.svar1);
1161 
1162 		write_coords_32(&fw, state.coords);
1163 
1164 		if (state.diranims) {
1165 			fw.unsigned_8(1);
1166 			for (int dir = 1; dir <= 6; ++dir)
1167 				fw.c_string(descr().get_animation_name(state.diranims.get_animation(dir)).c_str());
1168 		} else {
1169 			fw.unsigned_8(0);
1170 		}
1171 
1172 		if (state.path) {
1173 			fw.unsigned_8(1);
1174 			state.path->save(fw);
1175 		} else {
1176 			fw.unsigned_8(0);
1177 		}
1178 
1179 		if (state.route) {
1180 			fw.unsigned_8(1);
1181 			state.route->save(fw, eg, mos);
1182 		} else {
1183 			fw.unsigned_8(0);
1184 		}
1185 
1186 		fw.c_string(state.program ? state.program->name() : "");
1187 	}
1188 }
1189 }  // namespace Widelands
1190