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