1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name unit.cpp - The unit source file. */
12 //
13 //      (c) Copyright 1998-2019 by Lutz Sammer, Jimmy Salmon and Andrettin
14 //
15 //      This program is free software; you can redistribute it and/or modify
16 //      it under the terms of the GNU General Public License as published by
17 //      the Free Software Foundation; only version 2 of the License.
18 //
19 //      This program is distributed in the hope that it will be useful,
20 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //      GNU General Public License for more details.
23 //
24 //      You should have received a copy of the GNU General Public License
25 //      along with this program; if not, write to the Free Software
26 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 //      02111-1307, USA.
28 //
29 
30 //@{
31 
32 /*----------------------------------------------------------------------------
33 --  Includes
34 ----------------------------------------------------------------------------*/
35 
36 #include "stratagus.h"
37 
38 #include "unit/unit.h"
39 
40 #include "action/action_attack.h"
41 //Wyrmgus start
42 #include "action/action_resource.h"
43 #include "action/action_upgradeto.h"
44 //Wyrmgus end
45 
46 #include "actions.h"
47 #include "ai.h"
48 //Wyrmgus start
49 #include "../ai/ai_local.h" //for using AiHelpers
50 //Wyrmgus end
51 #include "animation.h"
52 #include "character.h"
53 #include "civilization.h"
54 #include "commands.h"
55 #include "construct.h"
56 #include "game.h"
57 #include "editor.h"
58 //Wyrmgus start
59 #include "grand_strategy.h"
60 //Wyrmgus end
61 //Wyrmgus start
62 #include "item.h"
63 //Wyrmgus end
64 #include "luacallback.h"
65 #include "map/map.h"
66 #include "map/map_layer.h"
67 #include "map/site.h"
68 #include "map/tileset.h"
69 #include "missile.h"
70 #include "network.h"
71 #include "pathfinder.h"
72 #include "plane.h"
73 #include "player.h"
74 //Wyrmgus start
75 #include "quest.h"
76 //Wyrmgus end
77 #include "religion/deity.h"
78 #include "script.h"
79 #include "sound.h"
80 #include "sound_server.h"
81 #include "spells.h"
82 #include "time/time_of_day.h"
83 #include "translate.h"
84 #include "ui/button_action.h"
85 #include "ui/interface.h"
86 #include "ui/ui.h"
87 #include "unit/unit_find.h"
88 #include "unit/unit_manager.h"
89 #include "unit/unittype.h"
90 #include "unit/unit_type_variation.h"
91 #include "unitsound.h"
92 #include "upgrade/dependency.h"
93 #include "upgrade/upgrade.h"
94 #include "upgrade/upgrade_modifier.h"
95 //Wyrmgus start
96 #include "util.h"
97 //Wyrmgus end
98 #include "video.h"
99 
100 #include <math.h>
101 
102 
103 /*----------------------------------------------------------------------------
104 -- Documentation
105 ----------------------------------------------------------------------------*/
106 
107 /**
108 **  @class CUnit unit.h
109 **
110 **  \#include "unit/unit.h"
111 **
112 **  Everything belonging to a unit. FIXME: rearrange for less memory.
113 **
114 **  This class contains all information about a unit in game.
115 **  A unit could be anything: a man, a vehicle, a ship, or a building.
116 **  Currently only a tile, a unit, or a missile could be placed on the map.
117 **
118 **  The unit structure members:
119 **
120 **  CUnit::Refs
121 **
122 **  The reference counter of the unit. If the pointer to the unit
123 **  is stored the counter must be incremented and if this reference
124 **  is destroyed the counter must be decremented. Alternative it
125 **  would be possible to implement a garbage collector for this.
126 **
127 **  CUnit::Slot
128 **
129 **  This is the unique slot number. It is not possible that two
130 **  units have the same slot number at the same time. The slot
131 **  numbers are reused.
132 **  This field could be accessed by the macro UnitNumber(Unit *).
133 **
134 **  CUnit::UnitSlot
135 **
136 **  This is the index into #Units[], where the unit pointer is
137 **  stored.  #Units[] is a table of all units currently active in
138 **  game. This pointer is only needed to speed up, the remove of
139 **  the unit pointer from #Units[], it didn't must be searched in
140 **  the table.
141 **
142 **  CUnit::PlayerSlot
143 **
144 **  The index into Player::Units[], where the unit pointer is
145 **  stored. Player::Units[] is a table of all units currently
146 **  belonging to a player. This pointer is only needed to speed
147 **  up, the remove of the unit pointer from Player::Units[].
148 **
149 **  CUnit::Container
150 **
151 **  Pointer to the unit containing it, or null if the unit is
152 **  free. This points to the transporter for units on board, or to
153 **  the building for peasants inside(when they are mining).
154 **
155 **  CUnit::UnitInside
156 **
157 **  Pointer to the last unit added inside. Order doesn't really
158 **  matter. All units inside are kept in a circular linked list.
159 **  This is null if there are no units inside. Multiple levels
160 **  of inclusion are allowed, though not very useful right now
161 **
162 **  CUnit::NextContained, CUnit::PrevContained
163 **
164 **  The next and previous element in the curent container. Bogus
165 **  values allowed for units not contained.
166 **
167 **  CUnit::InsideCount
168 **
169 **  The number of units inside the container.
170 **
171 **  CUnit::BoardCount
172 **
173 **  The number of units transported inside the container. This
174 **  does not include for instance stuff like harvesters returning
175 **  cargo.
176 **
177 **  CUnit::tilePos
178 **
179 **  The tile map coordinates of the unit.
180 **  0,0 is the upper left on the map.
181 **
182 **  CUnit::Type
183 **
184 **  Pointer to the unit-type (::UnitType). The unit-type contains
185 **  all information that all units of the same type shares.
186 **  (Animations, Name, Stats, ...)
187 **
188 **  CUnit::SeenType
189 **  Pointer to the unit-type that this unit was, when last seen.
190 **  Currently only used by buildings.
191 **
192 **  CUnit::Player
193 **
194 **  Pointer to the owner of this unit (::Player). An unit could
195 **  only be owned by one player.
196 **
197 **  CUnit::Stats
198 **
199 **  Pointer to the current status (::UnitStats) of a unit. The
200 **  units of the same player and the same type could share the same
201 **  stats. The status contains all values which could be different
202 **  for each player. This f.e. the upgradeable abilities of an
203 **  unit.  (CUnit::Stats::SightRange, CUnit::Stats::Armor,
204 **  CUnit::Stats::HitPoints, ...)
205 **
206 **  CUnit::CurrentSightRange
207 **
208 **  Current sight range of a unit, this changes when a unit enters
209 **  a transporter or building or exits one of these.
210 **
211 **  CUnit::Colors
212 **
213 **  Player colors of the unit. Contains the hardware dependent
214 **  pixel values for the player colors (palette index #208-#211).
215 **  Setup from the global palette. This is a pointer.
216 **  @note Index #208-#211 are various SHADES of the team color
217 **  (#208 is brightest shade, #211 is darkest shade) .... these
218 **  numbers are NOT red=#208, blue=#209, etc
219 **
220 **  CUnit::IX CUnit::IY
221 **
222 **  Coordinate displacement in pixels or coordinates inside a tile.
223 **  Currently only !=0, if the unit is moving from one tile to
224 **  another (0-32 and for ships/flyers 0-64).
225 **
226 **  CUnit::Frame
227 **
228 **  Current graphic image of the animation sequence. The high bit
229 **  (128) is used to flip this image horizontal (x direction).
230 **  This also limits the number of different frames/image to 126.
231 **
232 **  CUnit::SeenFrame
233 **
234 **  Graphic image (see CUnit::Frame) what the player on this
235 **  computer has last seen. If UnitNotSeen the player haven't seen
236 **  this unit yet.
237 **
238 **  CUnit::Direction
239 **
240 **  Contains the binary angle (0-255) in which the direction the
241 **  unit looks. 0, 32, 64, 128, 160, 192, 224, 256 corresponds to
242 **  0?, 45?, 90?, 135?, 180?, 225?, 270?, 315?, 360? or north,
243 **  north-east, east, south-east, south, south-west, west,
244 **  north-west, north. Currently only 8 directions are used, this
245 **  is more for the future.
246 **
247 **  CUnit::Attacked
248 **
249 **  Last cycle the unit was attacked. 0 means never.
250 **
251 **  CUnit::Burning
252 **
253 **  If Burning is non-zero, the unit is burning.
254 **
255 **  CUnit::VisCount[PlayerMax]
256 **
257 **              Used to keep track of visible units on the map, it counts the
258 **              Number of seen tiles for each player. This is only modified
259 **              in UnitsMarkSeen and UnitsUnmarkSeen, from fow.
260 **              We keep track of visilibty for each player, and combine with
261 **              Shared vision ONLY when querying and such.
262 **
263 **  CUnit::SeenByPlayer
264 **
265 **              This is a bitmask of 1 and 0 values. SeenByPlayer & (1<<p) is 0
266 **              If p never saw the unit and 1 if it did. This is important for
267 **              keeping track of dead units under fog. We only keep track of units
268 **              that are visible under fog with this.
269 **
270 **  CUnit::Destroyed
271 **
272 ** @todo docu.
273 **  If you need more information, please send me an email or write it self.
274 **
275 **  CUnit::Removed
276 **
277 **  This flag means the unit is not active on map. This flag
278 **  have workers set if they are inside a building, units that are
279 **  on board of a transporter.
280 **
281 **  CUnit::Selected
282 **
283 **  Unit is selected. (So you can give it orders)
284 **
285 **  CUnit::UnderConstruction
286 **  Set when a building is under construction, and still using the
287 **  generic building animation.
288 **
289 **  CUnit::SeenUnderConstruction
290 **  Last seen state of construction.  Used to draw correct building
291 **  frame. See CUnit::UnderConstruction for more information.
292 **
293 **  CUnit::SeenState
294 **  The Seen State of the building.
295 **  01 The building in being built when last seen.
296 **  10 The building was been upgraded when last seen.
297 **
298 **  CUnit::Boarded
299 **
300 **  This is 1 if the unit is on board a transporter.
301 **
302 **
303 **  CUnit::XP
304 **
305 **  Number of XP of the unit.
306 **
307 **  CUnit::Kills
308 **
309 **  How many units have been killed by the unit.
310 **
311 **  CUnit::GroupId
312 **
313 **  Number of the group to that the unit belongs. This is the main
314 **  group showed on map, a unit can belong to many groups.
315 **
316 **  CUnit::LastGroup
317 **
318 **  Automatic group number, to reselect the same units. When the
319 **  user selects more than one unit all units is given the next
320 **  same number. (Used for ALT-CLICK)
321 **
322 **  CUnit::Value
323 **
324 **  This values hold the amount of resources in a resource or in
325 **  in a harvester.
326 **  @todo continue documentation
327 **
328 **  CUnit::Wait
329 **
330 **  The unit is forced too wait for that many cycles. Be careful,
331 **  setting this to 0 will lock the unit.
332 **
333 **  CUnit::State
334 **
335 **  Animation state, currently position in the animation script.
336 **  0 if an animation has just started, it should only be changed
337 **  inside of actions.
338 **
339 **  CUnit::Reset
340 **
341 **  @todo continue documentation
342 **
343 **  CUnit::Blink
344 **
345 **
346 **  CUnit::Moving
347 **
348 **
349 **  CUnit::RescuedFrom
350 **
351 **  Pointer to the original owner of a unit. It will be null if
352 **  the unit was not rescued.
353 **
354 **  CUnit::Orders
355 **
356 **  Contains all orders of the unit. Slot 0 is always used.
357 **
358 **  CUnit::SavedOrder
359 **
360 **  This order is executed, if the current order is finished.
361 **  This is used for attacking units, to return to the old
362 **  place or for patrolling units to return to patrol after
363 **  killing some enemies. Any new order given to the unit,
364 **  clears this saved order.
365 **
366 **  CUnit::NewOrder
367 **
368 **  This field is only used by buildings and this order is
369 **  assigned to any by this building new trained unit.
370 **  This is can be used to set the exit or gathering point of a
371 **  building.
372 **
373 **  CUnit::Goal
374 **
375 **  Generic goal pointer. Used by teleporters to point to circle of power.
376 **
377 **
378 ** @todo continue documentation
379 **
380 */
381 
382 /*----------------------------------------------------------------------------
383 --  Variables
384 ----------------------------------------------------------------------------*/
385 
386 bool EnableTrainingQueue;                 /// Config: training queues enabled
387 bool EnableBuildingCapture = false;               /// Config: capture buildings enabled
388 bool RevealAttacker;                      /// Config: reveal attacker enabled
389 int ResourcesMultiBuildersMultiplier = 0; /// Config: spend resources for building with multiple workers
390 
391 static unsigned long HelpMeLastCycle;     /// Last cycle HelpMe sound played
392 static int HelpMeLastX;                   /// Last X coordinate HelpMe sound played
393 static int HelpMeLastY;                   /// Last Y coordinate HelpMe sound played
394 
395 /*----------------------------------------------------------------------------
396 --  Functions
397 ----------------------------------------------------------------------------*/
398 
399 static void RemoveUnitFromContainer(CUnit &unit);
400 
401 extern int ExtraDeathIndex(const char *death);
402 
403 /**
404 **  Increase a unit's reference count.
405 */
RefsIncrease()406 void CUnit::RefsIncrease()
407 {
408 	Assert(Refs && !Destroyed);
409 	if (!SaveGameLoading) {
410 		++Refs;
411 	}
412 }
413 
414 /**
415 **  Decrease a unit's reference count.
416 */
RefsDecrease()417 void CUnit::RefsDecrease()
418 {
419 	Assert(Refs);
420 	if (!SaveGameLoading) {
421 		if (Destroyed) {
422 			if (!--Refs) {
423 				Release();
424 			}
425 		} else {
426 			--Refs;
427 			Assert(Refs);
428 		}
429 	}
430 }
431 
Init()432 void CUnit::Init()
433 {
434 	Refs = 0;
435 	ReleaseCycle = 0;
436 	PlayerSlot = static_cast<size_t>(-1);
437 	InsideCount = 0;
438 	BoardCount = 0;
439 	UnitInside = nullptr;
440 	Container = nullptr;
441 	NextContained = nullptr;
442 	PrevContained = nullptr;
443 	NextWorker = nullptr;
444 
445 	Resource.Workers = nullptr;
446 	Resource.Assigned = 0;
447 	Resource.Active = 0;
448 
449 	//Wyrmgus start
450 	for (int i = 0; i < MaxItemSlots; ++i) {
451 		EquippedItems[i].clear();
452 	}
453 	SoldUnits.clear();
454 	//Wyrmgus end
455 
456 	tilePos.x = 0;
457 	tilePos.y = 0;
458 	//Wyrmgus start
459 	RallyPointPos.x = -1;
460 	RallyPointPos.y = -1;
461 	MapLayer = nullptr;
462 	RallyPointMapLayer = nullptr;
463 	//Wyrmgus end
464 	Offset = 0;
465 	Type = nullptr;
466 	Player = nullptr;
467 	Stats = nullptr;
468 	//Wyrmgus start
469 	Character = nullptr;
470 	Settlement = nullptr;
471 	Trait = nullptr;
472 	Prefix = nullptr;
473 	Suffix = nullptr;
474 	Spell = nullptr;
475 	Work = nullptr;
476 	Elixir = nullptr;
477 	Unique = nullptr;
478 	Bound = false;
479 	Identified = true;
480 	ConnectingDestination = nullptr;
481 	//Wyrmgus end
482 	CurrentSightRange = 0;
483 
484 	pathFinderData = new PathFinderData;
485 	pathFinderData->input.SetUnit(*this);
486 
487 	Colors = nullptr;
488 	//Wyrmgus start
489 	Name.clear();
490 	ExtraName.clear();
491 	FamilyName.clear();
492 	Variation = 0;
493 	memset(LayerVariation, -1, sizeof(LayerVariation));
494 	//Wyrmgus end
495 	IX = 0;
496 	IY = 0;
497 	Frame = 0;
498 	Direction = 0;
499 	DamagedType = ANIMATIONS_DEATHTYPES;
500 	Attacked = 0;
501 	Burning = 0;
502 	Destroyed = 0;
503 	Removed = 0;
504 	Selected = 0;
505 	TeamSelected = 0;
506 	UnderConstruction = 0;
507 	Active = 0;
508 	Boarded = 0;
509 	RescuedFrom = nullptr;
510 	memset(VisCount, 0, sizeof(VisCount));
511 	memset(&Seen, 0, sizeof(Seen));
512 	Variable = nullptr;
513 	TTL = 0;
514 	Threshold = 0;
515 	GroupId = 0;
516 	LastGroup = 0;
517 	ResourcesHeld = 0;
518 	Wait = 0;
519 	Blink = 0;
520 	Moving = 0;
521 	ReCast = 0;
522 	CacheLock = 0;
523 	Summoned = 0;
524 	Waiting = 0;
525 	MineLow = 0;
526 	memset(&Anim, 0, sizeof(Anim));
527 	memset(&WaitBackup, 0, sizeof(WaitBackup));
528 	GivesResource = 0;
529 	CurrentResource = 0;
530 	StepCount = 0;
531 	Orders.clear();
532 	delete SavedOrder;
533 	SavedOrder = nullptr;
534 	delete NewOrder;
535 	NewOrder = nullptr;
536 	delete CriticalOrder;
537 	CriticalOrder = nullptr;
538 	AutoCastSpell = nullptr;
539 	SpellCoolDownTimers = nullptr;
540 	AutoRepair = 0;
541 	Goal = nullptr;
542 	IndividualUpgrades.clear();
543 }
544 
545 /**
546 **  Release an unit.
547 **
548 **  The unit is only released, if all references are dropped.
549 */
Release(bool final)550 void CUnit::Release(bool final)
551 {
552 	if (Type == nullptr) {
553 		DebugPrint("unit already free\n");
554 		return;
555 	}
556 	//Wyrmgus start
557 	if (Orders.size() != 1) {
558 		fprintf(stderr, "Unit to be released has more than 1 order; Unit Type: \"%s\", Orders: %d, First Order Type: %d.\n", this->Type->Ident.c_str(), (int)Orders.size(), this->CurrentAction());
559 	}
560 	//Wyrmgus end
561 	Assert(Orders.size() == 1);
562 	// Must be removed before here
563 	Assert(Removed);
564 
565 	// First release, remove from lists/tables.
566 	if (!Destroyed) {
567 		DebugPrint("%d: First release %d\n" _C_ Player->Index _C_ UnitNumber(*this));
568 
569 		// Are more references remaining?
570 		Destroyed = 1; // mark as destroyed
571 
572 		if (Container && !final) {
573 			if (Boarded) {
574 				Container->BoardCount--;
575 			}
576 			MapUnmarkUnitSight(*this);
577 			RemoveUnitFromContainer(*this);
578 		}
579 
580 		while (Resource.Workers) {
581 			Resource.Workers->DeAssignWorkerFromMine(*this);
582 		}
583 
584 		if (--Refs > 0) {
585 			return;
586 		}
587 	}
588 
589 	Assert(!Refs);
590 
591 	//
592 	// No more references remaining, but the network could have an order
593 	// on the way. We must wait a little time before we could free the
594 	// memory.
595 	//
596 
597 	Type = nullptr;
598 	//Wyrmgus start
599 	Character = nullptr;
600 	if (this->Settlement && this->Settlement->SiteUnit == this) {
601 		this->Settlement->SiteUnit = nullptr;
602 	}
603 	Settlement = nullptr;
604 	Trait = nullptr;
605 	Prefix = nullptr;
606 	Suffix = nullptr;
607 	Spell = nullptr;
608 	Work = nullptr;
609 	Elixir = nullptr;
610 	Unique = nullptr;
611 	Bound = false;
612 	Identified = true;
613 	ConnectingDestination = nullptr;
614 
615 	for (int i = 0; i < MaxItemSlots; ++i) {
616 		EquippedItems[i].clear();
617 	}
618 	SoldUnits.clear();
619 	//Wyrmgus end
620 
621 	delete pathFinderData;
622 	delete[] AutoCastSpell;
623 	delete[] SpellCoolDownTimers;
624 	delete[] Variable;
625 	for (std::vector<COrder *>::iterator order = Orders.begin(); order != Orders.end(); ++order) {
626 		delete *order;
627 	}
628 	Orders.clear();
629 
630 	// Remove the unit from the global units table.
631 	UnitManager.ReleaseUnit(this);
632 }
633 
634 //Wyrmgus start
SetResourcesHeld(int quantity)635 void CUnit::SetResourcesHeld(int quantity)
636 {
637 	this->ResourcesHeld = quantity;
638 
639 	const CUnitTypeVariation *variation = this->GetVariation();
640 	if (
641 		variation
642 		&& (
643 			(variation->ResourceMin && this->ResourcesHeld < variation->ResourceMin)
644 			|| (variation->ResourceMax && this->ResourcesHeld > variation->ResourceMax)
645 		)
646 	) {
647 		this->ChooseVariation();
648 	}
649 }
650 
ChangeResourcesHeld(int quantity)651 void CUnit::ChangeResourcesHeld(int quantity)
652 {
653 	this->SetResourcesHeld(this->ResourcesHeld + quantity);
654 }
655 
ReplaceOnTop(CUnit & replaced_unit)656 void CUnit::ReplaceOnTop(CUnit &replaced_unit)
657 {
658 	if (replaced_unit.Unique != nullptr) {
659 		this->SetUnique(replaced_unit.Unique);
660 	} else {
661 		if (replaced_unit.Prefix != nullptr) {
662 			this->SetPrefix(replaced_unit.Prefix);
663 		}
664 		if (replaced_unit.Suffix != nullptr) {
665 			this->SetSuffix(replaced_unit.Suffix);
666 		}
667 		if (replaced_unit.Spell != nullptr) {
668 			this->SetSpell(replaced_unit.Spell);
669 		}
670 	}
671 	if (replaced_unit.Settlement != nullptr) {
672 		this->Settlement = replaced_unit.Settlement;
673 		if (this->Type->BoolFlag[TOWNHALL_INDEX].value) {
674 			this->Settlement->SiteUnit = this;
675 			Map.SiteUnits.erase(std::remove(Map.SiteUnits.begin(), Map.SiteUnits.end(), &replaced_unit), Map.SiteUnits.end());
676 			Map.SiteUnits.push_back(this);
677 		}
678 	}
679 
680 	this->SetResourcesHeld(replaced_unit.ResourcesHeld); // We capture the value of what is beneath.
681 	this->Variable[GIVERESOURCE_INDEX].Value = replaced_unit.Variable[GIVERESOURCE_INDEX].Value;
682 	this->Variable[GIVERESOURCE_INDEX].Max = replaced_unit.Variable[GIVERESOURCE_INDEX].Max;
683 	this->Variable[GIVERESOURCE_INDEX].Enable = replaced_unit.Variable[GIVERESOURCE_INDEX].Enable;
684 
685 	replaced_unit.Remove(nullptr); // Destroy building beneath
686 	UnitLost(replaced_unit);
687 	UnitClearOrders(replaced_unit);
688 	replaced_unit.Release();
689 }
690 
ChangeExperience(int amount,int around_range)691 void CUnit::ChangeExperience(int amount, int around_range)
692 {
693 	std::vector<CUnit *> table;
694 	if (around_range > 0) {
695 		SelectAroundUnit(*this, around_range, table, MakeAndPredicate(HasSamePlayerAs(*this->Player), IsNotBuildingType()));
696 	}
697 
698 	amount /= 1 + table.size();
699 
700 	if (this->Type->BoolFlag[ORGANIC_INDEX].value) {
701 		this->Variable[XP_INDEX].Max += amount;
702 		this->Variable[XP_INDEX].Value = this->Variable[XP_INDEX].Max;
703 		this->XPChanged();
704 	}
705 
706 	if (around_range > 0) {
707 		for (size_t i = 0; i != table.size(); ++i) {
708 			if (table[i]->Type->BoolFlag[ORGANIC_INDEX].value) {
709 				table[i]->Variable[XP_INDEX].Max += amount;
710 				table[i]->Variable[XP_INDEX].Value = table[i]->Variable[XP_INDEX].Max;
711 				table[i]->XPChanged();
712 			}
713 		}
714 	}
715 }
716 
IncreaseLevel(int level_quantity,bool automatic_learning)717 void CUnit::IncreaseLevel(int level_quantity, bool automatic_learning)
718 {
719 	while (level_quantity > 0) {
720 		this->Variable[LEVEL_INDEX].Value += 1;
721 		if (this->Type->Stats[this->Player->Index].Variables[LEVEL_INDEX].Value < this->Variable[LEVEL_INDEX].Value) {
722 			if (GetAvailableLevelUpUpgrades(true) == 0 || (this->Variable[LEVEL_INDEX].Value - this->Type->Stats[this->Player->Index].Variables[LEVEL_INDEX].Value) > 1) {
723 				this->Variable[POINTS_INDEX].Max += 5 * (this->Variable[LEVEL_INDEX].Value + 1);
724 				this->Variable[POINTS_INDEX].Value += 5 * (this->Variable[LEVEL_INDEX].Value + 1);
725 			}
726 
727 			this->Variable[LEVELUP_INDEX].Value += 1;
728 			this->Variable[LEVELUP_INDEX].Max = this->Variable[LEVELUP_INDEX].Value;
729 			// if there are no level-up upgrades available for the unit, increase its HP instead
730 			if (this->GetAvailableLevelUpUpgrades() < this->Variable[LEVELUP_INDEX].Value) {
731 				this->Variable[HP_INDEX].Max += 10;
732 				this->Variable[LEVELUP_INDEX].Value -= 1;
733 				this->Variable[LEVELUP_INDEX].Max = this->Variable[LEVELUP_INDEX].Value;
734 			}
735 		}
736 		this->Variable[HP_INDEX].Value = this->GetModifiedVariable(HP_INDEX, VariableMax);
737 		level_quantity -= 1;
738 	}
739 
740 	UpdateXPRequired();
741 
742 	bool upgrade_found = true;
743 	while (this->Variable[LEVELUP_INDEX].Value > 0 && upgrade_found && automatic_learning) {
744 		upgrade_found = false;
745 
746 		if (((int) AiHelpers.ExperienceUpgrades.size()) > Type->Slot) {
747 			std::vector<CUnitType *> potential_upgrades;
748 
749 			if ((this->Player->AiEnabled || this->Character == nullptr) && this->Type->BoolFlag[HARVESTER_INDEX].value && this->CurrentResource && AiHelpers.ExperienceUpgrades[Type->Slot].size() > 1) {
750 				//if is a harvester who is currently gathering, try to upgrade to a unit type which is best for harvesting the current resource
751 				unsigned int best_gathering_rate = 0;
752 				for (size_t i = 0; i != AiHelpers.ExperienceUpgrades[Type->Slot].size(); ++i) {
753 					CUnitType *experience_upgrade_type = AiHelpers.ExperienceUpgrades[Type->Slot][i];
754 					if (CheckDependencies(experience_upgrade_type, this, true)) {
755 						if (this->Character == nullptr || std::find(this->Character->ForbiddenUpgrades.begin(), this->Character->ForbiddenUpgrades.end(), experience_upgrade_type) == this->Character->ForbiddenUpgrades.end()) {
756 							if (!experience_upgrade_type->ResInfo[this->CurrentResource]) {
757 								continue;
758 							}
759 							unsigned int gathering_rate = experience_upgrade_type->GetResourceStep(this->CurrentResource, this->Player->Index);
760 							if (gathering_rate >= best_gathering_rate) {
761 								if (gathering_rate > best_gathering_rate) {
762 									best_gathering_rate = gathering_rate;
763 									potential_upgrades.clear();
764 								}
765 								potential_upgrades.push_back(experience_upgrade_type);
766 							}
767 						}
768 					}
769 				}
770 			} else if (this->Player->AiEnabled || (this->Character == nullptr && AiHelpers.ExperienceUpgrades[Type->Slot].size() == 1)) {
771 				for (size_t i = 0; i != AiHelpers.ExperienceUpgrades[Type->Slot].size(); ++i) {
772 					if (CheckDependencies(AiHelpers.ExperienceUpgrades[Type->Slot][i], this, true)) {
773 						if (this->Character == nullptr || std::find(this->Character->ForbiddenUpgrades.begin(), this->Character->ForbiddenUpgrades.end(), AiHelpers.ExperienceUpgrades[Type->Slot][i]) == this->Character->ForbiddenUpgrades.end()) {
774 							potential_upgrades.push_back(AiHelpers.ExperienceUpgrades[Type->Slot][i]);
775 						}
776 					}
777 				}
778 			}
779 
780 			if (potential_upgrades.size() > 0) {
781 				this->Variable[LEVELUP_INDEX].Value -= 1;
782 				this->Variable[LEVELUP_INDEX].Max = this->Variable[LEVELUP_INDEX].Value;
783 				CUnitType *chosen_unit_type = potential_upgrades[SyncRand(potential_upgrades.size())];
784 				if (this->Player == ThisPlayer) {
785 					this->Player->Notify(NotifyGreen, this->tilePos, this->MapLayer->ID, _("%s has upgraded to %s!"), this->GetMessageName().c_str(), chosen_unit_type->Name.c_str());
786 				}
787 				TransformUnitIntoType(*this, *chosen_unit_type);
788 				upgrade_found = true;
789 			}
790 		}
791 
792 		if ((this->Player->AiEnabled || this->Character == nullptr) && this->Variable[LEVELUP_INDEX].Value) {
793 			if (((int) AiHelpers.LearnableAbilities.size()) > Type->Slot) {
794 				std::vector<CUpgrade *> potential_abilities;
795 				for (size_t i = 0; i != AiHelpers.LearnableAbilities[Type->Slot].size(); ++i) {
796 					if (CanLearnAbility(AiHelpers.LearnableAbilities[Type->Slot][i])) {
797 						potential_abilities.push_back(AiHelpers.LearnableAbilities[Type->Slot][i]);
798 					}
799 				}
800 				if (potential_abilities.size() > 0) {
801 					if (potential_abilities.size() == 1 || this->Player->AiEnabled) { //if can only acquire one particular ability, get it automatically
802 						CUpgrade *chosen_ability = potential_abilities[SyncRand(potential_abilities.size())];
803 						AbilityAcquire(*this, chosen_ability);
804 						upgrade_found = true;
805 						if (this->Player == ThisPlayer) {
806 							this->Player->Notify(NotifyGreen, this->tilePos, this->MapLayer->ID, _("%s has acquired the %s ability!"), this->GetMessageName().c_str(), chosen_ability->Name.c_str());
807 						}
808 					}
809 				}
810 			}
811 		}
812 	}
813 
814 	this->Variable[LEVELUP_INDEX].Enable = 1;
815 
816 	this->Player->UpdateLevelUpUnits();
817 }
818 
Retrain()819 void CUnit::Retrain()
820 {
821 	//lose all abilities (the AbilityLost function also returns the level-ups to the unit)
822 	for (size_t i = 0; i < AllUpgrades.size(); ++i) {
823 		if (this->GetIndividualUpgrade(AllUpgrades[i])) {
824 			if (AllUpgrades[i]->Ability && std::find(this->Type->StartingAbilities.begin(), this->Type->StartingAbilities.end(), AllUpgrades[i]) == this->Type->StartingAbilities.end()) {
825 				AbilityLost(*this, AllUpgrades[i], true);
826 			} else if (!strncmp(AllUpgrades[i]->Ident.c_str(), "upgrade-deity-", 14) && strncmp(AllUpgrades[i]->Ident.c_str(), "upgrade-deity-domain-", 21) && this->Character && this->Character->Custom) { //allow changing the deity for custom heroes
827 				IndividualUpgradeLost(*this, AllUpgrades[i], true);
828 			}
829 		}
830 	}
831 
832 	std::string unit_name = GetMessageName();
833 
834 	//now, revert the unit's type to the level 1 one
835 	while (this->Type->Stats[this->Player->Index].Variables[LEVEL_INDEX].Value > 1) {
836 		bool found_previous_unit_type = false;
837 		for (size_t i = 0; i != UnitTypes.size(); ++i) {
838 			if (this->Character != nullptr && std::find(this->Character->ForbiddenUpgrades.begin(), this->Character->ForbiddenUpgrades.end(), UnitTypes[i]) != this->Character->ForbiddenUpgrades.end()) {
839 				continue;
840 			}
841 			if (((int) AiHelpers.ExperienceUpgrades.size()) > i) {
842 				for (size_t j = 0; j != AiHelpers.ExperienceUpgrades[i].size(); ++j) {
843 					if (AiHelpers.ExperienceUpgrades[i][j] == this->Type) {
844 						this->Variable[LEVELUP_INDEX].Value += 1;
845 						this->Variable[LEVELUP_INDEX].Max = this->Variable[LEVELUP_INDEX].Value;
846 						this->Variable[LEVELUP_INDEX].Enable = 1;
847 						TransformUnitIntoType(*this, *UnitTypes[i]);
848 						if (!IsNetworkGame() && Character != nullptr) {	//save the unit-type experience upgrade for persistent characters
849 							if (Character->Type->Slot != i) {
850 								if (Player->AiEnabled == false) {
851 									Character->Type = UnitTypes[i];
852 									SaveHero(Character);
853 									CheckAchievements();
854 								}
855 							}
856 						}
857 						found_previous_unit_type = true;
858 						break;
859 					}
860 				}
861 			}
862 			if (found_previous_unit_type) {
863 				break;
864 			}
865 		}
866 		if (!found_previous_unit_type) {
867 			break;
868 		}
869 	}
870 
871 	if (this->Player == ThisPlayer) {
872 		this->Player->Notify(NotifyGreen, this->tilePos, this->MapLayer->ID, _("%s's level-up choices have been reset."), unit_name.c_str());
873 	}
874 }
875 
HealingItemAutoUse()876 void CUnit::HealingItemAutoUse()
877 {
878 	if (!HasInventory()) {
879 		return;
880 	}
881 
882 	CUnit *uins = this->UnitInside;
883 
884 	for (int i = 0; i < this->InsideCount; ++i, uins = uins->NextContained) {
885 		if (!uins->Type->BoolFlag[ITEM_INDEX].value || uins->Elixir) {
886 			continue;
887 		}
888 
889 		if (!IsItemClassConsumable(uins->Type->ItemClass)) {
890 			continue;
891 		}
892 
893 		if (uins->Variable[HITPOINTHEALING_INDEX].Value > 0) {
894 			if (
895 				uins->Variable[HITPOINTHEALING_INDEX].Value <= (this->GetModifiedVariable(HP_INDEX, VariableMax) - this->Variable[HP_INDEX].Value)
896 				|| (this->Variable[HP_INDEX].Value * 100 / this->GetModifiedVariable(HP_INDEX, VariableMax)) <= 20 // use a healing item if has less than 20% health
897 			) {
898 				if (!this->CriticalOrder) {
899 					this->CriticalOrder = COrder::NewActionUse(*uins);
900 				}
901 				break;
902 			}
903 		}
904 	}
905 }
906 
SetCharacter(const std::string & character_ident,bool custom_hero)907 void CUnit::SetCharacter(const std::string &character_ident, bool custom_hero)
908 {
909 	if (this->CurrentAction() == UnitActionDie) {
910 		return;
911 	}
912 
913 	if (this->Character != nullptr) {
914 		this->Player->Heroes.erase(std::remove(this->Player->Heroes.begin(), this->Player->Heroes.end(), this), this->Player->Heroes.end());
915 
916 		this->Variable[HERO_INDEX].Max = this->Variable[HERO_INDEX].Value = this->Variable[HERO_INDEX].Enable = 0;
917 	}
918 
919 	CCharacter *character = nullptr;
920 	if (!custom_hero) {
921 		character = CCharacter::GetCharacter(character_ident);
922 	} else {
923 		character = GetCustomHero(character_ident);
924 	}
925 
926 	if (character) {
927 		this->Character = character;
928 	} else {
929 		fprintf(stderr, "Character \"%s\" doesn't exist.\n", character_ident.c_str());
930 		return;
931 	}
932 
933 	int old_mana_percent = 0;
934 	if (this->Variable[MANA_INDEX].Max > 0) {
935 		old_mana_percent = this->Variable[MANA_INDEX].Value * 100 / this->Variable[MANA_INDEX].Max;
936 	}
937 
938 	this->Name = this->Character->Name;
939 	this->ExtraName = this->Character->ExtraName;
940 	this->FamilyName = this->Character->FamilyName;
941 
942 	if (this->Character->Type != nullptr) {
943 		if (this->Character->Type != this->Type) { //set type to that of the character
944 			TransformUnitIntoType(*this, *this->Character->Type);
945 		}
946 
947 		memcpy(Variable, this->Character->Type->Stats[this->Player->Index].Variables, UnitTypeVar.GetNumberVariable() * sizeof(*Variable));
948 	} else {
949 		fprintf(stderr, "Character \"%s\" has no unit type.\n", character_ident.c_str());
950 		return;
951 	}
952 
953 	this->IndividualUpgrades.clear(); //reset the individual upgrades and then apply the character's
954 	this->Trait = nullptr;
955 
956 	if (this->Type->Civilization != -1 && !PlayerRaces.CivilizationUpgrades[this->Type->Civilization].empty()) {
957 		CUpgrade *civilization_upgrade = CUpgrade::Get(PlayerRaces.CivilizationUpgrades[this->Type->Civilization]);
958 		if (civilization_upgrade) {
959 			this->SetIndividualUpgrade(civilization_upgrade, 1);
960 		}
961 	}
962 	if (this->Type->Civilization != -1 && this->Type->Faction != -1 && !PlayerRaces.Factions[this->Type->Faction]->FactionUpgrade.empty()) {
963 		CUpgrade *faction_upgrade = CUpgrade::Get(PlayerRaces.Factions[this->Type->Faction]->FactionUpgrade);
964 		if (faction_upgrade) {
965 			this->SetIndividualUpgrade(faction_upgrade, 1);
966 		}
967 	}
968 
969 	if (this->Character->Trait != nullptr) { //set trait
970 		TraitAcquire(*this, this->Character->Trait);
971 	} else if (Editor.Running == EditorNotRunning && this->Type->Traits.size() > 0) {
972 		TraitAcquire(*this, this->Type->Traits[SyncRand(this->Type->Traits.size())]);
973 	}
974 
975 	if (this->Character->Deity != nullptr && this->Character->Deity->CharacterUpgrade != nullptr) {
976 		IndividualUpgradeAcquire(*this, this->Character->Deity->CharacterUpgrade);
977 	}
978 
979 	//load worshipped deities
980 	for (size_t i = 0; i < this->Character->Deities.size(); ++i) {
981 		CUpgrade *deity_upgrade = this->Character->Deities[i]->DeityUpgrade;
982 		if (deity_upgrade) {
983 			IndividualUpgradeAcquire(*this, deity_upgrade);
984 		}
985 	}
986 
987 	for (const CUpgrade *ability_upgrade : this->Type->StartingAbilities) {
988 		if (CheckDependencies(ability_upgrade, this)) {
989 			IndividualUpgradeAcquire(*this, ability_upgrade);
990 		}
991 	}
992 
993 	this->Variable[LEVEL_INDEX].Max = 100000; // because the code above sets the max level to the unit type stats' Level variable (which is the same as its value)
994 	if (this->Variable[LEVEL_INDEX].Value < this->Character->Level) {
995 		this->IncreaseLevel(this->Character->Level - this->Variable[LEVEL_INDEX].Value, false);
996 	}
997 
998 	this->Variable[XP_INDEX].Enable = 1;
999 	this->Variable[XP_INDEX].Value = this->Variable[XPREQUIRED_INDEX].Value * this->Character->ExperiencePercent / 100;
1000 	this->Variable[XP_INDEX].Max = this->Variable[XP_INDEX].Value;
1001 
1002 	if (this->Variable[MANA_INDEX].Max > 0) {
1003 		this->Variable[MANA_INDEX].Value = this->Variable[MANA_INDEX].Max * old_mana_percent / 100;
1004 	}
1005 
1006 	//load learned abilities
1007 	std::vector<CUpgrade *> abilities_to_remove;
1008 	for (size_t i = 0; i < this->Character->Abilities.size(); ++i) {
1009 		if (CanLearnAbility(this->Character->Abilities[i])) {
1010 			AbilityAcquire(*this, this->Character->Abilities[i], false);
1011 		} else { //can't learn the ability? something changed in the game's code, remove it from persistent data and allow the hero to repick the ability
1012 			abilities_to_remove.push_back(this->Character->Abilities[i]);
1013 		}
1014 	}
1015 
1016 	for (size_t i = 0; i < abilities_to_remove.size(); ++i) {
1017 		this->Character->Abilities.erase(std::remove(this->Character->Abilities.begin(), this->Character->Abilities.end(), abilities_to_remove[i]), this->Character->Abilities.end());
1018 		SaveHero(this->Character);
1019 	}
1020 
1021 	//load read works
1022 	for (size_t i = 0; i < this->Character->ReadWorks.size(); ++i) {
1023 		ReadWork(this->Character->ReadWorks[i], false);
1024 	}
1025 
1026 	//load consumed elixirs
1027 	for (size_t i = 0; i < this->Character->ConsumedElixirs.size(); ++i) {
1028 		ConsumeElixir(this->Character->ConsumedElixirs[i], false);
1029 	}
1030 
1031 	//load items
1032 	for (size_t i = 0; i < this->Character->Items.size(); ++i) {
1033 		CUnit *item = MakeUnitAndPlace(this->tilePos, *this->Character->Items[i]->Type, &Players[PlayerNumNeutral], this->MapLayer->ID);
1034 		if (this->Character->Items[i]->Prefix != nullptr) {
1035 			item->SetPrefix(this->Character->Items[i]->Prefix);
1036 		}
1037 		if (this->Character->Items[i]->Suffix != nullptr) {
1038 			item->SetSuffix(this->Character->Items[i]->Suffix);
1039 		}
1040 		if (this->Character->Items[i]->Spell != nullptr) {
1041 			item->SetSpell(this->Character->Items[i]->Spell);
1042 		}
1043 		if (this->Character->Items[i]->Work != nullptr) {
1044 			item->SetWork(this->Character->Items[i]->Work);
1045 		}
1046 		if (this->Character->Items[i]->Elixir != nullptr) {
1047 			item->SetElixir(this->Character->Items[i]->Elixir);
1048 		}
1049 		item->Unique = this->Character->Items[i]->Unique;
1050 		if (!this->Character->Items[i]->Name.empty()) {
1051 			item->Name = this->Character->Items[i]->Name;
1052 		}
1053 		item->Bound = this->Character->Items[i]->Bound;
1054 		item->Identified = this->Character->Items[i]->Identified;
1055 		item->Remove(this);
1056 		if (this->Character->IsItemEquipped(this->Character->Items[i])) {
1057 			EquipItem(*item, false);
1058 		}
1059 	}
1060 
1061 	if (this->Character != nullptr) {
1062 		this->Player->Heroes.push_back(this);
1063 	}
1064 
1065 	this->Variable[HERO_INDEX].Max = this->Variable[HERO_INDEX].Value = this->Variable[HERO_INDEX].Enable = 1;
1066 
1067 	this->ChooseVariation(); //choose a new variation now
1068 	for (int i = 0; i < MaxImageLayers; ++i) {
1069 		ChooseVariation(nullptr, false, i);
1070 	}
1071 	this->UpdateButtonIcons();
1072 	this->UpdateXPRequired();
1073 }
1074 
CheckTerrainForVariation(const CUnitTypeVariation * variation) const1075 bool CUnit::CheckTerrainForVariation(const CUnitTypeVariation *variation) const
1076 {
1077 	//if the variation has one or more terrain set as a precondition, then all tiles underneath the unit must match at least one of those terrains
1078 	if (variation->Terrains.size() > 0) {
1079 		if (!Map.Info.IsPointOnMap(this->tilePos, this->MapLayer)) {
1080 			return false;
1081 		}
1082 		bool terrain_check = true;
1083 		for (int x = 0; x < this->Type->TileSize.x; ++x) {
1084 			for (int y = 0; y < this->Type->TileSize.y; ++y) {
1085 				if (Map.Info.IsPointOnMap(this->tilePos + Vec2i(x, y), this->MapLayer)) {
1086 					if (std::find(variation->Terrains.begin(), variation->Terrains.end(), Map.GetTileTopTerrain(this->tilePos + Vec2i(x, y), false, this->MapLayer->ID, true)) == variation->Terrains.end()) {
1087 						terrain_check = false;
1088 						break;
1089 					}
1090 				}
1091 			}
1092 			if (!terrain_check) {
1093 				break;
1094 			}
1095 		}
1096 		if (!terrain_check) {
1097 			return false;
1098 		}
1099 	}
1100 
1101 	//if the variation has one or more terrains set as a forbidden precondition, then no tiles underneath the unit may match one of those terrains
1102 	if (variation->TerrainsForbidden.size() > 0) {
1103 		if (!Map.Info.IsPointOnMap(this->tilePos, this->MapLayer)) {
1104 			return false;
1105 		}
1106 		bool terrain_check = true;
1107 		for (int x = 0; x < this->Type->TileSize.x; ++x) {
1108 			for (int y = 0; y < this->Type->TileSize.y; ++y) {
1109 				if (Map.Info.IsPointOnMap(this->tilePos + Vec2i(x, y), this->MapLayer)) {
1110 					if (std::find(variation->TerrainsForbidden.begin(), variation->TerrainsForbidden.end(), Map.GetTileTopTerrain(this->tilePos + Vec2i(x, y), false, this->MapLayer->ID, true)) == variation->TerrainsForbidden.end()) {
1111 						terrain_check = false;
1112 						break;
1113 					}
1114 				}
1115 			}
1116 			if (!terrain_check) {
1117 				break;
1118 			}
1119 		}
1120 		if (terrain_check) {
1121 			return false;
1122 		}
1123 	}
1124 
1125 	return true;
1126 }
1127 
CheckSeasonForVariation(const CUnitTypeVariation * variation) const1128 bool CUnit::CheckSeasonForVariation(const CUnitTypeVariation *variation) const
1129 {
1130 	if (
1131 		!variation->Seasons.empty()
1132 		&& (!this->MapLayer || std::find(variation->Seasons.begin(), variation->Seasons.end(), this->MapLayer->GetSeason()) == variation->Seasons.end())
1133 	) {
1134 		return false;
1135 	}
1136 
1137 	if (
1138 		!variation->ForbiddenSeasons.empty()
1139 		&& this->MapLayer
1140 		&& std::find(variation->ForbiddenSeasons.begin(), variation->ForbiddenSeasons.end(), this->MapLayer->GetSeason()) != variation->ForbiddenSeasons.end()
1141 	) {
1142 		return false;
1143 	}
1144 
1145 	return true;
1146 }
1147 
ChooseVariation(const CUnitType * new_type,bool ignore_old_variation,int image_layer)1148 void CUnit::ChooseVariation(const CUnitType *new_type, bool ignore_old_variation, int image_layer)
1149 {
1150 	std::string priority_variation;
1151 	if (image_layer == -1) {
1152 		if (this->Character != nullptr && !this->Character->HairVariation.empty()) {
1153 			priority_variation = this->Character->HairVariation;
1154 		} else if (this->GetVariation() != nullptr) {
1155 			priority_variation = this->GetVariation()->VariationId;
1156 		}
1157 	} else {
1158 		if (image_layer == HairImageLayer && this->Character != nullptr && !this->Character->HairVariation.empty()) {
1159 			priority_variation = this->Character->HairVariation;
1160 		} else if (this->GetLayerVariation(image_layer)) {
1161 			priority_variation = this->GetLayerVariation(image_layer)->VariationId;
1162 		}
1163 	}
1164 
1165 	std::vector<CUnitTypeVariation *> type_variations;
1166 	const std::vector<CUnitTypeVariation *> &variation_list = image_layer == -1 ? (new_type != nullptr ? new_type->Variations : this->Type->Variations) : (new_type != nullptr ? new_type->LayerVariations[image_layer] : this->Type->LayerVariations[image_layer]);
1167 
1168 	bool found_similar = false;
1169 	for (CUnitTypeVariation *variation : variation_list) {
1170 		if (variation->ResourceMin && this->ResourcesHeld < variation->ResourceMin) {
1171 			continue;
1172 		}
1173 		if (variation->ResourceMax && this->ResourcesHeld > variation->ResourceMax) {
1174 			continue;
1175 		}
1176 
1177 		if (!this->CheckSeasonForVariation(variation)) {
1178 			continue;
1179 		}
1180 
1181 		if (!this->CheckTerrainForVariation(variation)) {
1182 			continue;
1183 		}
1184 
1185 		bool upgrades_check = true;
1186 		bool requires_weapon = false;
1187 		bool found_weapon = false;
1188 		bool requires_shield = false;
1189 		bool found_shield = false;
1190 		for (const CUpgrade *required_upgrade : variation->UpgradesRequired) {
1191 			if (required_upgrade->Weapon) {
1192 				requires_weapon = true;
1193 				if (UpgradeIdentAllowed(*this->Player, required_upgrade->Ident.c_str()) == 'R' || this->GetIndividualUpgrade(required_upgrade)) {
1194 					found_weapon = true;
1195 				}
1196 			} else if (required_upgrade->Shield) {
1197 				requires_shield = true;
1198 				if (UpgradeIdentAllowed(*this->Player, required_upgrade->Ident.c_str()) == 'R' || this->GetIndividualUpgrade(required_upgrade)) {
1199 					found_shield = true;
1200 				}
1201 			} else if (UpgradeIdentAllowed(*this->Player, required_upgrade->Ident.c_str()) != 'R' && this->GetIndividualUpgrade(required_upgrade) == false) {
1202 				upgrades_check = false;
1203 				break;
1204 			}
1205 		}
1206 
1207 		if (upgrades_check) {
1208 			for (const CUpgrade *forbidden_upgrade : variation->UpgradesForbidden) {
1209 				if (UpgradeIdentAllowed(*this->Player, forbidden_upgrade->Ident.c_str()) == 'R' || this->GetIndividualUpgrade(forbidden_upgrade)) {
1210 					upgrades_check = false;
1211 					break;
1212 				}
1213 			}
1214 		}
1215 
1216 		for (size_t j = 0; j < variation->ItemClassesNotEquipped.size(); ++j) {
1217 			if (this->IsItemClassEquipped(variation->ItemClassesNotEquipped[j])) {
1218 				upgrades_check = false;
1219 				break;
1220 			}
1221 		}
1222 		for (size_t j = 0; j < variation->ItemsNotEquipped.size(); ++j) {
1223 			if (this->IsItemTypeEquipped(variation->ItemsNotEquipped[j])) {
1224 				upgrades_check = false;
1225 				break;
1226 			}
1227 		}
1228 		if (upgrades_check == false) {
1229 			continue;
1230 		}
1231 		for (size_t j = 0; j < variation->ItemClassesEquipped.size(); ++j) {
1232 			if (GetItemClassSlot(variation->ItemClassesEquipped[j]) == WeaponItemSlot) {
1233 				requires_weapon = true;
1234 				if (IsItemClassEquipped(variation->ItemClassesEquipped[j])) {
1235 					found_weapon = true;
1236 				}
1237 			} else if (GetItemClassSlot(variation->ItemClassesEquipped[j]) == ShieldItemSlot) {
1238 				requires_shield = true;
1239 				if (IsItemClassEquipped(variation->ItemClassesEquipped[j])) {
1240 					found_shield = true;
1241 				}
1242 			}
1243 		}
1244 		for (size_t j = 0; j < variation->ItemsEquipped.size(); ++j) {
1245 			if (GetItemClassSlot(variation->ItemsEquipped[j]->ItemClass) == WeaponItemSlot) {
1246 				requires_weapon = true;
1247 				if (this->IsItemTypeEquipped(variation->ItemsEquipped[j])) {
1248 					found_weapon = true;
1249 				}
1250 			} else if (GetItemClassSlot(variation->ItemsEquipped[j]->ItemClass) == ShieldItemSlot) {
1251 				requires_shield = true;
1252 				if (this->IsItemTypeEquipped(variation->ItemsEquipped[j])) {
1253 					found_shield = true;
1254 				}
1255 			}
1256 		}
1257 		if ((requires_weapon && !found_weapon) || (requires_shield && !found_shield)) {
1258 			continue;
1259 		}
1260 		if (!ignore_old_variation && !priority_variation.empty() && (variation->VariationId.find(priority_variation) != std::string::npos || priority_variation.find(variation->VariationId) != std::string::npos)) { // if the priority variation's ident is included in that of a new viable variation (or vice-versa), give priority to the new variation over others
1261 			if (!found_similar) {
1262 				found_similar = true;
1263 				type_variations.clear();
1264 			}
1265 		} else {
1266 			if (found_similar) {
1267 				continue;
1268 			}
1269 		}
1270 		for (int j = 0; j < variation->Weight; ++j) {
1271 			type_variations.push_back(variation);
1272 		}
1273 	}
1274 	if (type_variations.size() > 0) {
1275 		this->SetVariation(type_variations[SyncRand(type_variations.size())], new_type, image_layer);
1276 	}
1277 }
1278 
SetVariation(CUnitTypeVariation * new_variation,const CUnitType * new_type,int image_layer)1279 void CUnit::SetVariation(CUnitTypeVariation *new_variation, const CUnitType *new_type, int image_layer)
1280 {
1281 	if (image_layer == -1) {
1282 		if (
1283 			(this->GetVariation() && this->GetVariation()->Animations)
1284 			|| (new_variation && new_variation->Animations)
1285 		) { //if the old (if any) or the new variation has specific animations, set the unit's frame to its type's still frame
1286 			this->Frame = this->Type->StillFrame;
1287 		}
1288 		this->Variation = new_variation ? new_variation->ID : 0;
1289 	} else {
1290 		this->LayerVariation[image_layer] = new_variation ? new_variation->ID : -1;
1291 	}
1292 }
1293 
GetVariation() const1294 const CUnitTypeVariation *CUnit::GetVariation() const
1295 {
1296 	if (this->Variation < (int) this->Type->Variations.size()) {
1297 		return this->Type->Variations[this->Variation];
1298 	}
1299 
1300 	return nullptr;
1301 }
1302 
GetLayerVariation(const unsigned int image_layer) const1303 const CUnitTypeVariation *CUnit::GetLayerVariation(const unsigned int image_layer) const
1304 {
1305 	if (this->LayerVariation[image_layer] >= 0 && this->LayerVariation[image_layer] < (int) this->Type->LayerVariations[image_layer].size()) {
1306 		return this->Type->LayerVariations[image_layer][this->LayerVariation[image_layer]];
1307 	}
1308 
1309 	return nullptr;
1310 }
1311 
UpdateButtonIcons()1312 void CUnit::UpdateButtonIcons()
1313 {
1314 	this->ChooseButtonIcon(ButtonAttack);
1315 	this->ChooseButtonIcon(ButtonStop);
1316 	this->ChooseButtonIcon(ButtonMove);
1317 	this->ChooseButtonIcon(ButtonStandGround);
1318 	this->ChooseButtonIcon(ButtonPatrol);
1319 	if (this->Type->BoolFlag[HARVESTER_INDEX].value) {
1320 		this->ChooseButtonIcon(ButtonReturn);
1321 	}
1322 }
1323 
ChooseButtonIcon(int button_action)1324 void CUnit::ChooseButtonIcon(int button_action)
1325 {
1326 	if (button_action == ButtonAttack) {
1327 		if (this->EquippedItems[ArrowsItemSlot].size() > 0 && this->EquippedItems[ArrowsItemSlot][0]->GetIcon().Icon != nullptr) {
1328 			this->ButtonIcons[button_action] = this->EquippedItems[ArrowsItemSlot][0]->GetIcon().Icon;
1329 			return;
1330 		}
1331 
1332 		if (this->EquippedItems[WeaponItemSlot].size() > 0 && this->EquippedItems[WeaponItemSlot][0]->Type->ItemClass != BowItemClass && this->EquippedItems[WeaponItemSlot][0]->GetIcon().Icon != nullptr) {
1333 			this->ButtonIcons[button_action] = this->EquippedItems[WeaponItemSlot][0]->GetIcon().Icon;
1334 			return;
1335 		}
1336 	} else if (button_action == ButtonStop) {
1337 		if (this->EquippedItems[ShieldItemSlot].size() > 0 && this->EquippedItems[ShieldItemSlot][0]->Type->ItemClass == ShieldItemClass && this->EquippedItems[ShieldItemSlot][0]->GetIcon().Icon != nullptr) {
1338 			this->ButtonIcons[button_action] = this->EquippedItems[ShieldItemSlot][0]->GetIcon().Icon;
1339 			return;
1340 		}
1341 	} else if (button_action == ButtonMove) {
1342 		if (this->EquippedItems[BootsItemSlot].size() > 0 && this->EquippedItems[BootsItemSlot][0]->GetIcon().Icon != nullptr) {
1343 			this->ButtonIcons[button_action] = this->EquippedItems[BootsItemSlot][0]->GetIcon().Icon;
1344 			return;
1345 		}
1346 	} else if (button_action == ButtonStandGround) {
1347 		if (this->EquippedItems[ArrowsItemSlot].size() > 0 && this->EquippedItems[ArrowsItemSlot][0]->Type->ButtonIcons.find(button_action) != this->EquippedItems[ArrowsItemSlot][0]->Type->ButtonIcons.end()) {
1348 			this->ButtonIcons[button_action] = this->EquippedItems[ArrowsItemSlot][0]->Type->ButtonIcons.find(button_action)->second.Icon;
1349 			return;
1350 		}
1351 
1352 		if (this->EquippedItems[WeaponItemSlot].size() > 0 && this->EquippedItems[WeaponItemSlot][0]->Type->ButtonIcons.find(button_action) != this->EquippedItems[WeaponItemSlot][0]->Type->ButtonIcons.end()) {
1353 			this->ButtonIcons[button_action] = this->EquippedItems[WeaponItemSlot][0]->Type->ButtonIcons.find(button_action)->second.Icon;
1354 			return;
1355 		}
1356 	}
1357 
1358 	const CUnitTypeVariation *variation = this->GetVariation();
1359 	if (variation && variation->ButtonIcons.find(button_action) != variation->ButtonIcons.end()) {
1360 		this->ButtonIcons[button_action] = variation->ButtonIcons.find(button_action)->second.Icon;
1361 		return;
1362 	}
1363 	for (int i = 0; i < MaxImageLayers; ++i) {
1364 		const CUnitTypeVariation *layer_variation = this->GetLayerVariation(i);
1365 		if (layer_variation && layer_variation->ButtonIcons.find(button_action) != layer_variation->ButtonIcons.end()) {
1366 			this->ButtonIcons[button_action] = layer_variation->ButtonIcons.find(button_action)->second.Icon;
1367 			return;
1368 		}
1369 	}
1370 
1371 	int all_upgrades_size = AllUpgrades.size();
1372 
1373 	for (int i = (CUpgradeModifier::UpgradeModifiers.size() - 1); i >= 0; --i) {
1374 		const CUpgradeModifier *modifier = CUpgradeModifier::UpgradeModifiers[i];
1375 		const CUpgrade *upgrade = AllUpgrades[modifier->UpgradeId];
1376 		if (this->Player->Allow.Upgrades[upgrade->ID] == 'R' && modifier->ApplyTo[this->Type->Slot] == 'X') {
1377 			if (
1378 				(
1379 					(button_action == ButtonAttack && ((upgrade->Weapon && upgrade->Item->ItemClass != BowItemClass) || upgrade->Arrows))
1380 					|| (button_action == ButtonStop && upgrade->Shield)
1381 					|| (button_action == ButtonMove && upgrade->Boots)
1382 				)
1383 				&& upgrade->Item->Icon.Icon != nullptr
1384 			) {
1385 				this->ButtonIcons[button_action] = upgrade->Item->Icon.Icon;
1386 				return;
1387 			} else if (button_action == ButtonStandGround && (upgrade->Weapon || upgrade->Arrows) && upgrade->Item->ButtonIcons.find(button_action) != upgrade->Item->ButtonIcons.end()) {
1388 				this->ButtonIcons[button_action] = upgrade->Item->ButtonIcons.find(button_action)->second.Icon;
1389 				return;
1390 			}
1391 		}
1392 	}
1393 
1394 	if (button_action == ButtonAttack) {
1395 		if (this->Type->DefaultEquipment.find(ArrowsItemSlot) != this->Type->DefaultEquipment.end() && this->Type->DefaultEquipment.find(ArrowsItemSlot)->second->Icon.Icon != nullptr) {
1396 			this->ButtonIcons[button_action] = this->Type->DefaultEquipment.find(ArrowsItemSlot)->second->Icon.Icon;
1397 			return;
1398 		}
1399 
1400 		if (this->Type->DefaultEquipment.find(WeaponItemSlot) != this->Type->DefaultEquipment.end() && this->Type->DefaultEquipment.find(WeaponItemSlot)->second->Icon.Icon != nullptr) {
1401 			this->ButtonIcons[button_action] = this->Type->DefaultEquipment.find(WeaponItemSlot)->second->Icon.Icon;
1402 			return;
1403 		}
1404 	} else if (button_action == ButtonStop) {
1405 		if (this->Type->DefaultEquipment.find(ShieldItemSlot) != this->Type->DefaultEquipment.end() && this->Type->DefaultEquipment.find(ShieldItemSlot)->second->ItemClass == ShieldItemClass && this->Type->DefaultEquipment.find(ShieldItemSlot)->second->Icon.Icon != nullptr) {
1406 			this->ButtonIcons[button_action] = this->Type->DefaultEquipment.find(ShieldItemSlot)->second->Icon.Icon;
1407 			return;
1408 		}
1409 	} else if (button_action == ButtonMove) {
1410 		if (this->Type->DefaultEquipment.find(BootsItemSlot) != this->Type->DefaultEquipment.end() && this->Type->DefaultEquipment.find(BootsItemSlot)->second->Icon.Icon != nullptr) {
1411 			this->ButtonIcons[button_action] = this->Type->DefaultEquipment.find(BootsItemSlot)->second->Icon.Icon;
1412 			return;
1413 		}
1414 	} else if (button_action == ButtonStandGround) {
1415 		if (this->Type->DefaultEquipment.find(ArrowsItemSlot) != this->Type->DefaultEquipment.end() && this->Type->DefaultEquipment.find(ArrowsItemSlot)->second->ButtonIcons.find(button_action) != this->Type->DefaultEquipment.find(ArrowsItemSlot)->second->ButtonIcons.end()) {
1416 			this->ButtonIcons[button_action] = this->Type->DefaultEquipment.find(ArrowsItemSlot)->second->ButtonIcons.find(button_action)->second.Icon;
1417 			return;
1418 		}
1419 
1420 		if (this->Type->DefaultEquipment.find(WeaponItemSlot) != this->Type->DefaultEquipment.end() && this->Type->DefaultEquipment.find(WeaponItemSlot)->second->ButtonIcons.find(button_action) != this->Type->DefaultEquipment.find(WeaponItemSlot)->second->ButtonIcons.end()) {
1421 			this->ButtonIcons[button_action] = this->Type->DefaultEquipment.find(WeaponItemSlot)->second->ButtonIcons.find(button_action)->second.Icon;
1422 			return;
1423 		}
1424 	}
1425 
1426 	if (this->Type->ButtonIcons.find(button_action) != this->Type->ButtonIcons.end()) {
1427 		this->ButtonIcons[button_action] = this->Type->ButtonIcons.find(button_action)->second.Icon;
1428 		return;
1429 	}
1430 
1431 	if (this->Type->Civilization != -1) {
1432 		int civilization = this->Type->Civilization;
1433 		int faction = this->Type->Faction;
1434 
1435 		if (faction == -1 && this->Player->Race == civilization) {
1436 			faction = this->Player->Faction;
1437 		}
1438 
1439 		if (faction != -1 && PlayerRaces.Factions[faction]->ButtonIcons.find(button_action) != PlayerRaces.Factions[faction]->ButtonIcons.end()) {
1440 			this->ButtonIcons[button_action] = PlayerRaces.Factions[faction]->ButtonIcons[button_action].Icon;
1441 			return;
1442 		} else if (PlayerRaces.ButtonIcons[civilization].find(button_action) != PlayerRaces.ButtonIcons[civilization].end()) {
1443 			this->ButtonIcons[button_action] = PlayerRaces.ButtonIcons[civilization][button_action].Icon;
1444 			return;
1445 		}
1446 	}
1447 
1448 	if (this->ButtonIcons.find(button_action) != this->ButtonIcons.end()) { //if no proper button icon found, make sure any old button icon set for this button action isn't used either
1449 		this->ButtonIcons.erase(button_action);
1450 	}
1451 }
1452 
EquipItem(CUnit & item,bool affect_character)1453 void CUnit::EquipItem(CUnit &item, bool affect_character)
1454 {
1455 	int item_class = item.Type->ItemClass;
1456 	int item_slot = GetItemClassSlot(item_class);
1457 
1458 	if (item_slot == -1) {
1459 		fprintf(stderr, "Trying to equip item of type \"%s\", which has no item slot.\n", item.GetTypeName().c_str());
1460 		return;
1461 	}
1462 
1463 	if (GetItemSlotQuantity(item_slot) > 0 && EquippedItems[item_slot].size() == GetItemSlotQuantity(item_slot)) {
1464 		DeequipItem(*EquippedItems[item_slot][EquippedItems[item_slot].size() - 1]);
1465 	}
1466 
1467 	if (item_slot == WeaponItemSlot && EquippedItems[item_slot].size() == 0) {
1468 		// remove the upgrade modifiers from weapon technologies or from abilities which require the base weapon class but aren't compatible with this weapon's class; and apply upgrade modifiers from abilities which require this weapon's class
1469 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1470 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1471 			if (
1472 				(modifier_upgrade->Weapon && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X')
1473 				|| (modifier_upgrade->Ability && this->GetIndividualUpgrade(modifier_upgrade) && modifier_upgrade->WeaponClasses.size() > 0 && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), this->Type->WeaponClasses[0]) != modifier_upgrade->WeaponClasses.end() && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), item_class) == modifier_upgrade->WeaponClasses.end())
1474 			) {
1475 				if (this->GetIndividualUpgrade(modifier_upgrade)) {
1476 					for (int i = 0; i < this->GetIndividualUpgrade(modifier_upgrade); ++i) {
1477 						RemoveIndividualUpgradeModifier(*this, modifier);
1478 					}
1479 				} else {
1480 					RemoveIndividualUpgradeModifier(*this, modifier);
1481 				}
1482 			} else if (
1483 				modifier_upgrade->Ability && this->GetIndividualUpgrade(modifier_upgrade) && modifier_upgrade->WeaponClasses.size() > 0 && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), this->Type->WeaponClasses[0]) == modifier_upgrade->WeaponClasses.end() && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), item_class) != modifier_upgrade->WeaponClasses.end()
1484 			) {
1485 				if (this->GetIndividualUpgrade(modifier_upgrade)) {
1486 					for (int i = 0; i < this->GetIndividualUpgrade(modifier_upgrade); ++i) {
1487 						ApplyIndividualUpgradeModifier(*this, modifier);
1488 					}
1489 				} else {
1490 					ApplyIndividualUpgradeModifier(*this, modifier);
1491 				}
1492 			}
1493 		}
1494 	} else if (item_slot == ShieldItemSlot && EquippedItems[item_slot].size() == 0) {
1495 		// remove the upgrade modifiers from shield technologies
1496 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1497 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1498 			if (modifier_upgrade->Shield && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') {
1499 				RemoveIndividualUpgradeModifier(*this, modifier);
1500 			}
1501 		}
1502 	} else if (item_slot == BootsItemSlot && EquippedItems[item_slot].size() == 0) {
1503 		// remove the upgrade modifiers from boots technologies
1504 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1505 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1506 			if (modifier_upgrade->Boots && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') {
1507 				RemoveIndividualUpgradeModifier(*this, modifier);
1508 			}
1509 		}
1510 	} else if (item_slot == ArrowsItemSlot && EquippedItems[item_slot].size() == 0) {
1511 		// remove the upgrade modifiers from arrows technologies
1512 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1513 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1514 			if (modifier_upgrade->Arrows && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') {
1515 				RemoveIndividualUpgradeModifier(*this, modifier);
1516 			}
1517 		}
1518 	}
1519 
1520 	if (item.Unique && item.Unique->Set && this->EquippingItemCompletesSet(&item)) {
1521 		for (const CUpgradeModifier *modifier : item.Unique->Set->UpgradeModifiers) {
1522 			ApplyIndividualUpgradeModifier(*this, modifier);
1523 		}
1524 	}
1525 
1526 	if (!IsNetworkGame() && Character && this->Player->AiEnabled == false && affect_character) {
1527 		if (Character->GetItem(item) != nullptr) {
1528 			if (!Character->IsItemEquipped(Character->GetItem(item))) {
1529 				Character->EquippedItems[item_slot].push_back(Character->GetItem(item));
1530 				SaveHero(Character);
1531 			} else {
1532 				fprintf(stderr, "Item is not equipped by character \"%s\"'s unit, but is equipped by the character itself.\n", Character->Ident.c_str());
1533 			}
1534 		} else {
1535 			fprintf(stderr, "Item is present in the inventory of the character \"%s\"'s unit, but not in the character's inventory itself.\n", Character->Ident.c_str());
1536 		}
1537 	}
1538 	EquippedItems[item_slot].push_back(&item);
1539 
1540 	//change variation, if the current one has become forbidden
1541 	const CUnitTypeVariation *variation = this->GetVariation();
1542 	if (
1543 		variation
1544 		&& (
1545 			std::find(variation->ItemClassesNotEquipped.begin(), variation->ItemClassesNotEquipped.end(), item.Type->ItemClass) != variation->ItemClassesNotEquipped.end()
1546 			|| std::find(variation->ItemsNotEquipped.begin(), variation->ItemsNotEquipped.end(), item.Type) != variation->ItemsNotEquipped.end()
1547 		)
1548 	) {
1549 		ChooseVariation(); //choose a new variation now
1550 	}
1551 	for (int i = 0; i < MaxImageLayers; ++i) {
1552 		const CUnitTypeVariation *layer_variation = this->GetLayerVariation(i);
1553 		if (
1554 			layer_variation
1555 			&& (
1556 				std::find(layer_variation->ItemClassesNotEquipped.begin(), layer_variation->ItemClassesNotEquipped.end(), item.Type->ItemClass) != layer_variation->ItemClassesNotEquipped.end()
1557 				|| std::find(layer_variation->ItemsNotEquipped.begin(), layer_variation->ItemsNotEquipped.end(), item.Type) != layer_variation->ItemsNotEquipped.end()
1558 			)
1559 		) {
1560 			ChooseVariation(nullptr, false, i);
1561 		}
1562 	}
1563 
1564 	if (item_slot == WeaponItemSlot || item_slot == ArrowsItemSlot) {
1565 		this->ChooseButtonIcon(ButtonAttack);
1566 		this->ChooseButtonIcon(ButtonStandGround);
1567 	} else if (item_slot == ShieldItemSlot) {
1568 		this->ChooseButtonIcon(ButtonStop);
1569 	} else if (item_slot == BootsItemSlot) {
1570 		this->ChooseButtonIcon(ButtonMove);
1571 	}
1572 	this->ChooseButtonIcon(ButtonPatrol);
1573 
1574 	//add item bonuses
1575 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); i++) {
1576 		if (
1577 			i == BASICDAMAGE_INDEX || i == PIERCINGDAMAGE_INDEX || i == THORNSDAMAGE_INDEX
1578 			|| i == FIREDAMAGE_INDEX || i == COLDDAMAGE_INDEX || i == ARCANEDAMAGE_INDEX || i == LIGHTNINGDAMAGE_INDEX
1579 			|| i == AIRDAMAGE_INDEX || i == EARTHDAMAGE_INDEX || i == WATERDAMAGE_INDEX || i == ACIDDAMAGE_INDEX
1580 			|| i == ARMOR_INDEX || i == FIRERESISTANCE_INDEX || i == COLDRESISTANCE_INDEX || i == ARCANERESISTANCE_INDEX || i == LIGHTNINGRESISTANCE_INDEX
1581 			|| i == AIRRESISTANCE_INDEX || i == EARTHRESISTANCE_INDEX || i == WATERRESISTANCE_INDEX || i == ACIDRESISTANCE_INDEX
1582 			|| i == HACKRESISTANCE_INDEX || i == PIERCERESISTANCE_INDEX || i == BLUNTRESISTANCE_INDEX
1583 			|| i == ACCURACY_INDEX || i == EVASION_INDEX || i == SPEED_INDEX || i == CHARGEBONUS_INDEX || i == BACKSTAB_INDEX
1584 			|| i == ATTACKRANGE_INDEX
1585 		) {
1586 			Variable[i].Value += item.Variable[i].Value;
1587 			Variable[i].Max += item.Variable[i].Max;
1588 		} else if (i == HITPOINTBONUS_INDEX) {
1589 			Variable[HP_INDEX].Value += item.Variable[i].Value;
1590 			Variable[HP_INDEX].Max += item.Variable[i].Max;
1591 			Variable[HP_INDEX].Increase += item.Variable[i].Increase;
1592 		} else if (i == SIGHTRANGE_INDEX || i == DAYSIGHTRANGEBONUS_INDEX || i == NIGHTSIGHTRANGEBONUS_INDEX) {
1593 			if (!SaveGameLoading) {
1594 				MapUnmarkUnitSight(*this);
1595 			}
1596 			Variable[i].Value += item.Variable[i].Value;
1597 			Variable[i].Max += item.Variable[i].Max;
1598 			if (!SaveGameLoading) {
1599 				if (i == SIGHTRANGE_INDEX) {
1600 					CurrentSightRange = Variable[i].Value;
1601 				}
1602 				UpdateUnitSightRange(*this);
1603 				MapMarkUnitSight(*this);
1604 			}
1605 		}
1606 	}
1607 }
1608 
DeequipItem(CUnit & item,bool affect_character)1609 void CUnit::DeequipItem(CUnit &item, bool affect_character)
1610 {
1611 	//remove item bonuses
1612 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); i++) {
1613 		if (
1614 			i == BASICDAMAGE_INDEX || i == PIERCINGDAMAGE_INDEX || i == THORNSDAMAGE_INDEX
1615 			|| i == FIREDAMAGE_INDEX || i == COLDDAMAGE_INDEX || i == ARCANEDAMAGE_INDEX || i == LIGHTNINGDAMAGE_INDEX
1616 			|| i == AIRDAMAGE_INDEX || i == EARTHDAMAGE_INDEX || i == WATERDAMAGE_INDEX || i == ACIDDAMAGE_INDEX
1617 			|| i == ARMOR_INDEX || i == FIRERESISTANCE_INDEX || i == COLDRESISTANCE_INDEX || i == ARCANERESISTANCE_INDEX || i == LIGHTNINGRESISTANCE_INDEX
1618 			|| i == AIRRESISTANCE_INDEX || i == EARTHRESISTANCE_INDEX || i == WATERRESISTANCE_INDEX || i == ACIDRESISTANCE_INDEX
1619 			|| i == HACKRESISTANCE_INDEX || i == PIERCERESISTANCE_INDEX || i == BLUNTRESISTANCE_INDEX
1620 			|| i == ACCURACY_INDEX || i == EVASION_INDEX || i == SPEED_INDEX || i == CHARGEBONUS_INDEX || i == BACKSTAB_INDEX
1621 			|| i == ATTACKRANGE_INDEX
1622 		) {
1623 			Variable[i].Value -= item.Variable[i].Value;
1624 			Variable[i].Max -= item.Variable[i].Max;
1625 		} else if (i == HITPOINTBONUS_INDEX) {
1626 			Variable[HP_INDEX].Value -= item.Variable[i].Value;
1627 			Variable[HP_INDEX].Max -= item.Variable[i].Max;
1628 			Variable[HP_INDEX].Increase -= item.Variable[i].Increase;
1629 		} else if (i == SIGHTRANGE_INDEX || i == DAYSIGHTRANGEBONUS_INDEX || i == NIGHTSIGHTRANGEBONUS_INDEX) {
1630 			MapUnmarkUnitSight(*this);
1631 			Variable[i].Value -= item.Variable[i].Value;
1632 			Variable[i].Max -= item.Variable[i].Max;
1633 			if (i == SIGHTRANGE_INDEX) {
1634 				CurrentSightRange = Variable[i].Value;
1635 			}
1636 			UpdateUnitSightRange(*this);
1637 			MapMarkUnitSight(*this);
1638 		}
1639 	}
1640 
1641 	if (item.Unique && item.Unique->Set && this->DeequippingItemBreaksSet(&item)) {
1642 		for (const CUpgradeModifier *modifier : item.Unique->Set->UpgradeModifiers) {
1643 			RemoveIndividualUpgradeModifier(*this, modifier);
1644 		}
1645 	}
1646 
1647 	int item_class = item.Type->ItemClass;
1648 	int item_slot = GetItemClassSlot(item_class);
1649 
1650 	if (item_slot == -1) {
1651 		fprintf(stderr, "Trying to de-equip item of type \"%s\", which has no item slot.\n", item.GetTypeName().c_str());
1652 		return;
1653 	}
1654 
1655 	if (!IsNetworkGame() && Character && this->Player->AiEnabled == false && affect_character) {
1656 		if (Character->GetItem(item) != nullptr) {
1657 			if (Character->IsItemEquipped(Character->GetItem(item))) {
1658 				Character->EquippedItems[item_slot].erase(std::remove(Character->EquippedItems[item_slot].begin(), Character->EquippedItems[item_slot].end(), Character->GetItem(item)), Character->EquippedItems[item_slot].end());
1659 				SaveHero(Character);
1660 			} else {
1661 				fprintf(stderr, "Item is equipped by character \"%s\"'s unit, but not by the character itself.\n", Character->Ident.c_str());
1662 			}
1663 		} else {
1664 			fprintf(stderr, "Item is present in the inventory of the character \"%s\"'s unit, but not in the character's inventory itself.\n", Character->Ident.c_str());
1665 		}
1666 	}
1667 	EquippedItems[item_slot].erase(std::remove(EquippedItems[item_slot].begin(), EquippedItems[item_slot].end(), &item), EquippedItems[item_slot].end());
1668 
1669 	if (item_slot == WeaponItemSlot && EquippedItems[item_slot].size() == 0) {
1670 		// restore the upgrade modifiers from weapon technologies, and apply ability effects that are weapon class-specific accordingly
1671 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1672 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1673 			if (
1674 				(modifier_upgrade->Weapon && Player->Allow.Upgrades[modifier->UpgradeId] == 'R' && modifier->ApplyTo[Type->Slot] == 'X')
1675 				|| (modifier_upgrade->Ability && this->GetIndividualUpgrade(modifier_upgrade) && modifier_upgrade->WeaponClasses.size() > 0 && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), this->Type->WeaponClasses[0]) != modifier_upgrade->WeaponClasses.end() && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), item_class) == modifier_upgrade->WeaponClasses.end())
1676 			) {
1677 				if (this->GetIndividualUpgrade(modifier_upgrade)) {
1678 					for (int i = 0; i < this->GetIndividualUpgrade(modifier_upgrade); ++i) {
1679 						ApplyIndividualUpgradeModifier(*this, modifier);
1680 					}
1681 				} else {
1682 					ApplyIndividualUpgradeModifier(*this, modifier);
1683 				}
1684 			} else if (
1685 				modifier_upgrade->Ability && this->GetIndividualUpgrade(modifier_upgrade) && modifier_upgrade->WeaponClasses.size() > 0 && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), this->Type->WeaponClasses[0]) == modifier_upgrade->WeaponClasses.end() && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), item_class) != modifier_upgrade->WeaponClasses.end()
1686 			) {
1687 				if (this->GetIndividualUpgrade(modifier_upgrade)) {
1688 					for (int i = 0; i < this->GetIndividualUpgrade(modifier_upgrade); ++i) {
1689 						RemoveIndividualUpgradeModifier(*this, modifier);
1690 					}
1691 				} else {
1692 					RemoveIndividualUpgradeModifier(*this, modifier);
1693 				}
1694 			}
1695 		}
1696 	} else if (item_slot == ShieldItemSlot && EquippedItems[item_slot].size() == 0) {
1697 		// restore the upgrade modifiers from shield technologies
1698 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1699 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1700 			if (modifier_upgrade->Shield && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') {
1701 				ApplyIndividualUpgradeModifier(*this, modifier);
1702 			}
1703 		}
1704 	} else if (item_slot == BootsItemSlot && EquippedItems[item_slot].size() == 0) {
1705 		// restore the upgrade modifiers from boots technologies
1706 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1707 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1708 			if (modifier_upgrade->Boots && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') {
1709 				ApplyIndividualUpgradeModifier(*this, modifier);
1710 			}
1711 		}
1712 	} else if (item_slot == ArrowsItemSlot && EquippedItems[item_slot].size() == 0) {
1713 		// restore the upgrade modifiers from arrows technologies
1714 		for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
1715 			const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
1716 			if (modifier_upgrade->Arrows && Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') {
1717 				ApplyIndividualUpgradeModifier(*this, modifier);
1718 			}
1719 		}
1720 	}
1721 
1722 	//change variation, if the current one has become forbidden
1723 	const CUnitTypeVariation *variation = this->GetVariation();
1724 	if (
1725 		variation
1726 		&& (
1727 			std::find(variation->ItemClassesEquipped.begin(), variation->ItemClassesEquipped.end(), item.Type->ItemClass) != variation->ItemClassesEquipped.end()
1728 			|| std::find(variation->ItemsEquipped.begin(), variation->ItemsEquipped.end(), item.Type) != variation->ItemsEquipped.end()
1729 		)
1730 	) {
1731 		ChooseVariation(); //choose a new variation now
1732 	}
1733 	for (int i = 0; i < MaxImageLayers; ++i) {
1734 		const CUnitTypeVariation *layer_variation = this->GetLayerVariation(i);
1735 
1736 		if (
1737 			layer_variation
1738 			&& (
1739 				std::find(layer_variation->ItemClassesEquipped.begin(), layer_variation->ItemClassesEquipped.end(), item.Type->ItemClass) != layer_variation->ItemClassesEquipped.end()
1740 				|| std::find(layer_variation->ItemsEquipped.begin(), layer_variation->ItemsEquipped.end(), item.Type) != layer_variation->ItemsEquipped.end()
1741 			)
1742 		) {
1743 			ChooseVariation(nullptr, false, i);
1744 		}
1745 	}
1746 
1747 	if (item_slot == WeaponItemSlot || item_slot == ArrowsItemSlot) {
1748 		this->ChooseButtonIcon(ButtonAttack);
1749 		this->ChooseButtonIcon(ButtonStandGround);
1750 	} else if (item_slot == ShieldItemSlot) {
1751 		this->ChooseButtonIcon(ButtonStop);
1752 	} else if (item_slot == BootsItemSlot) {
1753 		this->ChooseButtonIcon(ButtonMove);
1754 	}
1755 	this->ChooseButtonIcon(ButtonPatrol);
1756 }
1757 
ReadWork(CUpgrade * work,bool affect_character)1758 void CUnit::ReadWork(CUpgrade *work, bool affect_character)
1759 {
1760 	IndividualUpgradeAcquire(*this, work);
1761 
1762 	if (!IsNetworkGame() && Character && this->Player->AiEnabled == false && affect_character) {
1763 		if (std::find(Character->ReadWorks.begin(), Character->ReadWorks.end(), work) == Character->ReadWorks.end()) {
1764 			Character->ReadWorks.push_back(work);
1765 			SaveHero(Character);
1766 		}
1767 	}
1768 }
1769 
ConsumeElixir(CUpgrade * elixir,bool affect_character)1770 void CUnit::ConsumeElixir(CUpgrade *elixir, bool affect_character)
1771 {
1772 	IndividualUpgradeAcquire(*this, elixir);
1773 
1774 	if (!IsNetworkGame() && Character && this->Player->AiEnabled == false && affect_character) {
1775 		if (std::find(Character->ConsumedElixirs.begin(), Character->ConsumedElixirs.end(), elixir) == Character->ConsumedElixirs.end()) {
1776 			Character->ConsumedElixirs.push_back(elixir);
1777 			SaveHero(Character);
1778 		}
1779 	}
1780 }
1781 
ApplyAura(int aura_index)1782 void CUnit::ApplyAura(int aura_index)
1783 {
1784 	if (aura_index == LEADERSHIPAURA_INDEX) {
1785 		if (!this->IsInCombat()) {
1786 			return;
1787 		}
1788 	}
1789 
1790 	this->ApplyAuraEffect(aura_index);
1791 
1792 	//apply aura to all appropriate nearby units
1793 	int aura_range = AuraRange - (this->Type->TileSize.x - 1);
1794 	std::vector<CUnit *> table;
1795 	SelectAroundUnit(*this, aura_range, table, MakeOrPredicate(HasSamePlayerAs(*this->Player), IsAlliedWith(*this->Player)), true);
1796 	for (size_t i = 0; i != table.size(); ++i) {
1797 		table[i]->ApplyAuraEffect(aura_index);
1798 	}
1799 
1800 	table.clear();
1801 	SelectAroundUnit(*this, aura_range, table, MakeOrPredicate(MakeOrPredicate(HasSamePlayerAs(*this->Player), IsAlliedWith(*this->Player)), HasSamePlayerAs(Players[PlayerNumNeutral])), true);
1802 	for (size_t i = 0; i != table.size(); ++i) {
1803 		if (table[i]->UnitInside) {
1804 			CUnit *uins = table[i]->UnitInside;
1805 			for (int j = 0; j < table[i]->InsideCount; ++j, uins = uins->NextContained) {
1806 				if (uins->Player == this->Player || uins->IsAllied(*this->Player)) {
1807 					uins->ApplyAuraEffect(aura_index);
1808 				}
1809 			}
1810 		}
1811 	}
1812 }
1813 
ApplyAuraEffect(int aura_index)1814 void CUnit::ApplyAuraEffect(int aura_index)
1815 {
1816 	int effect_index = -1;
1817 	if (aura_index == LEADERSHIPAURA_INDEX) {
1818 		if (this->Type->BoolFlag[BUILDING_INDEX].value) {
1819 			return;
1820 		}
1821 		effect_index = LEADERSHIP_INDEX;
1822 	} else if (aura_index == REGENERATIONAURA_INDEX) {
1823 		if (!this->Type->BoolFlag[ORGANIC_INDEX].value || this->Variable[HP_INDEX].Value >= this->GetModifiedVariable(HP_INDEX, VariableMax)) {
1824 			return;
1825 		}
1826 		effect_index = REGENERATION_INDEX;
1827 	} else if (aura_index == HYDRATINGAURA_INDEX) {
1828 		if (!this->Type->BoolFlag[ORGANIC_INDEX].value) {
1829 			return;
1830 		}
1831 		effect_index = HYDRATING_INDEX;
1832 		this->Variable[DEHYDRATION_INDEX].Max = 0;
1833 		this->Variable[DEHYDRATION_INDEX].Value = 0;
1834 	}
1835 
1836 	if (effect_index == -1) {
1837 		return;
1838 	}
1839 
1840 	this->Variable[effect_index].Enable = 1;
1841 	this->Variable[effect_index].Max = std::max(CYCLES_PER_SECOND + 1, this->Variable[effect_index].Max);
1842 	this->Variable[effect_index].Value = std::max(CYCLES_PER_SECOND + 1, this->Variable[effect_index].Value);
1843 }
1844 
SetPrefix(CUpgrade * prefix)1845 void CUnit::SetPrefix(CUpgrade *prefix)
1846 {
1847 	if (Prefix != nullptr) {
1848 		for (size_t z = 0; z < Prefix->UpgradeModifiers.size(); ++z) {
1849 			RemoveIndividualUpgradeModifier(*this, Prefix->UpgradeModifiers[z]);
1850 		}
1851 		this->Variable[MAGICLEVEL_INDEX].Value -= Prefix->MagicLevel;
1852 		this->Variable[MAGICLEVEL_INDEX].Max -= Prefix->MagicLevel;
1853 	}
1854 	if (!IsNetworkGame() && Container && Container->Character && Container->Player->AiEnabled == false && Container->Character->GetItem(*this) != nullptr && Container->Character->GetItem(*this)->Prefix != prefix) { //update the persistent item, if applicable and if it hasn't been updated yet
1855 		Container->Character->GetItem(*this)->Prefix = prefix;
1856 		SaveHero(Container->Character);
1857 	}
1858 	Prefix = prefix;
1859 	if (Prefix != nullptr) {
1860 		for (size_t z = 0; z < Prefix->UpgradeModifiers.size(); ++z) {
1861 			ApplyIndividualUpgradeModifier(*this, Prefix->UpgradeModifiers[z]);
1862 		}
1863 		this->Variable[MAGICLEVEL_INDEX].Value += Prefix->MagicLevel;
1864 		this->Variable[MAGICLEVEL_INDEX].Max += Prefix->MagicLevel;
1865 	}
1866 
1867 	this->UpdateItemName();
1868 }
1869 
SetSuffix(CUpgrade * suffix)1870 void CUnit::SetSuffix(CUpgrade *suffix)
1871 {
1872 	if (Suffix != nullptr) {
1873 		for (size_t z = 0; z < Suffix->UpgradeModifiers.size(); ++z) {
1874 			RemoveIndividualUpgradeModifier(*this, Suffix->UpgradeModifiers[z]);
1875 		}
1876 		this->Variable[MAGICLEVEL_INDEX].Value -= Suffix->MagicLevel;
1877 		this->Variable[MAGICLEVEL_INDEX].Max -= Suffix->MagicLevel;
1878 	}
1879 	if (!IsNetworkGame() && Container && Container->Character && Container->Player->AiEnabled == false && Container->Character->GetItem(*this) != nullptr && Container->Character->GetItem(*this)->Suffix != suffix) { //update the persistent item, if applicable and if it hasn't been updated yet
1880 		Container->Character->GetItem(*this)->Suffix = suffix;
1881 		SaveHero(Container->Character);
1882 	}
1883 	Suffix = suffix;
1884 	if (Suffix != nullptr) {
1885 		for (size_t z = 0; z < Suffix->UpgradeModifiers.size(); ++z) {
1886 			ApplyIndividualUpgradeModifier(*this, Suffix->UpgradeModifiers[z]);
1887 		}
1888 		this->Variable[MAGICLEVEL_INDEX].Value += Suffix->MagicLevel;
1889 		this->Variable[MAGICLEVEL_INDEX].Max += Suffix->MagicLevel;
1890 	}
1891 
1892 	this->UpdateItemName();
1893 }
1894 
SetSpell(CSpell * spell)1895 void CUnit::SetSpell(CSpell *spell)
1896 {
1897 	if (!IsNetworkGame() && Container && Container->Character && Container->Player->AiEnabled == false && Container->Character->GetItem(*this) != nullptr && Container->Character->GetItem(*this)->Spell != spell) { //update the persistent item, if applicable and if it hasn't been updated yet
1898 		Container->Character->GetItem(*this)->Spell = spell;
1899 		SaveHero(Container->Character);
1900 	}
1901 	Spell = spell;
1902 
1903 	this->UpdateItemName();
1904 }
1905 
SetWork(CUpgrade * work)1906 void CUnit::SetWork(CUpgrade *work)
1907 {
1908 	if (this->Work != nullptr) {
1909 		this->Variable[MAGICLEVEL_INDEX].Value -= this->Work->MagicLevel;
1910 		this->Variable[MAGICLEVEL_INDEX].Max -= this->Work->MagicLevel;
1911 	}
1912 
1913 	if (!IsNetworkGame() && Container && Container->Character && Container->Player->AiEnabled == false && Container->Character->GetItem(*this) != nullptr && Container->Character->GetItem(*this)->Work != work) { //update the persistent item, if applicable and if it hasn't been updated yet
1914 		Container->Character->GetItem(*this)->Work = work;
1915 		SaveHero(Container->Character);
1916 	}
1917 
1918 	Work = work;
1919 
1920 	if (this->Work != nullptr) {
1921 		this->Variable[MAGICLEVEL_INDEX].Value += this->Work->MagicLevel;
1922 		this->Variable[MAGICLEVEL_INDEX].Max += this->Work->MagicLevel;
1923 	}
1924 
1925 	this->UpdateItemName();
1926 }
1927 
SetElixir(CUpgrade * elixir)1928 void CUnit::SetElixir(CUpgrade *elixir)
1929 {
1930 	if (this->Elixir != nullptr) {
1931 		this->Variable[MAGICLEVEL_INDEX].Value -= this->Elixir->MagicLevel;
1932 		this->Variable[MAGICLEVEL_INDEX].Max -= this->Elixir->MagicLevel;
1933 	}
1934 
1935 	if (!IsNetworkGame() && Container && Container->Character && Container->Player->AiEnabled == false && Container->Character->GetItem(*this) != nullptr && Container->Character->GetItem(*this)->Elixir != elixir) { //update the persistent item, if applicable and if it hasn't been updated yet
1936 		Container->Character->GetItem(*this)->Elixir = elixir;
1937 		SaveHero(Container->Character);
1938 	}
1939 
1940 	Elixir = elixir;
1941 
1942 	if (this->Elixir != nullptr) {
1943 		this->Variable[MAGICLEVEL_INDEX].Value += this->Elixir->MagicLevel;
1944 		this->Variable[MAGICLEVEL_INDEX].Max += this->Elixir->MagicLevel;
1945 	}
1946 
1947 	this->UpdateItemName();
1948 }
1949 
SetUnique(CUniqueItem * unique)1950 void CUnit::SetUnique(CUniqueItem *unique)
1951 {
1952 	if (this->Unique && this->Unique->Set) {
1953 		this->Variable[MAGICLEVEL_INDEX].Value -= this->Unique->Set->MagicLevel;
1954 		this->Variable[MAGICLEVEL_INDEX].Max -= this->Unique->Set->MagicLevel;
1955 	}
1956 
1957 	if (unique != nullptr) {
1958 		SetPrefix(unique->Prefix);
1959 		SetSuffix(unique->Suffix);
1960 		SetSpell(unique->Spell);
1961 		SetWork(unique->Work);
1962 		SetElixir(unique->Elixir);
1963 		if (unique->ResourcesHeld != 0) {
1964 			this->SetResourcesHeld(unique->ResourcesHeld);
1965 			this->Variable[GIVERESOURCE_INDEX].Value = unique->ResourcesHeld;
1966 			this->Variable[GIVERESOURCE_INDEX].Max = unique->ResourcesHeld;
1967 			this->Variable[GIVERESOURCE_INDEX].Enable = 1;
1968 		}
1969 		if (unique->Set) {
1970 			this->Variable[MAGICLEVEL_INDEX].Value += unique->Set->MagicLevel;
1971 			this->Variable[MAGICLEVEL_INDEX].Max += unique->Set->MagicLevel;
1972 		}
1973 		Name = unique->Name;
1974 		Unique = unique;
1975 	} else {
1976 		Name.clear();
1977 		Unique = nullptr;
1978 		SetPrefix(nullptr);
1979 		SetSuffix(nullptr);
1980 		SetSpell(nullptr);
1981 		SetWork(nullptr);
1982 		SetElixir(nullptr);
1983 	}
1984 }
1985 
Identify()1986 void CUnit::Identify()
1987 {
1988 	if (!IsNetworkGame() && Container && Container->Character && Container->Player->AiEnabled == false && Container->Character->GetItem(*this) != nullptr && Container->Character->GetItem(*this)->Identified != true) { //update the persistent item, if applicable and if it hasn't been updated yet
1989 		Container->Character->GetItem(*this)->Identified = true;
1990 		SaveHero(Container->Character);
1991 	}
1992 
1993 	this->Identified = true;
1994 
1995 	if (this->Container != nullptr && this->Container->Player == ThisPlayer) {
1996 		this->Container->Player->Notify(NotifyGreen, this->Container->tilePos, this->Container->MapLayer->ID, _("%s has identified the %s!"), this->Container->GetMessageName().c_str(), this->GetMessageName().c_str());
1997 	}
1998 }
1999 
CheckIdentification()2000 void CUnit::CheckIdentification()
2001 {
2002 	if (!HasInventory()) {
2003 		return;
2004 	}
2005 
2006 	CUnit *uins = this->UnitInside;
2007 
2008 	for (int i = 0; i < this->InsideCount; ++i, uins = uins->NextContained) {
2009 		if (!uins->Type->BoolFlag[ITEM_INDEX].value) {
2010 			continue;
2011 		}
2012 
2013 		if (!uins->Identified && this->Variable[KNOWLEDGEMAGIC_INDEX].Value >= uins->Variable[MAGICLEVEL_INDEX].Value) {
2014 			uins->Identify();
2015 		}
2016 	}
2017 }
2018 
CheckKnowledgeChange(int variable,int change)2019 void CUnit::CheckKnowledgeChange(int variable, int change) // this happens after the variable has already been changed
2020 {
2021 	if (!change) {
2022 		return;
2023 	}
2024 
2025 	if (variable == KNOWLEDGEMAGIC_INDEX) {
2026 		int mana_change = (this->Variable[variable].Value / 5) - ((this->Variable[variable].Value - change) / 5); // +1 max mana for every 5 levels in Knowledge (Magic)
2027 		this->Variable[MANA_INDEX].Max += mana_change;
2028 		if (mana_change < 0) {
2029 			this->Variable[MANA_INDEX].Value += mana_change;
2030 		}
2031 
2032 		this->CheckIdentification();
2033 	} else if (variable == KNOWLEDGEWARFARE_INDEX) {
2034 		int hp_change = (this->Variable[variable].Value / 5) - ((this->Variable[variable].Value - change) / 5); // +1 max HP for every 5 levels in Knowledge (Warfare)
2035 		this->Variable[HP_INDEX].Max += hp_change;
2036 		this->Variable[HP_INDEX].Value += hp_change;
2037 	} else if (variable == KNOWLEDGEMINING_INDEX) {
2038 		int stat_change = (this->Variable[variable].Value / 25) - ((this->Variable[variable].Value - change) / 25); // +1 mining gathering bonus for every 25 levels in Knowledge (Mining)
2039 		this->Variable[COPPERGATHERINGBONUS_INDEX].Max += stat_change;
2040 		this->Variable[COPPERGATHERINGBONUS_INDEX].Value += stat_change;
2041 		this->Variable[SILVERGATHERINGBONUS_INDEX].Max += stat_change;
2042 		this->Variable[SILVERGATHERINGBONUS_INDEX].Value += stat_change;
2043 		this->Variable[GOLDGATHERINGBONUS_INDEX].Max += stat_change;
2044 		this->Variable[GOLDGATHERINGBONUS_INDEX].Value += stat_change;
2045 		this->Variable[IRONGATHERINGBONUS_INDEX].Max += stat_change;
2046 		this->Variable[IRONGATHERINGBONUS_INDEX].Value += stat_change;
2047 		this->Variable[MITHRILGATHERINGBONUS_INDEX].Max += stat_change;
2048 		this->Variable[MITHRILGATHERINGBONUS_INDEX].Value += stat_change;
2049 		this->Variable[COALGATHERINGBONUS_INDEX].Max += stat_change;
2050 		this->Variable[COALGATHERINGBONUS_INDEX].Value += stat_change;
2051 		this->Variable[GEMSGATHERINGBONUS_INDEX].Max += stat_change;
2052 		this->Variable[GEMSGATHERINGBONUS_INDEX].Value += stat_change;
2053 	}
2054 }
2055 
UpdateItemName()2056 void CUnit::UpdateItemName()
2057 {
2058 	if (this->Unique) {
2059 		Name = _(this->Unique->Name.c_str());
2060 		return;
2061 	}
2062 
2063 	Name.clear();
2064 	if (Prefix == nullptr && Spell == nullptr && Work == nullptr && Suffix == nullptr) { //elixirs use the name of their unit type
2065 		return;
2066 	}
2067 
2068 	if (Prefix != nullptr) {
2069 		Name += _(Prefix->Name.c_str());
2070 		Name += " ";
2071 	}
2072 	if (Work != nullptr) {
2073 		Name += _(Work->Name.c_str());
2074 	} else {
2075 		Name += GetTypeName();
2076 	}
2077 	if (Suffix != nullptr) {
2078 		Name += " ";
2079 		Name += _(Suffix->Name.c_str());
2080 	} else if (Spell != nullptr) {
2081 		Name += " ";
2082 		Name += _("of");
2083 		Name += " ";
2084 		Name += _(Spell->Name.c_str());
2085 	}
2086 }
2087 
GenerateDrop()2088 void CUnit::GenerateDrop()
2089 {
2090 	bool base_based_mission = false;
2091 	for (int p = 0; p < PlayerMax; ++p) {
2092 		if (Players[p].NumTownHalls > 0 || Players[p].LostTownHallTimer) {
2093 			base_based_mission = true;
2094 		}
2095 	}
2096 
2097 	if (this->Type->BoolFlag[ORGANIC_INDEX].value && !this->Character && !this->Type->BoolFlag[FAUNA_INDEX].value && base_based_mission) { //if the unit is organic and isn't a character (and isn't fauna) and this is a base-based mission, don't generate a drop
2098 		return;
2099 	}
2100 
2101 	Vec2i drop_pos = this->tilePos;
2102 	drop_pos.x += SyncRand(this->Type->TileSize.x);
2103 	drop_pos.y += SyncRand(this->Type->TileSize.y);
2104 	CUnit *droppedUnit = nullptr;
2105 	CUnitType *chosen_drop = nullptr;
2106 	std::vector<CUnitType *> potential_drops;
2107 	for (size_t i = 0; i < this->Type->Drops.size(); ++i) {
2108 		if (CheckDependencies(this->Type->Drops[i], this)) {
2109 			potential_drops.push_back(this->Type->Drops[i]);
2110 		}
2111 	}
2112 	if (this->Player->AiEnabled) {
2113 		for (size_t i = 0; i < this->Type->AiDrops.size(); ++i) {
2114 			if (CheckDependencies(this->Type->AiDrops[i], this)) {
2115 				potential_drops.push_back(this->Type->AiDrops[i]);
2116 			}
2117 		}
2118 		for (std::map<std::string, std::vector<CUnitType *>>::const_iterator iterator = this->Type->ModAiDrops.begin(); iterator != this->Type->ModAiDrops.end(); ++iterator) {
2119 			for (size_t i = 0; i < iterator->second.size(); ++i) {
2120 				if (CheckDependencies(iterator->second[i], this)) {
2121 					potential_drops.push_back(iterator->second[i]);
2122 				}
2123 			}
2124 		}
2125 	}
2126 	if (potential_drops.size() > 0) {
2127 		chosen_drop = potential_drops[SyncRand(potential_drops.size())];
2128 	}
2129 
2130 	if (chosen_drop != nullptr) {
2131 		CBuildRestrictionOnTop *ontop_b = OnTopDetails(*this->Type, nullptr);
2132 		if (((chosen_drop->BoolFlag[ITEM_INDEX].value || chosen_drop->BoolFlag[POWERUP_INDEX].value) && (this->MapLayer->Field(drop_pos)->Flags & MapFieldItem)) || (ontop_b && ontop_b->ReplaceOnDie)) { //if the dropped unit is an item (and there's already another item there), or if this building is an ontop one (meaning another will appear under it after it is destroyed), search for another spot
2133 			Vec2i resPos;
2134 			FindNearestDrop(*chosen_drop, drop_pos, resPos, LookingW, this->MapLayer->ID);
2135 			droppedUnit = MakeUnitAndPlace(resPos, *chosen_drop, &Players[PlayerNumNeutral], this->MapLayer->ID);
2136 		} else {
2137 			droppedUnit = MakeUnitAndPlace(drop_pos, *chosen_drop, &Players[PlayerNumNeutral], this->MapLayer->ID);
2138 		}
2139 
2140 		if (droppedUnit != nullptr) {
2141 			if (droppedUnit->Type->BoolFlag[FAUNA_INDEX].value) {
2142 				droppedUnit->Name = droppedUnit->Type->GeneratePersonalName(nullptr, droppedUnit->Variable[GENDER_INDEX].Value);
2143 			}
2144 
2145 			droppedUnit->GenerateSpecialProperties(this, this->Player);
2146 
2147 			if (droppedUnit->Type->BoolFlag[ITEM_INDEX].value && !droppedUnit->Unique) { //save the initial cycle items were placed in the ground to destroy them if they have been there for too long
2148 				int ttl_cycles = (5 * 60 * CYCLES_PER_SECOND);
2149 				if (droppedUnit->Prefix != nullptr || droppedUnit->Suffix != nullptr || droppedUnit->Spell != nullptr || droppedUnit->Work != nullptr || droppedUnit->Elixir != nullptr) {
2150 					ttl_cycles *= 4;
2151 				}
2152 				droppedUnit->TTL = GameCycle + ttl_cycles;
2153 			}
2154 		}
2155 	}
2156 }
2157 
GenerateSpecialProperties(CUnit * dropper,CPlayer * dropper_player,bool allow_unique,bool sold_item,bool always_magic)2158 void CUnit::GenerateSpecialProperties(CUnit *dropper, CPlayer *dropper_player, bool allow_unique, bool sold_item, bool always_magic)
2159 {
2160 	int magic_affix_chance = 10; //10% chance of the unit having a magic prefix or suffix
2161 	int unique_chance = 5; //0.5% chance of the unit being unique
2162 	if (dropper != nullptr) {
2163 		if (dropper->Character) { //if the dropper is a character, multiply the chances of the item being magic or unique by the character's level
2164 			magic_affix_chance *= dropper->Character->Level;
2165 			unique_chance *= dropper->Character->Level;
2166 		} else if (dropper->Type->BoolFlag[BUILDING_INDEX].value) { //if the dropper is a building, multiply the chances of the drop being magic or unique by a factor according to whether the building itself is magic/unique
2167 			int chance_multiplier = 2;
2168 			if (dropper->Unique) {
2169 				chance_multiplier += 8;
2170 			} else {
2171 				if (dropper->Prefix != nullptr) {
2172 					chance_multiplier += 1;
2173 				}
2174 				if (dropper->Suffix != nullptr) {
2175 					chance_multiplier += 1;
2176 				}
2177 			}
2178 			magic_affix_chance *= chance_multiplier;
2179 			unique_chance *= chance_multiplier;
2180 		}
2181 	}
2182 
2183 	if (sold_item) {
2184 		magic_affix_chance /= 4;
2185 		unique_chance /= 4;
2186 	}
2187 
2188 	if (SyncRand(100) >= (100 - magic_affix_chance)) {
2189 		this->GenerateWork(dropper, dropper_player);
2190 	}
2191 	if (SyncRand(100) >= (100 - magic_affix_chance)) {
2192 		this->GeneratePrefix(dropper, dropper_player);
2193 	}
2194 	if (SyncRand(100) >= (100 - magic_affix_chance)) {
2195 		this->GenerateSuffix(dropper, dropper_player);
2196 	}
2197 	if (this->Prefix == nullptr && this->Suffix == nullptr && this->Work == nullptr && this->Elixir == nullptr && SyncRand(100) >= (100 - magic_affix_chance)) {
2198 		this->GenerateSpell(dropper, dropper_player);
2199 	}
2200 	if (allow_unique && SyncRand(1000) >= (1000 - unique_chance)) {
2201 		this->GenerateUnique(dropper, dropper_player);
2202 	}
2203 
2204 	if (this->Type->BoolFlag[ITEM_INDEX].value && (this->Prefix != nullptr || this->Suffix != nullptr)) {
2205 		this->Identified = false;
2206 	}
2207 
2208 	if (
2209 		this->Prefix == nullptr && this->Suffix == nullptr && this->Spell == nullptr && this->Work == nullptr && this->Elixir == nullptr
2210 		&& (this->Type->ItemClass == ScrollItemClass || this->Type->ItemClass == BookItemClass || this->Type->ItemClass == RingItemClass || this->Type->ItemClass == AmuletItemClass || this->Type->ItemClass == HornItemClass || always_magic)
2211 	) { //scrolls, books, jewelry and horns must always have a property
2212 		this->GenerateSpecialProperties(dropper, dropper_player, allow_unique, sold_item, always_magic);
2213 	}
2214 }
2215 
GeneratePrefix(CUnit * dropper,CPlayer * dropper_player)2216 void CUnit::GeneratePrefix(CUnit *dropper, CPlayer *dropper_player)
2217 {
2218 	std::vector<CUpgrade *> potential_prefixes;
2219 	for (size_t i = 0; i < this->Type->Affixes.size(); ++i) {
2220 		if ((this->Type->ItemClass == -1 && this->Type->Affixes[i]->MagicPrefix) || (this->Type->ItemClass != -1 && this->Type->Affixes[i]->ItemPrefix[Type->ItemClass])) {
2221 			potential_prefixes.push_back(this->Type->Affixes[i]);
2222 		}
2223 	}
2224 	if (dropper_player != nullptr) {
2225 		for (size_t i = 0; i < AllUpgrades.size(); ++i) {
2226 			if (this->Type->ItemClass != -1 && AllUpgrades[i]->ItemPrefix[Type->ItemClass] && CheckDependencies(AllUpgrades[i], dropper)) {
2227 				potential_prefixes.push_back(AllUpgrades[i]);
2228 			}
2229 		}
2230 	}
2231 
2232 	if (potential_prefixes.size() > 0) {
2233 		SetPrefix(potential_prefixes[SyncRand(potential_prefixes.size())]);
2234 	}
2235 }
2236 
GenerateSuffix(CUnit * dropper,CPlayer * dropper_player)2237 void CUnit::GenerateSuffix(CUnit *dropper, CPlayer *dropper_player)
2238 {
2239 	std::vector<CUpgrade *> potential_suffixes;
2240 	for (size_t i = 0; i < this->Type->Affixes.size(); ++i) {
2241 		if ((this->Type->ItemClass == -1 && this->Type->Affixes[i]->MagicSuffix) || (this->Type->ItemClass != -1 && this->Type->Affixes[i]->ItemSuffix[Type->ItemClass])) {
2242 			if (Prefix == nullptr || !this->Type->Affixes[i]->IncompatibleAffixes[Prefix->ID]) { //don't allow a suffix incompatible with the prefix to appear
2243 				potential_suffixes.push_back(this->Type->Affixes[i]);
2244 			}
2245 		}
2246 	}
2247 	if (dropper_player != nullptr) {
2248 		for (size_t i = 0; i < AllUpgrades.size(); ++i) {
2249 			if (this->Type->ItemClass != -1 && AllUpgrades[i]->ItemSuffix[Type->ItemClass] && CheckDependencies(AllUpgrades[i], dropper)) {
2250 				if (Prefix == nullptr || !AllUpgrades[i]->IncompatibleAffixes[Prefix->ID]) { //don't allow a suffix incompatible with the prefix to appear
2251 					potential_suffixes.push_back(AllUpgrades[i]);
2252 				}
2253 			}
2254 		}
2255 	}
2256 
2257 	if (potential_suffixes.size() > 0) {
2258 		SetSuffix(potential_suffixes[SyncRand(potential_suffixes.size())]);
2259 	}
2260 }
2261 
GenerateSpell(CUnit * dropper,CPlayer * dropper_player)2262 void CUnit::GenerateSpell(CUnit *dropper, CPlayer *dropper_player)
2263 {
2264 	std::vector<CSpell *> potential_spells;
2265 	if (dropper != nullptr) {
2266 		for (size_t i = 0; i < dropper->Type->DropSpells.size(); ++i) {
2267 			if (this->Type->ItemClass != -1 && dropper->Type->DropSpells[i]->ItemSpell[Type->ItemClass]) {
2268 				potential_spells.push_back(dropper->Type->DropSpells[i]);
2269 			}
2270 		}
2271 	}
2272 
2273 	if (potential_spells.size() > 0) {
2274 		SetSpell(potential_spells[SyncRand(potential_spells.size())]);
2275 	}
2276 }
2277 
GenerateWork(CUnit * dropper,CPlayer * dropper_player)2278 void CUnit::GenerateWork(CUnit *dropper, CPlayer *dropper_player)
2279 {
2280 	std::vector<CUpgrade *> potential_works;
2281 	for (size_t i = 0; i < this->Type->Affixes.size(); ++i) {
2282 		if (this->Type->ItemClass != -1 && this->Type->Affixes[i]->Work == this->Type->ItemClass && !this->Type->Affixes[i]->UniqueOnly) {
2283 			potential_works.push_back(this->Type->Affixes[i]);
2284 		}
2285 	}
2286 	if (dropper_player != nullptr) {
2287 		for (size_t i = 0; i < AllUpgrades.size(); ++i) {
2288 			if (this->Type->ItemClass != -1 && AllUpgrades[i]->Work == this->Type->ItemClass && CheckDependencies(AllUpgrades[i], dropper) && !AllUpgrades[i]->UniqueOnly) {
2289 				potential_works.push_back(AllUpgrades[i]);
2290 			}
2291 		}
2292 	}
2293 
2294 	if (potential_works.size() > 0) {
2295 		SetWork(potential_works[SyncRand(potential_works.size())]);
2296 	}
2297 }
2298 
GenerateUnique(CUnit * dropper,CPlayer * dropper_player)2299 void CUnit::GenerateUnique(CUnit *dropper, CPlayer *dropper_player)
2300 {
2301 	std::vector<CUniqueItem *> potential_uniques;
2302 	for (size_t i = 0; i < UniqueItems.size(); ++i) {
2303 		if (
2304 			Type == UniqueItems[i]->Type
2305 			&& ( //the dropper unit must be capable of generating this unique item's prefix to drop the item, or else the unit must be capable of generating it on its own
2306 				UniqueItems[i]->Prefix == nullptr
2307 				|| (dropper_player != nullptr && CheckDependencies(UniqueItems[i]->Prefix, dropper))
2308 				|| std::find(this->Type->Affixes.begin(), this->Type->Affixes.end(), UniqueItems[i]->Prefix) != this->Type->Affixes.end()
2309 			)
2310 			&& ( //the dropper unit must be capable of generating this unique item's suffix to drop the item, or else the unit must be capable of generating it on its own
2311 				UniqueItems[i]->Suffix == nullptr
2312 				|| (dropper_player != nullptr && CheckDependencies(UniqueItems[i]->Suffix, dropper))
2313 				|| std::find(this->Type->Affixes.begin(), this->Type->Affixes.end(), UniqueItems[i]->Suffix) != this->Type->Affixes.end()
2314 			)
2315 			&& ( //the dropper unit must be capable of generating this unique item's set to drop the item
2316 				UniqueItems[i]->Set == nullptr
2317 				|| (dropper_player != nullptr && CheckDependencies(UniqueItems[i]->Set, dropper))
2318 			)
2319 			&& ( //the dropper unit must be capable of generating this unique item's spell to drop the item
2320 				UniqueItems[i]->Spell == nullptr
2321 				|| (dropper != nullptr && std::find(dropper->Type->DropSpells.begin(), dropper->Type->DropSpells.end(), UniqueItems[i]->Spell) != dropper->Type->DropSpells.end())
2322 			)
2323 			&& ( //the dropper unit must be capable of generating this unique item's work to drop the item, or else the unit must be capable of generating it on its own
2324 				UniqueItems[i]->Work == nullptr
2325 				|| std::find(this->Type->Affixes.begin(), this->Type->Affixes.end(), UniqueItems[i]->Work) != this->Type->Affixes.end()
2326 				|| (dropper_player != nullptr && CheckDependencies(UniqueItems[i]->Work, dropper))
2327 			)
2328 			&& ( //the dropper unit must be capable of generating this unique item's elixir to drop the item, or else the unit must be capable of generating it on its own
2329 				UniqueItems[i]->Elixir == nullptr
2330 				|| std::find(this->Type->Affixes.begin(), this->Type->Affixes.end(), UniqueItems[i]->Elixir) != this->Type->Affixes.end()
2331 				|| (dropper_player != nullptr && CheckDependencies(UniqueItems[i]->Elixir, dropper))
2332 			)
2333 			&& UniqueItems[i]->CanDrop()
2334 		) {
2335 			potential_uniques.push_back(UniqueItems[i]);
2336 		}
2337 	}
2338 
2339 	if (potential_uniques.size() > 0) {
2340 		CUniqueItem *chosen_unique = potential_uniques[SyncRand(potential_uniques.size())];
2341 		SetUnique(chosen_unique);
2342 	}
2343 }
2344 
UpdateSoldUnits()2345 void CUnit::UpdateSoldUnits()
2346 {
2347 	if (!this->Type->BoolFlag[RECRUITHEROES_INDEX].value && this->Type->SoldUnits.empty() && this->SoldUnits.empty()) {
2348 		return;
2349 	}
2350 
2351 	if (this->UnderConstruction == 1 || !Map.Info.IsPointOnMap(this->tilePos, this->MapLayer) || Editor.Running != EditorNotRunning) {
2352 		return;
2353 	}
2354 
2355 	for (size_t i = 0; i < this->SoldUnits.size(); ++i) {
2356 		DestroyAllInside(*this->SoldUnits[i]);
2357 		LetUnitDie(*this->SoldUnits[i]);
2358 	}
2359 	this->SoldUnits.clear();
2360 
2361 	std::vector<CUnitType *> potential_items;
2362 	std::vector<CCharacter *> potential_heroes;
2363 	if (this->Type->BoolFlag[RECRUITHEROES_INDEX].value && !IsNetworkGame()) { // allow heroes to be recruited at town halls
2364 		int civilization_id = this->Type->Civilization;
2365 		if (civilization_id != -1 && civilization_id != this->Player->Race && this->Player->Race != -1 && this->Player->Faction != -1 && this->Type->Slot == PlayerRaces.GetFactionClassUnitType(this->Player->Faction, this->Type->Class)) {
2366 			civilization_id = this->Player->Race;
2367 		}
2368 
2369 		if (CurrentQuest == nullptr) {
2370 			for (CCharacter *character : CCharacter::Characters) {
2371 				if (this->Player->CanRecruitHero(character)) {
2372 					potential_heroes.push_back(character);
2373 				}
2374 			}
2375 		}
2376 		if (this->Player == ThisPlayer) {
2377 			for (std::map<std::string, CCharacter *>::iterator iterator = CustomHeroes.begin(); iterator != CustomHeroes.end(); ++iterator) {
2378 				if (
2379 					(iterator->second->Civilization && iterator->second->Civilization->ID == civilization_id || iterator->second->Type->Slot == PlayerRaces.GetCivilizationClassUnitType(civilization_id, iterator->second->Type->Class))
2380 					&& CheckDependencies(iterator->second->Type, this, true) && iterator->second->CanAppear()
2381 				) {
2382 					potential_heroes.push_back(iterator->second);
2383 				}
2384 			}
2385 		}
2386 	} else {
2387 		for (size_t i = 0; i < this->Type->SoldUnits.size(); ++i) {
2388 			if (CheckDependencies(this->Type->SoldUnits[i], this)) {
2389 				potential_items.push_back(this->Type->SoldUnits[i]);
2390 			}
2391 		}
2392 	}
2393 
2394 	if (potential_items.empty() && potential_heroes.empty()) {
2395 		return;
2396 	}
2397 
2398 	int sold_unit_max = 4;
2399 	if (!potential_items.empty()) {
2400 		sold_unit_max = 15;
2401 	}
2402 
2403 	for (int i = 0; i < sold_unit_max; ++i) {
2404 		CUnit *new_unit = nullptr;
2405 		if (!potential_heroes.empty()) {
2406 			CCharacter *chosen_hero = potential_heroes[SyncRand(potential_heroes.size())];
2407 			new_unit = MakeUnitAndPlace(this->tilePos, *chosen_hero->Type, &Players[PlayerNumNeutral], this->MapLayer->ID);
2408 			new_unit->SetCharacter(chosen_hero->Ident, chosen_hero->Custom);
2409 			potential_heroes.erase(std::remove(potential_heroes.begin(), potential_heroes.end(), chosen_hero), potential_heroes.end());
2410 		} else {
2411 			CUnitType *chosen_unit_type = potential_items[SyncRand(potential_items.size())];
2412 			new_unit = MakeUnitAndPlace(this->tilePos, *chosen_unit_type, &Players[PlayerNumNeutral], this->MapLayer->ID);
2413 			new_unit->GenerateSpecialProperties(this, this->Player, true, true);
2414 			new_unit->Identified = true;
2415 			if (new_unit->Unique && this->Player == ThisPlayer) { //send a notification if a unique item is being sold, we don't want the player to have to worry about missing it :)
2416 				this->Player->Notify(NotifyGreen, this->tilePos, this->MapLayer->ID, "%s", _("Unique item available for sale"));
2417 			}
2418 		}
2419 		new_unit->Remove(this);
2420 		this->SoldUnits.push_back(new_unit);
2421 		if (potential_heroes.empty() && potential_items.empty()) {
2422 			break;
2423 		}
2424 	}
2425 	if (IsOnlySelected(*this)) {
2426 		UI.ButtonPanel.Update();
2427 	}
2428 }
2429 
SellUnit(CUnit * sold_unit,int player)2430 void CUnit::SellUnit(CUnit *sold_unit, int player)
2431 {
2432 	this->SoldUnits.erase(std::remove(this->SoldUnits.begin(), this->SoldUnits.end(), sold_unit), this->SoldUnits.end());
2433 	DropOutOnSide(*sold_unit, sold_unit->Direction, this);
2434 	if (!sold_unit->Type->BoolFlag[ITEM_INDEX].value) {
2435 		sold_unit->ChangeOwner(Players[player]);
2436 	}
2437 	Players[player].ChangeResource(CopperCost, -sold_unit->GetPrice(), true);
2438 	if (Players[player].AiEnabled && !sold_unit->Type->BoolFlag[ITEM_INDEX].value && !sold_unit->Type->BoolFlag[HARVESTER_INDEX].value) { //add the hero to an AI force, if the hero isn't a harvester
2439 		Players[player].Ai->Force.RemoveDeadUnit();
2440 		Players[player].Ai->Force.Assign(*sold_unit, -1, true);
2441 	}
2442 	if (sold_unit->Character) {
2443 		Players[player].HeroCooldownTimer = HeroCooldownCycles;
2444 		sold_unit->Variable[MANA_INDEX].Value = 0; //start off with 0 mana
2445 	}
2446 	if (IsOnlySelected(*this)) {
2447 		UI.ButtonPanel.Update();
2448 	}
2449 }
2450 
2451 /**
2452 **  Produce a resource
2453 **
2454 **  @param resource  Resource to be produced.
2455 */
ProduceResource(const int resource)2456 void CUnit::ProduceResource(const int resource)
2457 {
2458 	if (resource == this->GivesResource) {
2459 		return;
2460 	}
2461 
2462 	int old_resource = this->GivesResource;
2463 
2464 	if (resource != 0) {
2465 		this->GivesResource = resource;
2466 		this->ResourcesHeld = 10000;
2467 	} else {
2468 		this->GivesResource = 0;
2469 		this->ResourcesHeld = 0;
2470 	}
2471 
2472 	if (old_resource != 0) {
2473 		if (this->Resource.Workers) {
2474 			for (CUnit *uins = this->Resource.Workers; uins; uins = uins->NextWorker) {
2475 				if (uins->Container == this) {
2476 					uins->CurrentOrder()->Finished = true;
2477 					DropOutOnSide(*uins, LookingW, this);
2478 				}
2479 			}
2480 		}
2481 		this->Resource.Active = 0;
2482 	}
2483 }
2484 
2485 /**
2486 **  Sells 100 of a resource for copper
2487 **
2488 **  @param resource  Resource to be sold.
2489 */
SellResource(const int resource,const int player)2490 void CUnit::SellResource(const int resource, const int player)
2491 {
2492 	if ((Players[player].Resources[resource] + Players[player].StoredResources[resource]) < 100) {
2493 		return;
2494 	}
2495 
2496 	Players[player].ChangeResource(resource, -100, true);
2497 	Players[player].ChangeResource(CopperCost, this->Player->GetEffectiveResourceSellPrice(resource), true);
2498 
2499 	this->Player->DecreaseResourcePrice(resource);
2500 }
2501 
2502 /**
2503 **  Buy a resource for copper
2504 **
2505 **  @param resource  Resource to be bought.
2506 */
BuyResource(const int resource,const int player)2507 void CUnit::BuyResource(const int resource, const int player)
2508 {
2509 	if ((Players[player].Resources[CopperCost] + Players[player].StoredResources[CopperCost]) < this->Player->GetEffectiveResourceBuyPrice(resource)) {
2510 		return;
2511 	}
2512 
2513 	Players[player].ChangeResource(resource, 100, true);
2514 	Players[player].ChangeResource(CopperCost, -this->Player->GetEffectiveResourceBuyPrice(resource), true);
2515 
2516 	this->Player->IncreaseResourcePrice(resource);
2517 }
2518 
Scout()2519 void CUnit::Scout()
2520 {
2521 	int scout_range = std::max(16, this->CurrentSightRange * 2);
2522 
2523 	Vec2i target_pos = this->tilePos;
2524 
2525 	target_pos.x += SyncRand(scout_range * 2 + 1) - scout_range;
2526 	target_pos.y += SyncRand(scout_range * 2 + 1) - scout_range;
2527 
2528 	// restrict to map
2529 	Map.Clamp(target_pos, this->MapLayer->ID);
2530 
2531 	// move if possible
2532 	if (target_pos != this->tilePos) {
2533 		// if the tile the scout is moving to happens to have a layer connector, use it
2534 		bool found_connector = false;
2535 		CUnitCache &unitcache = Map.Field(target_pos, this->MapLayer->ID)->UnitCache;
2536 		for (CUnitCache::iterator it = unitcache.begin(); it != unitcache.end(); ++it) {
2537 			CUnit *connector = *it;
2538 
2539 			if (connector->ConnectingDestination != nullptr && this->CanUseItem(connector)) {
2540 				CommandUse(*this, *connector, FlushCommands);
2541 				found_connector = true;
2542 				break;
2543 			}
2544 		}
2545 		if (found_connector) {
2546 			return;
2547 		}
2548 
2549 		UnmarkUnitFieldFlags(*this);
2550 		if (UnitCanBeAt(*this, target_pos, this->MapLayer->ID)) {
2551 			MarkUnitFieldFlags(*this);
2552 			CommandMove(*this, target_pos, FlushCommands, this->MapLayer->ID);
2553 			return;
2554 		}
2555 		MarkUnitFieldFlags(*this);
2556 	}
2557 }
2558 //Wyrmgus end
2559 
CurrentAction() const2560 unsigned int CUnit::CurrentAction() const
2561 {
2562 	return (CurrentOrder()->Action);
2563 }
2564 
ClearAction()2565 void CUnit::ClearAction()
2566 {
2567 	Orders[0]->Finished = true;
2568 
2569 	if (Selected) {
2570 		SelectedUnitChanged();
2571 	}
2572 }
2573 
2574 
IsIdle() const2575 bool CUnit::IsIdle() const
2576 {
2577 	//Wyrmgus start
2578 //	return Orders.size() == 1 && CurrentAction() == UnitActionStill;
2579 	return Orders.size() == 1 && CurrentAction() == UnitActionStill && this->Variable[STUN_INDEX].Value == 0;
2580 	//Wyrmgus end
2581 }
2582 
IsAlive() const2583 bool CUnit::IsAlive() const
2584 {
2585 	return !Destroyed && CurrentAction() != UnitActionDie;
2586 }
2587 
GetDrawLevel() const2588 int CUnit::GetDrawLevel() const
2589 {
2590 	return ((Type->CorpseType && CurrentAction() == UnitActionDie) ?
2591 		Type->CorpseType->DrawLevel :
2592 	((CurrentAction() == UnitActionDie) ? Type->DrawLevel - 10 : Type->DrawLevel));
2593 }
2594 
2595 /**
2596 **  Initialize the unit slot with default values.
2597 **
2598 **  @param type    Unit-type
2599 */
Init(const CUnitType & type)2600 void CUnit::Init(const CUnitType &type)
2601 {
2602 	//  Set refs to 1. This is the "I am alive ref", lost in ReleaseUnit.
2603 	Refs = 1;
2604 
2605 	//  Build all unit table
2606 	UnitManager.Add(this);
2607 
2608 	//  Initialise unit structure (must be zero filled!)
2609 	Type = &type;
2610 
2611 	Seen.Frame = UnitNotSeen; // Unit isn't yet seen
2612 
2613 	Frame = type.StillFrame;
2614 
2615 	if (UnitTypeVar.GetNumberVariable()) {
2616 		Assert(!Variable);
2617 		const unsigned int size = UnitTypeVar.GetNumberVariable();
2618 		Variable = new CVariable[size];
2619 		std::copy(type.MapDefaultStat.Variables, type.MapDefaultStat.Variables + size, Variable);
2620 	} else {
2621 		Variable = nullptr;
2622 	}
2623 
2624 	IndividualUpgrades.clear();
2625 
2626 	// Set a heading for the unit if it Handles Directions
2627 	// Don't set a building heading, as only 1 construction direction
2628 	//   is allowed.
2629 	if (type.NumDirections > 1 && type.BoolFlag[NORANDOMPLACING_INDEX].value == false && type.Sprite && !type.BoolFlag[BUILDING_INDEX].value) {
2630 		Direction = (SyncRand() >> 8) & 0xFF; // random heading
2631 		UnitUpdateHeading(*this);
2632 	}
2633 
2634 	// Create AutoCastSpell and SpellCoolDownTimers arrays for casters
2635 	//Wyrmgus start
2636 //	if (type.CanCastSpell) {
2637 	//to avoid crashes with spell items for units who cannot ordinarily cast spells
2638 	//Wyrmgus end
2639 		AutoCastSpell = new char[CSpell::Spells.size()];
2640 		SpellCoolDownTimers = new int[CSpell::Spells.size()];
2641 		memset(SpellCoolDownTimers, 0, CSpell::Spells.size() * sizeof(int));
2642 		if (Type->AutoCastActive) {
2643 			memcpy(AutoCastSpell, Type->AutoCastActive, CSpell::Spells.size());
2644 		} else {
2645 			memset(AutoCastSpell, 0, CSpell::Spells.size());
2646 		}
2647 	//Wyrmgus start
2648 //	}
2649 	//Wyrmgus end
2650 	Active = 1;
2651 	Removed = 1;
2652 
2653 	// Has StartingResources, Use those
2654 	//Wyrmgus start
2655 //	this->ResourcesHeld = type.StartingResources;
2656 	if (type.GivesResource) {
2657 		this->GivesResource = type.GivesResource;
2658 		if (type.StartingResources.size() > 0) {
2659 			this->ResourcesHeld = type.StartingResources[SyncRand(type.StartingResources.size())];
2660 		} else {
2661 			this->ResourcesHeld = 0;
2662 		}
2663 	}
2664 	//Wyrmgus end
2665 
2666 	Assert(Orders.empty());
2667 
2668 	Orders.push_back(COrder::NewActionStill());
2669 
2670 	Assert(NewOrder == nullptr);
2671 	NewOrder = nullptr;
2672 	Assert(SavedOrder == nullptr);
2673 	SavedOrder = nullptr;
2674 	Assert(CriticalOrder == nullptr);
2675 	CriticalOrder = nullptr;
2676 }
2677 
2678 /**
2679 **  Restore the saved order
2680 **
2681 **  @return      True if the saved order was restored
2682 */
RestoreOrder()2683 bool CUnit::RestoreOrder()
2684 {
2685 	COrder *savedOrder = this->SavedOrder;
2686 
2687 	if (savedOrder == nullptr) {
2688 		return false;
2689 	}
2690 
2691 	if (savedOrder->IsValid() == false) {
2692 		delete savedOrder;
2693 		this->SavedOrder = nullptr;
2694 		return false;
2695 	}
2696 
2697 	// Cannot delete this->Orders[0] since it is generally that order
2698 	// which call this method.
2699 	this->Orders[0]->Finished = true;
2700 
2701 	//copy
2702 	this->Orders.insert(this->Orders.begin() + 1, savedOrder);
2703 
2704 	this->SavedOrder = nullptr;
2705 	return true;
2706 }
2707 
2708 /**
2709 **  Check if we can store this order
2710 **
2711 **  @return      True if the order could be saved
2712 */
CanStoreOrder(COrder * order)2713 bool CUnit::CanStoreOrder(COrder *order)
2714 {
2715 	Assert(order);
2716 
2717 	if ((order && order->Finished == true) || order->IsValid() == false) {
2718 		return false;
2719 	}
2720 	if (this->SavedOrder != nullptr) {
2721 		return false;
2722 	}
2723 	return true;
2724 }
2725 
2726 /**
2727 **  Assigns a unit to a player, adjusting buildings, food and totals
2728 **
2729 **  @param player  player which have the unit.
2730 */
AssignToPlayer(CPlayer & player)2731 void CUnit::AssignToPlayer(CPlayer &player)
2732 {
2733 	const CUnitType &type = *Type;
2734 
2735 	// Build player unit table
2736 	//Wyrmgus start
2737 //	if (!type.BoolFlag[VANISHES_INDEX].value && CurrentAction() != UnitActionDie) {
2738 	if (!type.BoolFlag[VANISHES_INDEX].value && CurrentAction() != UnitActionDie && !this->Destroyed) {
2739 	//Wyrmgus end
2740 		player.AddUnit(*this);
2741 		if (!SaveGameLoading) {
2742 			// If unit is dying, it's already been lost by all players
2743 			// don't count again
2744 			if (type.BoolFlag[BUILDING_INDEX].value) {
2745 				// FIXME: support more races
2746 				//Wyrmgus start
2747 //				if (!type.BoolFlag[WALL_INDEX].value && &type != UnitTypeOrcWall && &type != UnitTypeHumanWall) {
2748 				//Wyrmgus end
2749 					player.TotalBuildings++;
2750 				//Wyrmgus start
2751 //				}
2752 				//Wyrmgus end
2753 			} else {
2754 				player.TotalUnits++;
2755 
2756 				for (CPlayerQuestObjective *objective : player.QuestObjectives) {
2757 					if (
2758 						(objective->ObjectiveType == BuildUnitsObjectiveType && std::find(objective->UnitTypes.begin(), objective->UnitTypes.end(), &type) != objective->UnitTypes.end())
2759 						|| (objective->ObjectiveType == BuildUnitsOfClassObjectiveType && objective->UnitClass == type.Class)
2760 					) {
2761 						objective->Counter = std::min(objective->Counter + 1, objective->Quantity);
2762 					}
2763 				}
2764 			}
2765 		}
2766 
2767 		player.IncreaseCountsForUnit(this);
2768 
2769 		player.Demand += type.Stats[player.Index].Variables[DEMAND_INDEX].Value; // food needed
2770 	}
2771 
2772 	// Don't Add the building if it's dying, used to load a save game
2773 	//Wyrmgus start
2774 //	if (type.BoolFlag[BUILDING_INDEX].value && CurrentAction() != UnitActionDie) {
2775 	if (type.BoolFlag[BUILDING_INDEX].value && CurrentAction() != UnitActionDie && !this->Destroyed && !type.BoolFlag[VANISHES_INDEX].value) {
2776 	//Wyrmgus end
2777 		//Wyrmgus start
2778 //		if (!type.BoolFlag[WALL_INDEX].value && &type != UnitTypeOrcWall && &type != UnitTypeHumanWall) {
2779 		//Wyrmgus end
2780 			player.NumBuildings++;
2781 			//Wyrmgus start
2782 			if (CurrentAction() == UnitActionBuilt) {
2783 				player.NumBuildingsUnderConstruction++;
2784 				player.ChangeUnitTypeUnderConstructionCount(&type, 1);
2785 			}
2786 			//Wyrmgus end
2787 		//Wyrmgus start
2788 //		}
2789 		//Wyrmgus end
2790 	}
2791 	Player = &player;
2792 	Stats = &type.Stats[Player->Index];
2793 	Colors = &player.UnitColors;
2794 	if (!SaveGameLoading) {
2795 		if (UnitTypeVar.GetNumberVariable()) {
2796 			Assert(Variable);
2797 			Assert(Stats->Variables);
2798 			memcpy(Variable, Stats->Variables, UnitTypeVar.GetNumberVariable() * sizeof(*Variable));
2799 		}
2800 	}
2801 
2802 	//Wyrmgus start
2803 	if (!SaveGameLoading) {
2804 		//assign a gender to the unit
2805 		if (this->Variable[GENDER_INDEX].Value == NoGender && this->Type->BoolFlag[ORGANIC_INDEX].value) { // Gender: 0 = Not Set, 1 = Male, 2 = Female, 3 = Asexual
2806 			this->Variable[GENDER_INDEX].Value = SyncRand(2) + 1;
2807 			this->Variable[GENDER_INDEX].Max = MaxGenders;
2808 			this->Variable[GENDER_INDEX].Enable = 1;
2809 		}
2810 
2811 		//generate a personal name for the unit, if applicable
2812 		if (this->Character == nullptr) {
2813 			this->UpdatePersonalName();
2814 		}
2815 
2816 		this->UpdateSoldUnits();
2817 	}
2818 	//Wyrmgus end
2819 }
2820 
2821 /**
2822 **  Create a new unit.
2823 **
2824 **  @param type      Pointer to unit-type.
2825 **  @param player    Pointer to owning player.
2826 **
2827 **  @return          Pointer to created unit.
2828 */
MakeUnit(const CUnitType & type,CPlayer * player)2829 CUnit *MakeUnit(const CUnitType &type, CPlayer *player)
2830 {
2831 	CUnit *unit = UnitManager.AllocUnit();
2832 	if (unit == nullptr) {
2833 		return nullptr;
2834 	}
2835 	unit->Init(type);
2836 	// Only Assign if a Player was specified
2837 	if (player) {
2838 		unit->AssignToPlayer(*player);
2839 
2840 		//Wyrmgus start
2841 		unit->ChooseVariation(nullptr, true);
2842 		for (int i = 0; i < MaxImageLayers; ++i) {
2843 			unit->ChooseVariation(nullptr, true, i);
2844 		}
2845 		unit->UpdateButtonIcons();
2846 		unit->UpdateXPRequired();
2847 		//Wyrmgus end
2848 	}
2849 
2850 	//Wyrmgus start
2851 	// grant the unit the civilization/faction upgrades of its respective civilization/faction, so that it is able to pursue its upgrade line in experience upgrades even if it changes hands
2852 	if (unit->Type->Civilization != -1 && !PlayerRaces.CivilizationUpgrades[unit->Type->Civilization].empty()) {
2853 		CUpgrade *civilization_upgrade = CUpgrade::Get(PlayerRaces.CivilizationUpgrades[unit->Type->Civilization]);
2854 		if (civilization_upgrade) {
2855 			unit->SetIndividualUpgrade(civilization_upgrade, 1);
2856 		}
2857 	}
2858 	if (unit->Type->Civilization != -1 && unit->Type->Faction != -1 && !PlayerRaces.Factions[unit->Type->Faction]->FactionUpgrade.empty()) {
2859 		CUpgrade *faction_upgrade = CUpgrade::Get(PlayerRaces.Factions[unit->Type->Faction]->FactionUpgrade);
2860 		if (faction_upgrade) {
2861 			unit->SetIndividualUpgrade(faction_upgrade, 1);
2862 		}
2863 	}
2864 
2865 	// generate a trait for the unit, if any are available (only if the editor isn't running)
2866 	if (Editor.Running == EditorNotRunning && unit->Type->Traits.size() > 0) {
2867 		TraitAcquire(*unit, unit->Type->Traits[SyncRand(unit->Type->Traits.size())]);
2868 	}
2869 
2870 	for (size_t i = 0; i < unit->Type->StartingAbilities.size(); ++i) {
2871 		if (CheckDependencies(unit->Type->StartingAbilities[i], unit)) {
2872 			IndividualUpgradeAcquire(*unit, unit->Type->StartingAbilities[i]);
2873 		}
2874 	}
2875 
2876 	if (unit->Type->Elixir) { //set the unit type's elixir, if any
2877 		unit->SetElixir(unit->Type->Elixir);
2878 	}
2879 
2880 	unit->Variable[MANA_INDEX].Value = 0; //start off with 0 mana
2881 	//Wyrmgus end
2882 
2883 	if (unit->Type->OnInit) {
2884 		unit->Type->OnInit->pushPreamble();
2885 		unit->Type->OnInit->pushInteger(UnitNumber(*unit));
2886 		unit->Type->OnInit->run();
2887 	}
2888 
2889 	//  fancy buildings: mirror buildings (but shadows not correct)
2890 	if (type.BoolFlag[BUILDING_INDEX].value && FancyBuildings
2891 		&& unit->Type->BoolFlag[NORANDOMPLACING_INDEX].value == false && (SyncRand() & 1) != 0) {
2892 		unit->Frame = -unit->Frame - 1;
2893 	}
2894 
2895 	return unit;
2896 }
2897 
2898 /**
2899 **  (Un)Mark on vision table the Sight of the unit
2900 **  (and units inside for transporter (recursively))
2901 **
2902 **  @param unit    Unit to (un)mark.
2903 **  @param pos     coord of first container of unit.
2904 **  @param width   Width of the first container of unit.
2905 **  @param height  Height of the first container of unit.
2906 **  @param f       Function to (un)mark for normal vision.
2907 **  @param f2        Function to (un)mark for cloaking vision.
2908 */
MapMarkUnitSightRec(const CUnit & unit,const Vec2i & pos,int width,int height,MapMarkerFunc * f,MapMarkerFunc * f2,MapMarkerFunc * f3)2909 static void MapMarkUnitSightRec(const CUnit &unit, const Vec2i &pos, int width, int height,
2910 								//Wyrmgus start
2911 //								MapMarkerFunc *f, MapMarkerFunc *f2)
2912 								MapMarkerFunc *f, MapMarkerFunc *f2, MapMarkerFunc *f3)
2913 								//Wyrmgus end
2914 {
2915 	Assert(f);
2916 	//Wyrmgus start
2917 	/*
2918 	MapSight(*unit.Player, pos, width, height,
2919 			 unit.GetFirstContainer()->CurrentSightRange, f);
2920 
2921 	if (unit.Type && unit.Type->BoolFlag[DETECTCLOAK_INDEX].value && f2) {
2922 		MapSight(*unit.Player, pos, width, height,
2923 				 unit.GetFirstContainer()->CurrentSightRange, f2);
2924 	}
2925 	*/
2926 
2927 	MapSight(*unit.Player, pos, width, height,
2928 			 unit.Container && unit.Container->CurrentSightRange >= unit.CurrentSightRange ? unit.Container->CurrentSightRange : unit.CurrentSightRange, f, unit.MapLayer->ID);
2929 
2930 	if (unit.Type && unit.Type->BoolFlag[DETECTCLOAK_INDEX].value && f2) {
2931 		MapSight(*unit.Player, pos, width, height,
2932 				 unit.Container && unit.Container->CurrentSightRange >= unit.CurrentSightRange ? unit.Container->CurrentSightRange : unit.CurrentSightRange, f2, unit.MapLayer->ID);
2933 	}
2934 
2935 	if (unit.Variable[ETHEREALVISION_INDEX].Value && f3) {
2936 		MapSight(*unit.Player, pos, width, height,
2937 				 unit.Container && unit.Container->CurrentSightRange >= unit.CurrentSightRange ? unit.Container->CurrentSightRange : unit.CurrentSightRange, f3, unit.MapLayer->ID);
2938 	}
2939 	//Wyrmgus end
2940 
2941 	CUnit *unit_inside = unit.UnitInside;
2942 	for (int i = unit.InsideCount; i--; unit_inside = unit_inside->NextContained) {
2943 		//Wyrmgus start
2944 //		MapMarkUnitSightRec(*unit_inside, pos, width, height, f, f2);
2945 		MapMarkUnitSightRec(*unit_inside, pos, width, height, f, f2, f3);
2946 		//Wyrmgus end
2947 	}
2948 }
2949 
2950 /**
2951 **	@brief	Return the topmost container for the unit
2952 **
2953 **	@param	unit	The unit for which to get the topmost container
2954 **
2955 **	@return	The unit's topmost container if present, or the unit itself otherwise; this function should never return null
2956 */
GetFirstContainer() const2957 CUnit *CUnit::GetFirstContainer() const
2958 {
2959 	const CUnit *container = this;
2960 
2961 	while (container->Container) {
2962 		container = container->Container;
2963 	}
2964 
2965 	return const_cast<CUnit *>(container);
2966 }
2967 
2968 /**
2969 **  Mark on vision table the Sight of the unit
2970 **  (and units inside for transporter)
2971 **
2972 **  @param unit  unit to unmark its vision.
2973 **  @see MapUnmarkUnitSight.
2974 */
MapMarkUnitSight(CUnit & unit)2975 void MapMarkUnitSight(CUnit &unit)
2976 {
2977 	CUnit *container = unit.GetFirstContainer();// First container of the unit.
2978 	Assert(container->Type);
2979 
2980 	MapMarkUnitSightRec(unit, container->tilePos, container->Type->TileSize.x, container->Type->TileSize.y,
2981 						//Wyrmgus start
2982 //						MapMarkTileSight, MapMarkTileDetectCloak);
2983 						MapMarkTileSight, MapMarkTileDetectCloak, MapMarkTileDetectEthereal);
2984 						//Wyrmgus end
2985 
2986 	// Never mark radar, except if the top unit, and unit is usable
2987 	if (&unit == container && !unit.IsUnusable()) {
2988 		if (unit.Stats->Variables[RADAR_INDEX].Value) {
2989 			MapMarkRadar(*unit.Player, unit.tilePos, unit.Type->TileSize.x,
2990 						 unit.Type->TileSize.y, unit.Stats->Variables[RADAR_INDEX].Value, unit.MapLayer->ID);
2991 		}
2992 		if (unit.Stats->Variables[RADARJAMMER_INDEX].Value) {
2993 			MapMarkRadarJammer(*unit.Player, unit.tilePos, unit.Type->TileSize.x,
2994 							   unit.Type->TileSize.y, unit.Stats->Variables[RADARJAMMER_INDEX].Value, unit.MapLayer->ID);
2995 		}
2996 	}
2997 
2998 	//Wyrmgus start
2999 	if (unit.Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value) {
3000 		MapMarkOwnership(*unit.Player, unit.tilePos, unit.Type->TileSize.x,
3001 						   unit.Type->TileSize.y, unit.Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value, unit.MapLayer->ID);
3002 	}
3003 	//Wyrmgus end
3004 }
3005 
3006 /**
3007 **  Unmark on vision table the Sight of the unit
3008 **  (and units inside for transporter)
3009 **
3010 **  @param unit    unit to unmark its vision.
3011 **  @see MapMarkUnitSight.
3012 */
MapUnmarkUnitSight(CUnit & unit)3013 void MapUnmarkUnitSight(CUnit &unit)
3014 {
3015 	Assert(unit.Type);
3016 
3017 	CUnit *container = unit.GetFirstContainer();
3018 	Assert(container->Type);
3019 	MapMarkUnitSightRec(unit,
3020 						container->tilePos, container->Type->TileSize.x, container->Type->TileSize.y,
3021 						//Wyrmgus start
3022 //						MapUnmarkTileSight, MapUnmarkTileDetectCloak);
3023 						MapUnmarkTileSight, MapUnmarkTileDetectCloak, MapUnmarkTileDetectEthereal);
3024 						//Wyrmgus end
3025 
3026 	// Never mark radar, except if the top unit?
3027 	if (&unit == container && !unit.IsUnusable()) {
3028 		if (unit.Stats->Variables[RADAR_INDEX].Value) {
3029 			MapUnmarkRadar(*unit.Player, unit.tilePos, unit.Type->TileSize.x,
3030 						   unit.Type->TileSize.y, unit.Stats->Variables[RADAR_INDEX].Value, unit.MapLayer->ID);
3031 		}
3032 		if (unit.Stats->Variables[RADARJAMMER_INDEX].Value) {
3033 			MapUnmarkRadarJammer(*unit.Player, unit.tilePos, unit.Type->TileSize.x,
3034 								 unit.Type->TileSize.y, unit.Stats->Variables[RADARJAMMER_INDEX].Value, unit.MapLayer->ID);
3035 		}
3036 
3037 	}
3038 
3039 	//Wyrmgus start
3040 	if (unit.Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value) {
3041 		MapUnmarkOwnership(*unit.Player, unit.tilePos, unit.Type->TileSize.x,
3042 							 unit.Type->TileSize.y, unit.Variable[OWNERSHIPINFLUENCERANGE_INDEX].Value, unit.MapLayer->ID);
3043 	}
3044 	//Wyrmgus end
3045 }
3046 
3047 /**
3048 **  Update the Unit Current sight range to good value and transported units inside.
3049 **
3050 **  @param unit  unit to update SightRange
3051 **
3052 **  @internal before using it, MapUnmarkUnitSight(unit)
3053 **  and after MapMarkUnitSight(unit)
3054 **  are often necessary.
3055 **
3056 **  FIXME @todo manage differently unit inside with option.
3057 **  (no vision, min, host value, own value, bonus value, ...)
3058 */
UpdateUnitSightRange(CUnit & unit)3059 void UpdateUnitSightRange(CUnit &unit)
3060 {
3061 //Wyrmgus start
3062 /*
3063 #if 0 // which is the better ? caller check ?
3064 	if (SaveGameLoading) {
3065 		return ;
3066 	}
3067 #else
3068 	Assert(!SaveGameLoading);
3069 #endif
3070 */
3071 //Wyrmgus end
3072 	// FIXME : these values must be configurable.
3073 	//Wyrmgus start
3074 	int unit_sight_range = unit.Variable[SIGHTRANGE_INDEX].Max;
3075 	if (unit.MapLayer) {
3076 		if (unit.MapLayer->GetTimeOfDay() && unit.MapLayer->GetTimeOfDay()->Day) {
3077 			unit_sight_range += unit.Variable[DAYSIGHTRANGEBONUS_INDEX].Value;
3078 		}
3079 		else if (unit.MapLayer->GetTimeOfDay() && unit.MapLayer->GetTimeOfDay()->Night) {
3080 			unit_sight_range += unit.Variable[NIGHTSIGHTRANGEBONUS_INDEX].Value;
3081 		}
3082 	}
3083 	unit_sight_range = std::max<int>(1, unit_sight_range);
3084 	//Wyrmgus end
3085 	if (unit.UnderConstruction) { // Units under construction have no sight range.
3086 		unit.CurrentSightRange = 1;
3087 	} else if (!unit.Container) { // proper value.
3088 		//Wyrmgus start
3089 //		unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max;
3090 		unit.CurrentSightRange = unit_sight_range;
3091 		//Wyrmgus end
3092 	} else { // value of it container.
3093 		//Wyrmgus start
3094 //		unit.CurrentSightRange = unit.Container->CurrentSightRange;
3095 		//if a unit is inside a container, then use the sight of the unit or the container, whichever is greater
3096 		if (unit_sight_range <= unit.Container->CurrentSightRange) {
3097 			unit.CurrentSightRange = unit.Container->CurrentSightRange;
3098 		} else {
3099 			unit.CurrentSightRange = unit_sight_range;
3100 		}
3101 		//Wyrmgus end
3102 	}
3103 
3104 	CUnit *unit_inside = unit.UnitInside;
3105 	for (int i = unit.InsideCount; i--; unit_inside = unit_inside->NextContained) {
3106 		UpdateUnitSightRange(*unit_inside);
3107 	}
3108 }
3109 
3110 /**
3111 **  Mark the field with the FieldFlags.
3112 **
3113 **  @param unit  unit to mark.
3114 */
MarkUnitFieldFlags(const CUnit & unit)3115 void MarkUnitFieldFlags(const CUnit &unit)
3116 {
3117 	const unsigned int flags = unit.Type->FieldFlags;
3118 	int h = unit.Type->TileSize.y;          // Tile height of the unit.
3119 	const int width = unit.Type->TileSize.x; // Tile width of the unit.
3120 	unsigned int index = unit.Offset;
3121 
3122 	//Wyrmgus start
3123 //	if (unit.Type->BoolFlag[VANISHES_INDEX].value) {
3124 	if (unit.Type->BoolFlag[VANISHES_INDEX].value || unit.CurrentAction() == UnitActionDie) {
3125 	//Wyrmgus end
3126 		return ;
3127 	}
3128 	do {
3129 		CMapField *mf = unit.MapLayer->Field(index);
3130 		int w = width;
3131 		do {
3132 			mf->Flags |= flags;
3133 			++mf;
3134 		} while (--w);
3135 		index += unit.MapLayer->GetWidth();
3136 	} while (--h);
3137 }
3138 
3139 class _UnmarkUnitFieldFlags
3140 {
3141 public:
_UnmarkUnitFieldFlags(const CUnit & unit,CMapField * mf)3142 	_UnmarkUnitFieldFlags(const CUnit &unit, CMapField *mf) : main(&unit), mf(mf)
3143 	{}
3144 
operator ()(CUnit * const unit) const3145 	void operator()(CUnit *const unit) const
3146 	{
3147 		if (main != unit && unit->CurrentAction() != UnitActionDie) {
3148 			mf->Flags |= unit->Type->FieldFlags;
3149 		}
3150 	}
3151 private:
3152 	const CUnit *const main;
3153 	CMapField *mf;
3154 };
3155 
3156 
3157 /**
3158 **  Mark the field with the FieldFlags.
3159 **
3160 **  @param unit  unit to mark.
3161 */
UnmarkUnitFieldFlags(const CUnit & unit)3162 void UnmarkUnitFieldFlags(const CUnit &unit)
3163 {
3164 	const unsigned int flags = ~unit.Type->FieldFlags;
3165 	const int width = unit.Type->TileSize.x;
3166 	int h = unit.Type->TileSize.y;
3167 	unsigned int index = unit.Offset;
3168 
3169 	if (unit.Type->BoolFlag[VANISHES_INDEX].value) {
3170 		return ;
3171 	}
3172 	do {
3173 		CMapField *mf = unit.MapLayer->Field(index);
3174 
3175 		int w = width;
3176 		do {
3177 			mf->Flags &= flags;//clean flags
3178 			_UnmarkUnitFieldFlags funct(unit, mf);
3179 
3180 			mf->UnitCache.for_each(funct);
3181 			++mf;
3182 		} while (--w);
3183 		index += unit.MapLayer->GetWidth();
3184 	} while (--h);
3185 }
3186 
3187 /**
3188 **  Add unit to a container. It only updates linked list stuff.
3189 **
3190 **  @param host  Pointer to container.
3191 */
AddInContainer(CUnit & host)3192 void CUnit::AddInContainer(CUnit &host)
3193 {
3194 	Assert(Container == nullptr);
3195 	Container = &host;
3196 	if (host.InsideCount == 0) {
3197 		NextContained = PrevContained = this;
3198 	} else {
3199 		NextContained = host.UnitInside;
3200 		PrevContained = host.UnitInside->PrevContained;
3201 		host.UnitInside->PrevContained->NextContained = this;
3202 		host.UnitInside->PrevContained = this;
3203 	}
3204 	host.UnitInside = this;
3205 	host.InsideCount++;
3206 	//Wyrmgus start
3207 	if (!SaveGameLoading) { //if host has no range by itself, but the unit has range, and the unit can attack from a transporter, change the host's range to the unit's; but don't do this while loading, as it causes a crash (since one unit needs to be loaded before the other, and when this function is processed both won't already have their variables set)
3208 		host.UpdateContainerAttackRange();
3209 	}
3210 	//Wyrmgus end
3211 }
3212 
3213 /**
3214 **  Remove unit from a container. It only updates linked list stuff.
3215 **
3216 **  @param unit  Pointer to unit.
3217 */
RemoveUnitFromContainer(CUnit & unit)3218 static void RemoveUnitFromContainer(CUnit &unit)
3219 {
3220 	CUnit *host = unit.Container; // transporter which contain unit.
3221 	Assert(unit.Container);
3222 	Assert(unit.Container->InsideCount > 0);
3223 
3224 	host->InsideCount--;
3225 	unit.NextContained->PrevContained = unit.PrevContained;
3226 	unit.PrevContained->NextContained = unit.NextContained;
3227 	if (host->InsideCount == 0) {
3228 		host->UnitInside = nullptr;
3229 	} else {
3230 		if (host->UnitInside == &unit) {
3231 			host->UnitInside = unit.NextContained;
3232 		}
3233 	}
3234 	unit.Container = nullptr;
3235 	//Wyrmgus start
3236 	//reset host attack range
3237 	host->UpdateContainerAttackRange();
3238 	//Wyrmgus end
3239 }
3240 
3241 //Wyrmgus start
UpdateContainerAttackRange()3242 void CUnit::UpdateContainerAttackRange()
3243 {
3244 	//reset attack range, if this unit is a transporter (or garrisonable building) from which units can attack
3245 	if (this->Type->CanTransport() && this->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && !this->Type->CanAttack) {
3246 		this->Variable[ATTACKRANGE_INDEX].Enable = 0;
3247 		this->Variable[ATTACKRANGE_INDEX].Max = 0;
3248 		this->Variable[ATTACKRANGE_INDEX].Value = 0;
3249 		if (this->BoardCount > 0) {
3250 			CUnit *boarded_unit = this->UnitInside;
3251 			for (int i = 0; i < this->InsideCount; ++i, boarded_unit = boarded_unit->NextContained) {
3252 				if (boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX) > this->Variable[ATTACKRANGE_INDEX].Value && boarded_unit->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value) { //if container has no range by itself, but the unit has range, and the unit can attack from a transporter, change the container's range to the unit's
3253 					this->Variable[ATTACKRANGE_INDEX].Enable = 1;
3254 					this->Variable[ATTACKRANGE_INDEX].Max = boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX);
3255 					this->Variable[ATTACKRANGE_INDEX].Value = boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX);
3256 				}
3257 			}
3258 		}
3259 	}
3260 }
3261 
UpdateXPRequired()3262 void CUnit::UpdateXPRequired()
3263 {
3264 	if (!this->Type->BoolFlag[ORGANIC_INDEX].value) {
3265 		return;
3266 	}
3267 
3268 	this->Variable[XPREQUIRED_INDEX].Value = this->Type->Stats[this->Player->Index].Variables[POINTS_INDEX].Value * 4 * this->Type->Stats[this->Player->Index].Variables[LEVEL_INDEX].Value;
3269 	int extra_levels = this->Variable[LEVEL_INDEX].Value - this->Type->Stats[this->Player->Index].Variables[LEVEL_INDEX].Value;
3270 	for (int i = 1; i <= extra_levels; ++i) {
3271 		this->Variable[XPREQUIRED_INDEX].Value += 50 * 4 * i;
3272 	}
3273 	this->Variable[XPREQUIRED_INDEX].Max = this->Variable[XPREQUIRED_INDEX].Value;
3274 	this->Variable[XPREQUIRED_INDEX].Enable = 1;
3275 	this->Variable[XP_INDEX].Enable = 1;
3276 }
3277 
UpdatePersonalName(bool update_settlement_name)3278 void CUnit::UpdatePersonalName(bool update_settlement_name)
3279 {
3280 	if (this->Character != nullptr) {
3281 		return;
3282 	} else if (this->Type->BoolFlag[ITEM_INDEX].value || this->Unique || this->Prefix || this->Suffix) {
3283 		this->UpdateItemName();
3284 		return;
3285 	}
3286 
3287 	int civilization_id = this->Type->Civilization;
3288 
3289 	CFaction *faction = nullptr;
3290 	if (this->Player->Faction != -1) {
3291 		faction = PlayerRaces.Factions[this->Player->Faction];
3292 
3293 		if (civilization_id != -1 && civilization_id != faction->Civilization->ID && PlayerRaces.Species[civilization_id] == PlayerRaces.Species[faction->Civilization->ID] && this->Type->Slot == PlayerRaces.GetFactionClassUnitType(faction->ID, this->Type->Class)) {
3294 			civilization_id = faction->Civilization->ID;
3295 		}
3296 	}
3297 
3298 	CLanguage *language = PlayerRaces.GetCivilizationLanguage(civilization_id);
3299 
3300 	if (this->Name.empty()) { //this is the first time the unit receives a name
3301 		if (!this->Type->BoolFlag[FAUNA_INDEX].value && this->Trait != nullptr && this->Trait->Epithets.size() > 0 && SyncRand(4) == 0) { // 25% chance to give the unit an epithet based on their trait
3302 			this->ExtraName = this->Trait->Epithets[SyncRand(this->Trait->Epithets.size())];
3303 		}
3304 	}
3305 
3306 	if (!this->Type->IsPersonalNameValid(this->Name, faction, this->Variable[GENDER_INDEX].Value)) {
3307 		// first see if can translate the current personal name
3308 		std::string new_personal_name = PlayerRaces.TranslateName(this->Name, language);
3309 		if (!new_personal_name.empty()) {
3310 			this->Name = new_personal_name;
3311 		} else {
3312 			this->Name = this->Type->GeneratePersonalName(faction, this->Variable[GENDER_INDEX].Value);
3313 		}
3314 	}
3315 
3316 	if (update_settlement_name && (this->Type->BoolFlag[TOWNHALL_INDEX].value || (this->Type->BoolFlag[BUILDING_INDEX].value && !this->Settlement))) {
3317 		this->UpdateSettlement();
3318 	}
3319 }
3320 
UpdateExtraName()3321 void CUnit::UpdateExtraName()
3322 {
3323 	if (this->Character != nullptr || !this->Type->BoolFlag[ORGANIC_INDEX].value || this->Type->BoolFlag[FAUNA_INDEX].value) {
3324 		return;
3325 	}
3326 
3327 	if (this->Trait == nullptr) {
3328 		return;
3329 	}
3330 
3331 	this->ExtraName.clear();
3332 
3333 	if (this->Trait->Epithets.size() > 0 && SyncRand(4) == 0) { // 25% chance to give the unit an epithet based on their trait
3334 		this->ExtraName = this->Trait->Epithets[SyncRand(this->Trait->Epithets.size())];
3335 	}
3336 }
3337 
UpdateSettlement()3338 void CUnit::UpdateSettlement()
3339 {
3340 	if (this->Removed || Editor.Running != EditorNotRunning) {
3341 		return;
3342 	}
3343 
3344 	if (!this->Type->BoolFlag[BUILDING_INDEX].value || this->Type->TerrainType) {
3345 		return;
3346 	}
3347 
3348 	if (this->Type->BoolFlag[TOWNHALL_INDEX].value || this->Type == SettlementSiteUnitType) {
3349 		if (!this->Settlement) {
3350 			int civilization_id = this->Type->Civilization;
3351 			if (civilization_id != -1 && this->Player->Faction != -1 && (this->Player->Race == civilization_id || this->Type->Slot == PlayerRaces.GetFactionClassUnitType(this->Player->Faction, this->Type->Class))) {
3352 				civilization_id = this->Player->Race;
3353 			}
3354 			const CCivilization *civilization = nullptr;
3355 			if (civilization_id != -1) {
3356 				civilization = CCivilization::Civilizations[civilization_id];
3357 			}
3358 
3359 			int faction_id = this->Type->Faction;
3360 			if (this->Player->Race == civilization_id && this->Type->Slot == PlayerRaces.GetFactionClassUnitType(this->Player->Faction, this->Type->Class)) {
3361 				faction_id = this->Player->Faction;
3362 			}
3363 			const CFaction *faction = nullptr;
3364 			if (faction_id != -1) {
3365 				PlayerRaces.Factions[faction_id];
3366 			}
3367 
3368 			std::vector<CSite *> potential_settlements;
3369 			if (civilization) {
3370 				for (CSite *site : civilization->Sites) {
3371 					if (!site->SiteUnit) {
3372 						potential_settlements.push_back(site);
3373 					}
3374 				}
3375 			}
3376 
3377 			if (potential_settlements.empty() && faction) {
3378 				for (CSite *site : faction->Sites) {
3379 					if (!site->SiteUnit) {
3380 						potential_settlements.push_back(site);
3381 					}
3382 				}
3383 			}
3384 
3385 			if (potential_settlements.empty()) {
3386 				for (CSite *site : CSite::Sites) {
3387 					if (!site->SiteUnit) {
3388 						potential_settlements.push_back(site);
3389 					}
3390 				}
3391 			}
3392 
3393 			if (potential_settlements.size() > 0) {
3394 				this->Settlement = potential_settlements[SyncRand(potential_settlements.size())];
3395 				this->Settlement->SiteUnit = this;
3396 				Map.SiteUnits.push_back(this);
3397 			}
3398 		}
3399 		if (this->Settlement) {
3400 			this->UpdateBuildingSettlementAssignment();
3401 		}
3402 	} else {
3403 		if (this->Player->Index == PlayerNumNeutral) {
3404 			return;
3405 		}
3406 
3407 		this->Settlement = this->Player->GetNearestSettlement(this->tilePos, this->MapLayer->ID, this->Type->TileSize);
3408 	}
3409 }
3410 
UpdateBuildingSettlementAssignment(CSite * old_settlement)3411 void CUnit::UpdateBuildingSettlementAssignment(CSite *old_settlement)
3412 {
3413 	if (Editor.Running != EditorNotRunning) {
3414 		return;
3415 	}
3416 
3417 	if (this->Player->Index == PlayerNumNeutral) {
3418 		return;
3419 	}
3420 
3421 	for (int p = 0; p < PlayerMax; ++p) {
3422 		if (!Players[p].HasNeutralFactionType() && this->Player->Index != Players[p].Index) {
3423 			continue;
3424 		}
3425 		for (int i = 0; i < Players[p].GetUnitCount(); ++i) {
3426 			CUnit *settlement_unit = &Players[p].GetUnit(i);
3427 			if (!settlement_unit || !settlement_unit->IsAliveOnMap() || !settlement_unit->Type->BoolFlag[BUILDING_INDEX].value || settlement_unit->Type->BoolFlag[TOWNHALL_INDEX].value || settlement_unit->Type == SettlementSiteUnitType || this->MapLayer != settlement_unit->MapLayer) {
3428 				continue;
3429 			}
3430 			if (old_settlement && settlement_unit->Settlement != old_settlement) {
3431 				continue;
3432 			}
3433 			settlement_unit->UpdateSettlement();
3434 		}
3435 	}
3436 }
3437 
XPChanged()3438 void CUnit::XPChanged()
3439 {
3440 	if (!this->Type->BoolFlag[ORGANIC_INDEX].value || this->Type->BoolFlag[BUILDING_INDEX].value) {
3441 		return;
3442 	}
3443 
3444 	if (this->Variable[XPREQUIRED_INDEX].Value == 0) {
3445 		return;
3446 	}
3447 
3448 	while (this->Variable[XP_INDEX].Value >= this->Variable[XPREQUIRED_INDEX].Value) {
3449 		this->Variable[XP_INDEX].Max -= this->Variable[XPREQUIRED_INDEX].Max;
3450 		this->Variable[XP_INDEX].Value -= this->Variable[XPREQUIRED_INDEX].Value;
3451 		if (this->Player == ThisPlayer) {
3452 			this->Player->Notify(NotifyGreen, this->tilePos, this->MapLayer->ID, _("%s has leveled up!"), GetMessageName().c_str());
3453 		}
3454 		this->IncreaseLevel(1);
3455 	}
3456 
3457 	if (!IsNetworkGame() && this->Character != nullptr && this->Player->AiEnabled == false) {
3458 		this->Character->ExperiencePercent = (this->Variable[XP_INDEX].Value * 100) / this->Variable[XPREQUIRED_INDEX].Value;
3459 		SaveHero(this->Character);
3460 	}
3461 }
3462 //Wyrmgus end
3463 
3464 /**
3465 **  Affect Tile coord of a unit (with units inside) to tile (x, y).
3466 **
3467 **  @param unit  unit to move.
3468 **  @param pos   map tile position.
3469 **
3470 **  @internal before use it, Map.Remove(unit), MapUnmarkUnitSight(unit)
3471 **  and after Map.Insert(unit), MapMarkUnitSight(unit)
3472 **  are often necessary. Check Flag also for Pathfinder.
3473 */
UnitInXY(CUnit & unit,const Vec2i & pos,const int z)3474 static void UnitInXY(CUnit &unit, const Vec2i &pos, const int z)
3475 {
3476 	const CMapLayer *old_map_layer = unit.MapLayer;
3477 
3478 	CUnit *unit_inside = unit.UnitInside;
3479 
3480 	unit.tilePos = pos;
3481 	unit.Offset = Map.getIndex(pos, z);
3482 	unit.MapLayer = Map.MapLayers[z];
3483 
3484 	//Wyrmgus start
3485 	if (!SaveGameLoading && old_map_layer != unit.MapLayer) {
3486 		UpdateUnitSightRange(unit);
3487 	}
3488 	//Wyrmgus end
3489 
3490 	for (int i = unit.InsideCount; i--; unit_inside = unit_inside->NextContained) {
3491 		UnitInXY(*unit_inside, pos, z);
3492 	}
3493 }
3494 
3495 /**
3496 **  Move a unit (with units inside) to tile (pos).
3497 **  (Do stuff with vision, cachelist and pathfinding).
3498 **
3499 **  @param pos  map tile position.
3500 **
3501 */
3502 //Wyrmgus start
3503 //void CUnit::MoveToXY(const Vec2i &pos)
MoveToXY(const Vec2i & pos,int z)3504 void CUnit::MoveToXY(const Vec2i &pos, int z)
3505 //Wyrmgus end
3506 {
3507 	MapUnmarkUnitSight(*this);
3508 	Map.Remove(*this);
3509 	UnmarkUnitFieldFlags(*this);
3510 
3511 	//Wyrmgus start
3512 //	Assert(UnitCanBeAt(*this, pos));
3513 	Assert(UnitCanBeAt(*this, pos, z));
3514 	//Wyrmgus end
3515 	// Move the unit.
3516 	//Wyrmgus start
3517 //	UnitInXY(*this, pos);
3518 	UnitInXY(*this, pos, z);
3519 	//Wyrmgus end
3520 
3521 	Map.Insert(*this);
3522 	MarkUnitFieldFlags(*this);
3523 	//  Recalculate the seen count.
3524 	UnitCountSeen(*this);
3525 	MapMarkUnitSight(*this);
3526 
3527 	//Wyrmgus start
3528 	// if there is a trap in the new tile, trigger it
3529 	if ((this->Type->UnitType != UnitTypeFly && this->Type->UnitType != UnitTypeFlyLow) || !this->Type->BoolFlag[ORGANIC_INDEX].value) {
3530 		const CUnitCache &cache = Map.Field(pos, z)->UnitCache;
3531 		for (size_t i = 0; i != cache.size(); ++i) {
3532 			if (!cache[i]) {
3533 				fprintf(stderr, "Error in CUnit::MoveToXY (pos %d, %d): a unit in the tile's unit cache is null.\n", pos.x, pos.y);
3534 			}
3535 			CUnit &unit = *cache[i];
3536 			if (unit.IsAliveOnMap() && unit.Type->BoolFlag[TRAP_INDEX].value) {
3537 				FireMissile(unit, this, this->tilePos, this->MapLayer->ID);
3538 				LetUnitDie(unit);
3539 			}
3540 		}
3541 	}
3542 	//Wyrmgus end
3543 }
3544 
3545 /**
3546 **  Place unit on map.
3547 **
3548 **  @param pos  map tile position.
3549 */
Place(const Vec2i & pos,int z)3550 void CUnit::Place(const Vec2i &pos, int z)
3551 {
3552 	Assert(Removed);
3553 
3554 	const CMapLayer *old_map_layer = this->MapLayer;
3555 
3556 	if (Container) {
3557 		MapUnmarkUnitSight(*this);
3558 		RemoveUnitFromContainer(*this);
3559 	}
3560 	if (!SaveGameLoading) {
3561 		UpdateUnitSightRange(*this);
3562 	}
3563 	Removed = 0;
3564 	UnitInXY(*this, pos, z);
3565 	// Pathfinding info.
3566 	MarkUnitFieldFlags(*this);
3567 	// Tha cache list.
3568 	Map.Insert(*this);
3569 	//  Calculate the seen count.
3570 	UnitCountSeen(*this);
3571 	// Vision
3572 	MapMarkUnitSight(*this);
3573 
3574 	// Correct directions for wall units
3575 	if (this->Type->BoolFlag[WALL_INDEX].value && this->CurrentAction() != UnitActionBuilt) {
3576 		CorrectWallDirections(*this);
3577 		UnitUpdateHeading(*this);
3578 		CorrectWallNeighBours(*this);
3579 	}
3580 
3581 	//Wyrmgus start
3582 	if (this->IsAlive()) {
3583 		if (this->Type->BoolFlag[BUILDING_INDEX].value) {
3584 			this->UpdateSettlement(); // update the settlement name of a building when placing it
3585 		}
3586 
3587 		//remove pathways, destroyed walls and decoration units under buildings
3588 		if (this->Type->BoolFlag[BUILDING_INDEX].value && !this->Type->TerrainType) {
3589 			for (int x = this->tilePos.x; x < this->tilePos.x + this->Type->TileSize.x; ++x) {
3590 				for (int y = this->tilePos.y; y < this->tilePos.y + this->Type->TileSize.y; ++y) {
3591 					if (!Map.Info.IsPointOnMap(x, y, this->MapLayer)) {
3592 						continue;
3593 					}
3594 					Vec2i building_tile_pos(x, y);
3595 					CMapField &mf = *this->MapLayer->Field(building_tile_pos);
3596 					if ((mf.Flags & MapFieldRoad) || (mf.Flags & MapFieldRailroad) || (mf.Flags & MapFieldWall)) {
3597 						Map.RemoveTileOverlayTerrain(building_tile_pos, this->MapLayer->ID);
3598 					}
3599 					//remove decorations if a building has been built here
3600 					std::vector<CUnit *> table;
3601 					Select(building_tile_pos, building_tile_pos, table, this->MapLayer->ID);
3602 					for (size_t i = 0; i != table.size(); ++i) {
3603 						if (table[i] && table[i]->IsAlive() && table[i]->Type->UnitType == UnitTypeLand && table[i]->Type->BoolFlag[DECORATION_INDEX].value) {
3604 							if (Editor.Running == EditorNotRunning) {
3605 								LetUnitDie(*table[i]);
3606 							} else {
3607 								EditorActionRemoveUnit(*table[i], false);
3608 							}
3609 						}
3610 					}
3611 				}
3612 			}
3613 		}
3614 
3615 		const CUnitTypeVariation *variation = this->GetVariation();
3616 		if (variation) {
3617 			// if a unit that is on the tile has a terrain-dependent or season-dependent variation that is not compatible with the new tile, or if this is the first position the unit is being placed in, repick the unit's variation
3618 			if (!old_map_layer || !this->CheckTerrainForVariation(variation) || !this->CheckSeasonForVariation(variation)) {
3619 				this->ChooseVariation();
3620 			}
3621 		}
3622 	}
3623 	//Wyrmgus end
3624 }
3625 
3626 /**
3627 **  Create new unit and place on map.
3628 **
3629 **  @param pos     map tile position.
3630 **  @param type    Pointer to unit-type.
3631 **  @param player  Pointer to owning player.
3632 **
3633 **  @return        Pointer to created unit.
3634 */
3635 //Wyrmgus start
3636 //CUnit *MakeUnitAndPlace(const Vec2i &pos, const CUnitType &type, CPlayer *player)
MakeUnitAndPlace(const Vec2i & pos,const CUnitType & type,CPlayer * player,int z)3637 CUnit *MakeUnitAndPlace(const Vec2i &pos, const CUnitType &type, CPlayer *player, int z)
3638 //Wyrmgus end
3639 {
3640 	CUnit *unit = MakeUnit(type, player);
3641 
3642 	if (unit != nullptr) {
3643 		unit->Place(pos, z);
3644 	}
3645 	return unit;
3646 }
3647 
3648 //Wyrmgus start
3649 /**
3650 **  Create a new unit and place it on the map, updating its player accordingly.
3651 **
3652 **  @param pos     map tile position.
3653 **  @param type    Pointer to unit-type.
3654 **  @param player  Pointer to owning player.
3655 **
3656 **  @return        Pointer to created unit.
3657 */
CreateUnit(const Vec2i & pos,const CUnitType & type,CPlayer * player,int z,bool no_bordering_building)3658 CUnit *CreateUnit(const Vec2i &pos, const CUnitType &type, CPlayer *player, int z, bool no_bordering_building)
3659 {
3660 	CUnit *unit = MakeUnit(type, player);
3661 
3662 	if (unit != nullptr) {
3663 		Vec2i res_pos;
3664 		const int heading = SyncRand() % 256;
3665 		FindNearestDrop(type, pos, res_pos, heading, z, no_bordering_building);
3666 
3667 		if (type.BoolFlag[BUILDING_INDEX].value) {
3668 			CBuildRestrictionOnTop *b = OnTopDetails(type, nullptr);
3669 			if (b && b->ReplaceOnBuild) {
3670 				CUnitCache &unitCache = Map.Field(res_pos, z)->UnitCache;
3671 				CUnitCache::iterator it = std::find_if(unitCache.begin(), unitCache.end(), HasSameTypeAs(*b->Parent));
3672 
3673 				if (it != unitCache.end()) {
3674 					CUnit &replacedUnit = **it;
3675 					unit->ReplaceOnTop(replacedUnit);
3676 				}
3677 			}
3678 		}
3679 
3680 		unit->Place(res_pos, z);
3681 		UpdateForNewUnit(*unit, 0);
3682 	}
3683 	return unit;
3684 }
3685 
CreateResourceUnit(const Vec2i & pos,const CUnitType & type,int z,bool allow_unique)3686 CUnit *CreateResourceUnit(const Vec2i &pos, const CUnitType &type, int z, bool allow_unique)
3687 {
3688 	CUnit *unit = CreateUnit(pos, type, &Players[PlayerNumNeutral], z, true);
3689 	unit->GenerateSpecialProperties(nullptr, nullptr, allow_unique);
3690 
3691 	// create metal rocks near metal resources
3692 	CUnitType *metal_rock_type = nullptr;
3693 	if (type.Ident == "unit-gold-deposit") {
3694 		metal_rock_type = UnitTypeByIdent("unit-gold-rock");
3695 	} else if (type.Ident == "unit-silver-deposit") {
3696 		metal_rock_type = UnitTypeByIdent("unit-silver-rock");
3697 	} else if (type.Ident == "unit-copper-deposit") {
3698 		metal_rock_type = UnitTypeByIdent("unit-copper-rock");
3699 	} else if (type.Ident == "unit-diamond-deposit") {
3700 		metal_rock_type = UnitTypeByIdent("unit-diamond-rock");
3701 	} else if (type.Ident == "unit-emerald-deposit") {
3702 		metal_rock_type = UnitTypeByIdent("unit-emerald-rock");
3703 	}
3704 	if (metal_rock_type) {
3705 		Vec2i metal_rock_offset((type.TileSize - 1) / 2);
3706 		for (int i = 0; i < 9; ++i) {
3707 			CUnit *metal_rock_unit = CreateUnit(unit->tilePos + metal_rock_offset, *metal_rock_type, &Players[PlayerNumNeutral], z);
3708 		}
3709 	}
3710 
3711 	return unit;
3712 }
3713 //Wyrmgus end
3714 
3715 /**
3716 **  Find the nearest position at which unit can be placed.
3717 **
3718 **  @param type     Type of the dropped unit.
3719 **  @param goalPos  Goal map tile position.
3720 **  @param resPos   Holds the nearest point.
3721 **  @param heading  preferense side to drop out of.
3722 */
3723 //Wyrmgus start
3724 //void FindNearestDrop(const CUnitType &type, const Vec2i &goalPos, Vec2i &resPos, int heading)
FindNearestDrop(const CUnitType & type,const Vec2i & goalPos,Vec2i & resPos,int heading,int z,bool no_bordering_building,bool ignore_construction_requirements)3725 void FindNearestDrop(const CUnitType &type, const Vec2i &goalPos, Vec2i &resPos, int heading, int z, bool no_bordering_building, bool ignore_construction_requirements)
3726 //Wyrmgus end
3727 {
3728 	int addx = 0;
3729 	int addy = 0;
3730 	Vec2i pos = goalPos;
3731 
3732 	if (heading < LookingNE || heading > LookingNW) {
3733 		goto starts;
3734 	} else if (heading < LookingSE) {
3735 		goto startw;
3736 	} else if (heading < LookingSW) {
3737 		goto startn;
3738 	} else {
3739 		goto starte;
3740 	}
3741 
3742 	// FIXME: don't search outside of the map
3743 	for (;;) {
3744 startw:
3745 		for (int i = addy; i--; ++pos.y) {
3746 			//Wyrmgus start
3747 //			if (UnitTypeCanBeAt(type, pos)) {
3748 			if (
3749 				(UnitTypeCanBeAt(type, pos, z) || (type.BoolFlag[BUILDING_INDEX].value && OnTopDetails(type, nullptr) && !ignore_construction_requirements))
3750 				&& (!type.BoolFlag[BUILDING_INDEX].value || ignore_construction_requirements || CanBuildHere(nullptr, type, pos, z, no_bordering_building) != nullptr)
3751 			) {
3752 			//Wyrmgus end
3753 				goto found;
3754 			}
3755 		}
3756 		++addx;
3757 starts:
3758 		for (int i = addx; i--; ++pos.x) {
3759 			//Wyrmgus start
3760 //			if (UnitTypeCanBeAt(type, pos)) {
3761 			if (
3762 				(UnitTypeCanBeAt(type, pos, z) || (type.BoolFlag[BUILDING_INDEX].value && OnTopDetails(type, nullptr) && !ignore_construction_requirements))
3763 				&& (!type.BoolFlag[BUILDING_INDEX].value || ignore_construction_requirements || CanBuildHere(nullptr, type, pos, z, no_bordering_building) != nullptr)
3764 			) {
3765 			//Wyrmgus end
3766 				goto found;
3767 			}
3768 		}
3769 		++addy;
3770 starte:
3771 		for (int i = addy; i--; --pos.y) {
3772 			//Wyrmgus start
3773 //			if (UnitTypeCanBeAt(type, pos)) {
3774 			if (
3775 				(UnitTypeCanBeAt(type, pos, z) || (type.BoolFlag[BUILDING_INDEX].value && OnTopDetails(type, nullptr) && !ignore_construction_requirements))
3776 				&& (!type.BoolFlag[BUILDING_INDEX].value || ignore_construction_requirements || CanBuildHere(nullptr, type, pos, z, no_bordering_building) != nullptr)
3777 			) {
3778 			//Wyrmgus end
3779 				goto found;
3780 			}
3781 		}
3782 		++addx;
3783 startn:
3784 		for (int i = addx; i--; --pos.x) {
3785 			//Wyrmgus start
3786 //			if (UnitTypeCanBeAt(type, pos)) {
3787 			if (
3788 				(UnitTypeCanBeAt(type, pos, z) || (type.BoolFlag[BUILDING_INDEX].value && OnTopDetails(type, nullptr) && !ignore_construction_requirements))
3789 				&& (!type.BoolFlag[BUILDING_INDEX].value || ignore_construction_requirements || CanBuildHere(nullptr, type, pos, z, no_bordering_building) != nullptr)
3790 			) {
3791 			//Wyrmgus end
3792 				goto found;
3793 			}
3794 		}
3795 		++addy;
3796 	}
3797 
3798 found:
3799 	resPos = pos;
3800 }
3801 
3802 /**
3803 **  Remove unit from map.
3804 **
3805 **  Update selection.
3806 **  Update panels.
3807 **  Update map.
3808 **
3809 **  @param host  Pointer to housing unit.
3810 */
Remove(CUnit * host)3811 void CUnit::Remove(CUnit *host)
3812 {
3813 	if (Removed) { // could happen!
3814 		// If unit is removed (inside) and building is destroyed.
3815 		DebugPrint("unit '%s(%d)' already removed\n" _C_ Type->Ident.c_str() _C_ UnitNumber(*this));
3816 		return;
3817 	}
3818 	Map.Remove(*this);
3819 	MapUnmarkUnitSight(*this);
3820 	UnmarkUnitFieldFlags(*this);
3821 	if (host) {
3822 		AddInContainer(*host);
3823 		UpdateUnitSightRange(*this);
3824 		UnitInXY(*this, host->tilePos, host->MapLayer->ID);
3825 		MapMarkUnitSight(*this);
3826 	}
3827 
3828 	Removed = 1;
3829 
3830 	// Correct surrounding walls directions
3831 	if (this->Type->BoolFlag[WALL_INDEX].value) {
3832 		CorrectWallNeighBours(*this);
3833 	}
3834 
3835 	//  Remove unit from the current selection
3836 	if (this->Selected) {
3837 		if (::Selected.size() == 1) { //  Remove building cursor
3838 			CancelBuildingMode();
3839 		}
3840 		UnSelectUnit(*this);
3841 		//Wyrmgus start
3842 //		SelectionChanged();
3843 		if (GameRunning) { // to avoid a crash when SelectionChanged() calls UI.ButtonPanel.Update()
3844 			SelectionChanged();
3845 		}
3846 		//Wyrmgus end
3847 	}
3848 	// Remove unit from team selections
3849 	if (!Selected && TeamSelected) {
3850 		UnSelectUnit(*this);
3851 	}
3852 
3853 	// Unit is seen as under cursor
3854 	if (UnitUnderCursor == this) {
3855 		UnitUnderCursor = nullptr;
3856 	}
3857 }
3858 
3859 /**
3860 **  Update information for lost units.
3861 **
3862 **  @param unit  Pointer to unit.
3863 **
3864 **  @note Also called by ChangeUnitOwner
3865 */
UnitLost(CUnit & unit)3866 void UnitLost(CUnit &unit)
3867 {
3868 	CPlayer &player = *unit.Player;
3869 
3870 	Assert(&player);  // Next code didn't support no player!
3871 
3872 	//  Call back to AI, for killed or lost units.
3873 	if (Editor.Running == EditorNotRunning) {
3874 		if (player.AiEnabled) {
3875 			AiUnitKilled(unit);
3876 		} else {
3877 			//  Remove unit from its groups
3878 			if (unit.GroupId) {
3879 				RemoveUnitFromGroups(unit);
3880 			}
3881 		}
3882 	}
3883 
3884 	//  Remove the unit from the player's units table.
3885 
3886 	const CUnitType &type = *unit.Type;
3887 	if (!type.BoolFlag[VANISHES_INDEX].value) {
3888 		player.RemoveUnit(unit);
3889 
3890 		if (type.BoolFlag[BUILDING_INDEX].value) {
3891 			// FIXME: support more races
3892 			//Wyrmgus start
3893 //			if (!type.BoolFlag[WALL_INDEX].value && &type != UnitTypeOrcWall && &type != UnitTypeHumanWall) {
3894 			//Wyrmgus end
3895 				player.NumBuildings--;
3896 				//Wyrmgus start
3897 				if (unit.CurrentAction() == UnitActionBuilt) {
3898 					player.NumBuildingsUnderConstruction--;
3899 					player.ChangeUnitTypeUnderConstructionCount(&type, -1);
3900 				}
3901 				//Wyrmgus end
3902 			//Wyrmgus start
3903 //			}
3904 			//Wyrmgus end
3905 		}
3906 		if (unit.CurrentAction() != UnitActionBuilt) {
3907 			if (player.AiEnabled && player.Ai) {
3908 				if (std::find(player.Ai->Scouts.begin(), player.Ai->Scouts.end(), &unit) != player.Ai->Scouts.end()) {
3909 					if (player.Ai->Scouting) { //if an AI player's scout has been lost, unmark it as "scouting" so that the force can see if it now has a viable target
3910 						player.Ai->Scouting = false;
3911 					}
3912 				}
3913 				for (std::map<int, std::vector<CUnit *>>::iterator iterator = player.Ai->Transporters.begin(); iterator != player.Ai->Transporters.end(); ++iterator) {
3914 					if (std::find(iterator->second.begin(), iterator->second.end(), &unit) != iterator->second.end()) {
3915 						iterator->second.erase(std::remove(iterator->second.begin(), iterator->second.end(), &unit), iterator->second.end());
3916 					}
3917 				}
3918 			}
3919 
3920 			player.DecreaseCountsForUnit(&unit);
3921 
3922 			if (unit.Variable[LEVELUP_INDEX].Value > 0) {
3923 				player.UpdateLevelUpUnits(); //recalculate level-up units, since this unit no longer should be in that vector
3924 			}
3925 			//Wyrmgus end
3926 		}
3927 	}
3928 
3929 	//  Handle unit demand. (Currently only food supported.)
3930 	player.Demand -= type.Stats[player.Index].Variables[DEMAND_INDEX].Value;
3931 
3932 	//  Update information.
3933 	if (unit.CurrentAction() != UnitActionBuilt) {
3934 		player.Supply -= unit.Variable[SUPPLY_INDEX].Value;
3935 		// Decrease resource limit
3936 		for (int i = 0; i < MaxCosts; ++i) {
3937 			if (player.MaxResources[i] != -1 && type.Stats[player.Index].Storing[i]) {
3938 				const int newMaxValue = player.MaxResources[i] - type.Stats[player.Index].Storing[i];
3939 
3940 				player.MaxResources[i] = std::max(0, newMaxValue);
3941 				player.SetResource(i, player.StoredResources[i], STORE_BUILDING);
3942 			}
3943 		}
3944 		//  Handle income improvements, look if a player loses a building
3945 		//  which have given him a better income, find the next best
3946 		//  income.
3947 		for (int i = 1; i < MaxCosts; ++i) {
3948 			if (player.Incomes[i] && type.Stats[player.Index].ImproveIncomes[i] == player.Incomes[i]) {
3949 				int m = CResource::Resources[i]->DefaultIncome;
3950 
3951 				for (int j = 0; j < player.GetUnitCount(); ++j) {
3952 					m = std::max(m, player.GetUnit(j).Type->Stats[player.Index].ImproveIncomes[i]);
3953 				}
3954 				player.Incomes[i] = m;
3955 			}
3956 		}
3957 
3958 		if (type.Stats[player.Index].Variables[TRADECOST_INDEX].Enable) {
3959 			int m = DefaultTradeCost;
3960 
3961 			for (int j = 0; j < player.GetUnitCount(); ++j) {
3962 				if (player.GetUnit(j).Type->Stats[player.Index].Variables[TRADECOST_INDEX].Enable) {
3963 					m = std::min(m, player.GetUnit(j).Type->Stats[player.Index].Variables[TRADECOST_INDEX].Value);
3964 				}
3965 			}
3966 			player.TradeCost = m;
3967 		}
3968 
3969 		//Wyrmgus start
3970 		if (type.BoolFlag[TOWNHALL_INDEX].value) {
3971 			bool lost_town_hall = true;
3972 			for (int j = 0; j < player.GetUnitCount(); ++j) {
3973 				if (player.GetUnit(j).Type->BoolFlag[TOWNHALL_INDEX].value) {
3974 					lost_town_hall = false;
3975 				}
3976 			}
3977 			if (lost_town_hall && ThisPlayer->HasContactWith(player)) {
3978 				player.LostTownHallTimer = GameCycle + (30 * CYCLES_PER_SECOND); //30 seconds until being revealed
3979 				for (int j = 0; j < NumPlayers; ++j) {
3980 					if (player.Index != j && Players[j].Type != PlayerNobody) {
3981 						Players[j].Notify(_("%s has lost their last town hall, and will be revealed in thirty seconds!"), player.Name.c_str());
3982 					} else {
3983 						Players[j].Notify("%s", _("You have lost your last town hall, and will be revealed in thirty seconds!"));
3984 					}
3985 				}
3986 			}
3987 		}
3988 		//Wyrmgus end
3989 	}
3990 
3991 	//  Handle order cancels.
3992 	unit.CurrentOrder()->Cancel(unit);
3993 
3994 	DebugPrint("%d: Lost %s(%d)\n" _C_ player.Index _C_ type.Ident.c_str() _C_ UnitNumber(unit));
3995 
3996 	// Destroy resource-platform, must re-make resource patch.
3997 	//Wyrmgus start
3998 //	CBuildRestrictionOnTop *b = OnTopDetails(unit, nullptr);
3999 	CBuildRestrictionOnTop *b = OnTopDetails(*unit.Type, nullptr);
4000 	//Wyrmgus end
4001 	if (b != nullptr) {
4002 		//Wyrmgus start
4003 //		if (b->ReplaceOnDie && (type.GivesResource && unit.ResourcesHeld != 0)) {
4004 		if (b->ReplaceOnDie && (!type.GivesResource || unit.ResourcesHeld != 0)) {
4005 		//Wyrmgus end
4006 			CUnit *temp = MakeUnitAndPlace(unit.tilePos, *b->Parent, &Players[PlayerNumNeutral], unit.MapLayer->ID);
4007 			if (temp == nullptr) {
4008 				DebugPrint("Unable to allocate Unit");
4009 			} else {
4010 				//Wyrmgus start
4011 //				temp->ResourcesHeld = unit.ResourcesHeld;
4012 //				temp->Variable[GIVERESOURCE_INDEX].Value = unit.Variable[GIVERESOURCE_INDEX].Value;
4013 //				temp->Variable[GIVERESOURCE_INDEX].Max = unit.Variable[GIVERESOURCE_INDEX].Max;
4014 //				temp->Variable[GIVERESOURCE_INDEX].Enable = unit.Variable[GIVERESOURCE_INDEX].Enable;
4015 				//Wyrmgus end
4016 				//Wyrmgus start
4017 				if (unit.Unique != nullptr) {
4018 					temp->SetUnique(unit.Unique);
4019 				} else {
4020 					if (unit.Prefix != nullptr) {
4021 						temp->SetPrefix(unit.Prefix);
4022 					}
4023 					if (unit.Suffix != nullptr) {
4024 						temp->SetSuffix(unit.Suffix);
4025 					}
4026 					if (unit.Spell != nullptr) {
4027 						temp->SetSpell(unit.Spell);
4028 					}
4029 				}
4030 				if (unit.Settlement != nullptr) {
4031 					if (unit.Type->BoolFlag[TOWNHALL_INDEX].value) {
4032 						temp->Settlement = unit.Settlement;
4033 						temp->Settlement->SiteUnit = temp;
4034 						Map.SiteUnits.erase(std::remove(Map.SiteUnits.begin(), Map.SiteUnits.end(), &unit), Map.SiteUnits.end());
4035 						Map.SiteUnits.push_back(temp);
4036 					}
4037 				}
4038 				if (type.GivesResource && unit.ResourcesHeld != 0) {
4039 					temp->SetResourcesHeld(unit.ResourcesHeld);
4040 					temp->Variable[GIVERESOURCE_INDEX].Value = unit.Variable[GIVERESOURCE_INDEX].Value;
4041 					temp->Variable[GIVERESOURCE_INDEX].Max = unit.Variable[GIVERESOURCE_INDEX].Max;
4042 					temp->Variable[GIVERESOURCE_INDEX].Enable = unit.Variable[GIVERESOURCE_INDEX].Enable;
4043 				}
4044 				//Wyrmgus end
4045 			}
4046 		//Wyrmgus start
4047 		} else if (unit.Settlement && unit.Settlement->SiteUnit == &unit) {
4048 			unit.Settlement->SiteUnit = nullptr;
4049 		//Wyrmgus end
4050 		}
4051 	}
4052 }
4053 
4054 /**
4055 **  Removes all orders from a unit.
4056 **
4057 **  @param unit  The unit that will have all its orders cleared
4058 */
UnitClearOrders(CUnit & unit)4059 void UnitClearOrders(CUnit &unit)
4060 {
4061 	for (size_t i = 0; i != unit.Orders.size(); ++i) {
4062 		delete unit.Orders[i];
4063 	}
4064 	unit.Orders.clear();
4065 	unit.Orders.push_back(COrder::NewActionStill());
4066 }
4067 
4068 /**
4069 **  Update for new unit. Food and income ...
4070 **
4071 **  @param unit     New unit pointer.
4072 **  @param upgrade  True unit was upgraded.
4073 */
UpdateForNewUnit(const CUnit & unit,int upgrade)4074 void UpdateForNewUnit(const CUnit &unit, int upgrade)
4075 {
4076 	const CUnitType &type = *unit.Type;
4077 	CPlayer &player = *unit.Player;
4078 
4079 	// Handle unit supply and max resources.
4080 	// Note an upgraded unit can't give more supply.
4081 	if (!upgrade) {
4082 		player.Supply += unit.Variable[SUPPLY_INDEX].Value;
4083 		for (int i = 0; i < MaxCosts; ++i) {
4084 			if (player.MaxResources[i] != -1 && type.Stats[player.Index].Storing[i]) {
4085 				player.MaxResources[i] += type.Stats[player.Index].Storing[i];
4086 			}
4087 		}
4088 	}
4089 
4090 	// Update resources
4091 	for (int u = 1; u < MaxCosts; ++u) {
4092 		player.Incomes[u] = std::max(player.Incomes[u], type.Stats[player.Index].ImproveIncomes[u]);
4093 	}
4094 
4095 	if (type.Stats[player.Index].Variables[TRADECOST_INDEX].Enable) {
4096 		player.TradeCost = std::min(player.TradeCost, type.Stats[player.Index].Variables[TRADECOST_INDEX].Value);
4097 	}
4098 
4099 	//Wyrmgus start
4100 	if (player.LostTownHallTimer != 0 && type.BoolFlag[TOWNHALL_INDEX].value && ThisPlayer->HasContactWith(player)) {
4101 		player.LostTownHallTimer = 0;
4102 		player.Revealed = false;
4103 		for (int j = 0; j < NumPlayers; ++j) {
4104 			if (player.Index != j && Players[j].Type != PlayerNobody) {
4105 				Players[j].Notify(_("%s has rebuilt a town hall, and will no longer be revealed!"), player.Name.c_str());
4106 			} else {
4107 				Players[j].Notify("%s", _("You have rebuilt a town hall, and will no longer be revealed!"));
4108 			}
4109 		}
4110 	}
4111 	//Wyrmgus end
4112 }
4113 
4114 /**
4115 **  Find nearest point of unit.
4116 **
4117 **  @param unit  Pointer to unit.
4118 **  @param pos   tile map position.
4119 **  @param dpos  Out: nearest point tile map position to (tx,ty).
4120 */
NearestOfUnit(const CUnit & unit,const Vec2i & pos,Vec2i * dpos)4121 void NearestOfUnit(const CUnit &unit, const Vec2i &pos, Vec2i *dpos)
4122 {
4123 	const int x = unit.tilePos.x;
4124 	const int y = unit.tilePos.y;
4125 
4126 	*dpos = pos;
4127 	clamp<short int>(&dpos->x, x, x + unit.Type->TileSize.x - 1);
4128 	clamp<short int>(&dpos->y, y, y + unit.Type->TileSize.y - 1);
4129 }
4130 
4131 /**
4132 **  Copy the unit look in Seen variables. This should be called when
4133 **  buildings go under fog of war for ThisPlayer.
4134 **
4135 **  @param unit  The unit to work on
4136 */
UnitFillSeenValues(CUnit & unit)4137 static void UnitFillSeenValues(CUnit &unit)
4138 {
4139 	// Seen values are undefined for visible units.
4140 	unit.Seen.tilePos = unit.tilePos;
4141 	unit.Seen.IY = unit.IY;
4142 	unit.Seen.IX = unit.IX;
4143 	unit.Seen.Frame = unit.Frame;
4144 	unit.Seen.Type = unit.Type;
4145 	unit.Seen.UnderConstruction = unit.UnderConstruction;
4146 
4147 	unit.CurrentOrder()->FillSeenValues(unit);
4148 }
4149 
4150 // Wall unit positions
4151 enum {
4152 	W_NORTH = 0x10,
4153 	W_WEST = 0x20,
4154 	W_SOUTH = 0x40,
4155 	W_EAST = 0x80
4156 };
4157 
4158 /**
4159 **  Correct direction for placed wall.
4160 **
4161 **  @param unit    The wall unit.
4162 */
CorrectWallDirections(CUnit & unit)4163 void CorrectWallDirections(CUnit &unit)
4164 {
4165 	Assert(unit.Type->BoolFlag[WALL_INDEX].value);
4166 	Assert(unit.Type->NumDirections == 16);
4167 	Assert(!unit.Type->Flip);
4168 
4169 	if (!Map.Info.IsPointOnMap(unit.tilePos, unit.MapLayer)) {
4170 		return;
4171 	}
4172 	const struct {
4173 		Vec2i offset;
4174 		const int dirFlag;
4175 	} configs[] = {{Vec2i(0, -1), W_NORTH}, {Vec2i(1, 0), W_EAST},
4176 		{Vec2i(0, 1), W_SOUTH}, {Vec2i(-1, 0), W_WEST}
4177 	};
4178 	int flags = 0;
4179 
4180 	for (int i = 0; i != sizeof(configs) / sizeof(*configs); ++i) {
4181 		const Vec2i pos = unit.tilePos + configs[i].offset;
4182 		const int dirFlag = configs[i].dirFlag;
4183 
4184 		if (Map.Info.IsPointOnMap(pos, unit.MapLayer) == false) {
4185 			flags |= dirFlag;
4186 		} else {
4187 			const CUnitCache &unitCache = Map.Field(pos, unit.MapLayer->ID)->UnitCache;
4188 			const CUnit *neighbor = unitCache.find(HasSamePlayerAndTypeAs(unit));
4189 
4190 			if (neighbor != nullptr) {
4191 				flags |= dirFlag;
4192 			}
4193 		}
4194 	}
4195 	unit.Direction = flags;
4196 }
4197 
4198 /**
4199 ** Correct the surrounding walls.
4200 **
4201 ** @param unit The wall unit.
4202 */
CorrectWallNeighBours(CUnit & unit)4203 void CorrectWallNeighBours(CUnit &unit)
4204 {
4205 	Assert(unit.Type->BoolFlag[WALL_INDEX].value);
4206 
4207 	const Vec2i offset[] = {Vec2i(1, 0), Vec2i(-1, 0), Vec2i(0, 1), Vec2i(0, -1)};
4208 
4209 	for (unsigned int i = 0; i < sizeof(offset) / sizeof(*offset); ++i) {
4210 		const Vec2i pos = unit.tilePos + offset[i];
4211 
4212 		if (Map.Info.IsPointOnMap(pos, unit.MapLayer) == false) {
4213 			continue;
4214 		}
4215 		CUnitCache &unitCache = unit.MapLayer->Field(pos)->UnitCache;
4216 		CUnit *neighbor = unitCache.find(HasSamePlayerAndTypeAs(unit));
4217 
4218 		if (neighbor != nullptr) {
4219 			CorrectWallDirections(*neighbor);
4220 			UnitUpdateHeading(*neighbor);
4221 		}
4222 	}
4223 }
4224 
4225 /**
4226 **  This function should get called when a unit goes under fog of war.
4227 **
4228 **  @param unit    The unit that goes under fog.
4229 **  @param player  The player the unit goes out of fog for.
4230 */
UnitGoesUnderFog(CUnit & unit,const CPlayer & player)4231 void UnitGoesUnderFog(CUnit &unit, const CPlayer &player)
4232 {
4233 	if (unit.Type->BoolFlag[VISIBLEUNDERFOG_INDEX].value) {
4234 		if (player.Type == PlayerPerson && !unit.Destroyed) {
4235 			unit.RefsIncrease();
4236 		}
4237 		//
4238 		// Icky yucky icky Seen.Destroyed trickery.
4239 		// We track for each player if he's seen the unit as destroyed.
4240 		// Remember, a unit is marked Destroyed when it's gone as in
4241 		// completely gone, the corpses vanishes. In that case the unit
4242 		// only survives since some players did NOT see the unit destroyed.
4243 		// Keeping trackof that is hard, mostly due to complex shared vision
4244 		// configurations.
4245 		// A unit does NOT get a reference when it goes under fog if it's
4246 		// Destroyed. Furthermore, it shouldn't lose a reference if it was
4247 		// Seen destroyed. That only happened with complex shared vision, and
4248 		// it's sort of the whole point of this tracking.
4249 		//
4250 		if (unit.Destroyed) {
4251 			unit.Seen.Destroyed |= (1 << player.Index);
4252 		}
4253 		if (&player == ThisPlayer) {
4254 			UnitFillSeenValues(unit);
4255 		}
4256 	}
4257 }
4258 
4259 /**
4260 **  This function should get called when a unit goes out of fog of war.
4261 **
4262 **  @param unit    The unit that goes out of fog.
4263 **  @param player  The player the unit goes out of fog for.
4264 **
4265 **  @note For units that are visible under fog (mostly buildings)
4266 **  we use reference counts, from the players that know about
4267 **  the building. When an building goes under fog it gets a refs
4268 **  increase, and when it shows up it gets a decrease. It must
4269 **  not get an decrease the first time it's seen, so we have to
4270 **  keep track of what player saw what units, with SeenByPlayer.
4271 */
UnitGoesOutOfFog(CUnit & unit,const CPlayer & player)4272 void UnitGoesOutOfFog(CUnit &unit, const CPlayer &player)
4273 {
4274 	if (!unit.Type->BoolFlag[VISIBLEUNDERFOG_INDEX].value) {
4275 		return;
4276 	}
4277 	if (unit.Seen.ByPlayer & (1 << (player.Index))) {
4278 		if ((player.Type == PlayerPerson) && (!(unit.Seen.Destroyed & (1 << player.Index)))) {
4279 			unit.RefsDecrease();
4280 		}
4281 	} else {
4282 		unit.Seen.ByPlayer |= (1 << (player.Index));
4283 	}
4284 }
4285 
4286 /**
4287 **  Recalculates a units visiblity count. This happens really often,
4288 **  Like every time a unit moves. It's really fast though, since we
4289 **  have per-tile counts.
4290 **
4291 **  @param unit  pointer to the unit to check if seen
4292 */
UnitCountSeen(CUnit & unit)4293 void UnitCountSeen(CUnit &unit)
4294 {
4295 	Assert(unit.Type);
4296 
4297 	// FIXME: optimize, only work on certain players?
4298 	// This is for instance good for updating shared vision...
4299 
4300 	//  Store old values in oldv[p]. This store if the player could see the
4301 	//  unit before this calc.
4302 	int oldv[PlayerMax];
4303 	for (int p = 0; p < PlayerMax; ++p) {
4304 		if (Players[p].Type != PlayerNobody) {
4305 			oldv[p] = unit.IsVisible(Players[p]);
4306 		}
4307 	}
4308 
4309 	//  Calculate new VisCount values.
4310 	const int height = unit.Type->TileSize.y;
4311 	const int width = unit.Type->TileSize.x;
4312 
4313 	for (int p = 0; p < PlayerMax; ++p) {
4314 		if (Players[p].Type != PlayerNobody) {
4315 			int newv = 0;
4316 			int y = height;
4317 			unsigned int index = unit.Offset;
4318 			do {
4319 				CMapField *mf = unit.MapLayer->Field(index);
4320 				int x = width;
4321 				do {
4322 					if (unit.Type->BoolFlag[PERMANENTCLOAK_INDEX].value && unit.Player != &Players[p]) {
4323 						if (mf->playerInfo.VisCloak[p]) {
4324 							newv++;
4325 						}
4326 					//Wyrmgus start
4327 					} else if (unit.Type->BoolFlag[ETHEREAL_INDEX].value && unit.Player != &Players[p]) {
4328 						if (mf->playerInfo.VisEthereal[p]) {
4329 							newv++;
4330 						}
4331 					//Wyrmgus end
4332 					} else {
4333 						if (mf->playerInfo.IsVisible(Players[p])) {
4334 							newv++;
4335 						}
4336 					}
4337 					++mf;
4338 				} while (--x);
4339 				index += unit.MapLayer->GetWidth();
4340 			} while (--y);
4341 			unit.VisCount[p] = newv;
4342 		}
4343 	}
4344 
4345 	//
4346 	// Now here comes the tricky part. We have to go in and out of fog
4347 	// for players. Hopefully this works with shared vision just great.
4348 	//
4349 	for (int p = 0; p < PlayerMax; ++p) {
4350 		if (Players[p].Type != PlayerNobody) {
4351 			int newv = unit.IsVisible(Players[p]);
4352 			if (!oldv[p] && newv) {
4353 				// Might have revealed a destroyed unit which caused it to
4354 				// be released
4355 				if (!unit.Type) {
4356 					break;
4357 				}
4358 				UnitGoesOutOfFog(unit, Players[p]);
4359 			}
4360 			if (oldv[p] && !newv) {
4361 				UnitGoesUnderFog(unit, Players[p]);
4362 			}
4363 		}
4364 	}
4365 }
4366 
4367 /**
4368 **  Returns true, if the unit is visible. It check the Viscount of
4369 **  the player and everyone who shares vision with him.
4370 **
4371 **  @note This understands shared vision, and should be used all around.
4372 **
4373 **  @param player  The player to check.
4374 */
IsVisible(const CPlayer & player) const4375 bool CUnit::IsVisible(const CPlayer &player) const
4376 {
4377 	if (VisCount[player.Index]) {
4378 		return true;
4379 	}
4380 	for (int p = 0; p < PlayerMax; ++p) {
4381 		//Wyrmgus start
4382 //		if (p != player.Index && player.IsBothSharedVision(Players[p])) {
4383 		if (p != player.Index && (player.IsBothSharedVision(Players[p]) || Players[p].Revealed)) {
4384 		//Wyrmgus end
4385 			if (VisCount[p]) {
4386 				return true;
4387 			}
4388 		}
4389 	}
4390 	return false;
4391 }
4392 
4393 /**
4394 **  Returns true, if unit is shown on minimap.
4395 **
4396 **  @warning This is for ::ThisPlayer only.
4397 **  @todo radar support
4398 **
4399 **  @return      True if visible, false otherwise.
4400 */
IsVisibleOnMinimap() const4401 bool CUnit::IsVisibleOnMinimap() const
4402 {
4403 	//Wyrmgus start
4404 	if (this->MapLayer != UI.CurrentMapLayer) {
4405 		return false;
4406 	}
4407 	//Wyrmgus end
4408 
4409 	// Invisible units.
4410 	if (IsInvisibile(*ThisPlayer)) {
4411 		return false;
4412 	}
4413 	if (IsVisible(*ThisPlayer) || ReplayRevealMap || IsVisibleOnRadar(*ThisPlayer)) {
4414 		return IsAliveOnMap();
4415 	} else {
4416 		return Type->BoolFlag[VISIBLEUNDERFOG_INDEX].value && Seen.State != 3
4417 			   && (Seen.ByPlayer & (1 << ThisPlayer->Index))
4418 			   //Wyrmgus start
4419 //			   && !(Seen.Destroyed & (1 << ThisPlayer->Index));
4420 			   && !(Seen.Destroyed & (1 << ThisPlayer->Index))
4421 			   && !Destroyed
4422 			   && Map.Info.IsPointOnMap(this->tilePos, this->MapLayer)
4423 			   && this->MapLayer->Field(this->tilePos)->playerInfo.IsTeamExplored(*ThisPlayer);
4424 			   //Wyrmgus end
4425 	}
4426 }
4427 
4428 /**
4429 **  Returns true, if unit is visible in viewport.
4430 **
4431 **  @warning This is only true for ::ThisPlayer
4432 **
4433 **  @param vp  Viewport pointer.
4434 **
4435 **  @return    True if visible, false otherwise.
4436 */
IsVisibleInViewport(const CViewport & vp) const4437 bool CUnit::IsVisibleInViewport(const CViewport &vp) const
4438 {
4439 	// Check if the graphic is inside the viewport.
4440 	//Wyrmgus start
4441 //	int x = tilePos.x * Map.GetMapLayerPixelTileSize(this->MapLayer).x + IX - (Type->Width - Type->TileSize.x * Map.GetMapLayerPixelTileSize(this->MapLayer).x) / 2 + Type->OffsetX;
4442 //	int y = tilePos.y * Map.GetMapLayerPixelTileSize(this->MapLayer).y + IY - (Type->Height - Type->TileSize.y * Map.GetMapLayerPixelTileSize(this->MapLayer).y) / 2 + Type->OffsetY;
4443 
4444 	int frame_width = Type->Width;
4445 	int frame_height = Type->Height;
4446 	const CUnitTypeVariation *variation = this->GetVariation();
4447 	if (variation && variation->FrameWidth && variation->FrameHeight) {
4448 		frame_width = variation->FrameWidth;
4449 		frame_height = variation->FrameHeight;
4450 	}
4451 
4452 	int x = tilePos.x * Map.GetMapLayerPixelTileSize(this->MapLayer->ID).x + IX - (frame_width - Type->TileSize.x * Map.GetMapLayerPixelTileSize(this->MapLayer->ID).x) / 2 + Type->OffsetX;
4453 	int y = tilePos.y * Map.GetMapLayerPixelTileSize(this->MapLayer->ID).y + IY - (frame_height - Type->TileSize.y * Map.GetMapLayerPixelTileSize(this->MapLayer->ID).y) / 2 + Type->OffsetY;
4454 	//Wyrmgus end
4455 	const PixelSize vpSize = vp.GetPixelSize();
4456 	const PixelPos vpTopLeftMapPos = Map.TilePosToMapPixelPos_TopLeft(vp.MapPos, UI.CurrentMapLayer) + vp.Offset;
4457 	const PixelPos vpBottomRightMapPos = vpTopLeftMapPos + vpSize;
4458 
4459 	//Wyrmgus start
4460 //	if (x + Type->Width < vpTopLeftMapPos.x || x > vpBottomRightMapPos.x
4461 //		|| y + Type->Height < vpTopLeftMapPos.y || y > vpBottomRightMapPos.y) {
4462 	if (x + frame_width < vpTopLeftMapPos.x || x > vpBottomRightMapPos.x
4463 		|| y + frame_height < vpTopLeftMapPos.y || y > vpBottomRightMapPos.y) {
4464 	//Wyrmgus end
4465 		return false;
4466 	}
4467 
4468 	if (!ThisPlayer) {
4469 		//FIXME: ARI: Added here for early game setup state by
4470 		// MakeAndPlaceUnit() from LoadMap(). ThisPlayer not yet set,
4471 		// so don't show anything until first real map-draw.
4472 		DebugPrint("FIXME: ThisPlayer not set yet?!\n");
4473 		return false;
4474 	}
4475 
4476 	// Those are never ever visible.
4477 	if (IsInvisibile(*ThisPlayer)) {
4478 		return false;
4479 	}
4480 
4481 	if (IsVisible(*ThisPlayer) || ReplayRevealMap) {
4482 		return !Destroyed;
4483 	} else {
4484 		// Unit has to be 'discovered'
4485 		// Destroyed units ARE visible under fog of war, if we haven't seen them like that.
4486 		if (!Destroyed || !(Seen.Destroyed & (1 << ThisPlayer->Index))) {
4487 			return (Type->BoolFlag[VISIBLEUNDERFOG_INDEX].value && (Seen.ByPlayer & (1 << ThisPlayer->Index)));
4488 		} else {
4489 			return false;
4490 		}
4491 	}
4492 }
4493 
4494 /**
4495 **  Change the unit's owner
4496 **
4497 **  @param newplayer  New owning player.
4498 */
4499 //Wyrmgus start
4500 //void CUnit::ChangeOwner(CPlayer &newplayer)
ChangeOwner(CPlayer & newplayer,bool show_change)4501 void CUnit::ChangeOwner(CPlayer &newplayer, bool show_change)
4502 //Wyrmgus end
4503 {
4504 	CPlayer *oldplayer = Player;
4505 
4506 	// This shouldn't happen
4507 	if (oldplayer == &newplayer) {
4508 		DebugPrint("Change the unit owner to the same player???\n");
4509 		return;
4510 	}
4511 
4512 	// Can't change owner for dead units
4513 	if (this->IsAlive() == false) {
4514 		return;
4515 	}
4516 
4517 	// Rescue all units in buildings/transporters.
4518 	//Wyrmgus start
4519 //	CUnit *uins = UnitInside;
4520 //	for (int i = InsideCount; i; --i, uins = uins->NextContained) {
4521 //		uins->ChangeOwner(newplayer);
4522 //	}
4523 
4524 	//only rescue units inside if the player is actually a rescuable player (to avoid, for example, unintended worker owner changes when a depot changes hands)
4525 	if (oldplayer->Type == PlayerRescueActive || oldplayer->Type == PlayerRescuePassive) {
4526 		CUnit *uins = UnitInside;
4527 		for (int i = InsideCount; i; --i, uins = uins->NextContained) {
4528 			uins->ChangeOwner(newplayer);
4529 		}
4530 	}
4531 	//Wyrmgus end
4532 
4533 	//  Must change food/gold and other.
4534 	UnitLost(*this);
4535 
4536 	//  Now the new side!
4537 
4538 	if (Type->BoolFlag[BUILDING_INDEX].value) {
4539 		//Wyrmgus start
4540 //		if (!Type->BoolFlag[WALL_INDEX].value) {
4541 		//Wyrmgus end
4542 			newplayer.TotalBuildings++;
4543 		//Wyrmgus start
4544 //		}
4545 		//Wyrmgus end
4546 	} else {
4547 		newplayer.TotalUnits++;
4548 	}
4549 
4550 	MapUnmarkUnitSight(*this);
4551 	newplayer.AddUnit(*this);
4552 	Stats = &Type->Stats[newplayer.Index];
4553 
4554 	//  Must change food/gold and other.
4555 	//Wyrmgus start
4556 //	if (Type->GivesResource) {
4557 	if (this->GivesResource) {
4558 	//Wyrmgus end
4559 		DebugPrint("Resource transfer not supported\n");
4560 	}
4561 	newplayer.Demand += Type->Stats[newplayer.Index].Variables[DEMAND_INDEX].Value;
4562 	newplayer.Supply += this->Variable[SUPPLY_INDEX].Value;
4563 	// Increase resource limit
4564 	for (int i = 0; i < MaxCosts; ++i) {
4565 		if (newplayer.MaxResources[i] != -1 && Type->Stats[newplayer.Index].Storing[i]) {
4566 			newplayer.MaxResources[i] += Type->Stats[newplayer.Index].Storing[i];
4567 		}
4568 	}
4569 	//Wyrmgus start
4570 //	if (Type->BoolFlag[BUILDING_INDEX].value && !Type->BoolFlag[WALL_INDEX].value) {
4571 	if (Type->BoolFlag[BUILDING_INDEX].value) {
4572 	//Wyrmgus end
4573 		newplayer.NumBuildings++;
4574 	}
4575 	//Wyrmgus start
4576 	if (CurrentAction() == UnitActionBuilt) {
4577 		newplayer.NumBuildingsUnderConstruction++;
4578 		newplayer.ChangeUnitTypeUnderConstructionCount(this->Type, 1);
4579 	}
4580 	//Wyrmgus end
4581 
4582 	//apply upgrades of the new player, if the old one doesn't have that upgrade
4583 	for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
4584 		const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
4585 		if (oldplayer->Allow.Upgrades[modifier_upgrade->ID] != 'R' && newplayer.Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X') { //if the old player doesn't have the modifier's upgrade (but the new one does), and the upgrade is applicable to the unit
4586 			//Wyrmgus start
4587 //			ApplyIndividualUpgradeModifier(*this, modifier);
4588 			if ( // don't apply equipment-related upgrades if the unit has an item of that equipment type equipped
4589 				(!modifier_upgrade->Weapon || EquippedItems[WeaponItemSlot].size() == 0)
4590 				&& (!modifier_upgrade->Shield || EquippedItems[ShieldItemSlot].size() == 0)
4591 				&& (!modifier_upgrade->Boots || EquippedItems[BootsItemSlot].size() == 0)
4592 				&& (!modifier_upgrade->Arrows || EquippedItems[ArrowsItemSlot].size() == 0)
4593 				&& !(newplayer.Race != -1 && modifier_upgrade->Ident == PlayerRaces.CivilizationUpgrades[newplayer.Race])
4594 				&& !(newplayer.Race != -1 && newplayer.Faction != -1 && modifier_upgrade->Ident == PlayerRaces.Factions[newplayer.Faction]->FactionUpgrade)
4595 			) {
4596 				ApplyIndividualUpgradeModifier(*this, modifier);
4597 			}
4598 			//Wyrmgus end
4599 		}
4600 	}
4601 
4602 	newplayer.IncreaseCountsForUnit(this);
4603 	UpdateForNewUnit(*this, 1);
4604 
4605 	UpdateUnitSightRange(*this);
4606 	MapMarkUnitSight(*this);
4607 
4608 	//not very elegant way to make sure the tile ownership is calculated correctly
4609 	MapUnmarkUnitSight(*this);
4610 	MapMarkUnitSight(*this);
4611 
4612 	//Wyrmgus start
4613 	if (newplayer.Index == ThisPlayer->Index && show_change) {
4614 		this->Blink = 5;
4615 		PlayGameSound(GameSounds.Rescue[newplayer.Race].Sound, MaxSampleVolume);
4616 	}
4617 	//Wyrmgus end
4618 }
4619 
IsMineAssignedBy(const CUnit & mine,const CUnit & worker)4620 static bool IsMineAssignedBy(const CUnit &mine, const CUnit &worker)
4621 {
4622 	for (CUnit *it = mine.Resource.Workers; it; it = it->NextWorker) {
4623 		if (it == &worker) {
4624 			return true;
4625 		}
4626 	}
4627 	return false;
4628 }
4629 
4630 
AssignWorkerToMine(CUnit & mine)4631 void CUnit::AssignWorkerToMine(CUnit &mine)
4632 {
4633 	if (IsMineAssignedBy(mine, *this) == true) {
4634 		return;
4635 	}
4636 	Assert(this->NextWorker == nullptr);
4637 
4638 	CUnit *head = mine.Resource.Workers;
4639 #if 0
4640 	DebugPrint("%d: Worker [%d] is adding into %s [%d] on %d pos\n"
4641 			   _C_ this->Player->Index _C_ this->Slot
4642 			   _C_ mine.Type->Name.c_str()
4643 			   _C_ mine.Slot
4644 			   _C_ mine.Data.Resource.Assigned);
4645 #endif
4646 	this->RefsIncrease();
4647 	this->NextWorker = head;
4648 	mine.Resource.Workers = this;
4649 	mine.Resource.Assigned++;
4650 }
4651 
DeAssignWorkerFromMine(CUnit & mine)4652 void CUnit::DeAssignWorkerFromMine(CUnit &mine)
4653 {
4654 	if (IsMineAssignedBy(mine, *this) == false) {
4655 		return ;
4656 	}
4657 	CUnit *prev = nullptr, *worker = mine.Resource.Workers;
4658 #if 0
4659 	DebugPrint("%d: Worker [%d] is removing from %s [%d] left %d units assigned\n"
4660 			   _C_ this->Player->Index _C_ this->Slot
4661 			   _C_ mine.Type->Name.c_str()
4662 			   _C_ mine.Slot
4663 			   _C_ mine.CurrentOrder()->Data.Resource.Assigned);
4664 #endif
4665 	for (int i = 0; nullptr != worker; worker = worker->NextWorker, ++i) {
4666 		if (worker == this) {
4667 			CUnit *next = worker->NextWorker;
4668 			worker->NextWorker = nullptr;
4669 			if (prev) {
4670 				prev->NextWorker = next;
4671 			}
4672 			if (worker == mine.Resource.Workers) {
4673 				mine.Resource.Workers = next;
4674 			}
4675 			worker->RefsDecrease();
4676 			break;
4677 		}
4678 		prev = worker;
4679 		Assert(i <= mine.Resource.Assigned);
4680 	}
4681 	mine.Resource.Assigned--;
4682 	Assert(mine.Resource.Assigned >= 0);
4683 }
4684 
4685 
4686 /**
4687 **  Change the owner of all units of a player.
4688 **
4689 **  @param oldplayer    Old owning player.
4690 **  @param newplayer    New owning player.
4691 */
ChangePlayerOwner(CPlayer & oldplayer,CPlayer & newplayer)4692 static void ChangePlayerOwner(CPlayer &oldplayer, CPlayer &newplayer)
4693 {
4694 	if (&oldplayer == &newplayer) {
4695 		return ;
4696 	}
4697 
4698 	for (int i = 0; i != oldplayer.GetUnitCount(); ++i) {
4699 		CUnit &unit = oldplayer.GetUnit(i);
4700 
4701 		unit.Blink = 5;
4702 		unit.RescuedFrom = &oldplayer;
4703 	}
4704 	// ChangeOwner remove unit from the player: so change the array.
4705 	while (oldplayer.GetUnitCount() != 0) {
4706 		CUnit &unit = oldplayer.GetUnit(0);
4707 
4708 		unit.ChangeOwner(newplayer);
4709 	}
4710 }
4711 
4712 /**
4713 **  Rescue units.
4714 **
4715 **  Look through all rescueable players, if they could be rescued.
4716 */
RescueUnits()4717 void RescueUnits()
4718 {
4719 	if (NoRescueCheck) {  // all possible units are rescued
4720 		return;
4721 	}
4722 	NoRescueCheck = true;
4723 
4724 	//  Look if player could be rescued.
4725 	for (CPlayer *p = Players; p < Players + NumPlayers; ++p) {
4726 		if (p->Type != PlayerRescuePassive && p->Type != PlayerRescueActive) {
4727 			continue;
4728 		}
4729 		if (p->GetUnitCount() != 0) {
4730 			NoRescueCheck = false;
4731 			// NOTE: table is changed.
4732 			std::vector<CUnit *> table;
4733 			table.insert(table.begin(), p->UnitBegin(), p->UnitEnd());
4734 
4735 			const size_t l = table.size();
4736 			for (size_t j = 0; j != l; ++j) {
4737 				CUnit &unit = *table[j];
4738 				// Do not rescue removed units. Units inside something are
4739 				// rescued by ChangeUnitOwner
4740 				if (unit.Removed) {
4741 					continue;
4742 				}
4743 				std::vector<CUnit *> around;
4744 
4745 				SelectAroundUnit(unit, 1, around);
4746 				//  Look if ally near the unit.
4747 				for (size_t i = 0; i != around.size(); ++i) {
4748 					//Wyrmgus start
4749 //					if (around[i]->Type->CanAttack && unit.IsAllied(*around[i]) && around[i]->Player->Type != PlayerRescuePassive && around[i]->Player->Type != PlayerRescueActive) {
4750 					if (around[i]->CanAttack() && unit.IsAllied(*around[i]) && around[i]->Player->Type != PlayerRescuePassive && around[i]->Player->Type != PlayerRescueActive) {
4751 					//Wyrmgus end
4752 						//  City center converts complete race
4753 						//  NOTE: I use a trick here, centers could
4754 						//        store gold. FIXME!!!
4755 						//Wyrmgus start
4756 //						if (unit.Type->CanStore[GoldCost]) {
4757 						if (unit.Type->BoolFlag[TOWNHALL_INDEX].value) {
4758 						//Wyrmgus end
4759 							ChangePlayerOwner(*p, *around[i]->Player);
4760 							break;
4761 						}
4762 						unit.RescuedFrom = unit.Player;
4763 						//Wyrmgus start
4764 //						unit.ChangeOwner(*around[i]->Player);
4765 						unit.ChangeOwner(*around[i]->Player, true);
4766 //						unit.Blink = 5;
4767 //						PlayGameSound(GameSounds.Rescue[unit.Player->Race].Sound, MaxSampleVolume);
4768 						//Wyrmgus end
4769 						break;
4770 					}
4771 				}
4772 			}
4773 		}
4774 	}
4775 }
4776 
4777 /*----------------------------------------------------------------------------
4778 --  Unit headings
4779 ----------------------------------------------------------------------------*/
4780 
4781 /**
4782 **  Fast arc tangent function.
4783 **
4784 **  @param val  atan argument
4785 **
4786 **  @return     atan(val)
4787 */
myatan(int val)4788 static int myatan(int val)
4789 {
4790 	static int init;
4791 	static unsigned char atan_table[2608];
4792 
4793 	if (val >= 2608) {
4794 		return 63;
4795 	}
4796 	if (!init) {
4797 		for (; init < 2608; ++init) {
4798 			atan_table[init] =
4799 				(unsigned char)(atan((double)init / 64) * (64 * 4 / 6.2831853));
4800 		}
4801 	}
4802 
4803 	return atan_table[val];
4804 }
4805 
4806 /**
4807 **  Convert direction to heading.
4808 **
4809 **  @param delta  Delta.
4810 **
4811 **  @return         Angle (0..255)
4812 */
DirectionToHeading(const Vec2i & delta)4813 int DirectionToHeading(const Vec2i &delta)
4814 {
4815 	//  Check which quadrant.
4816 	if (delta.x > 0) {
4817 		if (delta.y < 0) { // Quadrant 1?
4818 			return myatan((delta.x * 64) / -delta.y);
4819 		}
4820 		// Quadrant 2?
4821 		return myatan((delta.y * 64) / delta.x) + 64;
4822 	}
4823 	if (delta.y > 0) { // Quadrant 3?
4824 		return myatan((delta.x * -64) / delta.y) + 64 * 2;
4825 	}
4826 	if (delta.x) { // Quadrant 4.
4827 		return myatan((delta.y * -64) / -delta.x) + 64 * 3;
4828 	}
4829 	return 0;
4830 }
4831 
4832 /**
4833 **  Convert direction to heading.
4834 **
4835 **  @param delta  Delta.
4836 **
4837 **  @return         Angle (0..255)
4838 */
DirectionToHeading(const PixelDiff & delta)4839 int DirectionToHeading(const PixelDiff &delta)
4840 {
4841 	// code is identic for Vec2i and PixelDiff
4842 	Vec2i delta2(delta.x, delta.y);
4843 	return DirectionToHeading(delta2);
4844 }
4845 
4846 /**
4847 **  Update sprite frame for new heading.
4848 */
UnitUpdateHeading(CUnit & unit)4849 void UnitUpdateHeading(CUnit &unit)
4850 {
4851 	//Wyrmgus start
4852 	//fix direction if it does not correspond to one of the defined directions
4853 	int num_dir = std::max<int>(8, unit.Type->NumDirections);
4854 	if (unit.Direction % (256 / num_dir) != 0) {
4855 		unit.Direction = unit.Direction - (unit.Direction % (256 / num_dir));
4856 	}
4857 	//Wyrmgus end
4858 
4859 	int dir;
4860 	int nextdir;
4861 	bool neg;
4862 
4863 	if (unit.Frame < 0) {
4864 		unit.Frame = -unit.Frame - 1;
4865 		neg = true;
4866 	} else {
4867 		neg = false;
4868 	}
4869 	unit.Frame /= unit.Type->NumDirections / 2 + 1;
4870 	unit.Frame *= unit.Type->NumDirections / 2 + 1;
4871 	// Remove heading, keep animation frame
4872 
4873 	nextdir = 256 / unit.Type->NumDirections;
4874 	dir = ((unit.Direction + nextdir / 2) & 0xFF) / nextdir;
4875 	if (dir <= LookingS / nextdir) { // north->east->south
4876 		unit.Frame += dir;
4877 	} else {
4878 		unit.Frame += 256 / nextdir - dir;
4879 		unit.Frame = -unit.Frame - 1;
4880 	}
4881 	if (neg && !unit.Frame && unit.Type->BoolFlag[BUILDING_INDEX].value) {
4882 		unit.Frame = -1;
4883 	}
4884 }
4885 
4886 /**
4887 **  Change unit heading/frame from delta direction x, y.
4888 **
4889 **  @param unit  Unit for new direction looking.
4890 **  @param delta  map tile delta direction.
4891 */
UnitHeadingFromDeltaXY(CUnit & unit,const Vec2i & delta)4892 void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta)
4893 {
4894 	//Wyrmgus start
4895 //	unit.Direction = DirectionToHeading(delta);
4896 	int num_dir = std::max<int>(8, unit.Type->NumDirections);
4897 	int heading = DirectionToHeading(delta) + ((256 / num_dir) / 2);
4898 	if (heading % (256 / num_dir) != 0) {
4899 		heading = heading - (heading % (256 / num_dir));
4900 	}
4901 	unit.Direction = heading;
4902 	//Wyrmgus end
4903 	UnitUpdateHeading(unit);
4904 }
4905 
4906 /*----------------------------------------------------------------------------
4907   -- Drop out units
4908   ----------------------------------------------------------------------------*/
4909 
4910 /**
4911 **  Place a unit on the map to the side of a unit.
4912 **
4913 **  @param unit       Unit to drop out.
4914 **  @param heading    Direction in which the unit should appear.
4915 **  @param container  Unit "containing" unit to drop (may be different of unit.Container).
4916 */
DropOutOnSide(CUnit & unit,int heading,const CUnit * container)4917 void DropOutOnSide(CUnit &unit, int heading, const CUnit *container)
4918 {
4919 	Vec2i pos;
4920 	int addx = 0;
4921 	int addy = 0;
4922 	//Wyrmgus start
4923 	int z;
4924 	//Wyrmgus end
4925 
4926 	if (container) {
4927 		pos = container->tilePos;
4928 		pos -= unit.Type->TileSize - 1;
4929 		addx = container->Type->TileSize.x + unit.Type->TileSize.x - 1;
4930 		addy = container->Type->TileSize.y + unit.Type->TileSize.y - 1;
4931 		z = container->MapLayer->ID;
4932 
4933 		if (heading < LookingNE || heading > LookingNW) {
4934 			pos.x += addx - 1;
4935 			--pos.y;
4936 			goto startn;
4937 		} else if (heading < LookingSE) {
4938 			pos.x += addx;
4939 			pos.y += addy - 1;
4940 			goto starte;
4941 		} else if (heading < LookingSW) {
4942 			pos.y += addy;
4943 			goto starts;
4944 		} else {
4945 			--pos.x;
4946 			goto startw;
4947 		}
4948 	} else {
4949 		pos = unit.tilePos;
4950 		z = unit.MapLayer->ID;
4951 
4952 		if (heading < LookingNE || heading > LookingNW) {
4953 			goto starts;
4954 		} else if (heading < LookingSE) {
4955 			goto startw;
4956 		} else if (heading < LookingSW) {
4957 			goto startn;
4958 		} else {
4959 			goto starte;
4960 		}
4961 	}
4962 	// FIXME: don't search outside of the map
4963 	for (;;) {
4964 startw:
4965 		for (int i = addy; i--; ++pos.y) {
4966 			//Wyrmgus start
4967 //			if (UnitCanBeAt(unit, pos)) {
4968 			if (UnitCanBeAt(unit, pos, z)) {
4969 			//Wyrmgus end
4970 				goto found;
4971 			}
4972 		}
4973 		++addx;
4974 starts:
4975 		for (int i = addx; i--; ++pos.x) {
4976 			//Wyrmgus start
4977 //			if (UnitCanBeAt(unit, pos)) {
4978 			if (UnitCanBeAt(unit, pos, z)) {
4979 			//Wyrmgus end
4980 				goto found;
4981 			}
4982 		}
4983 		++addy;
4984 starte:
4985 		for (int i = addy; i--; --pos.y) {
4986 			//Wyrmgus start
4987 //			if (UnitCanBeAt(unit, pos)) {
4988 			if (UnitCanBeAt(unit, pos, z)) {
4989 			//Wyrmgus end
4990 				goto found;
4991 			}
4992 		}
4993 		++addx;
4994 startn:
4995 		for (int i = addx; i--; --pos.x) {
4996 			//Wyrmgus start
4997 //			if (UnitCanBeAt(unit, pos)) {
4998 			if (UnitCanBeAt(unit, pos, z)) {
4999 			//Wyrmgus end
5000 				goto found;
5001 			}
5002 		}
5003 		++addy;
5004 	}
5005 
5006 found:
5007 	//Wyrmgus start
5008 //	unit.Place(pos);
5009 	unit.Place(pos, z);
5010 	//Wyrmgus end
5011 }
5012 
5013 /**
5014 **  Place a unit on the map nearest to goalPos.
5015 **
5016 **  @param unit  Unit to drop out.
5017 **  @param goalPos Goal map tile position.
5018 **  @param addx  Tile width of unit it's dropping out of.
5019 **  @param addy  Tile height of unit it's dropping out of.
5020 */
DropOutNearest(CUnit & unit,const Vec2i & goalPos,const CUnit * container)5021 void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container)
5022 {
5023 	Vec2i pos;
5024 	Vec2i bestPos;
5025 	int bestd = 99999;
5026 	int addx = 0;
5027 	int addy = 0;
5028 	//Wyrmgus start
5029 	int z;
5030 	//Wyrmgus end
5031 
5032 	if (container) {
5033 		Assert(unit.Removed);
5034 		pos = container->tilePos;
5035 		pos -= unit.Type->TileSize - 1;
5036 		addx = container->Type->TileSize.x + unit.Type->TileSize.x - 1;
5037 		addy = container->Type->TileSize.y + unit.Type->TileSize.y - 1;
5038 		--pos.x;
5039 		z = container->MapLayer->ID;
5040 	} else {
5041 		pos = unit.tilePos;
5042 		z = unit.MapLayer->ID;
5043 	}
5044 	// FIXME: if we reach the map borders we can go fast up, left, ...
5045 
5046 	for (;;) {
5047 		for (int i = addy; i--; ++pos.y) { // go down
5048 			//Wyrmgus start
5049 //			if (UnitCanBeAt(unit, pos)) {
5050 			if (UnitCanBeAt(unit, pos, z)) {
5051 			//Wyrmgus end
5052 				const int n = SquareDistance(goalPos, pos);
5053 
5054 				if (n < bestd) {
5055 					bestd = n;
5056 					bestPos = pos;
5057 				}
5058 			}
5059 		}
5060 		++addx;
5061 		for (int i = addx; i--; ++pos.x) { // go right
5062 			//Wyrmgus start
5063 //			if (UnitCanBeAt(unit, pos)) {
5064 			if (UnitCanBeAt(unit, pos, z)) {
5065 			//Wyrmgus end
5066 				const int n = SquareDistance(goalPos, pos);
5067 
5068 				if (n < bestd) {
5069 					bestd = n;
5070 					bestPos = pos;
5071 				}
5072 			}
5073 		}
5074 		++addy;
5075 		for (int i = addy; i--; --pos.y) { // go up
5076 			//Wyrmgus start
5077 //			if (UnitCanBeAt(unit, pos)) {
5078 			if (UnitCanBeAt(unit, pos, z)) {
5079 			//Wyrmgus end
5080 				const int n = SquareDistance(goalPos, pos);
5081 
5082 				if (n < bestd) {
5083 					bestd = n;
5084 					bestPos = pos;
5085 				}
5086 			}
5087 		}
5088 		++addx;
5089 		for (int i = addx; i--; --pos.x) { // go left
5090 			//Wyrmgus start
5091 //			if (UnitCanBeAt(unit, pos)) {
5092 			if (UnitCanBeAt(unit, pos, z)) {
5093 			//Wyrmgus end
5094 				const int n = SquareDistance(goalPos, pos);
5095 
5096 				if (n < bestd) {
5097 					bestd = n;
5098 					bestPos = pos;
5099 				}
5100 			}
5101 		}
5102 		if (bestd != 99999) {
5103 			//Wyrmgus start
5104 //			unit.Place(bestPos);
5105 			unit.Place(bestPos, z);
5106 			//Wyrmgus end
5107 			return;
5108 		}
5109 		++addy;
5110 	}
5111 }
5112 
5113 /**
5114 **  Drop out all units inside unit.
5115 **
5116 **  @param source  All units inside source are dropped out.
5117 */
DropOutAll(const CUnit & source)5118 void DropOutAll(const CUnit &source)
5119 {
5120 	CUnit *unit = source.UnitInside;
5121 
5122 	for (int i = source.InsideCount; i; --i, unit = unit->NextContained) {
5123 		DropOutOnSide(*unit, LookingW, &source);
5124 	}
5125 
5126 	//Wyrmgus start
5127 	if (unit->Type->BoolFlag[ITEM_INDEX].value && !unit->Unique) { //save the initial cycle items were placed in the ground to destroy them if they have been there for too long
5128 		int ttl_cycles = (5 * 60 * CYCLES_PER_SECOND);
5129 		if (unit->Prefix != nullptr || unit->Suffix != nullptr || unit->Spell != nullptr || unit->Work != nullptr || unit->Elixir != nullptr) {
5130 			ttl_cycles *= 4;
5131 		}
5132 		unit->TTL = GameCycle + ttl_cycles;
5133 	}
5134 	//Wyrmgus end
5135 }
5136 
5137 /*----------------------------------------------------------------------------
5138   -- Select units
5139   ----------------------------------------------------------------------------*/
5140 
5141 /**
5142 **  Unit on map screen.
5143 **
5144 **  Select units on screen. (x, y are in pixels relative to map 0,0).
5145 **  Not GAMEPLAY safe, uses ReplayRevealMap
5146 **
5147 **  More units on same position.
5148 **    Cycle through units.
5149 **    First take highest unit.
5150 **
5151 **  @param x      X pixel position.
5152 **  @param y      Y pixel position.
5153 **
5154 **  @return       An unit on x, y position.
5155 */
UnitOnScreen(int x,int y)5156 CUnit *UnitOnScreen(int x, int y)
5157 {
5158 	CUnit *candidate = nullptr;
5159 	for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) {
5160 		CUnit &unit = **it;
5161 		if (unit.MapLayer != UI.CurrentMapLayer) {
5162 			continue;
5163 		}
5164 		if (!ReplayRevealMap && !unit.IsVisibleAsGoal(*ThisPlayer)) {
5165 			continue;
5166 		}
5167 		const CUnitType &type = *unit.Type;
5168 		if (!type.Sprite) {
5169 			continue;
5170 		}
5171 
5172 		//
5173 		// Check if mouse is over the unit.
5174 		//
5175 		PixelPos unitSpritePos = unit.GetMapPixelPosCenter();
5176 		//Wyrmgus start
5177 //		unitSpritePos.x = unitSpritePos.x - type.BoxWidth / 2 -
5178 //						  (type.Width - type.Sprite->Width) / 2 + type.BoxOffsetX;
5179 //		unitSpritePos.y = unitSpritePos.y - type.BoxHeight / 2 -
5180 //						  (type.Height - type.Sprite->Height) / 2 + type.BoxOffsetY;
5181 		const CUnitTypeVariation *variation = unit.GetVariation();
5182 		if (variation && variation->FrameWidth && variation->FrameHeight && !variation->File.empty()) {
5183 			unitSpritePos.x = unitSpritePos.x - type.BoxWidth / 2 -
5184 							  (variation->FrameWidth - variation->Sprite->Width) / 2 + type.BoxOffsetX;
5185 			unitSpritePos.y = unitSpritePos.y - type.BoxHeight / 2 -
5186 							  (variation->FrameHeight - variation->Sprite->Height) / 2 + type.BoxOffsetY;
5187 		} else {
5188 			unitSpritePos.x = unitSpritePos.x - type.BoxWidth / 2 -
5189 							  (type.Width - type.Sprite->Width) / 2 + type.BoxOffsetX;
5190 			unitSpritePos.y = unitSpritePos.y - type.BoxHeight / 2 -
5191 							  (type.Height - type.Sprite->Height) / 2 + type.BoxOffsetY;
5192 		}
5193 		//Wyrmgus end
5194 		if (x >= unitSpritePos.x && x < unitSpritePos.x + type.BoxWidth
5195 			&& y >= unitSpritePos.y  && y < unitSpritePos.y + type.BoxHeight) {
5196 			// Check if there are other units on this place
5197 			candidate = &unit;
5198 			//Wyrmgus start
5199 			std::vector<CUnit *> table;
5200 			Select(candidate->tilePos, candidate->tilePos, table, candidate->MapLayer->ID, HasNotSamePlayerAs(Players[PlayerNumNeutral]));
5201 //			if (IsOnlySelected(*candidate) || candidate->Type->BoolFlag[ISNOTSELECTABLE_INDEX].value) {
5202 			if (IsOnlySelected(*candidate) || candidate->Type->BoolFlag[ISNOTSELECTABLE_INDEX].value || (candidate->Player->Type == PlayerNeutral && table.size()) || !candidate->IsAlive()) { // don't select a neutral unit if there's a player-owned unit there as well; don't selected a dead unit
5203 			//Wyrmgus end
5204 				continue;
5205 			} else {
5206 				break;
5207 			}
5208 		} else {
5209 			continue;
5210 		}
5211 	}
5212 	return candidate;
5213 }
5214 
GetMapPixelPosTopLeft() const5215 PixelPos CUnit::GetMapPixelPosTopLeft() const
5216 {
5217 	const PixelPos pos(tilePos.x * Map.GetMapLayerPixelTileSize(this->MapLayer->ID).x + IX, tilePos.y * Map.GetMapLayerPixelTileSize(this->MapLayer->ID).y + IY);
5218 	return pos;
5219 }
5220 
GetMapPixelPosCenter() const5221 PixelPos CUnit::GetMapPixelPosCenter() const
5222 {
5223 	return GetMapPixelPosTopLeft() + this->GetHalfTilePixelSize();
5224 }
5225 
5226 //Wyrmgus start
GetTileSize() const5227 Vec2i CUnit::GetTileSize() const
5228 {
5229 	return this->Type->GetTileSize();
5230 }
5231 
GetHalfTileSize() const5232 Vec2i CUnit::GetHalfTileSize() const
5233 {
5234 	return this->GetTileSize() / 2;
5235 }
5236 
GetTilePixelSize() const5237 PixelSize CUnit::GetTilePixelSize() const
5238 {
5239 	return PixelSize(this->GetTileSize()) * Map.GetMapLayerPixelTileSize(this->MapLayer->ID);
5240 }
5241 
GetHalfTilePixelSize() const5242 PixelSize CUnit::GetHalfTilePixelSize() const
5243 {
5244 	return this->GetTilePixelSize() / 2;
5245 }
5246 
GetTileCenterPos() const5247 Vec2i CUnit::GetTileCenterPos() const
5248 {
5249 	return this->tilePos + this->Type->GetTileCenterPosOffset();
5250 }
5251 
SetIndividualUpgrade(const CUpgrade * upgrade,int quantity)5252 void CUnit::SetIndividualUpgrade(const CUpgrade *upgrade, int quantity)
5253 {
5254 	if (!upgrade) {
5255 		return;
5256 	}
5257 
5258 	if (quantity <= 0) {
5259 		if (this->IndividualUpgrades.find(upgrade->ID) != this->IndividualUpgrades.end()) {
5260 			this->IndividualUpgrades.erase(upgrade->ID);
5261 		}
5262 	} else {
5263 		this->IndividualUpgrades[upgrade->ID] = quantity;
5264 	}
5265 }
5266 
GetIndividualUpgrade(const CUpgrade * upgrade) const5267 int CUnit::GetIndividualUpgrade(const CUpgrade *upgrade) const
5268 {
5269 	if (upgrade && this->IndividualUpgrades.find(upgrade->ID) != this->IndividualUpgrades.end()) {
5270 		return this->IndividualUpgrades.find(upgrade->ID)->second;
5271 	} else {
5272 		return 0;
5273 	}
5274 }
5275 
GetAvailableLevelUpUpgrades(bool only_units) const5276 int CUnit::GetAvailableLevelUpUpgrades(bool only_units) const
5277 {
5278 	int value = 0;
5279 	int upgrade_value = 0;
5280 
5281 	if (((int) AiHelpers.ExperienceUpgrades.size()) > Type->Slot) {
5282 		for (size_t i = 0; i != AiHelpers.ExperienceUpgrades[Type->Slot].size(); ++i) {
5283 			if (this->Character == nullptr || std::find(this->Character->ForbiddenUpgrades.begin(), this->Character->ForbiddenUpgrades.end(), AiHelpers.ExperienceUpgrades[Type->Slot][i]) == this->Character->ForbiddenUpgrades.end()) {
5284 				int local_upgrade_value = 1;
5285 
5286 				if (!only_units) {
5287 					local_upgrade_value += AiHelpers.ExperienceUpgrades[Type->Slot][i]->GetAvailableLevelUpUpgrades();
5288 				}
5289 
5290 				if (local_upgrade_value > upgrade_value) {
5291 					upgrade_value = local_upgrade_value;
5292 				}
5293 			}
5294 		}
5295 	}
5296 
5297 	value += upgrade_value;
5298 
5299 	if (!only_units && ((int) AiHelpers.LearnableAbilities.size()) > Type->Slot) {
5300 		for (size_t i = 0; i != AiHelpers.LearnableAbilities[Type->Slot].size(); ++i) {
5301 			value += AiHelpers.LearnableAbilities[Type->Slot][i]->MaxLimit - this->GetIndividualUpgrade(AiHelpers.LearnableAbilities[Type->Slot][i]);
5302 		}
5303 	}
5304 
5305 	return value;
5306 }
5307 
GetModifiedVariable(int index,int variable_type) const5308 int CUnit::GetModifiedVariable(int index, int variable_type) const
5309 {
5310 	int value = 0;
5311 	if (variable_type == VariableValue) {
5312 		value = this->Variable[index].Value;
5313 	} else if (variable_type == VariableMax) {
5314 		value = this->Variable[index].Max;
5315 	} else if (variable_type == VariableIncrease) {
5316 		value = this->Variable[index].Increase;
5317 	}
5318 
5319 	if (index == ATTACKRANGE_INDEX) {
5320 		if (this->Container && this->Container->Variable[GARRISONEDRANGEBONUS_INDEX].Enable) {
5321 			value += this->Container->Variable[GARRISONEDRANGEBONUS_INDEX].Value; //treat the container's attack range as a bonus to the unit's attack range
5322 		}
5323 		std::min<int>(this->CurrentSightRange, value); // if the unit's current sight range is smaller than its attack range, use it instead
5324 	} else if (index == SPEED_INDEX) {
5325 		if (this->MapLayer && this->Type->UnitType != UnitTypeFly && this->Type->UnitType != UnitTypeFlyLow) {
5326 			value += DefaultTileMovementCost - this->MapLayer->Field(this->Offset)->getCost();
5327 		}
5328 	}
5329 
5330 	return value;
5331 }
5332 
GetReactionRange() const5333 int CUnit::GetReactionRange() const
5334 {
5335 	int reaction_range = this->CurrentSightRange;
5336 
5337 	if (this->Player->Type != PlayerPerson) {
5338 		reaction_range += 2;
5339 	}
5340 
5341 	return reaction_range;
5342 }
5343 
GetItemSlotQuantity(int item_slot) const5344 int CUnit::GetItemSlotQuantity(int item_slot) const
5345 {
5346 	if (!HasInventory()) {
5347 		return 0;
5348 	}
5349 
5350 	if ( //if the item are arrows and the weapon of this unit's type is not a bow, return false
5351 		item_slot == ArrowsItemSlot
5352 		&& Type->WeaponClasses[0] != BowItemClass
5353 	) {
5354 		return 0;
5355 	}
5356 
5357 	if (item_slot == RingItemSlot) {
5358 		return 2;
5359 	}
5360 
5361 	return 1;
5362 }
5363 
GetCurrentWeaponClass() const5364 int CUnit::GetCurrentWeaponClass() const
5365 {
5366 	if (HasInventory() && EquippedItems[WeaponItemSlot].size() > 0) {
5367 		return EquippedItems[WeaponItemSlot][0]->Type->ItemClass;
5368 	}
5369 
5370 	return Type->WeaponClasses[0];
5371 }
5372 
GetItemVariableChange(const CUnit * item,int variable_index,bool increase) const5373 int CUnit::GetItemVariableChange(const CUnit *item, int variable_index, bool increase) const
5374 {
5375 	if (item->Type->ItemClass == -1) {
5376 		return 0;
5377 	}
5378 
5379 	int item_slot = GetItemClassSlot(item->Type->ItemClass);
5380 	if (item->Work == nullptr && item->Elixir == nullptr && (item_slot == -1 || this->GetItemSlotQuantity(item_slot) == 0 || !this->CanEquipItemClass(item->Type->ItemClass))) {
5381 		return 0;
5382 	}
5383 
5384 	int value = 0;
5385 	if (item->Work != nullptr) {
5386 		if (this->GetIndividualUpgrade(item->Work) == 0) {
5387 			for (size_t z = 0; z < item->Work->UpgradeModifiers.size(); ++z) {
5388 				if (!increase) {
5389 					value += item->Work->UpgradeModifiers[z]->Modifier.Variables[variable_index].Value;
5390 				} else {
5391 					value += item->Work->UpgradeModifiers[z]->Modifier.Variables[variable_index].Increase;
5392 				}
5393 			}
5394 		}
5395 	} else if (item->Elixir != nullptr) {
5396 		if (this->GetIndividualUpgrade(item->Elixir) == 0) {
5397 			for (size_t z = 0; z < item->Elixir->UpgradeModifiers.size(); ++z) {
5398 				if (!increase) {
5399 					value += item->Elixir->UpgradeModifiers[z]->Modifier.Variables[variable_index].Value;
5400 				} else {
5401 					value += item->Elixir->UpgradeModifiers[z]->Modifier.Variables[variable_index].Increase;
5402 				}
5403 			}
5404 		}
5405 	} else {
5406 		if (!increase) {
5407 			value = item->Variable[variable_index].Value;
5408 		} else {
5409 			value = item->Variable[variable_index].Increase;
5410 		}
5411 
5412 		if (!item->Identified) { //if the item is unidentified, don't show the effects of its affixes
5413 			for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
5414 				if (
5415 					(item->Prefix != nullptr && modifier->UpgradeId == item->Prefix->ID)
5416 					|| (item->Suffix != nullptr && modifier->UpgradeId == item->Suffix->ID)
5417 				) {
5418 					if (!increase) {
5419 						value -= modifier->Modifier.Variables[variable_index].Value;
5420 					} else {
5421 						value -= modifier->Modifier.Variables[variable_index].Increase;
5422 					}
5423 				}
5424 			}
5425 		}
5426 
5427 		if (item->Unique && item->Unique->Set) {
5428 			if (this->EquippingItemCompletesSet(item)) {
5429 				for (size_t z = 0; z < item->Unique->Set->UpgradeModifiers.size(); ++z) {
5430 					if (!increase) {
5431 						value += item->Unique->Set->UpgradeModifiers[z]->Modifier.Variables[variable_index].Value;
5432 					} else {
5433 						value += item->Unique->Set->UpgradeModifiers[z]->Modifier.Variables[variable_index].Increase;
5434 					}
5435 				}
5436 			}
5437 		}
5438 
5439 		if (EquippedItems[item_slot].size() == this->GetItemSlotQuantity(item_slot)) {
5440 			int item_slot_used = EquippedItems[item_slot].size() - 1;
5441 			for (size_t i = 0; i < EquippedItems[item_slot].size(); ++i) {
5442 				if (EquippedItems[item_slot][i] == item) {
5443 					item_slot_used = i;
5444 				}
5445 			}
5446 			if (!increase) {
5447 				value -= EquippedItems[item_slot][item_slot_used]->Variable[variable_index].Value;
5448 			} else {
5449 				value -= EquippedItems[item_slot][item_slot_used]->Variable[variable_index].Increase;
5450 			}
5451 			if (EquippedItems[item_slot][item_slot_used] != item && EquippedItems[item_slot][item_slot_used]->Unique && EquippedItems[item_slot][item_slot_used]->Unique->Set) {
5452 				if (this->DeequippingItemBreaksSet(EquippedItems[item_slot][item_slot_used])) {
5453 					for (size_t z = 0; z < EquippedItems[item_slot][item_slot_used]->Unique->Set->UpgradeModifiers.size(); ++z) {
5454 						if (!increase) {
5455 							value -= EquippedItems[item_slot][item_slot_used]->Unique->Set->UpgradeModifiers[z]->Modifier.Variables[variable_index].Value;
5456 						} else {
5457 							value -= EquippedItems[item_slot][item_slot_used]->Unique->Set->UpgradeModifiers[z]->Modifier.Variables[variable_index].Increase;
5458 						}
5459 					}
5460 				}
5461 			}
5462 		} else if (EquippedItems[item_slot].size() == 0 && (item_slot == WeaponItemSlot || item_slot == ShieldItemSlot || item_slot == BootsItemSlot || item_slot == ArrowsItemSlot)) {
5463 			for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
5464 				const CUpgrade *modifier_upgrade = AllUpgrades[modifier->UpgradeId];
5465 				if (
5466 					(
5467 						(
5468 							(modifier_upgrade->Weapon && item_slot == WeaponItemSlot)
5469 							|| (modifier_upgrade->Shield && item_slot == ShieldItemSlot)
5470 							|| (modifier_upgrade->Boots && item_slot == BootsItemSlot)
5471 							|| (modifier_upgrade->Arrows && item_slot == ArrowsItemSlot)
5472 						)
5473 						&& Player->Allow.Upgrades[modifier_upgrade->ID] == 'R' && modifier->ApplyTo[Type->Slot] == 'X'
5474 					)
5475 					|| (item_slot == WeaponItemSlot && modifier_upgrade->Ability && this->GetIndividualUpgrade(modifier_upgrade) && modifier_upgrade->WeaponClasses.size() > 0 && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), this->GetCurrentWeaponClass()) != modifier_upgrade->WeaponClasses.end() && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), item->Type->ItemClass) == modifier_upgrade->WeaponClasses.end())
5476 				) {
5477 					if (this->GetIndividualUpgrade(modifier_upgrade)) {
5478 						for (int i = 0; i < this->GetIndividualUpgrade(modifier_upgrade); ++i) {
5479 							if (!increase) {
5480 								value -= modifier->Modifier.Variables[variable_index].Value;
5481 							} else {
5482 								value -= modifier->Modifier.Variables[variable_index].Increase;
5483 							}
5484 						}
5485 					} else {
5486 						if (!increase) {
5487 							value -= modifier->Modifier.Variables[variable_index].Value;
5488 						} else {
5489 							value -= modifier->Modifier.Variables[variable_index].Increase;
5490 						}
5491 					}
5492 				} else if (
5493 					modifier_upgrade->Ability && this->GetIndividualUpgrade(modifier_upgrade) && modifier_upgrade->WeaponClasses.size() > 0 && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), this->GetCurrentWeaponClass()) == modifier_upgrade->WeaponClasses.end() && std::find(modifier_upgrade->WeaponClasses.begin(), modifier_upgrade->WeaponClasses.end(), item->Type->ItemClass) != modifier_upgrade->WeaponClasses.end()
5494 				) {
5495 					if (this->GetIndividualUpgrade(modifier_upgrade)) {
5496 						for (int i = 0; i < this->GetIndividualUpgrade(modifier_upgrade); ++i) {
5497 							if (!increase) {
5498 								value += modifier->Modifier.Variables[variable_index].Value;
5499 							} else {
5500 								value += modifier->Modifier.Variables[variable_index].Increase;
5501 							}
5502 						}
5503 					} else {
5504 						if (!increase) {
5505 							value += modifier->Modifier.Variables[variable_index].Value;
5506 						} else {
5507 							value += modifier->Modifier.Variables[variable_index].Increase;
5508 						}
5509 					}
5510 				}
5511 			}
5512 		}
5513 	}
5514 
5515 	return value;
5516 }
5517 
GetDisplayPlayer() const5518 int CUnit::GetDisplayPlayer() const
5519 {
5520 	if (this->Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value && this->Player != ThisPlayer) {
5521 		return PlayerNumNeutral;
5522 	} else {
5523 		return this->RescuedFrom ? this->RescuedFrom->Index : this->Player->Index;
5524 	}
5525 }
5526 
GetPrice() const5527 int CUnit::GetPrice() const
5528 {
5529 	int cost = this->Type->Stats[this->Player->Index].GetPrice();
5530 
5531 	if (this->Prefix != nullptr) {
5532 		cost += this->Prefix->MagicLevel * 1000;
5533 	}
5534 	if (this->Suffix != nullptr) {
5535 		cost += this->Suffix->MagicLevel * 1000;
5536 	}
5537 	if (this->Spell != nullptr) {
5538 		cost += 1000;
5539 	}
5540 	if (this->Work != nullptr) {
5541 		if (this->Type->ItemClass == BookItemClass) {
5542 			cost += 5000;
5543 		} else {
5544 			cost += 1000;
5545 		}
5546 	}
5547 	if (this->Elixir != nullptr) {
5548 		cost += this->Elixir->MagicLevel * 1000;
5549 	}
5550 	if (this->Character) {
5551 		cost += (this->Variable[LEVEL_INDEX].Value - this->Type->Stats[this->Player->Index].Variables[LEVEL_INDEX].Value) * 250;
5552 	}
5553 
5554 	return cost;
5555 }
5556 
GetUnitStock(CUnitType * unit_type) const5557 int CUnit::GetUnitStock(CUnitType *unit_type) const
5558 {
5559 	if (unit_type && this->UnitStock.find(unit_type) != this->UnitStock.end()) {
5560 		return this->UnitStock.find(unit_type)->second;
5561 	} else {
5562 		return 0;
5563 	}
5564 }
5565 
SetUnitStock(CUnitType * unit_type,int quantity)5566 void CUnit::SetUnitStock(CUnitType *unit_type, int quantity)
5567 {
5568 	if (!unit_type) {
5569 		return;
5570 	}
5571 
5572 	if (quantity <= 0) {
5573 		if (this->UnitStock.find(unit_type) != this->UnitStock.end()) {
5574 			this->UnitStock.erase(unit_type);
5575 		}
5576 	} else {
5577 		this->UnitStock[unit_type] = quantity;
5578 	}
5579 }
5580 
ChangeUnitStock(CUnitType * unit_type,int quantity)5581 void CUnit::ChangeUnitStock(CUnitType *unit_type, int quantity)
5582 {
5583 	this->SetUnitStock(unit_type, this->GetUnitStock(unit_type) + quantity);
5584 }
5585 
GetUnitStockReplenishmentTimer(CUnitType * unit_type) const5586 int CUnit::GetUnitStockReplenishmentTimer(CUnitType *unit_type) const
5587 {
5588 	if (this->UnitStockReplenishmentTimers.find(unit_type) != this->UnitStockReplenishmentTimers.end()) {
5589 		return this->UnitStockReplenishmentTimers.find(unit_type)->second;
5590 	} else {
5591 		return 0;
5592 	}
5593 }
5594 
SetUnitStockReplenishmentTimer(CUnitType * unit_type,int quantity)5595 void CUnit::SetUnitStockReplenishmentTimer(CUnitType *unit_type, int quantity)
5596 {
5597 	if (!unit_type) {
5598 		return;
5599 	}
5600 
5601 	if (quantity <= 0) {
5602 		if (this->UnitStockReplenishmentTimers.find(unit_type) != this->UnitStockReplenishmentTimers.end()) {
5603 			this->UnitStockReplenishmentTimers.erase(unit_type);
5604 		}
5605 	} else {
5606 		this->UnitStockReplenishmentTimers[unit_type] = quantity;
5607 	}
5608 }
5609 
ChangeUnitStockReplenishmentTimer(CUnitType * unit_type,int quantity)5610 void CUnit::ChangeUnitStockReplenishmentTimer(CUnitType *unit_type, int quantity)
5611 {
5612 	this->SetUnitStockReplenishmentTimer(unit_type, this->GetUnitStockReplenishmentTimer(unit_type) + quantity);
5613 }
5614 
GetResourceStep(const int resource) const5615 int CUnit::GetResourceStep(const int resource) const
5616 {
5617 	if (!this->Type->ResInfo[resource]) {
5618 		return 0;
5619 	}
5620 
5621 	int resource_step = this->Type->ResInfo[resource]->ResourceStep;
5622 
5623 	resource_step += this->Variable[GATHERINGBONUS_INDEX].Value;
5624 
5625 	if (resource == CopperCost) {
5626 		resource_step += this->Variable[COPPERGATHERINGBONUS_INDEX].Value;
5627 	} else if (resource == SilverCost) {
5628 		resource_step += this->Variable[SILVERGATHERINGBONUS_INDEX].Value;
5629 	} else if (resource == GoldCost) {
5630 		resource_step += this->Variable[GOLDGATHERINGBONUS_INDEX].Value;
5631 	} else if (resource == IronCost) {
5632 		resource_step += this->Variable[IRONGATHERINGBONUS_INDEX].Value;
5633 	} else if (resource == MithrilCost) {
5634 		resource_step += this->Variable[MITHRILGATHERINGBONUS_INDEX].Value;
5635 	} else if (resource == WoodCost) {
5636 		resource_step += this->Variable[LUMBERGATHERINGBONUS_INDEX].Value;
5637 	} else if (resource == StoneCost || resource == LimestoneCost) {
5638 		resource_step += this->Variable[STONEGATHERINGBONUS_INDEX].Value;
5639 	} else if (resource == CoalCost) {
5640 		resource_step += this->Variable[COALGATHERINGBONUS_INDEX].Value;
5641 	} else if (resource == JewelryCost) {
5642 		resource_step += this->Variable[JEWELRYGATHERINGBONUS_INDEX].Value;
5643 	} else if (resource == FurnitureCost) {
5644 		resource_step += this->Variable[FURNITUREGATHERINGBONUS_INDEX].Value;
5645 	} else if (resource == LeatherCost) {
5646 		resource_step += this->Variable[LEATHERGATHERINGBONUS_INDEX].Value;
5647 	} else if (resource == DiamondsCost || resource == EmeraldsCost) {
5648 		resource_step += this->Variable[GEMSGATHERINGBONUS_INDEX].Value;
5649 	}
5650 
5651 	return resource_step;
5652 }
5653 
GetTotalInsideCount(const CPlayer * player,const bool ignore_items,const bool ignore_saved_cargo,const CUnitType * type) const5654 int CUnit::GetTotalInsideCount(const CPlayer *player, const bool ignore_items, const bool ignore_saved_cargo, const CUnitType *type) const
5655 {
5656 	if (!this->UnitInside) {
5657 		return 0;
5658 	}
5659 
5660 	if (this->Type->BoolFlag[SAVECARGO_INDEX].value && ignore_saved_cargo) {
5661 		return 0;
5662 	}
5663 
5664 	int inside_count = 0;
5665 
5666 	CUnit *inside_unit = this->UnitInside;
5667 	for (int j = 0; j < this->InsideCount; ++j, inside_unit = inside_unit->NextContained) {
5668 		if ( //only count units of the faction, ignore items
5669 			(!player || inside_unit->Player == player)
5670 			&& (!ignore_items || !inside_unit->Type->BoolFlag[ITEM_INDEX].value)
5671 			&& (!type || inside_unit->Type == type)
5672 		) {
5673 			inside_count++;
5674 		}
5675 		inside_count += inside_unit->GetTotalInsideCount(player, ignore_items, ignore_saved_cargo);
5676 	}
5677 
5678 	return inside_count;
5679 }
5680 
CanAttack(bool count_inside) const5681 bool CUnit::CanAttack(bool count_inside) const
5682 {
5683 	if (this->Type->CanTransport() && this->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && !this->Type->BoolFlag[CANATTACK_INDEX].value) { //transporters without an attack can only attack through a unit within them
5684 		if (count_inside && this->BoardCount > 0) {
5685 			CUnit *boarded_unit = this->UnitInside;
5686 			for (int i = 0; i < this->InsideCount; ++i, boarded_unit = boarded_unit->NextContained) {
5687 				if (boarded_unit->GetModifiedVariable(ATTACKRANGE_INDEX) > 1 && boarded_unit->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value) {
5688 					return true;
5689 				}
5690 			}
5691 		}
5692 		return false;
5693 	}
5694 
5695 	if (this->Container && (!this->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value || !this->Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value)) {
5696 		return false;
5697 	}
5698 
5699 	return this->Type->BoolFlag[CANATTACK_INDEX].value;
5700 }
5701 
IsInCombat() const5702 bool CUnit::IsInCombat() const
5703 {
5704 	// Select all units around the unit
5705 	std::vector<CUnit *> table;
5706 	SelectAroundUnit(*this, this->GetReactionRange(), table, IsEnemyWith(*this->Player));
5707 
5708 	for (size_t i = 0; i < table.size(); ++i) {
5709 		const CUnit &target = *table[i];
5710 
5711 		if (target.IsVisibleAsGoal(*this->Player) && (CanTarget(*this->Type, *target.Type) || CanTarget(*target.Type, *this->Type))) {
5712 			return true;
5713 		}
5714 	}
5715 
5716 	return false;
5717 }
5718 
CanHarvest(const CUnit * dest,bool only_harvestable) const5719 bool CUnit::CanHarvest(const CUnit *dest, bool only_harvestable) const
5720 {
5721 	if (!dest) {
5722 		return false;
5723 	}
5724 
5725 	if (!dest->GivesResource) {
5726 		return false;
5727 	}
5728 
5729 	if (!this->Type->ResInfo[dest->GivesResource]) {
5730 		return false;
5731 	}
5732 
5733 	if (!dest->Type->BoolFlag[CANHARVEST_INDEX].value && only_harvestable) {
5734 		return false;
5735 	}
5736 
5737 	if (!this->Type->BoolFlag[HARVESTER_INDEX].value) {
5738 		return false;
5739 	}
5740 
5741 	if (dest->GivesResource == TradeCost) {
5742 		if (dest->Player == this->Player) { //can only trade with markets owned by other players
5743 			return false;
5744 		}
5745 
5746 		if (this->Type->UnitType != UnitTypeNaval && dest->Type->BoolFlag[SHOREBUILDING_INDEX].value) { //only ships can trade with docks
5747 			return false;
5748 		}
5749 		if (this->Type->UnitType == UnitTypeNaval && !dest->Type->BoolFlag[SHOREBUILDING_INDEX].value && dest->Type->UnitType != UnitTypeNaval) { //ships cannot trade with land markets
5750 			return false;
5751 		}
5752 	} else {
5753 		if (dest->Player != this->Player && !(dest->Player->IsAllied(*this->Player) && this->Player->IsAllied(*dest->Player)) && dest->Player->Index != PlayerNumNeutral) {
5754 			return false;
5755 		}
5756 	}
5757 
5758 	if (this->BoardCount) { //cannot harvest if carrying units
5759 		return false;
5760 	}
5761 
5762 	return true;
5763 }
5764 
CanReturnGoodsTo(const CUnit * dest,int resource) const5765 bool CUnit::CanReturnGoodsTo(const CUnit *dest, int resource) const
5766 {
5767 	if (!dest) {
5768 		return false;
5769 	}
5770 
5771 	if (!resource) {
5772 		resource = this->CurrentResource;
5773 	}
5774 
5775 	if (!resource) {
5776 		return false;
5777 	}
5778 
5779 	if (!dest->Type->CanStore[this->CurrentResource]) {
5780 		return false;
5781 	}
5782 
5783 	if (resource == TradeCost) {
5784 		if (dest->Player != this->Player) { //can only return trade to markets owned by the same player
5785 			return false;
5786 		}
5787 
5788 		if (this->Type->UnitType != UnitTypeNaval && dest->Type->BoolFlag[SHOREBUILDING_INDEX].value) { //only ships can return trade to docks
5789 			return false;
5790 		}
5791 		if (this->Type->UnitType == UnitTypeNaval && !dest->Type->BoolFlag[SHOREBUILDING_INDEX].value && dest->Type->UnitType != UnitTypeNaval) { //ships cannot return trade to land markets
5792 			return false;
5793 		}
5794 	} else {
5795 		if (dest->Player != this->Player && !(dest->Player->IsAllied(*this->Player) && this->Player->IsAllied(*dest->Player))) {
5796 			return false;
5797 		}
5798 	}
5799 
5800 	return true;
5801 }
5802 
5803 /**
5804 **	@brief	Get whether a unit can cast a given spell
5805 **
5806 **	@return	True if the unit can cast the given spell, or false otherwise
5807 */
CanCastSpell(const CSpell * spell,const bool ignore_mana_and_cooldown) const5808 bool CUnit::CanCastSpell(const CSpell *spell, const bool ignore_mana_and_cooldown) const
5809 {
5810 	if (spell->IsAvailableForUnit(*this)) {
5811 		if (!ignore_mana_and_cooldown) {
5812 			if (this->Variable[MANA_INDEX].Value < spell->ManaCost) {
5813 				return false;
5814 			}
5815 
5816 			if (this->SpellCoolDownTimers[spell->Slot]) {
5817 				return false;
5818 			}
5819 		}
5820 
5821 		return true;
5822 	} else {
5823 		return false;
5824 	}
5825 }
5826 
5827 /**
5828 **	@brief	Get whether a unit can cast any spell
5829 **
5830 **	@return	True if the unit can cast any spell, or false otherwise
5831 */
CanCastAnySpell() const5832 bool CUnit::CanCastAnySpell() const
5833 {
5834 	for (size_t i = 0; i < this->Type->Spells.size(); ++i) {
5835 		if (this->CanCastSpell(this->Type->Spells[i], true)) {
5836 			return true;
5837 		}
5838 	}
5839 
5840 	return false;
5841 }
5842 
5843 /**
5844 **	@brief	Get whether the unit can autocast a given spell
5845 **
5846 **	@param	spell	The spell
5847 **
5848 **	@return	True if the unit can autocast the spell, false otherwise
5849 */
CanAutoCastSpell(const CSpell * spell) const5850 bool CUnit::CanAutoCastSpell(const CSpell *spell) const
5851 {
5852 	if (!this->AutoCastSpell || !spell || !this->AutoCastSpell[spell->Slot] || !spell->AutoCast) {
5853 		return false;
5854 	}
5855 
5856 	if (!CanCastSpell(spell, false)) {
5857 		return false;
5858 	}
5859 
5860 	return true;
5861 }
5862 
IsItemEquipped(const CUnit * item) const5863 bool CUnit::IsItemEquipped(const CUnit *item) const
5864 {
5865 	int item_slot = GetItemClassSlot(item->Type->ItemClass);
5866 
5867 	if (item_slot == -1) {
5868 		return false;
5869 	}
5870 
5871 	if (std::find(EquippedItems[item_slot].begin(), EquippedItems[item_slot].end(), item) != EquippedItems[item_slot].end()) {
5872 		return true;
5873 	}
5874 
5875 	return false;
5876 }
5877 
IsItemClassEquipped(int item_class) const5878 bool CUnit::IsItemClassEquipped(int item_class) const
5879 {
5880 	int item_slot = GetItemClassSlot(item_class);
5881 
5882 	if (item_slot == -1) {
5883 		return false;
5884 	}
5885 
5886 	for (size_t i = 0; i < EquippedItems[item_slot].size(); ++i) {
5887 		if (EquippedItems[item_slot][i]->Type->ItemClass == item_class) {
5888 			return true;
5889 		}
5890 	}
5891 
5892 	return false;
5893 }
5894 
IsItemTypeEquipped(const CUnitType * item_type) const5895 bool CUnit::IsItemTypeEquipped(const CUnitType *item_type) const
5896 {
5897 	int item_slot = GetItemClassSlot(item_type->ItemClass);
5898 
5899 	if (item_slot == -1) {
5900 		return false;
5901 	}
5902 
5903 	for (size_t i = 0; i < EquippedItems[item_slot].size(); ++i) {
5904 		if (EquippedItems[item_slot][i]->Type == item_type) {
5905 			return true;
5906 		}
5907 	}
5908 
5909 	return false;
5910 }
5911 
IsUniqueItemEquipped(const CUniqueItem * unique) const5912 bool CUnit::IsUniqueItemEquipped(const CUniqueItem *unique) const
5913 {
5914 	int item_slot = GetItemClassSlot(unique->Type->ItemClass);
5915 
5916 	if (item_slot == -1) {
5917 		return false;
5918 	}
5919 
5920 	int item_equipped_quantity = 0;
5921 	for (size_t i = 0; i < this->EquippedItems[item_slot].size(); ++i) {
5922 		if (EquippedItems[item_slot][i]->Unique == unique) {
5923 			return true;
5924 		}
5925 	}
5926 
5927 	return false;
5928 }
5929 
CanEquipItem(CUnit * item) const5930 bool CUnit::CanEquipItem(CUnit *item) const
5931 {
5932 	if (item->Container != this) {
5933 		return false;
5934 	}
5935 
5936 	if (!item->Identified) {
5937 		return false;
5938 	}
5939 
5940 	if (!CanEquipItemClass(item->Type->ItemClass)) {
5941 		return false;
5942 	}
5943 
5944 	return true;
5945 }
5946 
CanEquipItemClass(int item_class) const5947 bool CUnit::CanEquipItemClass(int item_class) const
5948 {
5949 	if (item_class == -1) {
5950 		return false;
5951 	}
5952 
5953 	if (GetItemClassSlot(item_class) == -1) { //can't equip items that don't correspond to an equippable slot
5954 		return false;
5955 	}
5956 
5957 	if (GetItemClassSlot(item_class) == WeaponItemSlot && std::find(this->Type->WeaponClasses.begin(), this->Type->WeaponClasses.end(), item_class) == this->Type->WeaponClasses.end()) { //if the item is a weapon and its item class isn't a weapon class used by this unit's type, return false
5958 		return false;
5959 	}
5960 
5961 	if ( //if the item uses the shield (off-hand) slot, but that slot is unavailable for the weapon (because it is two-handed), return false
5962 		GetItemClassSlot(item_class) == ShieldItemSlot
5963 		&& this->Type->WeaponClasses.size() > 0
5964 		&& (
5965 			this->Type->WeaponClasses[0] == BowItemClass
5966 			// add other two-handed weapons here as necessary
5967 		)
5968 	) {
5969 		return false;
5970 	}
5971 
5972 	if ( //if the item is a shield and the weapon of this unit's type is incompatible with shields, return false
5973 		item_class == ShieldItemClass
5974 		&& (
5975 			Type->WeaponClasses.size() == 0
5976 			|| Type->WeaponClasses[0] == DaggerItemClass
5977 			|| Type->WeaponClasses[0] == ThrowingAxeItemClass
5978 			|| Type->WeaponClasses[0] == JavelinItemClass
5979 			|| Type->WeaponClasses[0] == GunItemClass
5980 			|| Type->BoolFlag[HARVESTER_INDEX].value //workers can't use shields
5981 		)
5982 	) {
5983 		return false;
5984 	}
5985 
5986 	if (this->GetItemSlotQuantity(GetItemClassSlot(item_class)) == 0) {
5987 		return false;
5988 	}
5989 
5990 	return true;
5991 }
5992 
CanUseItem(CUnit * item) const5993 bool CUnit::CanUseItem(CUnit *item) const
5994 {
5995 	if (item->ConnectingDestination != nullptr) {
5996 		if (item->Type->BoolFlag[ETHEREAL_INDEX].value && !this->Variable[ETHEREALVISION_INDEX].Value) {
5997 			return false;
5998 		}
5999 
6000 		if (this->Type->BoolFlag[RAIL_INDEX].value && !item->ConnectingDestination->HasAdjacentRailForUnitType(this->Type)) {
6001 			return false;
6002 		}
6003 
6004 		if (this->Player == item->Player || this->Player->IsAllied(*item->Player) || item->Player->Type == PlayerNeutral) {
6005 			return true;
6006 		}
6007 	}
6008 
6009 	if (!item->Type->BoolFlag[ITEM_INDEX].value && !item->Type->BoolFlag[POWERUP_INDEX].value) {
6010 		return false;
6011 	}
6012 
6013 	if (item->Type->BoolFlag[ITEM_INDEX].value && item->Type->ItemClass != FoodItemClass && item->Type->ItemClass != PotionItemClass && item->Type->ItemClass != ScrollItemClass && item->Type->ItemClass != BookItemClass) {
6014 		return false;
6015 	}
6016 
6017 	if (item->Spell != nullptr) {
6018 		if (!this->HasInventory() || !::CanCastSpell(*this, *item->Spell, this, this->tilePos, this->MapLayer)) {
6019 			return false;
6020 		}
6021 	}
6022 
6023 	if (item->Work != nullptr) {
6024 		if (!this->HasInventory() || this->GetIndividualUpgrade(item->Work)) {
6025 			return false;
6026 		}
6027 	}
6028 
6029 	if (item->Elixir != nullptr) {
6030 		if (!this->HasInventory() || this->GetIndividualUpgrade(item->Elixir)) {
6031 			return false;
6032 		}
6033 	}
6034 
6035 	if (item->Elixir == nullptr && item->Variable[HITPOINTHEALING_INDEX].Value > 0 && this->Variable[HP_INDEX].Value >= this->GetModifiedVariable(HP_INDEX, VariableMax)) {
6036 		return false;
6037 	}
6038 
6039 	return true;
6040 }
6041 
IsItemSetComplete(const CUnit * item) const6042 bool CUnit::IsItemSetComplete(const CUnit *item) const
6043 {
6044 	for (size_t i = 0; i < item->Unique->Set->UniqueItems.size(); ++i) {
6045 		if (!this->IsUniqueItemEquipped(item->Unique->Set->UniqueItems[i])) {
6046 			return false;
6047 		}
6048 	}
6049 
6050 	return true;
6051 }
6052 
EquippingItemCompletesSet(const CUnit * item) const6053 bool CUnit::EquippingItemCompletesSet(const CUnit *item) const
6054 {
6055 	for (size_t i = 0; i < item->Unique->Set->UniqueItems.size(); ++i) {
6056 		int item_slot = GetItemClassSlot(item->Unique->Set->UniqueItems[i]->Type->ItemClass);
6057 
6058 		if (item_slot == -1) {
6059 			return false;
6060 		}
6061 
6062 		bool has_item_equipped = false;
6063 		for (size_t j = 0; j < this->EquippedItems[item_slot].size(); ++j) {
6064 			if (EquippedItems[item_slot][j]->Unique == item->Unique->Set->UniqueItems[i]) {
6065 				has_item_equipped = true;
6066 				break;
6067 			}
6068 		}
6069 
6070 		if (has_item_equipped && item->Unique->Set->UniqueItems[i] == item->Unique) { //if the unique item is already equipped, it won't complete the set (either the set is already complete, or needs something else)
6071 			return false;
6072 		} else if (!has_item_equipped && item->Unique->Set->UniqueItems[i] != item->Unique) {
6073 			return false;
6074 		}
6075 
6076 	}
6077 
6078 	return true;
6079 }
6080 
DeequippingItemBreaksSet(const CUnit * item) const6081 bool CUnit::DeequippingItemBreaksSet(const CUnit *item) const
6082 {
6083 	if (!IsItemSetComplete(item)) {
6084 		return false;
6085 	}
6086 
6087 	int item_slot = GetItemClassSlot(item->Type->ItemClass);
6088 
6089 	if (item_slot == -1) {
6090 		return false;
6091 	}
6092 
6093 	int item_equipped_quantity = 0;
6094 	for (size_t i = 0; i < this->EquippedItems[item_slot].size(); ++i) {
6095 		if (EquippedItems[item_slot][i]->Unique == item->Unique) {
6096 			item_equipped_quantity += 1;
6097 		}
6098 	}
6099 
6100 	if (item_equipped_quantity > 1) {
6101 		return false;
6102 	} else {
6103 		return true;
6104 	}
6105 }
6106 
HasInventory() const6107 bool CUnit::HasInventory() const
6108 {
6109 	if (this->Type->BoolFlag[INVENTORY_INDEX].value) {
6110 		return true;
6111 	}
6112 
6113 	if (!this->Type->BoolFlag[FAUNA_INDEX].value) {
6114 		if (this->Character != nullptr) {
6115 			return true;
6116 		}
6117 
6118 		if (this->Variable[LEVEL_INDEX].Value >= 3 && this->Type->BoolFlag[ORGANIC_INDEX].value) {
6119 			return true;
6120 		}
6121 	}
6122 
6123 	return false;
6124 }
6125 
CanLearnAbility(CUpgrade * ability,bool pre) const6126 bool CUnit::CanLearnAbility(CUpgrade *ability, bool pre) const
6127 {
6128 	if (!strncmp(ability->Ident.c_str(), "upgrade-deity-", 14)) { //if is a deity choice "ability", only allow for custom heroes (but display the icon for already-acquired deities for all heroes)
6129 		if (!this->Character) {
6130 			return false;
6131 		}
6132 		if (!this->Character->Custom && this->GetIndividualUpgrade(ability) == 0) {
6133 			return false;
6134 		}
6135 		if (!pre && this->UpgradeRemovesExistingUpgrade(ability)) {
6136 			return false;
6137 		}
6138 	}
6139 
6140 	if (!pre && this->GetIndividualUpgrade(ability) >= ability->MaxLimit) { // already learned
6141 		return false;
6142 	}
6143 
6144 	if (!pre && this->Variable[LEVELUP_INDEX].Value < 1 && ability->Ability) {
6145 		return false;
6146 	}
6147 
6148 	if (!CheckDependencies(ability, this, false, pre)) {
6149 		return false;
6150 	}
6151 
6152 	return true;
6153 }
6154 
CanHireMercenary(CUnitType * type,int civilization_id) const6155 bool CUnit::CanHireMercenary(CUnitType *type, int civilization_id) const
6156 {
6157 	if (civilization_id == -1) {
6158 		civilization_id = type->Civilization;
6159 	}
6160 	for (int p = 0; p < PlayerMax; ++p) {
6161 		if (Players[p].Type != PlayerNobody && Players[p].Type != PlayerNeutral && civilization_id == Players[p].Race && CheckDependencies(type, &Players[p], true) && Players[p].StartMapLayer == this->MapLayer->ID) {
6162 			return true;
6163 		}
6164 	}
6165 
6166 	return false;
6167 }
6168 
CanEat(const CUnit & unit) const6169 bool CUnit::CanEat(const CUnit &unit) const
6170 {
6171 	if (this->Type->BoolFlag[CARNIVORE_INDEX].value && unit.Type->BoolFlag[FLESH_INDEX].value) {
6172 		return true;
6173 	}
6174 
6175 	if (this->Type->BoolFlag[INSECTIVORE_INDEX].value && unit.Type->BoolFlag[INSECT_INDEX].value) {
6176 		return true;
6177 	}
6178 
6179 	if (this->Type->BoolFlag[HERBIVORE_INDEX].value && unit.Type->BoolFlag[VEGETABLE_INDEX].value) {
6180 		return true;
6181 	}
6182 
6183 	if (
6184 		this->Type->BoolFlag[DETRITIVORE_INDEX].value
6185 		&& (
6186 			unit.Type->BoolFlag[DETRITUS_INDEX].value
6187 			|| (unit.CurrentAction() == UnitActionDie && (unit.Type->BoolFlag[FLESH_INDEX].value || unit.Type->BoolFlag[INSECT_INDEX].value))
6188 		)
6189 	) {
6190 		return true;
6191 	}
6192 
6193 	return false;
6194 }
6195 
LevelCheck(const int level) const6196 bool CUnit::LevelCheck(const int level) const
6197 {
6198 	if (this->Variable[LEVEL_INDEX].Value == 0) {
6199 		return false;
6200 	}
6201 
6202 	return SyncRand((this->Variable[LEVEL_INDEX].Value * 2) + 1) >= level;
6203 }
6204 
IsAbilityEmpowered(const CUpgrade * ability) const6205 bool CUnit::IsAbilityEmpowered(const CUpgrade *ability) const
6206 {
6207 	const CPlane *plane = this->MapLayer->Plane;
6208 	if (plane) {
6209 		if (!plane->EmpoweredDeityDomains.empty()) {
6210 			for (const CDeityDomain *deity_domain : ability->DeityDomains) {
6211 				if (std::find(plane->EmpoweredDeityDomains.begin(), plane->EmpoweredDeityDomains.end(), deity_domain) != plane->EmpoweredDeityDomains.end()) {
6212 					return true;
6213 				}
6214 			}
6215 		}
6216 
6217 		if (!plane->EmpoweredSchoolsOfMagic.empty()) {
6218 			for (const CSchoolOfMagic *school_of_magic : ability->SchoolsOfMagic) {
6219 				if (std::find(plane->EmpoweredSchoolsOfMagic.begin(), plane->EmpoweredSchoolsOfMagic.end(), school_of_magic) != plane->EmpoweredSchoolsOfMagic.end()) {
6220 					return true;
6221 				}
6222 			}
6223 		}
6224 	}
6225 
6226 	return false;
6227 }
6228 
IsSpellEmpowered(const CSpell * spell) const6229 bool CUnit::IsSpellEmpowered(const CSpell *spell) const
6230 {
6231 	if (spell->DependencyId != -1) {
6232 		return this->IsAbilityEmpowered(AllUpgrades[spell->DependencyId]);
6233 	} else {
6234 		return false;
6235 	}
6236 }
6237 
6238 /**
6239 **  Check if the upgrade removes an existing individual upgrade of the unit.
6240 **
6241 **  @param upgrade    Upgrade.
6242 */
UpgradeRemovesExistingUpgrade(const CUpgrade * upgrade) const6243 bool CUnit::UpgradeRemovesExistingUpgrade(const CUpgrade *upgrade) const
6244 {
6245 	for (size_t z = 0; z < upgrade->UpgradeModifiers.size(); ++z) {
6246 		for (size_t j = 0; j < upgrade->UpgradeModifiers[z]->RemoveUpgrades.size(); ++j) {
6247 			if (this->GetIndividualUpgrade(upgrade->UpgradeModifiers[z]->RemoveUpgrades[j]) > 0) {
6248 				return true;
6249 			}
6250 		}
6251 	}
6252 
6253 	return false;
6254 }
6255 
HasAdjacentRailForUnitType(const CUnitType * type) const6256 bool CUnit::HasAdjacentRailForUnitType(const CUnitType *type) const
6257 {
6258 	bool has_adjacent_rail = false;
6259 	Vec2i top_left_pos(this->tilePos - Vec2i(1, 1));
6260 	Vec2i bottom_right_pos(this->tilePos + this->Type->TileSize);
6261 
6262 	for (int x = top_left_pos.x; x <= bottom_right_pos.x; ++x) {
6263 		Vec2i tile_pos(x, top_left_pos.y);
6264 		if (Map.Info.IsPointOnMap(tile_pos, this->MapLayer) && UnitTypeCanBeAt(*type, tile_pos, this->MapLayer->ID)) {
6265 			has_adjacent_rail = true;
6266 			break;
6267 		}
6268 
6269 		tile_pos.y = bottom_right_pos.y;
6270 		if (Map.Info.IsPointOnMap(tile_pos, this->MapLayer) && UnitTypeCanBeAt(*type, tile_pos, this->MapLayer->ID)) {
6271 			has_adjacent_rail = true;
6272 			break;
6273 		}
6274 	}
6275 
6276 	if (!has_adjacent_rail) {
6277 		for (int y = top_left_pos.y; y <= bottom_right_pos.y; ++y) {
6278 			Vec2i tile_pos(top_left_pos.x, y);
6279 			if (Map.Info.IsPointOnMap(tile_pos, this->MapLayer) && UnitTypeCanBeAt(*type, tile_pos, this->MapLayer->ID)) {
6280 				has_adjacent_rail = true;
6281 				break;
6282 			}
6283 
6284 			tile_pos.x = bottom_right_pos.x;
6285 			if (Map.Info.IsPointOnMap(tile_pos, this->MapLayer) && UnitTypeCanBeAt(*type, tile_pos, this->MapLayer->ID)) {
6286 				has_adjacent_rail = true;
6287 				break;
6288 			}
6289 		}
6290 	}
6291 
6292 	return has_adjacent_rail;
6293 }
6294 
GetAnimations() const6295 CAnimations *CUnit::GetAnimations() const
6296 {
6297 	const CUnitTypeVariation *variation = this->GetVariation();
6298 	if (variation && variation->Animations) {
6299 		return variation->Animations;
6300 	} else {
6301 		return this->Type->Animations;
6302 	}
6303 }
6304 
GetConstruction() const6305 CConstruction *CUnit::GetConstruction() const
6306 {
6307 	const CUnitTypeVariation *variation = this->GetVariation();
6308 	if (variation && variation->Construction) {
6309 		return variation->Construction;
6310 	} else {
6311 		return this->Type->Construction;
6312 	}
6313 }
6314 
GetIcon() const6315 IconConfig CUnit::GetIcon() const
6316 {
6317 	if (this->Character != nullptr && this->Character->Level >= 3 && this->Character->HeroicIcon.Icon) {
6318 		return this->Character->HeroicIcon;
6319 	} else if (this->Character != nullptr && this->Character->Icon.Icon) {
6320 		return this->Character->Icon;
6321 	} else if (this->Unique != nullptr && this->Unique->Icon.Icon) {
6322 		return this->Unique->Icon;
6323 	}
6324 
6325 	const CUnitTypeVariation *variation = this->GetVariation();
6326 	if (variation && variation->Icon.Icon) {
6327 		return variation->Icon;
6328 	} else {
6329 		return this->Type->Icon;
6330 	}
6331 }
6332 
GetButtonIcon(int button_action) const6333 CIcon *CUnit::GetButtonIcon(int button_action) const
6334 {
6335 	if (this->ButtonIcons.find(button_action) != this->ButtonIcons.end()) {
6336 		return this->ButtonIcons.find(button_action)->second;
6337 	} else if (this->Player == ThisPlayer && ThisPlayer->Faction != -1 && PlayerRaces.Factions[ThisPlayer->Faction]->ButtonIcons.find(button_action) != PlayerRaces.Factions[ThisPlayer->Faction]->ButtonIcons.end()) {
6338 		return PlayerRaces.Factions[ThisPlayer->Faction]->ButtonIcons[button_action].Icon;
6339 	} else if (this->Player == ThisPlayer && PlayerRaces.ButtonIcons[ThisPlayer->Race].find(button_action) != PlayerRaces.ButtonIcons[ThisPlayer->Race].end()) {
6340 		return PlayerRaces.ButtonIcons[ThisPlayer->Race][button_action].Icon;
6341 	}
6342 
6343 	return nullptr;
6344 }
6345 
GetMissile() const6346 MissileConfig CUnit::GetMissile() const
6347 {
6348 	if (this->Variable[FIREDAMAGE_INDEX].Value > 0 && this->Type->FireMissile.Missile) {
6349 		return this->Type->FireMissile;
6350 	} else {
6351 		return this->Type->Missile;
6352 	}
6353 }
6354 
GetLayerSprite(int image_layer) const6355 CPlayerColorGraphic *CUnit::GetLayerSprite(int image_layer) const
6356 {
6357 	const CUnitTypeVariation *layer_variation = this->GetLayerVariation(image_layer);
6358 	if (layer_variation && layer_variation->Sprite) {
6359 		return layer_variation->Sprite;
6360 	}
6361 
6362 	const CUnitTypeVariation *variation = this->GetVariation();
6363 	if (variation && variation->LayerSprites[image_layer]) {
6364 		return variation->LayerSprites[image_layer];
6365 	} else if (this->Type->LayerSprites[image_layer])  {
6366 		return this->Type->LayerSprites[image_layer];
6367 	} else {
6368 		return nullptr;
6369 	}
6370 }
6371 
GetName() const6372 std::string CUnit::GetName() const
6373 {
6374 	if (GameRunning && this->Character && this->Character->Deity) {
6375 		if (ThisPlayer->Race >= 0) {
6376 			std::string cultural_name = this->Character->Deity->GetCulturalName(CCivilization::Civilizations[ThisPlayer->Race]);
6377 
6378 			if (!cultural_name.empty()) {
6379 				return cultural_name;
6380 			}
6381 		}
6382 
6383 		return this->Character->Deity->Name;
6384 	}
6385 
6386 	std::string name = this->Name;
6387 
6388 	if (name.empty()) {
6389 		return name;
6390 	}
6391 
6392 	if (!this->ExtraName.empty()) {
6393 		name += " ";
6394 		name += this->ExtraName;
6395 	}
6396 
6397 	if (!this->FamilyName.empty()) {
6398 		name += " ";
6399 		name += this->FamilyName;
6400 	}
6401 
6402 	return name;
6403 }
6404 
GetTypeName() const6405 std::string CUnit::GetTypeName() const
6406 {
6407 	if (this->Character && this->Character->Deity) {
6408 		return _("Deity");
6409 	}
6410 
6411 	const CUnitTypeVariation *variation = this->GetVariation();
6412 	if (variation && !variation->TypeName.empty()) {
6413 		return _(variation->TypeName.c_str());
6414 	} else {
6415 		return _(this->Type->Name.c_str());
6416 	}
6417 }
6418 
GetMessageName() const6419 std::string CUnit::GetMessageName() const
6420 {
6421 	std::string name = GetName();
6422 	if (name.empty()) {
6423 		return GetTypeName();
6424 	}
6425 
6426 	if (!this->Identified) {
6427 		return GetTypeName() + " (" + _("Unidentified") + ")";
6428 	}
6429 
6430 	if (!this->Unique && this->Work == nullptr && (this->Prefix != nullptr || this->Suffix != nullptr || this->Spell != nullptr)) {
6431 		return name;
6432 	}
6433 
6434 	return name + " (" + GetTypeName() + ")";
6435 }
6436 //Wyrmgus end
6437 
6438 /**
6439 **  Let an unit die.
6440 **
6441 **  @param unit    Unit to be destroyed.
6442 */
LetUnitDie(CUnit & unit,bool suicide)6443 void LetUnitDie(CUnit &unit, bool suicide)
6444 {
6445 	unit.Variable[HP_INDEX].Value = std::min<int>(0, unit.Variable[HP_INDEX].Value);
6446 	unit.Moving = 0;
6447 	unit.TTL = 0;
6448 	unit.Anim.Unbreakable = 0;
6449 
6450 	const CUnitType *type = unit.Type;
6451 
6452 	while (unit.Resource.Workers) {
6453 		unit.Resource.Workers->DeAssignWorkerFromMine(unit);
6454 	}
6455 
6456 	// removed units,  just remove.
6457 	if (unit.Removed) {
6458 		DebugPrint("Killing a removed unit?\n");
6459 		if (unit.UnitInside) {
6460 			DestroyAllInside(unit);
6461 		}
6462 		UnitLost(unit);
6463 		UnitClearOrders(unit);
6464 		unit.Release();
6465 		return;
6466 	}
6467 
6468 	PlayUnitSound(unit, VoiceDying);
6469 
6470 	//
6471 	// Catapults,... explodes.
6472 	//
6473 	if (type->ExplodeWhenKilled) {
6474 		const PixelPos pixelPos = unit.GetMapPixelPosCenter();
6475 
6476 		MakeMissile(*type->Explosion.Missile, pixelPos, pixelPos, unit.MapLayer->ID);
6477 	}
6478 	if (type->DeathExplosion) {
6479 		const PixelPos pixelPos = unit.GetMapPixelPosCenter();
6480 
6481 		type->DeathExplosion->pushPreamble();
6482 		//Wyrmgus start
6483 		type->DeathExplosion->pushInteger(UnitNumber(unit));
6484 		//Wyrmgus end
6485 		type->DeathExplosion->pushInteger(pixelPos.x);
6486 		type->DeathExplosion->pushInteger(pixelPos.y);
6487 		type->DeathExplosion->run();
6488 	}
6489 	if (suicide) {
6490 		const PixelPos pixelPos = unit.GetMapPixelPosCenter();
6491 
6492 		if (unit.GetMissile().Missile) {
6493 			MakeMissile(*unit.GetMissile().Missile, pixelPos, pixelPos, unit.MapLayer->ID);
6494 		}
6495 	}
6496 	// Handle Teleporter Destination Removal
6497 	if (type->BoolFlag[TELEPORTER_INDEX].value && unit.Goal) {
6498 		unit.Goal->Remove(nullptr);
6499 		UnitLost(*unit.Goal);
6500 		UnitClearOrders(*unit.Goal);
6501 		unit.Goal->Release();
6502 		unit.Goal = nullptr;
6503 	}
6504 
6505 	//Wyrmgus start
6506 	for (size_t i = 0; i < unit.SoldUnits.size(); ++i) {
6507 		DestroyAllInside(*unit.SoldUnits[i]);
6508 		LetUnitDie(*unit.SoldUnits[i]);
6509 	}
6510 	unit.SoldUnits.clear();
6511 	//Wyrmgus end
6512 
6513 	// Transporters lose or save their units and buildings their workers
6514 	//Wyrmgus start
6515 //	if (unit.UnitInside && unit.Type->BoolFlag[SAVECARGO_INDEX].value) {
6516 	if (
6517 		unit.UnitInside
6518 		&& (
6519 			unit.Type->BoolFlag[SAVECARGO_INDEX].value
6520 			|| (unit.HasInventory() && unit.Character == nullptr)
6521 		)
6522 	) {
6523 	//Wyrmgus end
6524 		DropOutAll(unit);
6525 	} else if (unit.UnitInside) {
6526 		DestroyAllInside(unit);
6527 	}
6528 
6529 	//Wyrmgus start
6530 	//if is a raft or bridge, destroy all land units on it
6531 	if (unit.Type->BoolFlag[BRIDGE_INDEX].value) {
6532 		std::vector<CUnit *> table;
6533 		Select(unit.tilePos, unit.tilePos, table, unit.MapLayer->ID);
6534 		for (size_t i = 0; i != table.size(); ++i) {
6535 			if (table[i]->IsAliveOnMap() && !table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->Type->UnitType == UnitTypeLand) {
6536 				table[i]->Variable[HP_INDEX].Value = std::min<int>(0, unit.Variable[HP_INDEX].Value);
6537 				table[i]->Moving = 0;
6538 				table[i]->TTL = 0;
6539 				table[i]->Anim.Unbreakable = 0;
6540 				PlayUnitSound(*table[i], VoiceDying);
6541 				table[i]->Remove(nullptr);
6542 				UnitLost(*table[i]);
6543 				UnitClearOrders(*table[i]);
6544 				table[i]->Release();
6545 			}
6546 		}
6547 	}
6548 	//Wyrmgus end
6549 
6550 	//Wyrmgus start
6551 	//drop items upon death
6552 	if (!suicide && unit.CurrentAction() != UnitActionBuilt && (unit.Character || unit.Type->BoolFlag[BUILDING_INDEX].value || SyncRand(100) >= 66)) { //66% chance nothing will be dropped, unless the unit is a character or building, in which it case it will always drop an item
6553 		unit.GenerateDrop();
6554 	}
6555 	//Wyrmgus end
6556 
6557 	//Wyrmgus start
6558 	std::vector<CUnit *> seeing_table; //units seeing this unit
6559 	if (type->BoolFlag[AIRUNPASSABLE_INDEX].value) {
6560 		SelectAroundUnit(unit, 16, seeing_table); //a range of 16 should be safe enough; there should be no unit or building in the game with a sight range that high, let alone higher
6561 		for (size_t i = 0; i != seeing_table.size(); ++i) {
6562 			MapUnmarkUnitSight(*seeing_table[i]);
6563 		}
6564 	}
6565 	//Wyrmgus end
6566 
6567 	unit.Remove(nullptr);
6568 	UnitLost(unit);
6569 	UnitClearOrders(unit);
6570 
6571 
6572 	// Unit has death animation.
6573 
6574 	// Not good: UnitUpdateHeading(unit);
6575 	delete unit.Orders[0];
6576 	unit.Orders[0] = COrder::NewActionDie();
6577 	if (type->CorpseType) {
6578 #ifdef DYNAMIC_LOAD
6579 		if (!type->Sprite) {
6580 			LoadUnitTypeSprite(type);
6581 		}
6582 #endif
6583 		unit.IX = (type->CorpseType->Width - type->CorpseType->Sprite->Width) / 2;
6584 		unit.IY = (type->CorpseType->Height - type->CorpseType->Sprite->Height) / 2;
6585 
6586 		unit.CurrentSightRange = type->CorpseType->Stats[unit.Player->Index].Variables[SIGHTRANGE_INDEX].Max;
6587 	} else {
6588 		unit.CurrentSightRange = 0;
6589 	}
6590 
6591 	// If we have a corpse, or a death animation, we are put back on the map
6592 	// This enables us to be tracked.  Possibly for spells (eg raise dead)
6593 	//Wyrmgus start
6594 //	if (type->CorpseType || (type->Animations && type->Animations->Death)) {
6595 	if (type->CorpseType || (unit.GetAnimations() && unit.GetAnimations()->Death)) {
6596 	//Wyrmgus end
6597 		unit.Removed = 0;
6598 		Map.Insert(unit);
6599 
6600 		// FIXME: rb: Maybe we need this here because corpse of cloaked units
6601 		//	may crash Sign code
6602 
6603 		// Recalculate the seen count.
6604 		//UnitCountSeen(unit);
6605 	}
6606 
6607 	MapMarkUnitSight(unit);
6608 
6609 	//Wyrmgus start
6610 	if (unit.Settlement) {
6611 		unit.UpdateBuildingSettlementAssignment(unit.Settlement);
6612 	}
6613 	//Wyrmgus end
6614 
6615 	//Wyrmgus start
6616 	if (type->BoolFlag[AIRUNPASSABLE_INDEX].value) {
6617 		for (size_t i = 0; i != seeing_table.size(); ++i) {
6618 			MapMarkUnitSight(*seeing_table[i]);
6619 		}
6620 	}
6621 	//Wyrmgus end
6622 }
6623 
6624 /**
6625 **  Destroy all units inside unit.
6626 **
6627 **  @param source  container.
6628 */
DestroyAllInside(CUnit & source)6629 void DestroyAllInside(CUnit &source)
6630 {
6631 	CUnit *unit = source.UnitInside;
6632 
6633 	// No Corpses, we are inside something, and we can't be seen
6634 	for (int i = source.InsideCount; i; --i, unit = unit->NextContained) {
6635 		// Transporter inside a transporter?
6636 		if (unit->UnitInside) {
6637 			DestroyAllInside(*unit);
6638 		}
6639 		UnitLost(*unit);
6640 		UnitClearOrders(*unit);
6641 		unit->Release();
6642 	}
6643 }
6644 
6645 /*----------------------------------------------------------------------------
6646   -- Unit AI
6647   ----------------------------------------------------------------------------*/
6648 
ThreatCalculate(const CUnit & unit,const CUnit & dest)6649 int ThreatCalculate(const CUnit &unit, const CUnit &dest)
6650 {
6651 	const CUnitType &type = *unit.Type;
6652 	const CUnitType &dtype = *dest.Type;
6653 	int cost = 0;
6654 
6655 	// Buildings, non-aggressive and invincible units have the lowest priority
6656 	if (dest.IsAgressive() == false || dest.Variable[UNHOLYARMOR_INDEX].Value > 0
6657 		|| dest.Type->BoolFlag[INDESTRUCTIBLE_INDEX].value) {
6658 		if (dest.Type->CanMove() == false) {
6659 			return INT_MAX;
6660 		} else {
6661 			return INT_MAX / 2;
6662 		}
6663 	}
6664 
6665 	// Priority 0-255
6666 	cost -= dest.Variable[PRIORITY_INDEX].Value * PRIORITY_FACTOR;
6667 	// Remaining HP (Health) 0-65535
6668 	//Wyrmgus start
6669 //	cost += dest.Variable[HP_INDEX].Value * 100 / dest.Variable[HP_INDEX].Max * HEALTH_FACTOR;
6670 	cost += dest.Variable[HP_INDEX].Value * 100 / dest.GetModifiedVariable(HP_INDEX, VariableMax) * HEALTH_FACTOR;
6671 	//Wyrmgus end
6672 
6673 	const int d = unit.MapDistanceTo(dest);
6674 
6675 	if (d <= unit.GetModifiedVariable(ATTACKRANGE_INDEX) && d >= type.MinAttackRange) {
6676 		cost += d * INRANGE_FACTOR;
6677 		cost -= INRANGE_BONUS;
6678 	} else {
6679 		cost += d * DISTANCE_FACTOR;
6680 	}
6681 
6682 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
6683 		if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
6684 			if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) & (dtype.BoolFlag[i].value)) {
6685 				cost -= AIPRIORITY_BONUS;
6686 			}
6687 			if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) & (dtype.BoolFlag[i].value)) {
6688 				cost += AIPRIORITY_BONUS;
6689 			}
6690 		}
6691 	}
6692 
6693 	// Unit can attack back.
6694 	if (CanTarget(dtype, type)) {
6695 		cost -= CANATTACK_BONUS;
6696 	}
6697 	return cost;
6698 }
6699 
HitUnit_LastAttack(const CUnit * attacker,CUnit & target)6700 static void HitUnit_LastAttack(const CUnit *attacker, CUnit &target)
6701 {
6702 	const unsigned long lastattack = target.Attacked;
6703 
6704 	target.Attacked = GameCycle ? GameCycle : 1;
6705 	if (target.Type->BoolFlag[WALL_INDEX].value || (lastattack && GameCycle <= lastattack + 2 * CYCLES_PER_SECOND)) {
6706 		return;
6707 	}
6708 	// NOTE: perhaps this should also be moved into the notify?
6709 	if (target.Player == ThisPlayer) {
6710 		// FIXME: Problem with load+save.
6711 
6712 		//
6713 		// One help cry each 2 second is enough
6714 		// If on same area ignore it for 2 minutes.
6715 		//
6716 		if (HelpMeLastCycle < GameCycle) {
6717 			if (!HelpMeLastCycle
6718 				|| HelpMeLastCycle + CYCLES_PER_SECOND * 120 < GameCycle
6719 				|| target.tilePos.x < HelpMeLastX - 14
6720 				|| target.tilePos.x > HelpMeLastX + 14
6721 				|| target.tilePos.y < HelpMeLastY - 14
6722 				|| target.tilePos.y > HelpMeLastY + 14) {
6723 				HelpMeLastCycle = GameCycle + CYCLES_PER_SECOND * 2;
6724 				HelpMeLastX = target.tilePos.x;
6725 				HelpMeLastY = target.tilePos.y;
6726 				PlayUnitSound(target, VoiceHelpMe);
6727 				target.Player->Notify(NotifyRed, target.tilePos, target.MapLayer->ID, _("%s attacked"), target.GetMessageName().c_str());
6728 			}
6729 		}
6730 	}
6731 
6732 	if (GameCycle > (lastattack + 2 * (CYCLES_PER_SECOND * 60)) && attacker && !target.Type->BoolFlag[BUILDING_INDEX].value) { //only trigger this every two minutes for the unit
6733 		if (
6734 			target.Player->AiEnabled
6735 			&& !attacker->Type->BoolFlag[INDESTRUCTIBLE_INDEX].value // don't attack indestructible units back
6736 		) {
6737 			AiHelpMe(attacker->GetFirstContainer(), target);
6738 		}
6739 	}
6740 }
6741 
HitUnit_Raid(CUnit * attacker,CUnit & target,int damage)6742 static void HitUnit_Raid(CUnit *attacker, CUnit &target, int damage)
6743 {
6744 	if (!attacker) {
6745 		return;
6746 	}
6747 
6748 	if (attacker->Player == target.Player || attacker->Player->Index == PlayerNumNeutral || target.Player->Index == PlayerNumNeutral) {
6749 		return;
6750 	}
6751 
6752 	int var_index;
6753 	if (target.Type->BoolFlag[BUILDING_INDEX].value) {
6754 		var_index = RAIDING_INDEX;
6755 	} else {
6756 		var_index = MUGGING_INDEX;
6757 	}
6758 
6759 	if (!attacker->Variable[var_index].Value) {
6760 		return;
6761 	}
6762 
6763 	if (!attacker->Variable[SHIELDPIERCING_INDEX].Value) {
6764 		int shieldDamage = target.Variable[SHIELDPERMEABILITY_INDEX].Value < 100
6765 							? std::min(target.Variable[SHIELD_INDEX].Value, damage * (100 - target.Variable[SHIELDPERMEABILITY_INDEX].Value) / 100)
6766 							: 0;
6767 
6768 		damage -= shieldDamage;
6769 	}
6770 
6771 	damage = std::min(damage, target.Variable[HP_INDEX].Value);
6772 
6773 	if (damage <= 0) {
6774 		return;
6775 	}
6776 
6777 	for (int i = 0; i < MaxCosts; ++i) {
6778 		if (target.Type->Stats[target.Player->Index].Costs[i] > 0) {
6779 			int resource_change = target.Type->Stats[target.Player->Index].Costs[i] * damage * attacker->Variable[var_index].Value / target.GetModifiedVariable(HP_INDEX, VariableMax) / 100;
6780 			resource_change = std::min(resource_change, target.Player->GetResource(i, STORE_BOTH));
6781 			attacker->Player->ChangeResource(i, resource_change);
6782 			attacker->Player->TotalResources[i] += resource_change;
6783 			target.Player->ChangeResource(i, -resource_change);
6784 		}
6785 	}
6786 }
6787 
HitUnit_IsUnitWillDie(const CUnit * attacker,const CUnit & target,int damage)6788 static bool HitUnit_IsUnitWillDie(const CUnit *attacker, const CUnit &target, int damage)
6789 {
6790 	int shieldDamage = target.Variable[SHIELDPERMEABILITY_INDEX].Value < 100
6791 					   ? std::min(target.Variable[SHIELD_INDEX].Value, damage * (100 - target.Variable[SHIELDPERMEABILITY_INDEX].Value) / 100)
6792 					   : 0;
6793 	return (target.Variable[HP_INDEX].Value <= damage && attacker && attacker->Variable[SHIELDPIERCING_INDEX].Value)
6794 		   || (target.Variable[HP_INDEX].Value <= damage - shieldDamage)
6795 		   || (target.Variable[HP_INDEX].Value == 0);
6796 }
6797 
HitUnit_IncreaseScoreForKill(CUnit & attacker,CUnit & target)6798 static void HitUnit_IncreaseScoreForKill(CUnit &attacker, CUnit &target)
6799 {
6800 	attacker.Player->Score += target.Variable[POINTS_INDEX].Value;
6801 	if (target.Type->BoolFlag[BUILDING_INDEX].value) {
6802 		attacker.Player->TotalRazings++;
6803 	} else {
6804 		attacker.Player->TotalKills++;
6805 	}
6806 
6807 	//Wyrmgus start
6808 	attacker.Player->UnitTypeKills[target.Type->Slot]++;
6809 
6810 	//distribute experience between nearby units belonging to the same player
6811 	if (!target.Type->BoolFlag[BUILDING_INDEX].value) {
6812 		attacker.ChangeExperience(UseHPForXp ? target.Variable[HP_INDEX].Value : target.Variable[POINTS_INDEX].Value, ExperienceRange);
6813 	}
6814 	//Wyrmgus end
6815 
6816 	attacker.Variable[KILL_INDEX].Value++;
6817 	attacker.Variable[KILL_INDEX].Max++;
6818 	attacker.Variable[KILL_INDEX].Enable = 1;
6819 
6820 	//Wyrmgus start
6821 	for (size_t i = 0; i < attacker.Player->QuestObjectives.size(); ++i) {
6822 		CPlayerQuestObjective *objective = attacker.Player->QuestObjectives[i];
6823 		if (
6824 			(objective->ObjectiveType == DestroyUnitsObjectiveType && std::find(objective->UnitTypes.begin(), objective->UnitTypes.end(), target.Type) != objective->UnitTypes.end() && (!objective->Settlement || objective->Settlement == target.Settlement))
6825 			|| (objective->ObjectiveType == DestroyHeroObjectiveType && target.Character && objective->Character == target.Character)
6826 			|| (objective->ObjectiveType == DestroyUniqueObjectiveType && target.Unique && objective->Unique == target.Unique)
6827 		) {
6828 			if (!objective->Faction || objective->Faction->ID == target.Player->Faction) {
6829 				objective->Counter = std::min(objective->Counter + 1, objective->Quantity);
6830 			}
6831 		} else if (objective->ObjectiveType == DestroyFactionObjectiveType) {
6832 			const CPlayer *faction_player = GetFactionPlayer(objective->Faction);
6833 
6834 			if (faction_player) {
6835 				int dying_faction_units = faction_player == target.Player ? 1 : 0;
6836 				dying_faction_units += target.GetTotalInsideCount(faction_player, true, true);
6837 
6838 				if (dying_faction_units > 0 && faction_player->GetUnitCount() <= dying_faction_units) {
6839 					objective->Counter = 1;
6840 				}
6841 			}
6842 		}
6843 	}
6844 
6845 	//also increase score for units inside the target that will be destroyed when the target dies
6846 	if (
6847 		target.UnitInside
6848 		&& !target.Type->BoolFlag[SAVECARGO_INDEX].value
6849 	) {
6850 		CUnit *boarded_unit = target.UnitInside;
6851 		for (int i = 0; i < target.InsideCount; ++i, boarded_unit = boarded_unit->NextContained) {
6852 			if (!boarded_unit->Type->BoolFlag[ITEM_INDEX].value) { //ignore items
6853 				HitUnit_IncreaseScoreForKill(attacker, *boarded_unit);
6854 			}
6855 		}
6856 	}
6857 	//Wyrmgus end
6858 }
6859 
HitUnit_ApplyDamage(CUnit * attacker,CUnit & target,int damage)6860 static void HitUnit_ApplyDamage(CUnit *attacker, CUnit &target, int damage)
6861 {
6862 	if (attacker && attacker->Variable[SHIELDPIERCING_INDEX].Value) {
6863 		target.Variable[HP_INDEX].Value -= damage;
6864 	} else {
6865 		int shieldDamage = target.Variable[SHIELDPERMEABILITY_INDEX].Value < 100
6866 						   ? std::min(target.Variable[SHIELD_INDEX].Value, damage * (100 - target.Variable[SHIELDPERMEABILITY_INDEX].Value) / 100)
6867 						   : 0;
6868 		if (shieldDamage) {
6869 			target.Variable[SHIELD_INDEX].Value -= shieldDamage;
6870 			clamp(&target.Variable[SHIELD_INDEX].Value, 0, target.Variable[SHIELD_INDEX].Max);
6871 		}
6872 		target.Variable[HP_INDEX].Value -= damage - shieldDamage;
6873 	}
6874 
6875 	//Wyrmgus start
6876 	//distribute experience between nearby units belonging to the same player
6877 
6878 //	if (UseHPForXp && attacker && target.IsEnemy(*attacker)) {
6879 	if (UseHPForXp && attacker && (target.IsEnemy(*attacker) || target.Player->Type == PlayerNeutral) && !target.Type->BoolFlag[BUILDING_INDEX].value) {
6880 		attacker->ChangeExperience(damage, ExperienceRange);
6881 	}
6882 	//Wyrmgus end
6883 
6884 	//Wyrmgus start
6885 	//use a healing item if any are available
6886 	if (target.HasInventory()) {
6887 		target.HealingItemAutoUse();
6888 	}
6889 	//Wyrmgus end
6890 }
6891 
HitUnit_BuildingCapture(CUnit * attacker,CUnit & target,int damage)6892 static void HitUnit_BuildingCapture(CUnit *attacker, CUnit &target, int damage)
6893 {
6894 	// FIXME: this is dumb. I made repairers capture. crap.
6895 	// david: capture enemy buildings
6896 	// Only worker types can capture.
6897 	// Still possible to destroy building if not careful (too many attackers)
6898 	if (EnableBuildingCapture && attacker
6899 		&& target.Type->BoolFlag[BUILDING_INDEX].value && target.Variable[HP_INDEX].Value <= damage * 3
6900 		&& attacker->IsEnemy(target)
6901 		&& attacker->Type->RepairRange) {
6902 		target.ChangeOwner(*attacker->Player);
6903 		CommandStopUnit(*attacker); // Attacker shouldn't continue attack!
6904 	}
6905 }
6906 
HitUnit_ShowDamageMissile(const CUnit & target,int damage)6907 static void HitUnit_ShowDamageMissile(const CUnit &target, int damage)
6908 {
6909 	const PixelPos targetPixelCenter = target.GetMapPixelPosCenter();
6910 
6911 	if ((target.IsVisibleOnMap(*ThisPlayer) || ReplayRevealMap) && !DamageMissile.empty()) {
6912 		const MissileType *mtype = MissileTypeByIdent(DamageMissile);
6913 		const PixelDiff offset(3, -mtype->Range);
6914 
6915 		MakeLocalMissile(*mtype, targetPixelCenter, targetPixelCenter + offset, target.MapLayer->ID)->Damage = -damage;
6916 	}
6917 }
6918 
HitUnit_ShowImpactMissile(const CUnit & target)6919 static void HitUnit_ShowImpactMissile(const CUnit &target)
6920 {
6921 	const PixelPos targetPixelCenter = target.GetMapPixelPosCenter();
6922 	const CUnitType &type = *target.Type;
6923 
6924 	if (target.Variable[SHIELD_INDEX].Value > 0
6925 		&& !type.Impact[ANIMATIONS_DEATHTYPES + 1].Name.empty()) { // shield impact
6926 		MakeMissile(*type.Impact[ANIMATIONS_DEATHTYPES + 1].Missile, targetPixelCenter, targetPixelCenter, target.MapLayer->ID);
6927 	} else if (target.DamagedType && !type.Impact[target.DamagedType].Name.empty()) { // specific to damage type impact
6928 		MakeMissile(*type.Impact[target.DamagedType].Missile, targetPixelCenter, targetPixelCenter, target.MapLayer->ID);
6929 	} else if (!type.Impact[ANIMATIONS_DEATHTYPES].Name.empty()) { // generic impact
6930 		MakeMissile(*type.Impact[ANIMATIONS_DEATHTYPES].Missile, targetPixelCenter, targetPixelCenter, target.MapLayer->ID);
6931 	}
6932 }
6933 
HitUnit_ChangeVariable(CUnit & target,const Missile & missile)6934 static void HitUnit_ChangeVariable(CUnit &target, const Missile &missile)
6935 {
6936 	const int var = missile.Type->ChangeVariable;
6937 
6938 	target.Variable[var].Enable = 1;
6939 	target.Variable[var].Value += missile.Type->ChangeAmount;
6940 	if (target.Variable[var].Value > target.Variable[var].Max) {
6941 		if (missile.Type->ChangeMax) {
6942 			target.Variable[var].Max = target.Variable[var].Value;
6943 		//Wyrmgus start
6944 //		} else {
6945 		} else if (target.Variable[var].Value > target.GetModifiedVariable(var, VariableMax)) {
6946 		//Wyrmgus end
6947 			//Wyrmgus start
6948 //			target.Variable[var].Value = target.Variable[var].Max;
6949 			target.Variable[var].Value = target.GetModifiedVariable(var, VariableMax);
6950 			//Wyrmgus end
6951 		}
6952 	}
6953 
6954 	//Wyrmgus start
6955 	if (var == ATTACKRANGE_INDEX && target.Container) {
6956 		target.Container->UpdateContainerAttackRange();
6957 	} else if (var == LEVEL_INDEX || var == POINTS_INDEX) {
6958 		target.UpdateXPRequired();
6959 	} else if (var == XP_INDEX) {
6960 		target.XPChanged();
6961 	} else if (var == STUN_INDEX && target.Variable[var].Value > 0) { //if unit has become stunned, stop it
6962 		CommandStopUnit(target);
6963 	} else if (var == KNOWLEDGEMAGIC_INDEX) {
6964 		target.CheckIdentification();
6965 	}
6966 	//Wyrmgus end
6967 }
6968 
6969 
HitUnit_Burning(CUnit & target)6970 static void HitUnit_Burning(CUnit &target)
6971 {
6972 	//Wyrmgus start
6973 //	const int f = (100 * target.Variable[HP_INDEX].Value) / target.Variable[HP_INDEX].Max;
6974 	const int f = (100 * target.Variable[HP_INDEX].Value) / target.GetModifiedVariable(HP_INDEX, VariableMax);
6975 	//Wyrmgus end
6976 	MissileType *fire = MissileBurningBuilding(f);
6977 
6978 	if (fire) {
6979 		const PixelPos targetPixelCenter = target.GetMapPixelPosCenter();
6980 		const PixelDiff offset(0, -Map.GetMapLayerPixelTileSize(target.MapLayer->ID).y);
6981 		Missile *missile = MakeMissile(*fire, targetPixelCenter + offset, targetPixelCenter + offset, target.MapLayer->ID);
6982 
6983 		missile->SourceUnit = &target;
6984 		target.Burning = 1;
6985 	}
6986 }
6987 
6988 //Wyrmgus start
HitUnit_NormalHitSpecialDamageEffects(CUnit & attacker,CUnit & target)6989 void HitUnit_NormalHitSpecialDamageEffects(CUnit &attacker, CUnit &target)
6990 {
6991 	if (attacker.Variable[FIREDAMAGE_INDEX].Value > 0 && attacker.Variable[FIREDAMAGE_INDEX].Value >= attacker.Variable[COLDDAMAGE_INDEX].Value) { // apply only either the fire damage or cold damage effects, but not both at the same time; apply the one with greater value, but if both are equal fire damage takes precedence
6992 		HitUnit_SpecialDamageEffect(target, FIREDAMAGE_INDEX);
6993 	} else if (attacker.Variable[COLDDAMAGE_INDEX].Value > 0) {
6994 		HitUnit_SpecialDamageEffect(target, COLDDAMAGE_INDEX);
6995 	}
6996 
6997 	if (attacker.Variable[LIGHTNINGDAMAGE_INDEX].Value > 0) {
6998 		HitUnit_SpecialDamageEffect(target, LIGHTNINGDAMAGE_INDEX);
6999 	}
7000 }
7001 
HitUnit_SpecialDamageEffect(CUnit & target,int dmg_var)7002 void HitUnit_SpecialDamageEffect(CUnit &target, int dmg_var)
7003 {
7004 	if (dmg_var == COLDDAMAGE_INDEX && target.Variable[COLDRESISTANCE_INDEX].Value < 100 && target.Type->BoolFlag[ORGANIC_INDEX].value) { //if resistance to cold is 100%, the effect has no chance of being applied
7005 		int rand_max = 100 * 100 / (100 - target.Variable[COLDRESISTANCE_INDEX].Value);
7006 		if (SyncRand(rand_max) == 0) {
7007 			target.Variable[SLOW_INDEX].Enable = 1;
7008 			target.Variable[SLOW_INDEX].Value = std::max(200, target.Variable[SLOW_INDEX].Value);
7009 			target.Variable[SLOW_INDEX].Max = 1000;
7010 		}
7011 	} else if (dmg_var == LIGHTNINGDAMAGE_INDEX && target.Variable[LIGHTNINGRESISTANCE_INDEX].Value < 100 && target.Type->BoolFlag[ORGANIC_INDEX].value) {
7012 		int rand_max = 100 * 100 / (100 - target.Variable[LIGHTNINGRESISTANCE_INDEX].Value);
7013 		if (SyncRand(rand_max) == 0) {
7014 			target.Variable[STUN_INDEX].Enable = 1;
7015 			target.Variable[STUN_INDEX].Value = std::max(50, target.Variable[STUN_INDEX].Value);
7016 			target.Variable[STUN_INDEX].Max = 1000;
7017 		}
7018 	}
7019 }
7020 //Wyrmgus end
7021 
7022 //Wyrmgus start
7023 //static void HitUnit_RunAway(CUnit &target, const CUnit &attacker)
HitUnit_RunAway(CUnit & target,const CUnit & attacker)7024 void HitUnit_RunAway(CUnit &target, const CUnit &attacker)
7025 //Wyrmgus end
7026 {
7027 	Vec2i pos = target.tilePos - attacker.tilePos;
7028 	int d = isqrt(pos.x * pos.x + pos.y * pos.y);
7029 
7030 	if (!d) {
7031 		d = 1;
7032 	}
7033 	pos.x = target.tilePos.x + (pos.x * 5) / d + (SyncRand() & 3);
7034 	pos.y = target.tilePos.y + (pos.y * 5) / d + (SyncRand() & 3);
7035 	Map.Clamp(pos, target.MapLayer->ID);
7036 	CommandStopUnit(target);
7037 	CommandMove(target, pos, 0, target.MapLayer->ID);
7038 }
7039 
HitUnit_AttackBack(CUnit & attacker,CUnit & target)7040 static void HitUnit_AttackBack(CUnit &attacker, CUnit &target)
7041 {
7042 	const int threshold = 30;
7043 	COrder *savedOrder = nullptr;
7044 
7045 	//Wyrmgus start
7046 //	if (target.Player->AiEnabled == false) {
7047 	if (target.Player->AiEnabled == false && target.Player->Type != PlayerNeutral) { // allow neutral units to strike back
7048 	//Wyrmgus end
7049 		if (target.CurrentAction() == UnitActionAttack) {
7050 			COrder_Attack &order = dynamic_cast<COrder_Attack &>(*target.CurrentOrder());
7051 			if (order.IsWeakTargetSelected() == false) {
7052 				return;
7053 			}
7054 		//Wyrmgus start
7055 //		} else {
7056 		} else if (target.CurrentAction() != UnitActionStill) {
7057 		//Wyrmgus end
7058 			return;
7059 		}
7060 	}
7061 	if (target.CanStoreOrder(target.CurrentOrder())) {
7062 		savedOrder = target.CurrentOrder()->Clone();
7063 	}
7064 	CUnit *oldgoal = target.CurrentOrder()->GetGoal();
7065 	CUnit *goal, *best = oldgoal;
7066 
7067 	if (RevealAttacker && CanTarget(*target.Type, *attacker.Type)) {
7068 		// Reveal Unit that is attacking
7069 		goal = &attacker;
7070 	} else {
7071 		if (target.CurrentAction() == UnitActionStandGround) {
7072 			goal = AttackUnitsInRange(target);
7073 		} else {
7074 			// Check for any other units in range
7075 			goal = AttackUnitsInReactRange(target);
7076 		}
7077 	}
7078 
7079 	// Calculate the best target we could attack
7080 	if (!best || (goal && (ThreatCalculate(target, *goal) < ThreatCalculate(target, *best)))) {
7081 		best = goal;
7082 	}
7083 	if (CanTarget(*target.Type, *attacker.Type)
7084 		&& (!best || (goal != &attacker
7085 					  && (ThreatCalculate(target, attacker) < ThreatCalculate(target, *best))))) {
7086 		best = &attacker;
7087 	}
7088 	//Wyrmgus start
7089 //	if (best && best != oldgoal && best->Player != target.Player && best->IsAllied(target) == false) {
7090 	if (best && best != oldgoal && (best->Player != target.Player || target.Player->Type == PlayerNeutral) && best->IsAllied(target) == false) {
7091 	//Wyrmgus end
7092 		CommandAttack(target, best->tilePos, best, FlushCommands, best->MapLayer->ID);
7093 		// Set threshold value only for aggressive units
7094 		if (best->IsAgressive()) {
7095 			target.Threshold = threshold;
7096 		}
7097 		if (savedOrder != nullptr) {
7098 			target.SavedOrder = savedOrder;
7099 		}
7100 	}
7101 }
7102 
7103 /**
7104 **  Unit is hit by missile or other damage.
7105 **
7106 **  @param attacker    Unit that attacks.
7107 **  @param target      Unit that is hit.
7108 **  @param damage      How many damage to take.
7109 **  @param missile     Which missile took the damage.
7110 */
7111 //Wyrmgus start
7112 //void HitUnit(CUnit *attacker, CUnit &target, int damage, const Missile *missile)
HitUnit(CUnit * attacker,CUnit & target,int damage,const Missile * missile,bool show_damage)7113 void HitUnit(CUnit *attacker, CUnit &target, int damage, const Missile *missile, bool show_damage)
7114 //Wyrmgus end
7115 {
7116 	const CUnitType *type = target.Type;
7117 	if (!damage) {
7118 		// Can now happen by splash damage
7119 		// Multiple places send x/y as damage, which may be zero
7120 		return;
7121 	}
7122 
7123 	if (target.Variable[UNHOLYARMOR_INDEX].Value > 0 || target.Type->BoolFlag[INDESTRUCTIBLE_INDEX].value) {
7124 		// vladi: units with active UnholyArmour are invulnerable
7125 		return;
7126 	}
7127 	if (target.Removed) {
7128 		DebugPrint("Removed target hit\n");
7129 		return;
7130 	}
7131 
7132 	Assert(damage != 0 && target.CurrentAction() != UnitActionDie && !target.Type->BoolFlag[VANISHES_INDEX].value);
7133 
7134 	//Wyrmgus start
7135 	if (
7136 		(attacker != nullptr && attacker->Player == ThisPlayer)
7137 		&& target.Player != ThisPlayer
7138 	) {
7139 		// If player is hitting or being hit add tension to our music
7140 		AddMusicTension(1);
7141 	}
7142 	//Wyrmgus end
7143 
7144 	if (GodMode) {
7145 		if (attacker && attacker->Player == ThisPlayer) {
7146 			damage = target.Variable[HP_INDEX].Value;
7147 		}
7148 		if (target.Player == ThisPlayer) {
7149 			damage = 0;
7150 		}
7151 	}
7152 	//Wyrmgus start
7153 //	HitUnit_LastAttack(attacker, target);
7154 	//Wyrmgus end
7155 	if (attacker) {
7156 		//Wyrmgus start
7157 		HitUnit_LastAttack(attacker, target); //only trigger the help me notification and AI code if there is actually an attacker
7158 		//Wyrmgus end
7159 		target.DamagedType = ExtraDeathIndex(attacker->Type->DamageType.c_str());
7160 	}
7161 
7162 	// OnHit callback
7163 	if (type->OnHit) {
7164 		const int tarSlot = UnitNumber(target);
7165 		const int atSlot = attacker && attacker->IsAlive() ? UnitNumber(*attacker) : -1;
7166 
7167 		type->OnHit->pushPreamble();
7168 		type->OnHit->pushInteger(tarSlot);
7169 		type->OnHit->pushInteger(atSlot);
7170 		type->OnHit->pushInteger(damage);
7171 		type->OnHit->run();
7172 	}
7173 
7174 	// Increase variables and call OnImpact
7175 	if (missile && missile->Type) {
7176 		if (missile->Type->ChangeVariable != -1) {
7177 			HitUnit_ChangeVariable(target, *missile);
7178 		}
7179 		if (missile->Type->OnImpact) {
7180 			const int attackerSlot = attacker ? UnitNumber(*attacker) : -1;
7181 			const int targetSlot = UnitNumber(target);
7182 			missile->Type->OnImpact->pushPreamble();
7183 			missile->Type->OnImpact->pushInteger(attackerSlot);
7184 			missile->Type->OnImpact->pushInteger(targetSlot);
7185 			missile->Type->OnImpact->pushInteger(damage);
7186 			missile->Type->OnImpact->run();
7187 		}
7188 	}
7189 
7190 	HitUnit_Raid(attacker, target, damage);
7191 
7192 	if (HitUnit_IsUnitWillDie(attacker, target, damage)) { // unit is killed or destroyed
7193 		if (attacker) {
7194 			//  Setting ai threshold counter to 0 so it can target other units
7195 			attacker->Threshold = 0;
7196 		}
7197 
7198 		CUnit *destroyer = attacker;
7199 		if (!destroyer) {
7200 			int best_distance = 0;
7201 			std::vector<CUnit *> table;
7202 			SelectAroundUnit(target, ExperienceRange, table, IsEnemyWith(*target.Player));
7203 			for (size_t i = 0; i < table.size(); i++) {
7204 				CUnit *potential_destroyer = table[i];
7205 				int distance = target.MapDistanceTo(*potential_destroyer);
7206 				if (!destroyer || distance < best_distance) {
7207 					destroyer = potential_destroyer;
7208 					best_distance = distance;
7209 				}
7210 			}
7211 		}
7212 		if (destroyer) {
7213 			if (target.IsEnemy(*destroyer) || target.Player->Type == PlayerNeutral) {
7214 				HitUnit_IncreaseScoreForKill(*destroyer, target);
7215 			}
7216 		}
7217 		LetUnitDie(target);
7218 		return;
7219 	}
7220 
7221 	HitUnit_ApplyDamage(attacker, target, damage);
7222 	HitUnit_BuildingCapture(attacker, target, damage);
7223 	//Wyrmgus start
7224 //	HitUnit_ShowDamageMissile(target, damage);
7225 	if (show_damage) {
7226 		HitUnit_ShowDamageMissile(target, damage);
7227 	}
7228 	//Wyrmgus end
7229 
7230 	HitUnit_ShowImpactMissile(target);
7231 
7232 	//Wyrmgus start
7233 //	if (type->BoolFlag[BUILDING_INDEX].value && !target.Burning) {
7234 	if (type->BoolFlag[BUILDING_INDEX].value && !target.Burning && !target.UnderConstruction && target.Type->TileSize.x != 1 && target.Type->TileSize.y != 1) { //the building shouldn't burn if it's still under construction, or if it's too small
7235 	//Wyrmgus end
7236 		HitUnit_Burning(target);
7237 	}
7238 
7239 	/* Target Reaction on Hit */
7240 	if (target.Player->AiEnabled) {
7241 		if (target.CurrentOrder()->OnAiHitUnit(target, attacker, damage)) {
7242 			return;
7243 		}
7244 	}
7245 
7246 	if (!attacker) {
7247 		return;
7248 	}
7249 
7250 	// Can't attack run away.
7251 	//Wyrmgus start
7252 //	if (!target.IsAgressive() && target.CanMove() && target.CurrentAction() == UnitActionStill && !target.BoardCount) {
7253 	if (
7254 		(!target.IsAgressive() || attacker->Type->BoolFlag[INDESTRUCTIBLE_INDEX].value)
7255 		&& target.CanMove()
7256 		&& (target.CurrentAction() == UnitActionStill || target.Variable[TERROR_INDEX].Value > 0)
7257 		&& !target.BoardCount
7258 		&& !target.Type->BoolFlag[BRIDGE_INDEX].value
7259 	) {
7260 	//Wyrmgus end
7261 		HitUnit_RunAway(target, *attacker);
7262 	}
7263 
7264 	const int threshold = 30;
7265 
7266 	if (target.Threshold && target.CurrentOrder()->HasGoal() && target.CurrentOrder()->GetGoal() == attacker) {
7267 		target.Threshold = threshold;
7268 		return;
7269 	}
7270 
7271 	//Wyrmgus start
7272 //	if (target.Threshold == 0 && target.IsAgressive() && target.CanMove() && !target.ReCast) {
7273 	if (
7274 		target.Threshold == 0
7275 		&& (target.IsAgressive() || (target.CanAttack() && target.Type->BoolFlag[COWARD_INDEX].value && (attacker->Type->BoolFlag[COWARD_INDEX].value || attacker->Variable[HP_INDEX].Value <= 3))) // attacks back if isn't coward, or if attacker is also coward, or if attacker has 3 HP or less
7276 		&& target.CanMove()
7277 		&& !target.ReCast
7278 		&& !attacker->Type->BoolFlag[INDESTRUCTIBLE_INDEX].value // don't attack indestructible units back
7279 	) {
7280 	//Wyrmgus end
7281 		// Attack units in range (which or the attacker?)
7282 		// Don't bother unit if it casting repeatable spell
7283 		HitUnit_AttackBack(*attacker->GetFirstContainer(), target); //if the unit is in a container, attack it instead of the unit (which is removed and thus unreachable)
7284 	}
7285 
7286 	// What should we do with workers on :
7287 	// case UnitActionRepair:
7288 	// Drop orders and run away or return after escape?
7289 }
7290 
7291 /*----------------------------------------------------------------------------
7292 --  Conflicts
7293 ----------------------------------------------------------------------------*/
7294 
7295 /**
7296  **	@brief	Returns the map distance between this unit and another one
7297  **
7298  **	@param	dst	The unit the distance to which is to be obtained
7299  **
7300  **	@return	The distance to the other unit, in tiles
7301  */
MapDistanceTo(const CUnit & dst) const7302 int CUnit::MapDistanceTo(const CUnit &dst) const
7303 {
7304 	if (this->MapLayer != dst.MapLayer) {
7305 		return 16384;
7306 	}
7307 
7308 	return MapDistanceBetweenTypes(*this->GetFirstContainer()->Type, this->tilePos, this->MapLayer->ID, *dst.Type, dst.tilePos, dst.MapLayer->ID);
7309 }
7310 
7311 /**
7312  **  Returns the map distance to unit.
7313  **
7314  **  @param pos   map tile position.
7315  **
7316  **  @return      The distance between in tiles.
7317  */
MapDistanceTo(const Vec2i & pos,int z) const7318 int CUnit::MapDistanceTo(const Vec2i &pos, int z) const
7319 {
7320 	//Wyrmgus start
7321 	if (z != this->MapLayer->ID) {
7322 		return 16384;
7323 	}
7324 	//Wyrmgus end
7325 
7326 	int dx;
7327 	int dy;
7328 
7329 	if (pos.x <= tilePos.x) {
7330 		dx = tilePos.x - pos.x;
7331 	//Wyrmgus start
7332 	} else if (this->Container) { //if unit is within another, use the tile size of the transporter to calculate the distance
7333 		dx = std::max<int>(0, pos.x - tilePos.x - this->Container->Type->TileSize.x + 1);
7334 	//Wyrmgus end
7335 	} else {
7336 		dx = std::max<int>(0, pos.x - tilePos.x - Type->TileSize.x + 1);
7337 	}
7338 	if (pos.y <= tilePos.y) {
7339 		dy = tilePos.y - pos.y;
7340 	//Wyrmgus start
7341 	} else if (this->Container) {
7342 		dy = std::max<int>(0, pos.y - tilePos.y - this->Container->Type->TileSize.y + 1);
7343 	//Wyrmgus end
7344 	} else {
7345 		dy = std::max<int>(0, pos.y - tilePos.y - Type->TileSize.y + 1);
7346 	}
7347 	return isqrt(dy * dy + dx * dx);
7348 }
7349 
7350 /**
7351 **	@brief	Returns the map distance between two points with unit type
7352 **
7353 **	@param	src		Source unit type
7354 **	@param	pos1	Map tile position of the source (upper-left)
7355 **	@param	dst		Destination unit type to take into account
7356 **	@param	pos2	Map tile position of the destination
7357 **
7358 **	@return	The distance between the types
7359 */
MapDistanceBetweenTypes(const CUnitType & src,const Vec2i & pos1,int src_z,const CUnitType & dst,const Vec2i & pos2,int dst_z)7360 int MapDistanceBetweenTypes(const CUnitType &src, const Vec2i &pos1, int src_z, const CUnitType &dst, const Vec2i &pos2, int dst_z)
7361 {
7362 	return MapDistance(src.TileSize, pos1, src_z, dst.TileSize, pos2, dst_z);
7363 }
7364 
MapDistance(const Vec2i & src_size,const Vec2i & pos1,int src_z,const Vec2i & dst_size,const Vec2i & pos2,int dst_z)7365 int MapDistance(const Vec2i &src_size, const Vec2i &pos1, int src_z, const Vec2i &dst_size, const Vec2i &pos2, int dst_z)
7366 {
7367 	if (src_z != dst_z) {
7368 		return 16384;
7369 	}
7370 
7371 	int dx;
7372 	int dy;
7373 
7374 	if (pos1.x + src_size.x <= pos2.x) {
7375 		dx = std::max<int>(0, pos2.x - pos1.x - src_size.x + 1);
7376 	} else {
7377 		dx = std::max<int>(0, pos1.x - pos2.x - dst_size.x + 1);
7378 	}
7379 	if (pos1.y + src_size.y <= pos2.y) {
7380 		dy = pos2.y - pos1.y - src_size.y + 1;
7381 	} else {
7382 		dy = std::max<int>(0, pos1.y - pos2.y - dst_size.y + 1);
7383 	}
7384 	return isqrt(dy * dy + dx * dx);
7385 }
7386 
7387 /**
7388 **  Compute the distance from the view point to a given point.
7389 **
7390 **  @param pos  map tile position.
7391 **
7392 **  @todo FIXME: is it the correct place to put this function in?
7393 */
ViewPointDistance(const Vec2i & pos)7394 int ViewPointDistance(const Vec2i &pos)
7395 {
7396 	const CViewport &vp = *UI.SelectedViewport;
7397 	const Vec2i vpSize(vp.MapWidth, vp.MapHeight);
7398 	const Vec2i middle = vp.MapPos + vpSize / 2;
7399 
7400 	return Distance(middle, pos);
7401 }
7402 
7403 /**
7404 **  Compute the distance from the view point to a given unit.
7405 **
7406 **  @param dest  Distance to this unit.
7407 **
7408 **  @todo FIXME: is it the correct place to put this function in?
7409 */
ViewPointDistanceToUnit(const CUnit & dest)7410 int ViewPointDistanceToUnit(const CUnit &dest)
7411 {
7412 	const CViewport &vp = *UI.SelectedViewport;
7413 	const Vec2i vpSize(vp.MapWidth, vp.MapHeight);
7414 	const Vec2i midPos = vp.MapPos + vpSize / 2;
7415 
7416 	//Wyrmgus start
7417 //	return dest.MapDistanceTo(midPos);
7418 	return dest.MapDistanceTo(midPos, UI.CurrentMapLayer->ID);
7419 	//Wyrmgus end
7420 }
7421 
7422 /**
7423 **  Can the source unit attack the destination unit.
7424 **
7425 **  @param source  Unit type pointer of the attacker.
7426 **  @param dest    Unit type pointer of the target.
7427 **
7428 **  @return        0 if attacker can't target the unit, else a positive number.
7429 */
CanTarget(const CUnitType & source,const CUnitType & dest)7430 int CanTarget(const CUnitType &source, const CUnitType &dest)
7431 {
7432 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
7433 		if (source.BoolFlag[i].CanTargetFlag != CONDITION_TRUE) {
7434 			if ((source.BoolFlag[i].CanTargetFlag == CONDITION_ONLY) ^
7435 				(dest.BoolFlag[i].value)) {
7436 				return 0;
7437 			}
7438 		}
7439 	}
7440 	if (dest.UnitType == UnitTypeLand) {
7441 		if (dest.BoolFlag[SHOREBUILDING_INDEX].value) {
7442 			return source.CanTarget & (CanTargetLand | CanTargetSea);
7443 		}
7444 		return source.CanTarget & CanTargetLand;
7445 	}
7446 	if (dest.UnitType == UnitTypeFly) {
7447 		return source.CanTarget & CanTargetAir;
7448 	}
7449 	//Wyrmgus start
7450 	if (dest.UnitType == UnitTypeFlyLow) {
7451 		return (source.CanTarget & CanTargetLand) || (source.CanTarget & CanTargetAir) || (source.CanTarget & CanTargetSea);
7452 	}
7453 	//Wyrmgus end
7454 	if (dest.UnitType == UnitTypeNaval) {
7455 		return source.CanTarget & CanTargetSea;
7456 	}
7457 	return 0;
7458 }
7459 
7460 /**
7461 **  Can the transporter transport the other unit.
7462 **
7463 **  @param transporter  Unit which is the transporter.
7464 **  @param unit         Unit which wants to go in the transporter.
7465 **
7466 **  @return             1 if transporter can transport unit, 0 else.
7467 */
CanTransport(const CUnit & transporter,const CUnit & unit)7468 int CanTransport(const CUnit &transporter, const CUnit &unit)
7469 {
7470 	if (!transporter.Type->CanTransport()) {
7471 		return 0;
7472 	}
7473 	if (transporter.CurrentAction() == UnitActionBuilt) { // Under construction
7474 		return 0;
7475 	}
7476 	if (&transporter == &unit) { // Cannot transporter itself.
7477 		return 0;
7478 	}
7479 	//Wyrmgus start
7480 	/*
7481 	if (transporter.BoardCount >= transporter.Type->MaxOnBoard) { // full
7482 		return 0;
7483 	}
7484 	*/
7485 
7486 	if (transporter.ResourcesHeld > 0 && transporter.CurrentResource) { //cannot transport units if already has cargo
7487 		return 0;
7488 	}
7489 	//Wyrmgus end
7490 
7491 	if (transporter.BoardCount + unit.Type->BoardSize > transporter.Type->MaxOnBoard) { // too big unit
7492 		return 0;
7493 	}
7494 
7495 	// Can transport only allied unit.
7496 	// FIXME : should be parametrable.
7497 	//Wyrmgus start
7498 //	if (!transporter.IsTeamed(unit)) {
7499 	if (!transporter.IsTeamed(unit) && !transporter.IsAllied(unit) && transporter.Player->Type != PlayerNeutral && unit.Player->Type != PlayerNeutral) {
7500 	//Wyrmgus end
7501 		return 0;
7502 	}
7503 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
7504 		if (transporter.Type->BoolFlag[i].CanTransport != CONDITION_TRUE) {
7505 			if ((transporter.Type->BoolFlag[i].CanTransport == CONDITION_ONLY) ^ unit.Type->BoolFlag[i].value) {
7506 				return 0;
7507 			}
7508 		}
7509 	}
7510 	return 1;
7511 }
7512 
7513 //Wyrmgus start
7514 /**
7515 **  Can the unit pick up the other unit.
7516 **
7517 **  @param picker		Unit which is the picker.
7518 **  @param unit         Unit which wants to be picked.
7519 **
7520 **  @return             true if picker can pick up unit, false else.
7521 */
CanPickUp(const CUnit & picker,const CUnit & unit)7522 bool CanPickUp(const CUnit &picker, const CUnit &unit)
7523 {
7524 	if (!picker.Type->BoolFlag[ORGANIC_INDEX].value) { //only organic units can pick up power-ups and items
7525 		return false;
7526 	}
7527 	if (!unit.Type->BoolFlag[ITEM_INDEX].value && !unit.Type->BoolFlag[POWERUP_INDEX].value) { //only item and powerup units can be picked up
7528 		return false;
7529 	}
7530 	if (!unit.Type->BoolFlag[POWERUP_INDEX].value && !picker.HasInventory() && !IsItemClassConsumable(unit.Type->ItemClass)) { //only consumable items can be picked up as if they were power-ups for units with no inventory
7531 		return false;
7532 	}
7533 	if (picker.CurrentAction() == UnitActionBuilt) { // Under construction
7534 		return false;
7535 	}
7536 	if (&picker == &unit) { // Cannot pick up itself.
7537 		return false;
7538 	}
7539 	if (picker.HasInventory() && unit.Type->BoolFlag[ITEM_INDEX].value && picker.InsideCount >= ((int) UI.InventoryButtons.size())) { // full
7540 		if (picker.Player == ThisPlayer) {
7541 			std::string picker_name = picker.Name + "'s (" + picker.GetTypeName() + ")";
7542 			picker.Player->Notify(NotifyRed, picker.tilePos, picker.MapLayer->ID, _("%s inventory is full."), picker_name.c_str());
7543 		}
7544 		return false;
7545 	}
7546 
7547 	return true;
7548 }
7549 //Wyrmgus end
7550 
7551 /**
7552 **  Check if the player is an enemy
7553 **
7554 **  @param player  Player to check
7555 */
IsEnemy(const CPlayer & player) const7556 bool CUnit::IsEnemy(const CPlayer &player) const
7557 {
7558 	//Wyrmgus start
7559 	if (this->Player->Index != player.Index && player.Type != PlayerNeutral && !this->Player->HasBuildingAccess(player) && this->Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value && this->IsAgressive()) {
7560 		return true;
7561 	}
7562 	//Wyrmgus end
7563 
7564 	return this->Player->IsEnemy(player);
7565 }
7566 
7567 /**
7568 **  Check if the unit is an enemy
7569 **
7570 **  @param unit  Unit to check
7571 */
IsEnemy(const CUnit & unit) const7572 bool CUnit::IsEnemy(const CUnit &unit) const
7573 {
7574 	//Wyrmgus start
7575 	if (
7576 		this->Player->Type == PlayerNeutral
7577 		&& this->Type->BoolFlag[FAUNA_INDEX].value
7578 		&& this->Type->BoolFlag[ORGANIC_INDEX].value
7579 		&& unit.Type->BoolFlag[ORGANIC_INDEX].value
7580 		&& this->Type->Slot != unit.Type->Slot
7581 	) {
7582 		if (
7583 			this->Type->BoolFlag[PREDATOR_INDEX].value
7584 			&& !unit.Type->BoolFlag[PREDATOR_INDEX].value
7585 			&& this->CanEat(unit)
7586 		) {
7587 			return true;
7588 		} else if (
7589 			this->Type->BoolFlag[PEOPLEAVERSION_INDEX].value
7590 			&& !unit.Type->BoolFlag[FAUNA_INDEX].value
7591 			&& unit.Player->Type != PlayerNeutral
7592 			&& this->MapDistanceTo(unit) <= 1
7593 		) {
7594 			return true;
7595 		}
7596 	}
7597 
7598 	if (
7599 		unit.Player->Type == PlayerNeutral
7600 		&& unit.Type->BoolFlag[PREDATOR_INDEX].value
7601 		&& this->Player->Type != PlayerNeutral
7602 	) {
7603 		return true;
7604 	}
7605 
7606 	if (
7607 		this->Player != unit.Player
7608 		&& this->Player->Type != PlayerNeutral
7609 		&& unit.CurrentAction() == UnitActionAttack
7610 		&& unit.CurrentOrder()->HasGoal()
7611 		&& unit.CurrentOrder()->GetGoal()->Player == this->Player
7612 		&& !unit.CurrentOrder()->GetGoal()->Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value
7613 	) {
7614 		return true;
7615 	}
7616 
7617 	if (
7618 		this->Player != unit.Player && this->Player->Type != PlayerNeutral && unit.Player->Type != PlayerNeutral && !this->Player->HasBuildingAccess(*unit.Player) && !this->Player->HasNeutralFactionType()
7619 		&& ((this->Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value && this->IsAgressive()) || (unit.Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value && unit.IsAgressive()))
7620 	) {
7621 		return true;
7622 	}
7623 
7624 	return IsEnemy(*unit.Player);
7625 	//Wyrmgus end
7626 }
7627 
7628 /**
7629 **  Check if the player is an ally
7630 **
7631 **  @param player  Player to check
7632 */
IsAllied(const CPlayer & player) const7633 bool CUnit::IsAllied(const CPlayer &player) const
7634 {
7635 	return this->Player->IsAllied(player);
7636 }
7637 
7638 /**
7639 **  Check if the unit is an ally
7640 **
7641 **  @param x  Unit to check
7642 */
IsAllied(const CUnit & unit) const7643 bool CUnit::IsAllied(const CUnit &unit) const
7644 {
7645 	return IsAllied(*unit.Player);
7646 }
7647 
7648 /**
7649 **  Check if unit shares vision with the player
7650 **
7651 **  @param x  Player to check
7652 */
IsSharedVision(const CPlayer & player) const7653 bool CUnit::IsSharedVision(const CPlayer &player) const
7654 {
7655 	return this->Player->IsSharedVision(player);
7656 }
7657 
7658 /**
7659 **  Check if the unit shares vision with the unit
7660 **
7661 **  @param x  Unit to check
7662 */
IsSharedVision(const CUnit & unit) const7663 bool CUnit::IsSharedVision(const CUnit &unit) const
7664 {
7665 	return IsSharedVision(*unit.Player);
7666 }
7667 
7668 /**
7669 **  Check if both players share vision
7670 **
7671 **  @param x  Player to check
7672 */
IsBothSharedVision(const CPlayer & player) const7673 bool CUnit::IsBothSharedVision(const CPlayer &player) const
7674 {
7675 	return this->Player->IsBothSharedVision(player);
7676 }
7677 
7678 /**
7679 **  Check if both units share vision
7680 **
7681 **  @param x  Unit to check
7682 */
IsBothSharedVision(const CUnit & unit) const7683 bool CUnit::IsBothSharedVision(const CUnit &unit) const
7684 {
7685 	return IsBothSharedVision(*unit.Player);
7686 }
7687 
7688 /**
7689 **  Check if the player is on the same team
7690 **
7691 **  @param x  Player to check
7692 */
IsTeamed(const CPlayer & player) const7693 bool CUnit::IsTeamed(const CPlayer &player) const
7694 {
7695 	return (this->Player->Team == player.Team);
7696 }
7697 
7698 /**
7699 **  Check if the unit is on the same team
7700 **
7701 **  @param x  Unit to check
7702 */
IsTeamed(const CUnit & unit) const7703 bool CUnit::IsTeamed(const CUnit &unit) const
7704 {
7705 	return this->IsTeamed(*unit.Player);
7706 }
7707 
7708 /**
7709 **  Check if the unit is unusable (for attacking...)
7710 **  @todo look if correct used (UnitActionBuilt is no problem if attacked)?
7711 */
IsUnusable(bool ignore_built_state) const7712 bool CUnit::IsUnusable(bool ignore_built_state) const
7713 {
7714 	return (!IsAliveOnMap() || (!ignore_built_state && CurrentAction() == UnitActionBuilt));
7715 }
7716 
7717 /**
7718 **  Check if the unit attacking its goal will result in a ranged attack
7719 */
7720 //Wyrmgus start
7721 //bool CUnit::IsAttackRanged(CUnit *goal, const Vec2i &goalPos)
IsAttackRanged(CUnit * goal,const Vec2i & goalPos,int z)7722 bool CUnit::IsAttackRanged(CUnit *goal, const Vec2i &goalPos, int z)
7723 //Wyrmgus end
7724 {
7725 	if (this->Variable[ATTACKRANGE_INDEX].Value <= 1) { //always return false if the units attack range is 1 or lower
7726 		return false;
7727 	}
7728 
7729 	if (this->Container) { //if the unit is inside a container, the attack will always be ranged
7730 		return true;
7731 	}
7732 
7733 	if (
7734 		goal
7735 		&& goal->IsAliveOnMap()
7736 		&& (
7737 			this->MapDistanceTo(*goal) > 1
7738 			|| (this->Type->UnitType != UnitTypeFly && goal->Type->UnitType == UnitTypeFly)
7739 			|| (this->Type->UnitType == UnitTypeFly && goal->Type->UnitType != UnitTypeFly)
7740 		)
7741 	) {
7742 		return true;
7743 	}
7744 
7745 	if (!goal && Map.Info.IsPointOnMap(goalPos, z) && this->MapDistanceTo(goalPos, z) > 1) {
7746 		return true;
7747 	}
7748 
7749 	return false;
7750 }
7751 
7752 /*----------------------------------------------------------------------------
7753 --  Initialize/Cleanup
7754 ----------------------------------------------------------------------------*/
7755 
7756 /**
7757 **  Initialize unit module.
7758 */
InitUnits()7759 void InitUnits()
7760 {
7761 	if (!SaveGameLoading) {
7762 		UnitManager.Init();
7763 	}
7764 }
7765 
7766 /**
7767 **  Clean up unit module.
7768 */
CleanUnits()7769 void CleanUnits()
7770 {
7771 	//  Free memory for all units in unit table.
7772 	std::vector<CUnit *> units(UnitManager.begin(), UnitManager.end());
7773 
7774 	for (std::vector<CUnit *>::iterator it = units.begin(); it != units.end(); ++it) {
7775 		//Wyrmgus start
7776 		if (*it == nullptr) {
7777 			fprintf(stderr, "Error in CleanUnits: unit is null.\n");
7778 			continue;
7779 		}
7780 		//Wyrmgus end
7781 		CUnit &unit = **it;
7782 
7783 		//Wyrmgus start
7784 		/*
7785 		if (&unit == nullptr) {
7786 			continue;
7787 		}
7788 		*/
7789 		//Wyrmgus end
7790 		//Wyrmgus start
7791 		if (unit.Type == nullptr) {
7792 			fprintf(stderr, "Unit \"%d\"'s type is null.\n", UnitNumber(unit));
7793 		}
7794 		//Wyrmgus end
7795 		if (!unit.Destroyed) {
7796 			if (!unit.Removed) {
7797 				unit.Remove(nullptr);
7798 			}
7799 			UnitClearOrders(unit);
7800 		}
7801 		unit.Release(true);
7802 	}
7803 
7804 	UnitManager.Init();
7805 
7806 	FancyBuildings = false;
7807 	HelpMeLastCycle = 0;
7808 }
7809 
7810 //@}
7811