00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00030
00031
00032
00033
00034
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <limits.h>
00039 #include <math.h>
00040
00041 #include "stratagus.h"
00042
00043 #include "unit.h"
00044 #include "unit_manager.h"
00045 #include "unit_cache.h"
00046 #include "video.h"
00047 #include "unitsound.h"
00048 #include "unittype.h"
00049 #include "animation.h"
00050 #include "player.h"
00051 #include "map.h"
00052 #include "actions.h"
00053 #include "sound_server.h"
00054 #include "missile.h"
00055 #include "interface.h"
00056 #include "sound.h"
00057 #include "ai.h"
00058 #include "pathfinder.h"
00059 #include "network.h"
00060 #include "ui.h"
00061 #include "script.h"
00062 #include "editor.h"
00063 #include "spells.h"
00064 #include "luacallback.h"
00065
00066
00067
00068
00069
00070 CUnit *Units[MAX_UNIT_SLOTS];
00071 int NumUnits;
00072
00073 bool EnableBuildingCapture;
00074
00075 static unsigned long HelpMeLastCycle;
00076 static int HelpMeLastX;
00077 static int HelpMeLastY;
00078
00079
00080
00081
00082
00083
00084 static void RemoveUnitFromContainer(CUnit *unit);
00085
00089 void CUnit::RefsIncrease()
00090 {
00091 Assert(Refs && !Destroyed);
00092 if (!SaveGameLoading) {
00093 ++Refs;
00094 }
00095 }
00096
00100 void CUnit::RefsDecrease()
00101 {
00102 Assert(Refs);
00103
00104 if (!SaveGameLoading) {
00105 if (Destroyed) {
00106 if (!--Refs) {
00107 Release();
00108 }
00109 } else {
00110 --Refs;
00111 Assert(Refs);
00112 }
00113 }
00114 }
00115
00121 void CUnit::Release()
00122 {
00123 CUnit *temp;
00124
00125 Assert(Type);
00126 Assert(OrderCount == 1);
00127 Assert(!Orders[0]->Goal);
00128
00129 Assert(Removed);
00130
00131
00132
00133
00134 if (!Destroyed) {
00135 DebugPrint("First release %d\n" _C_ Slot);
00136
00137
00138
00139
00140 Destroyed = 1;
00141
00142 if (Container) {
00143 MapUnmarkUnitSight(this);
00144 RemoveUnitFromContainer(this);
00145 }
00146
00147 if (--Refs > 0) {
00148 return;
00149 }
00150 }
00151
00152 Assert(!Refs);
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163 Assert(*UnitSlot == this);
00164 temp = Units[--NumUnits];
00165 temp->UnitSlot = UnitSlot;
00166 *UnitSlot = temp;
00167 Units[NumUnits] = NULL;
00168
00169 Type = NULL;
00170
00171 delete[] AutoCastSpell;
00172 delete[] Variable;
00173 for (std::vector<COrder *>::iterator order = Orders.begin(); order != Orders.end(); ++order) {
00174 delete *order;
00175 }
00176 Orders.clear();
00177
00178 UnitManager.ReleaseUnit(this);
00179 }
00180
00186 void CUnit::Init(CUnitType *type)
00187 {
00188
00189 Refs = 1;
00190
00191
00192
00193
00194 UnitSlot = &Units[NumUnits];
00195 Units[NumUnits++] = this;
00196
00197
00198
00199
00200 Type = type;
00201
00202 Seen.Frame = UnitNotSeen;
00203
00204 Frame = type->StillFrame;
00205
00206 if (UnitTypeVar.NumberVariable) {
00207 Assert(!Variable);
00208 Variable = new CVariable[UnitTypeVar.NumberVariable];
00209 memcpy(Variable, Type->Variable,
00210 UnitTypeVar.NumberVariable * sizeof(*Variable));
00211 }
00212
00213
00214
00215
00216 if (type->NumDirections > 1 && type->Sprite && !type->Building) {
00217 Direction = (MyRand() >> 8) & 0xFF;
00218 UnitUpdateHeading(this);
00219 }
00220
00221 if (type->CanCastSpell) {
00222 AutoCastSpell = new char[SpellTypeTable.size()];
00223 if (Type->AutoCastActive) {
00224 memcpy(AutoCastSpell, Type->AutoCastActive, SpellTypeTable.size());
00225 } else {
00226 memset(AutoCastSpell, 0, SpellTypeTable.size());
00227 }
00228 }
00229
00230 Removed = 1;
00231
00232 for (int i = 0; i < MaxCosts; ++i) {
00233 ResourcesHeld[i] = type->ProductionCosts[i];
00234 }
00235
00236 Assert(Orders.empty());
00237
00238 Orders.push_back(new COrder);
00239
00240 OrderCount = 1;
00241 Orders[0]->Action = UnitActionStill;
00242 Orders[0]->X = Orders[0]->Y = -1;
00243 Assert(!Orders[0]->Goal);
00244 NewOrder.Action = UnitActionStill;
00245 NewOrder.X = NewOrder.Y = -1;
00246 Assert(!NewOrder.Goal);
00247 SavedOrder.Action = UnitActionStill;
00248 SavedOrder.X = SavedOrder.Y = -1;
00249 Assert(!SavedOrder.Goal);
00250 }
00251
00257 void CUnit::AssignToPlayer(CPlayer *player)
00258 {
00259
00260
00261
00262 if (!Type->Vanishes && Orders[0]->Action != UnitActionDie) {
00263 PlayerSlot = player->Units + player->TotalNumUnits++;
00264 if (!SaveGameLoading) {
00265
00266
00267 if (Type->Building) {
00268 player->TotalBuildings++;
00269 } else {
00270 player->TotalUnits++;
00271 }
00272 }
00273 *PlayerSlot = this;
00274
00275 player->UnitTypesCount[Type->Slot]++;
00276 }
00277
00278
00279
00280 if (Type->Building && Orders[0]->Action != UnitActionDie) {
00281 player->NumBuildings++;
00282 }
00283 Player = player;
00284 Stats = &Type->Stats[Player->Index];
00285 Colors = &player->UnitColors;
00286 if (!SaveGameLoading) {
00287 if (UnitTypeVar.NumberVariable) {
00288 memcpy(Variable, Stats->Variables,
00289 UnitTypeVar.NumberVariable * sizeof(*Variable));
00290 }
00291 }
00292 }
00293
00302 CUnit *MakeUnit(CUnitType *type, CPlayer *player)
00303 {
00304 CUnit *unit;
00305
00306
00307
00308
00309 if (NumUnits >= UnitMax) {
00310 DebugPrint("Over all unit limit (%d) reached.\n" _C_ UnitMax);
00311 return NoUnitP;
00312 }
00313
00314 unit = UnitManager.AllocUnit();
00315 if (unit == NoUnitP) {
00316 return NoUnitP;
00317 }
00318
00319 unit->Init(type);
00320
00321
00322 if (player) {
00323 unit->AssignToPlayer(player);
00324 }
00325
00326 return unit;
00327 }
00328
00340 static void MapMarkUnitSightRec(const CUnit *unit, int x, int y, int width, int height,
00341 MapMarkerFunc *f)
00342 {
00343 CUnit *unit_inside;
00344
00345 MapSight(unit->Player, x, y, width, height,
00346 unit->Container ? unit->Container->CurrentSightRange : unit->CurrentSightRange, f);
00347
00348 unit_inside = unit->UnitInside;
00349 for (int i = unit->InsideCount; i--; unit_inside = unit_inside->NextContained) {
00350 MapMarkUnitSightRec(unit_inside, x, y, width, height, f);
00351 }
00352 }
00353
00361 static CUnit *GetFirstContainer(CUnit *unit)
00362 {
00363 while (unit->Container) {
00364 unit = unit->Container;
00365 }
00366 return unit;
00367 }
00368
00375 void MapMarkUnitSight(CUnit *unit)
00376 {
00377 CUnit *container = GetFirstContainer(unit);
00378
00379 MapMarkUnitSightRec(unit,
00380 container->X, container->Y, container->Type->TileWidth, container->Type->TileHeight,
00381 MapMarkTileSight);
00382
00383
00384 if (unit == container && !unit->IsUnusable()) {
00385 if (unit->Stats->Variables[RADAR_INDEX].Value) {
00386 MapMarkRadar(unit->Player, unit->X, unit->Y, unit->Type->TileWidth,
00387 unit->Type->TileHeight, unit->Stats->Variables[RADAR_INDEX].Value);
00388 }
00389 if (unit->Stats->Variables[RADARJAMMER_INDEX].Value) {
00390 MapMarkRadarJammer(unit->Player, unit->X, unit->Y, unit->Type->TileWidth,
00391 unit->Type->TileHeight, unit->Stats->Variables[RADARJAMMER_INDEX].Value);
00392 }
00393 }
00394 }
00395
00402 void MapUnmarkUnitSight(CUnit *unit)
00403 {
00404 CUnit *container = GetFirstContainer(unit);
00405
00406 MapMarkUnitSightRec(unit,
00407 container->X, container->Y, container->Type->TileWidth, container->Type->TileHeight,
00408 MapUnmarkTileSight);
00409
00410
00411 if (unit == container && !unit->IsUnusable()) {
00412 if (unit->Stats->Variables[RADAR_INDEX].Value) {
00413 MapUnmarkRadar(unit->Player, unit->X, unit->Y, unit->Type->TileWidth,
00414 unit->Type->TileHeight, unit->Stats->Variables[RADAR_INDEX].Value);
00415 }
00416 if (unit->Stats->Variables[RADARJAMMER_INDEX].Value) {
00417 MapUnmarkRadarJammer(unit->Player, unit->X, unit->Y, unit->Type->TileWidth,
00418 unit->Type->TileHeight, unit->Stats->Variables[RADARJAMMER_INDEX].Value);
00419 }
00420 }
00421 }
00422
00435 void UpdateUnitSightRange(CUnit *unit)
00436 {
00437 CUnit *unit_inside;
00438 int i;
00439
00440 #if 0 // which is the better ? caller check ?
00441 if (SaveGameLoading) {
00442 return ;
00443 }
00444 #else
00445 Assert(!SaveGameLoading);
00446 #endif
00447
00448 if (unit->Constructed) {
00449 unit->CurrentSightRange = 0;
00450 } else if (!unit->Container) {
00451 unit->CurrentSightRange = unit->Stats->Variables[SIGHTRANGE_INDEX].Max;
00452 } else {
00453 unit->CurrentSightRange = unit->Container->CurrentSightRange;
00454 }
00455
00456 unit_inside = unit->UnitInside;
00457 for (i = unit->InsideCount; i--; unit_inside = unit_inside->NextContained) {
00458 UpdateUnitSightRange(unit_inside);
00459 }
00460 }
00461
00467 void MarkUnitFieldFlags(const CUnit *unit)
00468 {
00469 unsigned flags = unit->Type->FieldFlags;
00470
00471 for (int h = unit->Type->TileHeight; h--;) {
00472 for (int w = unit->Type->TileWidth; w--;) {
00473 Map.Field(unit->X + w, unit->Y + h)->Flags |= flags;
00474 }
00475 }
00476 }
00477
00483 void UnmarkUnitFieldFlags(const CUnit *unit)
00484 {
00485 unsigned flags = unit->Type->FieldFlags;;
00486 CUnit *table[UnitMax];
00487
00488 for (int h = unit->Type->TileHeight; h--;) {
00489 for (int w = unit->Type->TileWidth; w--;) {
00490 Map.Field(unit->X + w, unit->Y + h)->Flags &= ~flags;
00491
00492 int n = UnitCache.Select(unit->X + w, unit->Y + h, table, UnitMax);
00493 while (n--) {
00494 if (table[n] != unit && table[n]->Orders[0]->Action != UnitActionDie) {
00495 Map.Field(unit->X + w, unit->Y + h)->Flags |= table[n]->Type->FieldFlags;
00496 }
00497 }
00498 }
00499 }
00500 }
00501
00507 void CUnit::AddInContainer(CUnit *host)
00508 {
00509 Assert(Container == NoUnitP);
00510 Container = host;
00511 if (host->InsideCount == 0) {
00512 NextContained = PrevContained = this;
00513 } else {
00514 NextContained = host->UnitInside;
00515 PrevContained = host->UnitInside->PrevContained;
00516 host->UnitInside->PrevContained->NextContained = this;
00517 host->UnitInside->PrevContained = this;
00518 }
00519 host->UnitInside = this;
00520 host->InsideCount++;
00521 }
00522
00528 static void RemoveUnitFromContainer(CUnit *unit)
00529 {
00530 CUnit *host;
00531
00532 host = unit->Container;
00533 Assert(unit->Container);
00534 Assert(unit->Container->InsideCount > 0);
00535 host->InsideCount--;
00536 unit->NextContained->PrevContained = unit->PrevContained;
00537 unit->PrevContained->NextContained = unit->NextContained;
00538 if (host->InsideCount == 0) {
00539 host->UnitInside = NoUnitP;
00540 } else {
00541 if (host->UnitInside == unit) {
00542 host->UnitInside = unit->NextContained;
00543 }
00544 }
00545 unit->Container = NoUnitP;
00546 }
00547
00548
00560 static void UnitInXY(CUnit *unit, int x, int y)
00561 {
00562 CUnit *unit_inside = unit->UnitInside;
00563
00564 unit->X = x;
00565 unit->Y = y;
00566
00567 for (int i = unit->InsideCount; i--; unit_inside = unit_inside->NextContained) {
00568 UnitInXY(unit_inside, x, y);
00569 }
00570 }
00571
00580 void CUnit::MoveToXY(int x, int y)
00581 {
00582 MapUnmarkUnitSight(this);
00583 UnitCache.Remove(this);
00584 UnmarkUnitFieldFlags(this);
00585
00586 Assert(UnitCanBeAt(this, x, y));
00587
00588 UnitInXY(this, x, y);
00589
00590 UnitCache.Insert(this);
00591 MarkUnitFieldFlags(this);
00592
00593 UnitCountSeen(this);
00594 MapMarkUnitSight(this);
00595 }
00596
00603 void CUnit::Place(int x, int y)
00604 {
00605 Assert(Removed);
00606
00607 if (Container) {
00608 MapUnmarkUnitSight(this);
00609 RemoveUnitFromContainer(this);
00610 }
00611 if (!SaveGameLoading) {
00612 UpdateUnitSightRange(this);
00613 }
00614 Removed = 0;
00615 UnitInXY(this, x, y);
00616
00617 MarkUnitFieldFlags(this);
00618
00619 UnitCache.Insert(this);
00620
00621 UnitCountSeen(this);
00622
00623 MapMarkUnitSight(this);
00624 }
00625
00636 CUnit *MakeUnitAndPlace(int x, int y, CUnitType *type, CPlayer *player)
00637 {
00638 CUnit *unit = MakeUnit(type, player);
00639
00640 if (unit != NoUnitP) {
00641 unit->Place(x, y);
00642 }
00643
00644 return unit;
00645 }
00646
00656 void CUnit::Remove(CUnit *host)
00657 {
00658 if (Removed) {
00659
00660 DebugPrint("unit '%s(%d)' already removed\n" _C_ Type->Ident.c_str() _C_ Slot);
00661 return;
00662 }
00663
00664 UnitCache.Remove(this);
00665 MapUnmarkUnitSight(this);
00666 UnmarkUnitFieldFlags(this);
00667
00668 if (host) {
00669 AddInContainer(host);
00670 UpdateUnitSightRange(this);
00671 UnitInXY(this, host->X, host->Y);
00672 MapMarkUnitSight(this);
00673 }
00674
00675 Removed = 1;
00676
00677
00678 if (Selected) {
00679 if (NumSelected == 1) {
00680 CancelBuildingMode();
00681 }
00682 UnSelectUnit(this);
00683 SelectionChanged();
00684 }
00685
00686 if (!Selected && TeamSelected) {
00687 UnSelectUnit(this);
00688 }
00689
00690
00691 if (this == UnitUnderCursor) {
00692 UnitUnderCursor = NULL;
00693 }
00694 }
00695
00703 void UnitLost(CUnit *unit)
00704 {
00705 CUnit *temp;
00706 CBuildRestrictionOnTop *b;
00707 const CUnitType *type;
00708 CPlayer *player;
00709
00710 player = unit->Player;
00711 Assert(player);
00712
00713
00714
00715
00716 if (player && player->AiEnabled) {
00717 AiUnitKilled(unit);
00718 }
00719
00720
00721
00722
00723 if (unit->GroupId) {
00724 RemoveUnitFromGroups(unit);
00725 }
00726
00727
00728
00729
00730 type = unit->Type;
00731 if (player && !type->Vanishes) {
00732 Assert(*unit->PlayerSlot == unit);
00733 temp = player->Units[--player->TotalNumUnits];
00734 temp->PlayerSlot = unit->PlayerSlot;
00735 *unit->PlayerSlot = temp;
00736 player->Units[player->TotalNumUnits] = NULL;
00737
00738 if (unit->Type->Building) {
00739 player->NumBuildings--;
00740 }
00741
00742 if (unit->Orders[0]->Action != UnitActionBuilt) {
00743 player->UnitTypesCount[type->Slot]--;
00744 }
00745 }
00746
00747 DebugPrint("Lost %s(%d)\n" _C_ unit->Type->Ident.c_str() _C_ UnitNumber(unit));
00748
00749
00750 if ((b = OnTopDetails(unit, NULL)) != NULL) {
00751 if (b->ReplaceOnDie && (unit->Type->CanHarvestFrom && UnitHoldsResources(unit))) {
00752 temp = MakeUnitAndPlace(unit->X, unit->Y, b->Parent, &Players[PlayerNumNeutral]);
00753 if (temp == NoUnitP) {
00754 DebugPrint("Unable to allocate Unit");
00755 } else {
00756 memcpy(temp->ResourcesHeld, unit->ResourcesHeld, sizeof(temp->ResourcesHeld));
00757 }
00758 }
00759 }
00760 Assert(player->NumBuildings <= UnitMax);
00761 Assert(player->TotalNumUnits <= UnitMax);
00762 Assert(player->UnitTypesCount[type->Slot] <= UnitMax);
00763 }
00764
00770 void UnitClearOrders(CUnit *unit)
00771 {
00772
00773
00774
00775 for (int i = unit->OrderCount; i-- > 0;) {
00776 if (unit->Orders[i]->Goal) {
00777 unit->Orders[i]->Goal->RefsDecrease();
00778 unit->Orders[i]->Goal = NoUnitP;
00779 }
00780 if (i != 0) {
00781 COrder *order = unit->Orders.back();
00782 delete order;
00783 unit->Orders.pop_back();
00784 }
00785 }
00786 unit->OrderCount = 1;
00787 if (unit->NewOrder.Goal) {
00788 unit->NewOrder.Goal->RefsDecrease();
00789 unit->NewOrder.Goal = NoUnitP;
00790 }
00791 if (unit->SavedOrder.Goal) {
00792 unit->SavedOrder.Goal->RefsDecrease();
00793 unit->SavedOrder.Goal = NoUnitP;
00794 }
00795 unit->Orders[0]->Action = UnitActionStill;
00796 unit->SubAction = unit->State = 0;
00797 }
00798
00805 void UpdateForNewUnit(const CUnit *unit, int upgrade)
00806 {
00807 const CUnitType *type = unit->Type;
00808 CPlayer *player = unit->Player;
00809
00810
00811
00812
00813 for (int u = 0; u < MaxCosts; ++u) {
00814 player->ProductionRate[u] += type->ProductionRate[u] * unit->ProductionEfficiency / 100;
00815 player->StorageCapacity[u] += type->StorageCapacity[u];
00816 }
00817 }
00818
00828 void NearestOfUnit(const CUnit *unit, int tx, int ty, int *dx, int *dy)
00829 {
00830 int x = unit->X;
00831 int y = unit->Y;
00832
00833 if (tx >= x + unit->Type->TileWidth) {
00834 *dx = x + unit->Type->TileWidth - 1;
00835 } else if (tx < x) {
00836 *dx = x;
00837 } else {
00838 *dx = tx;
00839 }
00840 if (ty >= y + unit->Type->TileHeight) {
00841 *dy = y + unit->Type->TileHeight - 1;
00842 } else if (ty < y) {
00843 *dy = y;
00844 } else {
00845 *dy = ty;
00846 }
00847 }
00848
00855 static void UnitFillSeenValues(CUnit *unit)
00856 {
00857
00858 unit->Seen.Y = unit->Y;
00859 unit->Seen.X = unit->X;
00860 unit->Seen.IY = unit->IY;
00861 unit->Seen.IX = unit->IX;
00862 unit->Seen.Frame = unit->Frame;
00863 unit->Seen.State = (unit->Orders[0]->Action == UnitActionBuilt);
00864 if (unit->Orders[0]->Action == UnitActionDie) {
00865 unit->Seen.State = 3;
00866 }
00867 unit->Seen.Type = unit->Type;
00868 unit->Seen.Constructed = unit->Constructed;
00869 if (unit->Orders[0]->Action == UnitActionBuilt) {
00870 unit->Seen.CFrame = unit->Data.Built.Frame;
00871 } else {
00872 unit->Seen.CFrame = NULL;
00873 }
00874 }
00875
00882 void UnitGoesUnderFog(CUnit *unit, const CPlayer *player)
00883 {
00884 if (unit->Type->VisibleUnderFog) {
00885 if (player->Type == PlayerPerson && !unit->Destroyed) {
00886 unit->RefsIncrease();
00887 }
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901 if (unit->Destroyed) {
00902 unit->Seen.Destroyed |= (1 << player->Index);
00903 }
00904 if (player == ThisPlayer) {
00905 UnitFillSeenValues(unit);
00906 }
00907 }
00908 }
00909
00923 void UnitGoesOutOfFog(CUnit *unit, const CPlayer *player)
00924 {
00925 if (unit->Type->VisibleUnderFog) {
00926 if (unit->Seen.ByPlayer & (1 << (player->Index))) {
00927 if ((player->Type == PlayerPerson) &&
00928 (!( unit->Seen.Destroyed & (1 << player->Index) )) ) {
00929 unit->RefsDecrease();
00930 }
00931 } else {
00932 unit->Seen.ByPlayer |= (1 << (player->Index));
00933 }
00934 }
00935 }
00936
00944 void UnitsOnTileMarkSeen(const CPlayer *player, int x, int y)
00945 {
00946 int n;
00947 CUnit *units[UnitMax];
00948
00949 n = UnitCache.Select(x, y, units, UnitMax);
00950 while (n) {
00951 CUnit *unit = units[--n];
00952
00953
00954
00955
00956
00957 for (int p = 0; p < PlayerMax; ++p) {
00958 if (player->IsBothSharedVision(&Players[p]) || (p == player->Index)) {
00959 if (!unit->IsVisible(Players + p)) {
00960 UnitGoesOutOfFog(unit, Players + p);
00961 }
00962 }
00963 }
00964 unit->VisCount[player->Index]++;
00965 }
00966 }
00967
00975 void UnitsOnTileUnmarkSeen(const CPlayer *player, int x, int y)
00976 {
00977 int n;
00978 CUnit *units[UnitMax];
00979
00980 n = UnitCache.Select(x, y, units, UnitMax);
00981 while (n) {
00982 CUnit *unit = units[--n];
00983 Assert(unit->X <= x && unit->X + unit->Type->TileWidth - 1 >= x &&
00984 unit->Y <= y && unit->Y + unit->Type->TileHeight - 1 >= y);
00985
00986 Assert(unit->VisCount[player->Index]);
00987 unit->VisCount[player->Index]--;
00988
00989
00990
00991
00992
00993
00994 if (!unit->VisCount[player->Index]) {
00995 for (int p = 0; p < PlayerMax; ++p) {
00996 if (player->IsBothSharedVision(&Players[p]) || p == player->Index) {
00997 if (!unit->IsVisible(Players + p)) {
00998 UnitGoesUnderFog(unit, Players + p);
00999 }
01000 }
01001 }
01002 }
01003 }
01004 }
01005
01013 void UnitCountSeen(CUnit *unit)
01014 {
01015 int x;
01016 int y;
01017 int p;
01018 int oldv[PlayerMax];
01019 int newv;
01020
01021
01022
01023
01024
01025
01026
01027
01028 for (p = 0; p < PlayerMax; ++p) {
01029 if (Players[p].Type != PlayerNobody) {
01030 oldv[p] = unit->IsVisible(&Players[p]);
01031 }
01032 }
01033
01034
01035 for (p = 0; p < PlayerMax; ++p) {
01036 if (Players[p].Type != PlayerNobody) {
01037 newv = 0;
01038 for (x = 0; x < unit->Type->TileWidth; ++x) {
01039 for (y = 0; y < unit->Type->TileHeight; ++y) {
01040
01041 if (Map.Field(unit->X + x, unit->Y + y)->Visible[p] >
01042 1 - (Map.NoFogOfWar ? 1 : 0)) {
01043 newv++;
01044 }
01045 }
01046 }
01047 unit->VisCount[p] = newv;
01048 }
01049 }
01050
01051
01052
01053
01054
01055 for (p = 0; p < PlayerMax; ++p) {
01056 if (Players[p].Type != PlayerNobody) {
01057 newv = unit->IsVisible(Players + p);
01058 if (!oldv[p] && newv) {
01059 UnitGoesOutOfFog(unit, Players + p);
01060
01061
01062 if (!unit->Type) {
01063 break;
01064 }
01065 }
01066 if (oldv[p] && !newv) {
01067 UnitGoesUnderFog(unit, Players + p);
01068 }
01069 }
01070 }
01071 }
01072
01081 bool CUnit::IsVisible(const CPlayer *player) const
01082 {
01083 if (VisCount[player->Index]) {
01084 return true;
01085 }
01086 for (int p = 0; p < PlayerMax; ++p) {
01087 if (player->IsBothSharedVision(&Players[p])) {
01088 if (VisCount[p]) {
01089 return true;
01090 }
01091 }
01092 }
01093 return false;
01094 }
01095
01104 bool CUnit::IsVisibleAsGoal(const CPlayer *player) const
01105 {
01106 if (IsVisible(player) || player->Type == PlayerComputer ||
01107 UnitVisibleOnRadar(player, this)) {
01108 return !Removed && !Destroyed && Orders[0]->Action != UnitActionDie;
01109 } else {
01110 return Type->VisibleUnderFog &&
01111 (Seen.ByPlayer & (1 << player->Index)) &&
01112 !(Seen.Destroyed & (1 << player->Index));
01113 }
01114 }
01115
01122 bool CUnit::IsAliveOnMap() const
01123 {
01124 return !Removed && !Destroyed && Orders[0]->Action != UnitActionDie;
01125 }
01126
01135 bool CUnit::IsVisibleOnMap(const CPlayer *player) const
01136 {
01137 return !Removed && !Destroyed &&
01138 Orders[0]->Action != UnitActionDie && IsVisible(player);
01139 }
01140
01149 bool CUnit::IsVisibleOnMinimap() const
01150 {
01151 if (IsVisible(ThisPlayer) || ReplayRevealMap ||
01152 UnitVisibleOnRadar(ThisPlayer, this)) {
01153 return !Removed && !Destroyed && (Orders[0]->Action != UnitActionDie);
01154 } else {
01155 if (!Type->VisibleUnderFog) {
01156 return false;
01157 }
01158 return (Seen.ByPlayer & (1 << ThisPlayer->Index)) &&
01159 Seen.State != 3 && !(Seen.Destroyed & (1 << ThisPlayer->Index));
01160 }
01161 }
01162
01172 bool CUnit::IsVisibleInViewport(const CViewport *vp) const
01173 {
01174
01175
01176
01177 int x = X * TileSizeX + IX - (Type->Width - Type->TileWidth * TileSizeX) / 2 + Type->OffsetX;
01178 int y = Y * TileSizeY + IY - (Type->Height - Type->TileHeight * TileSizeY) / 2 + Type->OffsetY;
01179
01180 if (x + Type->Width < vp->MapX * TileSizeX + vp->OffsetX ||
01181 x > vp->MapX * TileSizeX + vp->OffsetX + (vp->EndX - vp->X) ||
01182 y + Type->Height < vp->MapY * TileSizeY + vp->OffsetY ||
01183 y > vp->MapY * TileSizeY + vp->OffsetY + (vp->EndY - vp->Y))
01184 {
01185 return false;
01186 }
01187
01188 Assert(ThisPlayer);
01189 if (!ThisPlayer) {
01190 return false;
01191 }
01192
01193 if (IsVisible(ThisPlayer) || ReplayRevealMap) {
01194 return !Destroyed;
01195 } else {
01196
01197
01198 if (!Destroyed || !(Seen.Destroyed & (1 << ThisPlayer->Index))) {
01199 return (Type->VisibleUnderFog && (Seen.ByPlayer & (1 << ThisPlayer->Index)));
01200 } else {
01201 return false;
01202 }
01203 }
01204 }
01205
01211 bool CUnit::IsVisibleOnScreen() const
01212 {
01213 for (CViewport *vp = UI.Viewports; vp < UI.Viewports + UI.NumViewports; ++vp) {
01214 if (IsVisibleInViewport(vp)) {
01215 return true;
01216 }
01217 }
01218 return false;
01219 }
01220
01231 void CUnit::GetMapArea(int *sx, int *sy, int *ex, int *ey) const
01232 {
01233 *sx = X - (IX < 0);
01234 *ex = *sx + Type->TileWidth - !IX;
01235 *sy = Y - (IY < 0);
01236 *ey = *sy + Type->TileHeight - !IY;
01237 }
01238
01244 void CUnit::ChangeOwner(CPlayer *newplayer)
01245 {
01246 int i;
01247 CUnit *uins;
01248 CPlayer *oldplayer;
01249
01250 oldplayer = Player;
01251
01252
01253 if (oldplayer == newplayer) {
01254 DebugPrint("Change the unit owner to the same player???\n");
01255 return;
01256 }
01257
01258
01259 uins = UnitInside;
01260 for (i = InsideCount; i; --i, uins = uins->NextContained) {
01261 uins->ChangeOwner(newplayer);
01262 }
01263
01264
01265
01266
01267 UnitLost(this);
01268
01269
01270
01271
01272
01273
01274
01275 PlayerSlot = newplayer->Units + newplayer->TotalNumUnits++;
01276 if (Type->Building) {
01277 newplayer->TotalBuildings++;
01278 }
01279 else {
01280 newplayer->TotalUnits++;
01281 }
01282 *PlayerSlot = this;
01283
01284 MapUnmarkUnitSight(this);
01285 Player = newplayer;
01286 Stats = &Type->Stats[newplayer->Index];
01287 UpdateUnitSightRange(this);
01288 MapMarkUnitSight(this);
01289
01290
01291
01292
01293 if (Type->CanHarvestFrom) {
01294 DebugPrint("Resource transfer not supported\n");
01295 }
01296 if (Type->Building) {
01297 newplayer->NumBuildings++;
01298 }
01299 newplayer->UnitTypesCount[Type->Slot]++;
01300
01301 UpdateForNewUnit(this, 1);
01302 }
01303
01304
01310 void RescueUnits(void)
01311 {
01312 CUnit *table[UnitMax];
01313 CUnit *around[UnitMax];
01314 int n;
01315 int l;
01316
01317 if (NoRescueCheck) {
01318 return;
01319 }
01320 NoRescueCheck = 1;
01321
01322
01323
01324
01325 for (CPlayer *p = Players; p < Players + NumPlayers; ++p) {
01326 if (p->Type != PlayerRescuePassive && p->Type != PlayerRescueActive) {
01327 continue;
01328 }
01329 if (p->TotalNumUnits) {
01330 NoRescueCheck = 0;
01331
01332 l = p->TotalNumUnits;
01333 memcpy(table, p->Units, l * sizeof(CUnit *));
01334 for (int j = 0; j < l; ++j) {
01335 CUnit *unit = table[j];
01336
01337
01338 if (unit->Removed) {
01339 continue;
01340 }
01341
01342 if (unit->Type->UnitType == UnitTypeLand) {
01343 n = UnitCache.Select(
01344 unit->X - 1, unit->Y - 1,
01345 unit->X + unit->Type->TileWidth + 1,
01346 unit->Y + unit->Type->TileHeight + 1, around, UnitMax);
01347 } else {
01348 n = UnitCache.Select(
01349 unit->X - 2, unit->Y - 2,
01350 unit->X + unit->Type->TileWidth + 2,
01351 unit->Y + unit->Type->TileHeight + 2, around, UnitMax);
01352 }
01353
01354
01355
01356 for (int i = 0; i < n; ++i) {
01357 if (unit->IsAllied(around[i])) {
01358 unit->RescuedFrom = unit->Player;
01359 unit->ChangeOwner(around[i]->Player);
01360 unit->Blink = 5;
01361 PlayGameSound(GameSounds.Rescue.Sound, MaxSampleVolume);
01362 break;
01363 }
01364 }
01365 }
01366 }
01367 }
01368 }
01369
01377 bool UnitHoldsResources(const CUnit *unit)
01378 {
01379 for (int i = 0; i < MaxCosts; ++i) {
01380 if (unit->ResourcesHeld[i] != 0) {
01381 return true;
01382 }
01383 }
01384 return false;
01385 }
01386
01387
01388
01389
01390
01398 static int myatan(int val)
01399 {
01400 static int init;
01401 static unsigned char atan_table[2608];
01402
01403 if (val >= 2608) {
01404 return 63;
01405 }
01406 if (!init) {
01407 for (; init < 2608; ++init) {
01408 atan_table[init] =
01409 (unsigned char)(atan((double)init / 64) * (64 * 4 / 6.2831853));
01410 }
01411 }
01412
01413 return atan_table[val];
01414 }
01415
01424 int DirectionToHeading(int delta_x, int delta_y)
01425 {
01426
01427
01428
01429 if (delta_x > 0) {
01430 if (delta_y < 0) {
01431 return myatan((delta_x * 64) / -delta_y);
01432 }
01433
01434 return myatan((delta_y * 64) / delta_x) + 64;
01435 }
01436 if (delta_y>0) {
01437 return myatan((delta_x * -64) / delta_y) + 64 * 2;
01438 }
01439 if (delta_x) {
01440 return myatan((delta_y * -64) / -delta_x) + 64 * 3;
01441 }
01442 return 0;
01443 }
01444
01448 void UnitUpdateHeading(CUnit *unit)
01449 {
01450 int dir;
01451 int nextdir;
01452 bool neg;
01453
01454 if (unit->Frame < 0) {
01455 unit->Frame = -unit->Frame - 1;
01456 neg = true;
01457 } else {
01458 neg = false;
01459 }
01460 unit->Frame /= unit->Type->NumDirections / 2 + 1;
01461 unit->Frame *= unit->Type->NumDirections / 2 + 1;
01462
01463
01464 nextdir = 256 / unit->Type->NumDirections;
01465 dir = ((unit->Direction + nextdir / 2) & 0xFF) / nextdir;
01466 if (dir <= LookingS / nextdir) {
01467 unit->Frame += dir;
01468 } else {
01469 unit->Frame += 256 / nextdir - dir;
01470 unit->Frame = -unit->Frame - 1;
01471 }
01472 if (neg && !unit->Frame && unit->Type->Building) {
01473 unit->Frame = -1;
01474 }
01475 }
01476
01484 void UnitHeadingFromDeltaXY(CUnit *unit, int dx, int dy)
01485 {
01486 unit->Direction = DirectionToHeading(dx, dy);
01487 UnitUpdateHeading(unit);
01488 }
01489
01490
01491
01492
01493
01502 void DropOutOnSide(CUnit *unit, int heading, int addx, int addy)
01503 {
01504 int x;
01505 int y;
01506 int i;
01507
01508 if (unit->Container) {
01509 x = unit->Container->X;
01510 y = unit->Container->Y;
01511 } else {
01512 x = unit->X;
01513 y = unit->Y;
01514 }
01515
01516 if (heading < LookingNE || heading > LookingNW) {
01517 x += addx - 1;
01518 --y;
01519 goto startn;
01520 }
01521 if (heading < LookingSE) {
01522 x += addx;
01523 y += addy - 1;
01524 goto starte;
01525 }
01526 if (heading < LookingSW) {
01527 y += addy;
01528 goto starts;
01529 }
01530 --x;
01531 goto startw;
01532
01533
01534 for (;;) {
01535 startw:
01536 for (i = addy; i--; ++y) {
01537 if (UnitCanBeAt(unit, x, y)) {
01538 goto found;
01539 }
01540 }
01541 ++addx;
01542 starts:
01543 for (i = addx; i--; ++x) {
01544 if (UnitCanBeAt(unit, x, y)) {
01545 goto found;
01546 }
01547 }
01548 ++addy;
01549 starte:
01550 for (i = addy; i--; --y) {
01551 if (UnitCanBeAt(unit, x, y)) {
01552 goto found;
01553 }
01554 }
01555 ++addx;
01556 startn:
01557 for (i = addx; i--; --x) {
01558 if (UnitCanBeAt(unit, x, y)) {
01559 goto found;
01560 }
01561 }
01562 ++addy;
01563 }
01564
01565 found:
01566 unit->Place(x, y);
01567 }
01568
01578 void DropOutNearest(CUnit *unit, int gx, int gy, int addx, int addy)
01579 {
01580 int x;
01581 int y;
01582 int i;
01583 int bestx;
01584 int besty;
01585 int bestd;
01586 int n;
01587
01588 Assert(unit->Removed);
01589
01590 x = y = -1;
01591 if (unit->Container) {
01592 x = unit->Container->X;
01593 y = unit->Container->Y;
01594 } else {
01595 x = unit->X;
01596 y = unit->Y;
01597 }
01598
01599 Assert(x != -1 && y != -1);
01600
01601 bestd = 99999;
01602 bestx = besty = 0;
01603
01604
01605 --x;
01606 for (;;) {
01607 for (i = addy; i--; ++y) {
01608 if (UnitCanBeAt(unit, x, y)) {
01609 n = MapDistance(gx, gy, x, y);
01610 if (n < bestd) {
01611 bestd = n;
01612 bestx = x;
01613 besty = y;
01614 }
01615 }
01616 }
01617 ++addx;
01618 for (i = addx; i--; ++x) {
01619 if (UnitCanBeAt(unit, x, y)) {
01620 n = MapDistance(gx, gy, x, y);
01621 if (n < bestd) {
01622 bestd = n;
01623 bestx = x;
01624 besty = y;
01625 }
01626 }
01627 }
01628 ++addy;
01629 for (i = addy; i--; --y) {
01630 if (UnitCanBeAt(unit, x, y)) {
01631 n = MapDistance(gx, gy, x, y);
01632 if (n < bestd) {
01633 bestd = n;
01634 bestx = x;
01635 besty = y;
01636 }
01637 }
01638 }
01639 ++addx;
01640 for (i = addx; i--; --x) {
01641 if (UnitCanBeAt(unit, x, y)) {
01642 n = MapDistance(gx, gy, x, y);
01643 if (n < bestd) {
01644 bestd = n;
01645 bestx = x;
01646 besty = y;
01647 }
01648 }
01649 }
01650 if (bestd != 99999) {
01651 unit->Place(bestx, besty);
01652 return;
01653 }
01654 ++addy;
01655 }
01656 }
01657
01663 void DropOutAll(const CUnit *source)
01664 {
01665 CUnit *unit;
01666 int i;
01667
01668 unit = source->UnitInside;
01669 for (i = source->InsideCount; i; --i, unit = unit->NextContained) {
01670 DropOutOnSide(unit, LookingW,
01671 source->Type->TileWidth, source->Type->TileHeight);
01672 Assert(!unit->Orders[0]->Goal);
01673 unit->Orders[0]->Action = UnitActionStill;
01674 unit->SubAction = 0;
01675 }
01676 }
01677
01678
01679
01680
01681
01696 CUnit *UnitFindResource(const CUnit *unit, int x, int y, int range, int resource)
01697 {
01698 static const int xoffset[] = { 0,-1,+1, 0, -1,+1,-1,+1 };
01699 static const int yoffset[] = { -1, 0, 0,+1, -1,-1,+1,+1 };
01700 struct p {
01701 unsigned short X;
01702 unsigned short Y;
01703 } *points;
01704 int size;
01705 int rx;
01706 int ry;
01707 int mask;
01708 int wp;
01709 int rp;
01710 int ep;
01711 int i;
01712 int w;
01713 unsigned char *m;
01714 unsigned char *matrix;
01715 CUnit *res;
01716 CUnit *bestres;
01717 int destx;
01718 int desty;
01719 int bestd;
01720 int cdist;
01721
01722 destx = x;
01723 desty = y;
01724 size = std::min(Map.Info.MapWidth * Map.Info.MapHeight / 4, range * range * 5);
01725 points = new p[size];
01726
01727 bestd = 99999;
01728
01729 matrix = CreateMatrix();
01730 w = Map.Info.MapWidth + 2;
01731 matrix += w + w + 2;
01732
01733 mask = unit->Type->MovementMask;
01734
01735
01736 mask &= ~(MapFieldLandUnit | MapFieldSeaUnit | MapFieldAirUnit);
01737 points[0].X = x;
01738 points[0].Y = y;
01739 rp = 0;
01740 matrix[x + y * w] = 1;
01741 ep = wp = 1;
01742 cdist = 0;
01743 bestres = NoUnitP;
01744
01745
01746
01747
01748 for (;;) {
01749 while (rp != ep) {
01750 rx = points[rp].X;
01751 ry = points[rp].Y;
01752 for (i = 0; i < 8; ++i) {
01753 x = rx + xoffset[i];
01754 y = ry + yoffset[i];
01755 m = matrix + x + y * w;
01756 if (*m) {
01757 continue;
01758 }
01759
01760 if (!Map.IsFieldExplored(unit->Player, x, y)) {
01761 continue;
01762 }
01763
01764
01765
01766
01767 if ((res = ResourceOnMap(x, y, resource)) &&
01768 res->Type->CanHarvestFrom &&
01769 res->Player->Type == PlayerNeutral) {
01770 delete[] points;
01771 return res;
01772 }
01773
01774 if (CanMoveToMask(x, y, mask)) {
01775 *m = 1;
01776 points[wp].X = x;
01777 points[wp].Y = y;
01778 if (++wp >= size) {
01779 wp = 0;
01780 }
01781 if (wp == ep) {
01782
01783 break;
01784 }
01785 } else {
01786 *m = 99;
01787 }
01788 }
01789 if (++rp >= size) {
01790 rp = 0;
01791 }
01792 }
01793
01794 if (bestd != 99999) {
01795 delete[] points;
01796 return bestres;
01797 }
01798 ++cdist;
01799 if (rp == wp || cdist >= range) {
01800 break;
01801 }
01802
01803 ep = wp;
01804 }
01805 delete[] points;
01806 return NoUnitP;
01807 }
01808
01816 CUnit *FindIdleWorker(const CPlayer *player)
01817 {
01818 CUnit *unit;
01819 CUnit *firstUnitFound;
01820 int nunits;
01821 bool selectNextUnit = false;
01822
01823 firstUnitFound = NoUnitP;
01824
01825 nunits = player->TotalNumUnits;
01826
01827 for (int i = 0; i < nunits; ++i) {
01828 unit = player->Units[i];
01829 if (unit->Type->Harvester && !unit->Removed) {
01830 if (unit->Orders[0]->Action == UnitActionStill) {
01831 if (selectNextUnit) {
01832 return unit;
01833 }
01834 if (firstUnitFound == NoUnitP) {
01835
01836 firstUnitFound = unit;
01837 }
01838 if (!selectNextUnit && IsOnlySelected(unit)) {
01839
01840 selectNextUnit = true;
01841 }
01842 }
01843 }
01844 }
01845
01846 if (firstUnitFound != NoUnitP && !IsOnlySelected(firstUnitFound)) {
01847 return firstUnitFound;
01848 }
01849
01850 return NoUnitP;
01851 }
01852
01853
01854
01855
01856
01865 CUnit *UnitOnScreen(int x, int y)
01866 {
01867 CUnit *bestUnit = NULL;
01868
01869 for (int i = 0; i < NumUnits; ++i) {
01870 CUnit *unit = Units[i];
01871 CUnitType *type = unit->Type;
01872 int gx, gy;
01873
01874
01875 if (!unit->IsVisibleAsGoal(ThisPlayer) && !ReplayRevealMap) {
01876 continue;
01877 }
01878
01879
01880 gx = unit->X * TileSizeX + unit->IX;
01881 if (x + (type->BoxWidth - type->TileWidth * TileSizeX) / 2 < gx) {
01882 continue;
01883 }
01884 if (x > gx + (type->TileWidth * TileSizeX + type->BoxWidth) / 2) {
01885 continue;
01886 }
01887
01888 gy = unit->Y * TileSizeY + unit->IY;
01889 if (y + (type->BoxHeight - type->TileHeight * TileSizeY) / 2 < gy) {
01890 continue;
01891 }
01892 if (y > gy + (type->TileHeight * TileSizeY + type->BoxHeight) / 2) {
01893 continue;
01894 }
01895
01896
01897 if (!bestUnit || unit->Type->DrawLevel > bestUnit->Type->DrawLevel) {
01898 bestUnit = unit;
01899 }
01900 }
01901
01902 return bestUnit;
01903 }
01904
01908 void UnitRemoveConsumingResources(CUnit *unit)
01909 {
01910 if ((unit->Orders[0]->Action == UnitActionBuild && !unit->Type->BuilderOutside && unit->SubAction == 40) ||
01911 (unit->Orders[0]->Action == UnitActionRepair && unit->SubAction == 20)) {
01912 unit->Player->RemoveFromUnitsConsumingResources(unit);
01913 } else if (unit->Orders[0]->Action == UnitActionResource && unit->SubAction >= 55) {
01914 for (int u = 0; u < MaxCosts; ++u) {
01915 unit->Player->ProductionRate[u] -= unit->Data.Harvest.CurrentProduction[u];
01916 }
01917 } else if (unit->Orders[0]->Action == UnitActionTrain && unit->SubAction != 0) {
01918 unit->Player->RemoveFromUnitsConsumingResources(unit);
01919 }
01920 }
01921
01927 void LetUnitDie(CUnit *unit)
01928 {
01929 CUnitType *type;
01930
01931 unit->Moving = 0;
01932 unit->TTL = 0;
01933 unit->Anim.Unbreakable = 0;
01934
01935 type = unit->Type;
01936
01937
01938 if (unit->Removed) {
01939 DebugPrint("Killing a removed unit?\n");
01940 UnitLost(unit);
01941 UnitClearOrders(unit);
01942 unit->Release();
01943 return;
01944 }
01945
01946 PlayUnitSound(unit, VoiceDying);
01947
01948
01949
01950
01951 if (type->ExplodeWhenKilled) {
01952 MakeMissile(type->Explosion.Missile,
01953 unit->X * TileSizeX + type->TileWidth * TileSizeX / 2,
01954 unit->Y * TileSizeY + type->TileHeight * TileSizeY / 2,
01955 0, 0);
01956 }
01957 if (type->DeathExplosion) {
01958 type->DeathExplosion->pushPreamble();
01959 type->DeathExplosion->pushInteger(unit->X * TileSizeX +
01960 type->TileWidth * TileSizeX / 2);
01961 type->DeathExplosion->pushInteger(unit->Y * TileSizeY +
01962 type->TileHeight * TileSizeY / 2);
01963 type->DeathExplosion->run();
01964 }
01965
01966 UnitRemoveConsumingResources(unit);
01967 if (unit->Orders[0]->Action != UnitActionBuilt) {
01968 for (int u = 0; u < MaxCosts; ++u) {
01969 unit->Player->ProductionRate[u] -= unit->Type->ProductionRate[u] * unit->ProductionEfficiency / 100;
01970 unit->Player->StorageCapacity[u] -= unit->Type->StorageCapacity[u];
01971 if (unit->Player->StoredResources[u] > unit->Player->StorageCapacity[u]) {
01972 unit->Player->StoredResources[u] = unit->Player->StorageCapacity[u];
01973 }
01974 }
01975 }
01976
01977
01978
01979
01980 if (type->CanHarvestFrom &&
01981 unit->Orders[0]->Action == UnitActionBuilt &&
01982 unit->Data.Built.Worker) {
01983
01984 memcpy(unit->ResourcesHeld, unit->Data.Built.Worker->ResourcesHeld, sizeof(unit->ResourcesHeld));
01985 }
01986
01987
01988 if (unit->UnitInside) {
01989
01990 DestroyAllInside(unit);
01991 }
01992 unit->Remove(NULL);
01993 UnitLost(unit);
01994 UnitClearOrders(unit);
01995
01996
01997
01998
01999
02000
02001 unit->SubAction = 0;
02002 unit->State = 0;
02003 unit->Orders[0]->Action = UnitActionDie;
02004 if (type->CorpseType) {
02005 unit->IX = (type->CorpseType->Width - type->CorpseType->Sprite->Width) / 2;
02006 unit->IY = (type->CorpseType->Height - type->CorpseType->Sprite->Height) / 2;
02007
02008 unit->CurrentSightRange = type->CorpseType->Stats[unit->Player->Index].Variables[SIGHTRANGE_INDEX].Max;
02009 } else {
02010 unit->CurrentSightRange = 0;
02011 }
02012
02013
02014
02015 if (type->CorpseType || (type->Animations && type->Animations->Death)) {
02016 unit->Removed = 0;
02017 UnitCache.Insert(unit);
02018 }
02019 MapMarkUnitSight(unit);
02020 }
02021
02027 void DestroyAllInside(CUnit *source)
02028 {
02029 CUnit *unit;
02030
02031
02032 unit = source->UnitInside;
02033 for (int i = source->InsideCount; i; --i, unit = unit->NextContained) {
02034
02035 if (unit->UnitInside) {
02036 DestroyAllInside(unit);
02037 }
02038 UnitLost(unit);
02039 UnitClearOrders(unit);
02040 unit->Release();
02041 }
02042 }
02043
02044
02045
02046
02047
02048
02056 void HitUnit(CUnit *attacker, CUnit *target, int damage)
02057 {
02058 CUnitType *type;
02059 CUnit *goal;
02060 unsigned long lastattack;
02061
02062
02063
02064 if (!damage) {
02065 return;
02066 }
02067
02068 Assert(damage != 0 && target->Orders[0]->Action != UnitActionDie && !target->Type->Vanishes);
02069
02070 if (target->Type->Indestructible) {
02071 return;
02072 }
02073
02074 if (target->Removed) {
02075 DebugPrint("Removed target hit\n");
02076 return;
02077 }
02078
02079 if (GodMode) {
02080 if (attacker && attacker->Player == ThisPlayer) {
02081 damage = target->Variable[HP_INDEX].Value;
02082 }
02083 if (target->Player == ThisPlayer) {
02084 damage = 0;
02085 }
02086 }
02087
02088 type = target->Type;
02089 lastattack = target->Attacked;
02090 target->Attacked = GameCycle ? GameCycle : 1;
02091
02092
02093 if (!lastattack || lastattack + 2 * CYCLES_PER_SECOND < GameCycle) {
02094
02095 if (target->Player == ThisPlayer) {
02096
02097
02098
02099
02100
02101
02102 if (HelpMeLastCycle < GameCycle) {
02103 if (!HelpMeLastCycle ||
02104 HelpMeLastCycle + CYCLES_PER_SECOND * 120 < GameCycle ||
02105 target->X < HelpMeLastX - 14 ||
02106 target->X > HelpMeLastX + 14 ||
02107 target->Y < HelpMeLastY - 14 ||
02108 target->Y > HelpMeLastY + 14) {
02109 HelpMeLastCycle = GameCycle + CYCLES_PER_SECOND * 2;
02110 HelpMeLastX = target->X;
02111 HelpMeLastY = target->Y;
02112 PlayUnitSound(target, VoiceHelpMe);
02113 }
02114 }
02115 }
02116 target->Player->Notify(NotifyRed, target->X, target->Y,
02117 _("%s attacked"), target->Type->Name.c_str());
02118 if (target->Player->AiEnabled) {
02119 AiHelpMe(attacker, target);
02120 }
02121 }
02122
02123 if (target->Variable[HP_INDEX].Value <= damage) {
02124
02125
02126 if (attacker && target->IsEnemy(attacker)) {
02127 attacker->Player->Score += target->Type->Points;
02128 if (type->Building) {
02129 attacker->Player->TotalRazings++;
02130 } else {
02131 attacker->Player->TotalKills++;
02132 }
02133 attacker->Variable[KILL_INDEX].Value++;
02134 attacker->Variable[KILL_INDEX].Max++;
02135 attacker->Variable[KILL_INDEX].Enable = 1;
02136 }
02137 LetUnitDie(target);
02138 return;
02139 }
02140 target->Variable[HP_INDEX].Value -= damage;
02141
02142
02143
02144
02145
02146 if (EnableBuildingCapture && attacker &&
02147 type->Building && target->Variable[HP_INDEX].Value <= damage * 3 &&
02148 attacker->IsEnemy(target) &&
02149 attacker->Type->RepairRange) {
02150 target->ChangeOwner(attacker->Player);
02151 CommandStopUnit(attacker);
02152 }
02153
02154 if ((target->IsVisibleOnMap(ThisPlayer) || ReplayRevealMap) && !DamageMissile.empty()) {
02155 MakeLocalMissile(MissileTypeByIdent(DamageMissile),
02156 target->X * TileSizeX + target->Type->TileWidth * TileSizeX / 2,
02157 target->Y * TileSizeY + target->Type->TileHeight * TileSizeY / 2,
02158 target->X * TileSizeX + target->Type->TileWidth * TileSizeX / 2 + 3,
02159 target->Y * TileSizeY + target->Type->TileHeight * TileSizeY / 2 -
02160 MissileTypeByIdent(DamageMissile)->Range)->Damage = -damage;
02161 }
02162
02163 #if 0
02164
02165 if (type->Organic) {
02166 MakeMissile(MissileBlood,
02167 target->X * TileSizeX + TileSizeX / 2,
02168 target->Y * TileSizeY + TileSizeY / 2, 0, 0);
02169 }
02170 if (type->Building) {
02171 MakeMissile(MissileSmallFire,
02172 target->X * TileSizeX + (type->TileWidth * TileSizeX) / 2,
02173 target->Y * TileSizeY + (type->TileHeight * TileSizeY) / 2, 0, 0);
02174 }
02175 #endif
02176
02177 if (type->Building && !target->Burning) {
02178 int f;
02179 Missile *missile;
02180 MissileType *fire;
02181
02182 f = (100 * target->Variable[HP_INDEX].Value) / target->Variable[HP_INDEX].Max;
02183 fire = MissileBurningBuilding(f);
02184 if (fire) {
02185 missile = MakeMissile(fire,
02186 target->X * TileSizeX + (type->TileWidth * TileSizeX) / 2,
02187 target->Y * TileSizeY + (type->TileHeight * TileSizeY) / 2 - TileSizeY,
02188 0, 0);
02189 missile->SourceUnit = target;
02190 target->Burning = 1;
02191 target->RefsIncrease();
02192 }
02193 }
02194
02195
02196
02197
02198 if (target->Orders[0]->Action != UnitActionStill) {
02199 return;
02200 }
02201
02202
02203
02204
02205 if (attacker && !type->Coward) {
02206 if (type->CanAttack) {
02207 if (CanTarget(target->Type, attacker->Type)) {
02208
02209 goal = attacker;
02210 } else {
02211
02212 goal = AttackUnitsInReactRange(target);
02213 }
02214 if (goal) {
02215 if (target->SavedOrder.Action == UnitActionStill) {
02216
02217 CommandAttack(target, target->X, target->Y, NoUnitP,
02218 FlushCommands);
02219 target->SavedOrder = *target->Orders[1];
02220 }
02221 CommandAttack(target, goal->X, goal->Y, NoUnitP, FlushCommands);
02222 return;
02223 }
02224 }
02225 }
02226
02227
02228
02229
02230 if (CanMove(target)) {
02231 int x;
02232 int y;
02233 int d;
02234
02235 x = target->X - attacker->X;
02236 y = target->Y - attacker->Y;
02237 d = isqrt(x * x + y * y);
02238 if (!d) {
02239 d = 1;
02240 }
02241 x = target->X + (x * 5) / d + (SyncRand() & 3);
02242 if (x < 0) {
02243 x = 0;
02244 } else if (x >= Map.Info.MapWidth) {
02245 x = Map.Info.MapWidth - 1;
02246 }
02247 y = target->Y + (y * 5) / d + (SyncRand() & 3);
02248 if (y < 0) {
02249 y = 0;
02250 } else if (y >= Map.Info.MapHeight) {
02251 y = Map.Info.MapHeight - 1;
02252 }
02253 CommandStopUnit(target);
02254 CommandMove(target, x, y, 0);
02255 }
02256 }
02257
02258
02259
02260
02261
02272 int MapDistance(int x1, int y1, int x2, int y2)
02273 {
02274 return isqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
02275 }
02276
02288 int MapDistanceToType(int x1, int y1, const CUnitType *type, int x2, int y2)
02289 {
02290 int dx;
02291 int dy;
02292
02293 if (x1 <= x2) {
02294 dx = x2 - x1;
02295 } else {
02296 dx = x1 - x2 - type->TileWidth + 1;
02297 if (dx < 0) {
02298 dx = 0;
02299 }
02300 }
02301
02302 if (y1 <= y2) {
02303 dy = y2 - y1;
02304 } else {
02305 dy = y1 - y2 - type->TileHeight + 1;
02306 if (dy < 0) {
02307 dy = 0;
02308 }
02309 }
02310
02311 return isqrt(dy * dy + dx * dx);
02312 }
02313
02323 int MapDistanceToUnit(int x, int y, const CUnit *dest)
02324 {
02325 return MapDistanceToType(x, y, dest->Type, dest->X, dest->Y);
02326 }
02327
02336 int MapDistanceBetweenUnits(const CUnit *src, const CUnit *dst)
02337 {
02338 return MapDistanceBetweenTypes(src->Type, src->X, src->Y,
02339 dst->Type, dst->X, dst->Y);
02340 }
02341
02354 int MapDistanceBetweenTypes(const CUnitType *src, int x1, int y1, const CUnitType *dst, int x2, int y2)
02355 {
02356 int dx;
02357 int dy;
02358
02359 if (x1 + src->TileWidth <= x2) {
02360 dx = x2 - x1 - src->TileWidth + 1;
02361 if (dx < 0) {
02362 dx = 0;
02363 }
02364 } else {
02365 dx = x1 - x2 - dst->TileWidth + 1;
02366 if (dx < 0) {
02367 dx = 0;
02368 }
02369 }
02370
02371 if (y1 + src->TileHeight <= y2) {
02372 dy = y2 - y1 - src->TileHeight + 1;
02373 } else {
02374 dy = y1 - y2 - dst->TileHeight + 1;
02375 if (dy < 0) {
02376 dy = 0;
02377 }
02378 }
02379
02380 return isqrt(dy * dy + dx * dx);
02381 }
02382
02391 int ViewPointDistance(int x, int y)
02392 {
02393 const CViewport *vp;
02394
02395
02396 vp = UI.SelectedViewport;
02397
02398
02399 return MapDistance(vp->MapX + vp->MapWidth / 2,
02400 vp->MapY + vp->MapHeight / 2, x, y);
02401 }
02402
02410 int ViewPointDistanceToUnit(const CUnit *dest)
02411 {
02412 const CViewport *vp = UI.SelectedViewport;;
02413 return MapDistanceToUnit(vp->MapX + vp->MapWidth / 2,
02414 vp->MapY + vp->MapHeight / 2, dest);
02415 }
02416
02425 int CanTarget(const CUnitType *source, const CUnitType *dest)
02426 {
02427 if (dest->UnitType == UnitTypeLand) {
02428 if (dest->ShoreBuilding) {
02429 return source->CanTarget & (CanTargetLand | CanTargetSea);
02430 }
02431 return source->CanTarget & CanTargetLand;
02432 }
02433 if (dest->UnitType == UnitTypeFly) {
02434 return source->CanTarget & CanTargetAir;
02435 }
02436 if (dest->UnitType == UnitTypeNaval) {
02437 return source->CanTarget & CanTargetSea;
02438 }
02439 return 0;
02440 }
02441
02450 int CanTransport(const CUnit *transporter, const CUnit *unit)
02451 {
02452 if (!transporter->Type->CanTransport) {
02453 return 0;
02454 }
02455 if (transporter->Orders[0]->Action == UnitActionBuilt) {
02456 return 0;
02457 }
02458 if (transporter == unit) {
02459 return 0;
02460 }
02461 if (transporter->BoardCount >= transporter->Type->MaxOnBoard) {
02462 return 0;
02463 }
02464
02465 if (unit->Type->UnitType != UnitTypeLand) {
02466 return 0;
02467 }
02468
02469
02470 if (!transporter->IsTeamed(unit)) {
02471 return 0;
02472 }
02473
02474
02475 if (!unit->Type->Organic) {
02476 return 0;
02477 }
02478 return 1;
02479 }
02480
02486 bool CUnit::IsEnemy(const CPlayer *x) const
02487 {
02488 return (this->Player->Enemy & (1 << x->Index)) != 0;
02489 }
02490
02496 bool CUnit::IsEnemy(const CUnit *x) const
02497 {
02498 return IsEnemy(x->Player);
02499 }
02500
02506 bool CUnit::IsAllied(const CPlayer *x) const
02507 {
02508 return Player->IsAllied(x);
02509 }
02510
02516 bool CUnit::IsAllied(const CUnit *x) const
02517 {
02518 return IsAllied(x->Player);
02519 }
02520
02526 bool CUnit::IsSharedVision(const CPlayer *x) const
02527 {
02528 return (this->Player->SharedVision & (1 << x->Index)) != 0;
02529 }
02530
02536 bool CUnit::IsSharedVision(const CUnit *x) const
02537 {
02538 return IsSharedVision(x->Player);
02539 }
02540
02546 bool CUnit::IsBothSharedVision(const CPlayer *x) const
02547 {
02548 return (this->Player->SharedVision & (1 << x->Index)) != 0 &&
02549 (x->SharedVision & (1 << this->Player->Index)) != 0;
02550 }
02551
02557 bool CUnit::IsBothSharedVision(const CUnit *x) const
02558 {
02559 return IsBothSharedVision(x->Player);
02560 }
02561
02567 bool CUnit::IsTeamed(const CPlayer *x) const
02568 {
02569 return (this->Player->Team == x->Team);
02570 }
02571
02577 bool CUnit::IsTeamed(const CUnit *x) const
02578 {
02579 return this->IsTeamed(x->Player);
02580 }
02581
02586 bool CUnit::IsUnusable() const
02587 {
02588 return this->Removed || this->Orders[0]->Action == UnitActionDie ||
02589 this->Orders[0]->Action == UnitActionBuilt || this->Destroyed;
02590 }
02591
02592
02593
02594
02595
02596
02600 void InitUnits(void)
02601 {
02602 if (!SaveGameLoading) {
02603 NumUnits = 0;
02604 UnitManager.Init();
02605 }
02606 }
02607
02611 void CleanUnits(void)
02612 {
02613 CUnit **table;
02614
02615
02616
02617
02618 for (table = Units; table < &Units[NumUnits]; ++table) {
02619 delete[] (*table)->AutoCastSpell;
02620 delete[] (*table)->Variable;
02621 for (std::vector<COrder *>::iterator order = (*table)->Orders.begin(); order != (*table)->Orders.end(); ++order) {
02622 delete *order;
02623 }
02624 (*table)->Orders.clear();
02625 delete *table;
02626 *table = NULL;
02627 }
02628 NumUnits = 0;
02629
02630 UnitManager.Init();
02631
02632 HelpMeLastCycle = 0;
02633 }
02634