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 unittype.cpp - The unit types. */
12 //
13 //      (c) Copyright 1998-2015 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 "unittype.h"
39 
40 #include "animation.h"
41 #include "animation/animation_exactframe.h"
42 #include "animation/animation_frame.h"
43 #include "construct.h"
44 #include "iolib.h"
45 #include "luacallback.h"
46 #include "map.h"
47 #include "missile.h"
48 #include "player.h"
49 #include "script.h"
50 #include "sound.h"
51 #include "spells.h"
52 #include "tileset.h"
53 #include "translate.h"
54 #include "ui.h"
55 #include "unitsound.h"
56 #include "util.h"
57 #include "video.h"
58 
59 #include <ctype.h>
60 
61 #include <string>
62 #include <map>
63 
64 /*----------------------------------------------------------------------------
65 -- Documentation
66 ----------------------------------------------------------------------------*/
67 
68 /**
69 **  @class CUnitType unittype.h
70 **
71 **  \#include "unittype.h"
72 **
73 **  This class contains the information that is shared between all
74 **  units of the same type and determins if a unit is a building,
75 **  a person, ...
76 **
77 **  The unit-type class members:
78 **
79 **  CUnitType::Ident
80 **
81 **    Unique identifier of the unit-type, used to reference it in
82 **    config files and during startup. As convention they start with
83 **    "unit-" fe. "unit-farm".
84 **  @note Don't use this member in game, use instead the pointer
85 **  to this structure. See UnitTypeByIdent().
86 **
87 **  CUnitType::Name
88 **
89 **    Pretty name shown by the engine. The name should be shorter
90 **    than 17 characters and no word can be longer than 8 characters.
91 **
92 **  CUnitType::File
93 **
94 **    Path file name of the sprite file.
95 **
96 **  CUnitType::ShadowFile
97 **
98 **    Path file name of shadow sprite file.
99 **
100 **  CUnitType::DrawLevel
101 **
102 **    The Level/Order to draw this type of unit in. 0-255 usually.
103 **
104 **  CUnitType::Width CUnitType::Height
105 **
106 **    Size of a sprite frame in pixels. All frames of a sprite have
107 **    the same size. Also all sprites (tilesets) must have the same
108 **    size.
109 **
110 **  CUnitType::ShadowWidth CUnitType::ShadowHeight
111 **
112 **    Size of a shadow sprite frame in pixels. All frames of a sprite
113 **    have the same size. Also all sprites (tilesets) must have the
114 **    same size.
115 **
116 **  CUnitType::ShadowOffsetX CUnitType::ShadowOffsetY
117 **
118 **    Vertical offset to draw the shadow in pixels.
119 **
120 **  CUnitType::Animations
121 **
122 **    Animation scripts for the different actions. Currently the
123 **    animations still, move, attack and die are supported.
124 **  @see CAnimations
125 **  @see CAnimation
126 **
127 **  CUnitType::Icon
128 **
129 **    Icon to display for this unit-type. Contains configuration and
130 **    run time variable.
131 **  @note This icon can be used for training, but isn't used.
132 **
133 **  CUnitType::Missile
134 **
135 **    Configuration and run time variable of the missile weapon.
136 **  @note It is planned to support more than one weapons.
137 **  And the sound of the missile should be used as fire sound.
138 **
139 **  CUnitType::Explosion
140 **
141 **    Configuration and run time variable of the missile explosion.
142 **    This is the explosion that happens if unit is set to
143 **    ExplodeWhenKilled
144 **
145 **  CUnitType::CorpseName
146 **
147 **    Corpse unit-type name, should only be used during setup.
148 **
149 **  CUnitType::CorpseType
150 **
151 **    Corpse unit-type pointer, only this should be used during run
152 **    time. Many unit-types can share the same corpse.
153 **
154 **
155 **  @todo continue this documentation
156 **
157 **  CUnitType::Construction
158 **
159 **    What is shown in construction phase.
160 **
161 **  CUnitType::SightRange
162 **
163 **    Sight range
164 **
165 **  CUnitType::_HitPoints
166 **
167 **    Maximum hit points
168 **
169 **
170 **  CUnitType::_Costs[::MaxCosts]
171 **
172 **    How many resources needed
173 **
174 **  CUnitType::RepairHP
175 **
176 **    The HP given to a unit each cycle it's repaired.
177 **    If zero, unit cannot be repaired
178 **
179 **    CUnitType::RepairCosts[::MaxCosts]
180 **
181 **    Costs per repair cycle to fix a unit.
182 **
183 **  CUnitType::TileWidth
184 **
185 **    Tile size on map width
186 **
187 **  CUnitType::TileHeight
188 **
189 **    Tile size on map height
190 **
191 **  CUnitType::BoxWidth
192 **
193 **    Selected box size width
194 **
195 **  CUnitType::BoxHeight
196 **
197 **    Selected box size height
198 **
199 **  CUnitType::NumDirections
200 **
201 **    Number of directions the unit can face
202 **
203 **  CUnitType::MinAttackRange
204 **
205 **    Minimal attack range
206 **
207 **  CUnitType::ReactRangeComputer
208 **
209 **    Reacts on enemy for computer
210 **
211 **  CUnitType::ReactRangePerson
212 **
213 **    Reacts on enemy for person player
214 **
215 **  CUnitType::Priority
216 **
217 **    Priority value / AI Treatment
218 **
219 **  CUnitType::BurnPercent
220 **
221 **    The burning limit in percents. If the unit has lees than
222 **    this it will start to burn.
223 **
224 **  CUnitType::BurnDamageRate
225 **
226 **    Burn rate in HP per second
227 **
228 **  CUnitType::UnitType
229 **
230 **    Land / fly / naval
231 **
232 **  @note original only visual effect, we do more with this!
233 **
234 **  CUnitType::DecayRate
235 **
236 **    Decay rate in 1/6 seconds
237 **
238 **  CUnitType::AnnoyComputerFactor
239 **
240 **    How much this annoys the computer
241 **
242 **  @todo not used
243 **
244 **  CUnitType::MouseAction
245 **
246 **    Right click action
247 **
248 **  CUnitType::Points
249 **
250 **    How many points you get for unit. Used in the final score table.
251 **
252 **  CUnitType::CanTarget
253 **
254 **    Which units can it attack
255 **
256 **  CUnitType::LandUnit
257 **
258 **    Land animated
259 **
260 **  CUnitType::AirUnit
261 **
262 **    Air animated
263 **
264 **  CUnitType::SeaUnit
265 **
266 **    Sea animated
267 **
268 **  CUnitType::ExplodeWhenKilled
269 **
270 **    Death explosion animated
271 **
272 **  CUnitType::RandomMovementProbability
273 **
274 **    When the unit is idle this is the probability that it will
275 **    take a step in a random direction, in percents.
276 **
277 **  CUnitType::ClicksToExplode
278 **
279 **    If this is non-zero, then after that many clicks the unit will
280 **    commit suicide. Doesn't work with resource workers/resources.
281 **
282 **  CUnitType::Building
283 **
284 **    Unit is a Building
285 **
286 **  CUnitType::Transporter
287 **
288 **    Can transport units
289 **
290 **  CUnitType::MaxOnBoard
291 **
292 **    Maximum units on board (for transporters), and resources
293 **
294 **  CUnitType::StartingResources
295 **    Amount of Resources a unit has when It's Built
296 **
297 **  CUnitType::DamageType
298 **    Unit's missile damage type (used for extra death animations)
299 **
300 **  CUnitType::GivesResource
301 **
302 **    This equals to the resource Id of the resource given
303 **    or 0 (TimeCost) for other buildings.
304 **
305 **  CUnitType::ResInfo[::MaxCosts]
306 **
307 **    Information about resource harvesting. If NULL, it can't
308 **    harvest it.
309 **
310 **  CUnitType::NeutralMinimapColorRGB
311 **
312 **    Says what color a unit will have when it's neutral and
313 **    is displayed on the minimap.
314 **
315 **  CUnitType::CanStore[::MaxCosts]
316 **
317 **    What resource types we can store here.
318 **
319 **  CUnitType::CanCastSpell
320 **
321 **    Unit is able to use spells
322 **
323 **  CUnitType::CanAttack
324 **
325 **    Unit is able to attack.
326 **
327 **  CUnitType::RepairRange
328 **
329 **    Unit can repair buildings. It will use the actack animation.
330 **    It will heal 4 points for every repair cycle, and cost 1 of
331 **    each resource, alternatively(1 cycle wood, 1 cycle gold)
332 **  @todo The above should be more configurable.
333 **    If units have a repair range, they can repair, and this is the
334 **    distance.
335 **
336 **    CUnitType::ShieldPiercing
337 **
338 **    Can directly damage shield-protected units, without shield damaging.
339 **
340 **  CUnitType::Sound
341 **
342 **    Sounds for events
343 **
344 **  CUnitType::Weapon
345 **
346 **    Current sound for weapon
347 **
348 **  @todo temporary solution
349 **
350 **  CUnitType::FieldFlags
351 **
352 **    Flags that are set, if a unit enters a map field or cleared, if
353 **    a unit leaves a map field.
354 **
355 **  CUnitType::MovementMask
356 **
357 **    Movement mask, this value is and'ed to the map field flags, to
358 **    see if a unit can enter or placed on the map field.
359 **
360 **  CUnitType::Stats[::PlayerMax]
361 **
362 **    Unit status for each player
363 **  @todo This stats should? be moved into the player struct
364 **
365 **  CUnitType::Type
366 **
367 **    Type as number
368 **  @todo Should us a general name f.e. Slot here?
369 **
370 **  CUnitType::Sprite
371 **
372 **    Sprite images
373 **
374 **  CUnitType::ShadowSprite
375 **
376 **    Shadow sprite images
377 **
378 **
379 */
380 /**
381 **
382 **  @class ResourceInfo unittype.h
383 **
384 ** \#include "unittype.h"
385 **
386 **    This class contains information about how a unit will harvest a resource.
387 **
388 **  ResourceInfo::FileWhenLoaded
389 **
390 **    The harvester's animation file will change when it's loaded.
391 **
392 **  ResourceInfo::FileWhenEmpty;
393 **
394 **    The harvester's animation file will change when it's empty.
395 **    The standard animation is used only when building/repairing.
396 **
397 **
398 **  ResourceInfo::HarvestFromOutside
399 **
400 **    Unit will harvest from the outside. The unit will use it's
401 **    Attack animation (seems it turned into a generic Action anim.)
402 **
403 **  ResourceInfo::ResourceId
404 **
405 **    The resource this is for. Mostly redundant.
406 **
407 **  ResourceInfo::FinalResource
408 **
409 **    The resource is converted to this at the depot. Useful for
410 **    a fisherman who harvests fish, but it all turns to food at the
411 **    depot.
412 **
413 **  ResourceInfo::WaitAtResource
414 **
415 **    Cycles the unit waits while inside a resource.
416 **
417 **  ResourceInfo::ResourceStep
418 **
419 **    The unit makes so-caled mining cycles. Each mining cycle
420 **    it does some sort of animation and gains ResourceStep
421 **    resources. You can stop after any number of steps.
422 **    when the quantity in the harvester reaches the maximum
423 **    (ResourceCapacity) it will return home. I this is 0 then
424 **    it's considered infinity, and ResourceCapacity will now
425 **    be the limit.
426 **
427 **  ResourceInfo::ResourceCapacity
428 **
429 **    Maximum amount of resources a harvester can carry. The
430 **    actual amount can be modified while unloading.
431 **
432 **  ResourceInfo::LoseResources
433 **
434 **    Special lossy behaviour for loaded harvesters. Harvesters
435 **    with loads other than 0 and ResourceCapacity will lose their
436 **    cargo on any new order.
437 **
438 **  ResourceInfo::WaitAtDepot
439 **
440 **    Cycles the unit waits while inside the depot to unload.
441 **
442 **  ResourceInfo::TerrainHarvester
443 **
444 **    The unit will harvest terrain. For now this only works
445 **    for wood. maybe it could be made to work for rocks, but
446 **    more than that requires a tileset rewrite.
447 **  @todo more configurable.
448 **
449 */
450 
451 /*----------------------------------------------------------------------------
452 --  Variables
453 ----------------------------------------------------------------------------*/
454 
455 std::vector<CUnitType *> UnitTypes;   /// unit-types definition
456 std::map<std::string, CUnitType *> UnitTypeMap;
457 
458 /**
459 **  Next unit type are used hardcoded in the source.
460 **
461 **  @todo find a way to make it configurable!
462 */
463 CUnitType *UnitTypeHumanWall;       /// Human wall
464 CUnitType *UnitTypeOrcWall;         /// Orc wall
465 
466 /**
467 **  Default incomes for a new player.
468 */
469 int DefaultIncomes[MaxCosts];
470 
471 /**
472 **  Default action for the resources.
473 */
474 std::string DefaultActions[MaxCosts];
475 
476 /**
477 **  Default names for the resources.
478 */
479 std::string DefaultResourceNames[MaxCosts];
480 
481 /**
482 **  Default amounts for the resources.
483 */
484 int DefaultResourceAmounts[MaxCosts];
485 
486 /**
487 **  Default max amounts for the resources.
488 */
489 int DefaultResourceMaxAmounts[MaxCosts];
490 
491 /**
492 **  Default names for the resources.
493 */
494 std::string ExtraDeathTypes[ANIMATIONS_DEATHTYPES];
495 
496 /*----------------------------------------------------------------------------
497 --  Functions
498 ----------------------------------------------------------------------------*/
499 
GetResourceIdByName(const char * resourceName)500 int GetResourceIdByName(const char *resourceName)
501 {
502 	for (unsigned int res = 0; res < MaxCosts; ++res) {
503 		if (!strcmp(resourceName, DefaultResourceNames[res].c_str())) {
504 			return res;
505 		}
506 	}
507 	return -1;
508 }
509 
GetResourceIdByName(lua_State * l,const char * resourceName)510 int GetResourceIdByName(lua_State *l, const char *resourceName)
511 {
512 	const int res = GetResourceIdByName(resourceName);
513 	if (res == -1) {
514 		LuaError(l, "Resource not found: %s" _C_ resourceName);
515 	}
516 	return res;
517 }
518 
CUnitType()519 CUnitType::CUnitType() :
520 	Slot(0), Width(0), Height(0), OffsetX(0), OffsetY(0), DrawLevel(0),
521 	ShadowWidth(0), ShadowHeight(0), ShadowOffsetX(0), ShadowOffsetY(0),
522 	Animations(NULL), StillFrame(0),
523 	OnDeath(NULL), OnHit(NULL), OnEachCycle(NULL), OnEachSecond(NULL), OnInit(NULL),
524 	OnReady(NULL), TeleportCost(0), TeleportEffectIn(NULL), TeleportEffectOut(NULL),
525 	CorpseType(NULL), Construction(NULL), RepairHP(0), TileWidth(0), TileHeight(0),
526 	BoxWidth(0), BoxHeight(0), BoxOffsetX(0), BoxOffsetY(0), NumDirections(0),
527 	MinAttackRange(0), ReactRangeComputer(0), ReactRangePerson(0),
528 	BurnPercent(0), BurnDamageRate(0), RepairRange(0),
529 	CanCastSpell(NULL), AutoCastActive(NULL),
530 	AutoBuildRate(0), RandomMovementProbability(0), RandomMovementDistance(1), ClicksToExplode(0),
531 	MaxOnBoard(0), BoardSize(1), ButtonLevelForTransporter(0), StartingResources(0),
532 	UnitType(UnitTypeLand), DecayRate(0), AnnoyComputerFactor(0), AiAdjacentRange(-1),
533 	MouseAction(0), CanTarget(0),
534 	Flip(0), LandUnit(0), AirUnit(0), SeaUnit(0),
535 	ExplodeWhenKilled(0), Building(0),
536 	CanAttack(0),
537 	Neutral(0),
538 	GivesResource(0), PoisonDrain(0), FieldFlags(0), MovementMask(0),
539 	Sprite(NULL), ShadowSprite(NULL), ShadowSpriteFrame(0), ShadowScale(1)
540 {
541 #ifdef USE_MNG
542 	memset(&Portrait, 0, sizeof(Portrait));
543 #endif
544 	memset(RepairCosts, 0, sizeof(RepairCosts));
545 	memset(CanStore, 0, sizeof(CanStore));
546 	memset(ResInfo, 0, sizeof(ResInfo));
547 	memset(MissileOffsets, 0, sizeof(MissileOffsets));
548 }
549 
~CUnitType()550 CUnitType::~CUnitType()
551 {
552 	delete OnDeath;
553 	delete OnHit;
554 	delete OnEachCycle;
555 	delete OnEachSecond;
556 	delete OnInit;
557 	delete OnReady;
558 	delete TeleportEffectIn;
559 	delete TeleportEffectOut;
560 
561 	BoolFlag.clear();
562 
563 	// Free Building Restrictions if there are any
564 	for (std::vector<CBuildRestriction *>::iterator b = BuildingRules.begin();
565 		 b != BuildingRules.end(); ++b) {
566 		delete *b;
567 	}
568 	BuildingRules.clear();
569 	for (std::vector<CBuildRestriction *>::iterator b = AiBuildingRules.begin();
570 		 b != AiBuildingRules.end(); ++b) {
571 		delete *b;
572 	}
573 	AiBuildingRules.clear();
574 
575 	delete[] CanCastSpell;
576 	delete[] AutoCastActive;
577 
578 	for (int res = 0; res < MaxCosts; ++res) {
579 		if (this->ResInfo[res]) {
580 			if (this->ResInfo[res]->SpriteWhenLoaded) {
581 				CGraphic::Free(this->ResInfo[res]->SpriteWhenLoaded);
582 			}
583 			if (this->ResInfo[res]->SpriteWhenEmpty) {
584 				CGraphic::Free(this->ResInfo[res]->SpriteWhenEmpty);
585 			}
586 			delete this->ResInfo[res];
587 		}
588 	}
589 
590 	CGraphic::Free(Sprite);
591 	CGraphic::Free(ShadowSprite);
592 #ifdef USE_MNG
593 	if (this->Portrait.Num) {
594 		for (int j = 0; j < this->Portrait.Num; ++j) {
595 			delete this->Portrait.Mngs[j];
596 			// delete[] this->Portrait.Files[j];
597 		}
598 		delete[] this->Portrait.Mngs;
599 		delete[] this->Portrait.Files;
600 	}
601 #endif
602 }
603 
GetPixelSize() const604 PixelSize CUnitType::GetPixelSize() const
605 {
606 	return PixelSize(TileWidth * PixelTileSize.x, TileHeight * PixelTileSize.y);
607 }
608 
CheckUserBoolFlags(const char * BoolFlags) const609 bool CUnitType::CheckUserBoolFlags(const char *BoolFlags) const
610 {
611 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); ++i) { // User defined flags
612 		if (BoolFlags[i] != CONDITION_TRUE &&
613 			((BoolFlags[i] == CONDITION_ONLY) ^ (BoolFlag[i].value))) {
614 			return false;
615 		}
616 	}
617 	return true;
618 }
619 
CanMove() const620 bool CUnitType::CanMove() const
621 {
622 	return Animations && Animations->Move;
623 }
624 
CanSelect(GroupSelectionMode mode) const625 bool CUnitType::CanSelect(GroupSelectionMode mode) const
626 {
627 	if (!BoolFlag[ISNOTSELECTABLE_INDEX].value) {
628 		switch (mode) {
629 			case SELECTABLE_BY_RECTANGLE_ONLY:
630 				return BoolFlag[SELECTABLEBYRECTANGLE_INDEX].value;
631 			case NON_SELECTABLE_BY_RECTANGLE_ONLY:
632 				return !BoolFlag[SELECTABLEBYRECTANGLE_INDEX].value;
633 			default:
634 				return true;
635 		}
636 	}
637 	return false;
638 }
639 
UpdateUnitStats(CUnitType & type,int reset)640 void UpdateUnitStats(CUnitType &type, int reset)
641 {
642 	if (reset) {
643 		type.MapDefaultStat = type.DefaultStat;
644 		for (int player = 0; player < PlayerMax; ++player) {
645 			type.Stats[player] = type.MapDefaultStat;
646 		}
647 		type.MapSound = type.Sound;
648 	}
649 
650 	// Non-solid units can always be entered and they don't block anything
651 	if (type.BoolFlag[NONSOLID_INDEX].value) {
652 		if (type.Building) {
653 			if (type.BoolFlag[DECORATION_INDEX].value && type.MapDefaultStat.Variables[HP_INDEX].Max == 0) {
654 				// special case, a decoration with no HP can always be built over
655 				type.MovementMask = 0;
656 				type.FieldFlags = 0;
657 			} else {
658 				type.MovementMask = MapFieldLandUnit |
659 					MapFieldSeaUnit |
660 					MapFieldBuilding |
661 					MapFieldCoastAllowed |
662 					MapFieldWaterAllowed |
663 					MapFieldNoBuilding |
664 					MapFieldUnpassable;
665 				type.FieldFlags = MapFieldNoBuilding;
666 			}
667 		} else {
668 			type.MovementMask = 0;
669 			type.FieldFlags = 0;
670 		}
671 		return;
672 	}
673 
674 	//  As side effect we calculate the movement flags/mask here.
675 	switch (type.UnitType) {
676 		case UnitTypeLand:                              // on land
677 			type.MovementMask =
678 				MapFieldLandUnit |
679 				MapFieldSeaUnit |
680 				MapFieldBuilding | // already occuppied
681 				MapFieldCoastAllowed |
682 				MapFieldWaterAllowed | // can't move on this
683 				MapFieldUnpassable;
684 			break;
685 		case UnitTypeFly:                               // in air
686 			type.MovementMask = MapFieldAirUnit; // already occuppied
687 			break;
688 		case UnitTypeNaval:                             // on water
689 			if (type.CanTransport()) {
690 				type.MovementMask =
691 					MapFieldLandUnit |
692 					MapFieldSeaUnit |
693 					MapFieldBuilding | // already occuppied
694 					MapFieldLandAllowed; // can't move on this
695 				// Johns: MapFieldUnpassable only for land units?
696 			} else {
697 				type.MovementMask =
698 					MapFieldLandUnit |
699 					MapFieldSeaUnit |
700 					MapFieldBuilding | // already occuppied
701 					MapFieldCoastAllowed |
702 					MapFieldLandAllowed | // can't move on this
703 					MapFieldUnpassable;
704 			}
705 			break;
706 		default:
707 			DebugPrint("Where moves this unit?\n");
708 			type.MovementMask = 0;
709 			break;
710 	}
711 	if (type.Building || type.BoolFlag[SHOREBUILDING_INDEX].value) {
712 		// Shore building is something special.
713 		if (type.BoolFlag[SHOREBUILDING_INDEX].value) {
714 			type.MovementMask =
715 				MapFieldLandUnit |
716 				MapFieldSeaUnit |
717 				MapFieldBuilding | // already occuppied
718 				MapFieldLandAllowed; // can't build on this
719 		}
720 		type.MovementMask |= MapFieldNoBuilding;
721 		//
722 		// A little chaos, buildings without HP can be entered.
723 		// The oil-patch is a very special case.
724 		//
725 		if (type.MapDefaultStat.Variables[HP_INDEX].Max) {
726 			type.FieldFlags = MapFieldBuilding;
727 		} else {
728 			type.FieldFlags = MapFieldNoBuilding;
729 		}
730 	} else {
731 		switch (type.UnitType) {
732 			case UnitTypeLand: // on land
733 				type.FieldFlags = MapFieldLandUnit;
734 				break;
735 			case UnitTypeFly: // in air
736 				type.FieldFlags = MapFieldAirUnit;
737 				break;
738 			case UnitTypeNaval: // on water
739 				type.FieldFlags = MapFieldSeaUnit;
740 				break;
741 			default:
742 				DebugPrint("Where moves this unit?\n");
743 				type.FieldFlags = 0;
744 				break;
745 		}
746 	}
747 }
748 
749 
750 /**
751 **  Update the player stats for changed unit types.
752 **  @param reset indicates wether default value should be set to each stat (level, upgrades)
753 */
UpdateStats(int reset)754 void UpdateStats(int reset)
755 {
756 	// Update players stats
757 	for (std::vector<CUnitType *>::size_type j = 0; j < UnitTypes.size(); ++j) {
758 		CUnitType &type = *UnitTypes[j];
759 		UpdateUnitStats(type, reset);
760 	}
761 }
762 
763 /**
764 **  Save state of an unit-stats to file.
765 **
766 **  @param stats  Unit-stats to save.
767 **  @param ident  Unit-type ident.
768 **  @param plynr  Player number.
769 **  @param file   Output file.
770 */
SaveUnitStats(const CUnitStats & stats,const CUnitType & type,int plynr,CFile & file)771 static bool SaveUnitStats(const CUnitStats &stats, const CUnitType &type, int plynr,
772 						  CFile &file)
773 {
774 	Assert(plynr < PlayerMax);
775 
776 	if (stats == type.DefaultStat) {
777 		return false;
778 	}
779 	file.printf("DefineUnitStats(\"%s\", %d, {\n  ", type.Ident.c_str(), plynr);
780 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
781 		file.printf("\"%s\", {Value = %d, Max = %d, Increase = %d%s},\n  ",
782 					UnitTypeVar.VariableNameLookup[i], stats.Variables[i].Value,
783 					stats.Variables[i].Max, stats.Variables[i].Increase,
784 					stats.Variables[i].Enable ? ", Enable = true" : "");
785 	}
786 	file.printf("\"costs\", {");
787 	for (unsigned int i = 0; i < MaxCosts; ++i) {
788 		if (i) {
789 			file.printf(" ");
790 		}
791 		file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.Costs[i]);
792 	}
793 	file.printf("},\n\"storing\", {");
794 	for (unsigned int i = 0; i < MaxCosts; ++i) {
795 		if (i) {
796 			file.printf(" ");
797 		}
798 		file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.Storing[i]);
799 	}
800 	file.printf("},\n\"improve-production\", {");
801 	for (unsigned int i = 0; i < MaxCosts; ++i) {
802 		if (i) {
803 			file.printf(" ");
804 		}
805 		file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.ImproveIncomes[i]);
806 	}
807 	file.printf("}})\n");
808 	return true;
809 }
810 
811 /**
812 **  Save state of the unit-type table to file.
813 **
814 **  @param file  Output file.
815 */
SaveUnitTypes(CFile & file)816 void SaveUnitTypes(CFile &file)
817 {
818 	file.printf("\n--- -----------------------------------------\n");
819 	file.printf("--- MODULE: unittypes\n\n");
820 
821 	// Save all stats
822 	for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) {
823 		const CUnitType &type = *UnitTypes[i];
824 		bool somethingSaved = false;
825 
826 		for (int j = 0; j < PlayerMax; ++j) {
827 			if (Players[j].Type != PlayerNobody) {
828 				somethingSaved |= SaveUnitStats(type.Stats[j], type, j, file);
829 			}
830 		}
831 		if (somethingSaved) {
832 			file.printf("\n");
833 		}
834 	}
835 }
836 
837 /**
838 **  Find unit-type by identifier.
839 **
840 **  @param ident  The unit-type identifier.
841 **
842 **  @return       Unit-type pointer.
843 */
UnitTypeByIdent(const std::string & ident)844 CUnitType *UnitTypeByIdent(const std::string &ident)
845 {
846 	std::map<std::string, CUnitType *>::iterator ret = UnitTypeMap.find(ident);
847 	if (ret != UnitTypeMap.end()) {
848 		return (*ret).second;
849 	}
850 	return NULL;
851 }
852 
853 /**
854 **  Allocate an empty unit-type slot.
855 **
856 **  @param ident  Identifier to identify the slot (malloced by caller!).
857 **
858 **  @return       New allocated (zeroed) unit-type pointer.
859 */
NewUnitTypeSlot(const std::string & ident)860 CUnitType *NewUnitTypeSlot(const std::string &ident)
861 {
862 	size_t new_bool_size = UnitTypeVar.GetNumberBoolFlag();
863 	CUnitType *type = new CUnitType;
864 
865 	if (!type) {
866 		fprintf(stderr, "Out of memory\n");
867 		ExitFatal(-1);
868 	}
869 	type->Slot = UnitTypes.size();
870 	type->Ident = ident;
871 	type->BoolFlag.resize(new_bool_size);
872 
873 	type->DefaultStat.Variables = new CVariable[UnitTypeVar.GetNumberVariable()];
874 	for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
875 		type->DefaultStat.Variables[i] = UnitTypeVar.Variable[i];
876 	}
877 	UnitTypes.push_back(type);
878 	UnitTypeMap[type->Ident] = type;
879 	return type;
880 }
881 
882 /**
883 **  Draw unit-type on map.
884 **
885 **  @param type    Unit-type pointer.
886 **  @param sprite  Sprite to use for drawing
887 **  @param player  Player number for color substitution.
888 **  @param frame   Animation frame of unit-type.
889 **  @param screenPos  Screen pixel (top left) position to draw unit-type.
890 **
891 **  @todo  Do screen position caculation in high level.
892 **         Better way to handle in x mirrored sprites.
893 */
DrawUnitType(const CUnitType & type,CPlayerColorGraphic * sprite,int player,int frame,const PixelPos & screenPos)894 void DrawUnitType(const CUnitType &type, CPlayerColorGraphic *sprite, int player, int frame, const PixelPos &screenPos)
895 {
896 	PixelPos pos = screenPos;
897 	// FIXME: move this calculation to high level.
898 	pos.x -= (type.Width - type.TileWidth * PixelTileSize.x) / 2;
899 	pos.y -= (type.Height - type.TileHeight * PixelTileSize.y) / 2;
900 	pos.x += type.OffsetX;
901 	pos.y += type.OffsetY;
902 
903 	if (type.Flip) {
904 		if (frame < 0) {
905 			sprite->DrawPlayerColorFrameClipX(player, -frame - 1, pos.x, pos.y);
906 		} else {
907 			sprite->DrawPlayerColorFrameClip(player, frame, pos.x, pos.y);
908 		}
909 	} else {
910 		const int row = type.NumDirections / 2 + 1;
911 
912 		if (frame < 0) {
913 			frame = ((-frame - 1) / row) * type.NumDirections + type.NumDirections - (-frame - 1) % row;
914 		} else {
915 			frame = (frame / row) * type.NumDirections + frame % row;
916 		}
917 		sprite->DrawPlayerColorFrameClip(player, frame, pos.x, pos.y);
918 	}
919 }
920 
921 /**
922 **  Get the still animation frame
923 */
GetStillFrame(const CUnitType & type)924 static int GetStillFrame(const CUnitType &type)
925 {
926 	CAnimation *anim = type.Animations->Still;
927 
928 	while (anim) {
929 		if (anim->Type == AnimationFrame) {
930 			CAnimation_Frame &a_frame = *static_cast<CAnimation_Frame *>(anim);
931 			// Use the frame facing down
932 			return a_frame.ParseAnimInt(NULL) + type.NumDirections / 2;
933 		} else if (anim->Type == AnimationExactFrame) {
934 			CAnimation_ExactFrame &a_frame = *static_cast<CAnimation_ExactFrame *>(anim);
935 
936 			return a_frame.ParseAnimInt(NULL);
937 		}
938 		anim = anim->Next;
939 	}
940 	return type.NumDirections / 2;
941 }
942 
943 /**
944 **  Init unit types.
945 */
InitUnitTypes(int reset_player_stats)946 void InitUnitTypes(int reset_player_stats)
947 {
948 	for (size_t i = 0; i < UnitTypes.size(); ++i) {
949 		CUnitType &type = *UnitTypes[i];
950 		Assert(type.Slot == (int)i);
951 
952 		if (type.Animations == NULL) {
953 			DebugPrint(_("unit-type '%s' without animations, ignored.\n") _C_ type.Ident.c_str());
954 			continue;
955 		}
956 		//  Add idents to hash.
957 		UnitTypeMap[type.Ident] = UnitTypes[i];
958 
959 		// Determine still frame
960 		type.StillFrame = GetStillFrame(type);
961 
962 		// Lookup BuildingTypes
963 		for (std::vector<CBuildRestriction *>::iterator b = type.BuildingRules.begin();
964 			 b < type.BuildingRules.end(); ++b) {
965 			(*b)->Init();
966 		}
967 
968 		// Lookup AiBuildingTypes
969 		for (std::vector<CBuildRestriction *>::iterator b = type.AiBuildingRules.begin();
970 			 b < type.AiBuildingRules.end(); ++b) {
971 			(*b)->Init();
972 		}
973 	}
974 
975 	// LUDO : called after game is loaded -> don't reset stats !
976 	UpdateStats(reset_player_stats); // Calculate the stats
977 }
978 
979 /**
980 **  Loads the Sprite for a unit type
981 **
982 **  @param type  type of unit to load
983 */
LoadUnitTypeSprite(CUnitType & type)984 void LoadUnitTypeSprite(CUnitType &type)
985 {
986 	if (!type.ShadowFile.empty()) {
987 		type.ShadowSprite = CGraphic::ForceNew(type.ShadowFile, type.ShadowWidth, type.ShadowHeight);
988 		type.ShadowSprite->Load();
989 		if (type.ShadowScale != 1) {
990 			type.ShadowSprite->Resize(type.ShadowSprite->GraphicWidth / type.ShadowScale, type.ShadowSprite->GraphicHeight / type.ShadowScale);
991 		}
992 		if (!type.ShadowSpriteFrame) {
993 			if (type.Flip) {
994 				type.ShadowSprite->Flip();
995 			}
996 			type.ShadowSprite->MakeShadow();
997 		}
998 	}
999 
1000 	if (type.BoolFlag[HARVESTER_INDEX].value) {
1001 		for (int i = 0; i < MaxCosts; ++i) {
1002 			ResourceInfo *resinfo = type.ResInfo[i];
1003 			if (!resinfo) {
1004 				continue;
1005 			}
1006 			if (!resinfo->FileWhenLoaded.empty()) {
1007 				resinfo->SpriteWhenLoaded = CPlayerColorGraphic::New(resinfo->FileWhenLoaded,
1008 																	 type.Width, type.Height);
1009 				resinfo->SpriteWhenLoaded->Load();
1010 				if (type.Flip) {
1011 					resinfo->SpriteWhenLoaded->Flip();
1012 				}
1013 			}
1014 			if (!resinfo->FileWhenEmpty.empty()) {
1015 				resinfo->SpriteWhenEmpty = CPlayerColorGraphic::New(resinfo->FileWhenEmpty,
1016 																	type.Width, type.Height);
1017 				resinfo->SpriteWhenEmpty->Load();
1018 				if (type.Flip) {
1019 					resinfo->SpriteWhenEmpty->Flip();
1020 				}
1021 			}
1022 		}
1023 	}
1024 
1025 	if (!type.File.empty()) {
1026 		type.Sprite = CPlayerColorGraphic::New(type.File, type.Width, type.Height);
1027 		type.Sprite->Load();
1028 		if (type.Flip) {
1029 			type.Sprite->Flip();
1030 		}
1031 	}
1032 
1033 #ifdef USE_MNG
1034 	if (type.Portrait.Num) {
1035 		for (int i = 0; i < type.Portrait.Num; ++i) {
1036 			type.Portrait.Mngs[i] = new Mng;
1037 			type.Portrait.Mngs[i]->Load(type.Portrait.Files[i]);
1038 		}
1039 		// FIXME: should be configurable
1040 		type.Portrait.CurrMng = 0;
1041 		type.Portrait.NumIterations = MyRand() % 16 + 1;
1042 	}
1043 #endif
1044 }
1045 
1046 /**
1047 ** Load the graphics for the unit-types.
1048 */
LoadUnitTypes()1049 void LoadUnitTypes()
1050 {
1051 	for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) {
1052 		CUnitType &type = *UnitTypes[i];
1053 
1054 		// Lookup icons.
1055 		type.Icon.Load();
1056 		// Lookup missiles.
1057 		type.Missile.MapMissile();
1058 		type.Explosion.MapMissile();
1059 
1060 		// Lookup impacts
1061 		for (int i = 0; i < ANIMATIONS_DEATHTYPES + 2; ++i) {
1062 			type.Impact[i].MapMissile();
1063 		}
1064 		// Lookup corpse.
1065 		if (!type.CorpseName.empty()) {
1066 			type.CorpseType = UnitTypeByIdent(type.CorpseName);
1067 		}
1068 #ifndef DYNAMIC_LOAD
1069 		// Load Sprite
1070 		if (!type.Sprite) {
1071 			ShowLoadProgress(_("Unit \"%s\""), type.Name.c_str());
1072 			LoadUnitTypeSprite(type);
1073 		}
1074 #endif
1075 		// FIXME: should i copy the animations of same graphics?
1076 	}
1077 }
1078 
Init()1079 void CUnitTypeVar::Init()
1080 {
1081 	// Variables.
1082 	Variable.resize(GetNumberVariable());
1083 	size_t new_size = UnitTypeVar.GetNumberBoolFlag();
1084 	for (unsigned int i = 0; i < UnitTypes.size(); ++i) { // adjust array for unit already defined
1085 		UnitTypes[i]->BoolFlag.resize(new_size);
1086 	}
1087 }
1088 
Clear()1089 void CUnitTypeVar::Clear()
1090 {
1091 	Variable.clear();
1092 
1093 	for (std::vector<CDecoVar *>::iterator it = DecoVar.begin();
1094 		 it != DecoVar.end(); ++it) {
1095 		delete(*it);
1096 	}
1097 	DecoVar.clear();
1098 }
1099 
1100 /**
1101 **  Cleanup the unit-type module.
1102 */
CleanUnitTypes()1103 void CleanUnitTypes()
1104 {
1105 	DebugPrint("FIXME: icon, sounds not freed.\n");
1106 	FreeAnimations();
1107 
1108 	// Clean all unit-types
1109 	for (size_t i = 0; i < UnitTypes.size(); ++i) {
1110 		delete UnitTypes[i];
1111 	}
1112 	UnitTypes.clear();
1113 	UnitTypeMap.clear();
1114 	UnitTypeVar.Clear();
1115 
1116 	// Clean hardcoded unit types.
1117 	UnitTypeHumanWall = NULL;
1118 	UnitTypeOrcWall = NULL;
1119 }
1120 
1121 //@}
1122