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 <math.h>
00039
00040
00041 #include "stratagus.h"
00042
00043 #include <vector>
00044 #include <string>
00045 #include <map>
00046
00047 #include "video.h"
00048 #include "font.h"
00049 #include "map.h"
00050 #include "unitsound.h"
00051 #include "unittype.h"
00052 #include "unit_cache.h"
00053 #include "player.h"
00054 #include "unit.h"
00055 #include "missile.h"
00056 #include "sound.h"
00057 #include "ui.h"
00058 #include "iolib.h"
00059 #include "particle.h"
00060 #include "trigger.h"
00061 #include "luacallback.h"
00062
00063
00064
00065
00066
00067 unsigned int Missile::Count = 0;
00068
00069 static std::vector<MissileType *>MissileTypes;
00070
00071 static std::vector<Missile*> GlobalMissiles;
00072 static std::vector<Missile*> LocalMissiles;
00073
00075 static std::map<std::string, MissileType *> MissileTypeMap;
00076
00077 std::vector<BurningBuildingFrame *> BurningBuildingFrames;
00078
00079
00080
00081
00082
00086 void MissileType::LoadMissileSprite()
00087 {
00088 if (this->G && !this->G->IsLoaded()) {
00089 this->G->Load();
00090 if (this->Flip) {
00091 this->G->Flip();
00092 }
00093
00094
00095 Assert(this->G->NumFrames >= this->SpriteFrames);
00096 this->G->NumFrames = this->SpriteFrames;
00097
00098 }
00099 }
00100
00104 void LoadMissileSprites(void)
00105 {
00106 for (std::vector<MissileType*>::iterator i = MissileTypes.begin(); i != MissileTypes.end(); ++i) {
00107 (*i)->LoadMissileSprite();
00108 }
00109 }
00117 MissileType *MissileTypeByIdent(const std::string &ident)
00118 {
00119 return MissileTypeMap[ident];
00120 }
00121
00129 MissileType *NewMissileTypeSlot(const std::string &ident)
00130 {
00131 MissileType *mtype;
00132
00133 mtype = new MissileType(ident);
00134 MissileTypeMap[ident] = mtype;
00135 MissileTypes.push_back(mtype);
00136 return mtype;
00137 }
00138
00142 Missile::Missile() :
00143 SourceX(0), SourceY(0), X(0), Y(0), DX(0), DY(0),
00144 Type(NULL), SpriteFrame(0), State(0), AnimWait(0), Wait(0),
00145 Delay(0), SourceUnit(NULL), TargetUnit(NULL), Damage(0),
00146 TTL(-1), Hidden(0),
00147 CurrentStep(0), TotalStep(0),
00148 Local(0)
00149 {
00150 this->Slot = Missile::Count++;
00151 }
00152
00164 Missile *Missile::Init(MissileType *mtype, int sx, int sy, int dx, int dy)
00165 {
00166 Missile *missile = NULL;
00167
00168 switch (mtype->Class) {
00169 case MissileClassNone :
00170 missile = new MissileNone;
00171 break;
00172 case MissileClassPointToPoint :
00173 missile = new MissilePointToPoint;
00174 break;
00175 case MissileClassPointToPointWithHit :
00176 missile = new MissilePointToPointWithHit;
00177 break;
00178 case MissileClassPointToPointCycleOnce :
00179 missile = new MissilePointToPointCycleOnce;
00180 break;
00181 case MissileClassPointToPointBounce :
00182 missile = new MissilePointToPointBounce;
00183 break;
00184 case MissileClassStay :
00185 missile = new MissileStay;
00186 break;
00187 case MissileClassCycleOnce :
00188 missile = new MissileCycleOnce;
00189 break;
00190 case MissileClassFire :
00191 missile = new MissileFire;
00192 break;
00193 case MissileClassHit :
00194 missile = new MissileHit;
00195 break;
00196 case MissileClassParabolic :
00197 missile = new MissileParabolic;
00198 break;
00199 }
00200
00201 missile->X = sx - mtype->Width / 2;
00202 missile->Y = sy - mtype->Height / 2;
00203 missile->DX = dx - mtype->Width / 2;
00204 missile->DY = dy - mtype->Height / 2;
00205 missile->SourceX = missile->X;
00206 missile->SourceY = missile->Y;
00207 missile->Type = mtype;
00208 missile->Wait = mtype->Sleep;
00209 missile->Delay = mtype->StartDelay;
00210
00211 return missile;
00212 }
00213
00225 Missile *MakeMissile(MissileType *mtype, int sx, int sy, int dx, int dy)
00226 {
00227 Missile *missile;
00228
00229 missile = Missile::Init(mtype, sx, sy, dx, dy);
00230 GlobalMissiles.push_back(missile);
00231 return missile;
00232 }
00233
00245 Missile *MakeLocalMissile(MissileType *mtype, int sx, int sy, int dx, int dy)
00246 {
00247 Missile *missile;
00248
00249 missile = Missile::Init(mtype, sx, sy, dx, dy);
00250 missile->Local = 1;
00251 LocalMissiles.push_back(missile);
00252 return missile;
00253 }
00254
00261 static void FreeMissile(std::vector<Missile *> &missiles, size_t i)
00262 {
00263 Missile *missile;
00264 CUnit *unit;
00265
00266 missile = missiles[i];
00267
00268
00269
00270 if ((unit = missile->SourceUnit)) {
00271 unit->RefsDecrease();
00272 }
00273 if ((unit = missile->TargetUnit)) {
00274 unit->RefsDecrease();
00275 }
00276 for (std::vector<Missile*>::iterator j = missiles.begin(); j != missiles.end(); ++j) {
00277 if (*j == missile) {
00278 missiles.erase(j);
00279 break;
00280 }
00281 }
00282 delete missile;
00283 }
00284
00297 static int CalculateDamage(const CUnit *attacker, const CUnit *goal)
00298 {
00299 int damage;
00300 int basic_damage;
00301 int piercing_damage;
00302
00303 basic_damage = attacker->Stats->Variables[BASICDAMAGE_INDEX].Value;
00304 piercing_damage = attacker->Stats->Variables[PIERCINGDAMAGE_INDEX].Value;
00305
00306 damage = std::max(basic_damage - goal->Stats->Variables[ARMOR_INDEX].Value, 1);
00307 damage += piercing_damage;
00308 damage -= SyncRand() % ((damage + 2) / 2);
00309 Assert(damage >= 0);
00310
00311 return damage;
00312 }
00313
00319 void FireMissile(CUnit *unit)
00320 {
00321 int x;
00322 int y;
00323 int dx;
00324 int dy;
00325 CUnit *goal;
00326 Missile *missile;
00327
00328
00329
00330
00331 goal = unit->Orders[0]->Goal;
00332 if (goal) {
00333
00334
00335
00336 if (goal->Destroyed) {
00337 DebugPrint("destroyed unit\n");
00338 return;
00339 }
00340 if (goal->Removed || goal->Orders[0]->Action == UnitActionDie) {
00341 return;
00342 }
00343
00344
00345
00346 }
00347
00348
00349
00350
00351 if (unit->Type->Missile.Missile->Class == MissileClassNone) {
00352
00353 if (!goal) {
00354 DebugPrint("Missile-none hits no unit, shouldn't happen!\n");
00355 return;
00356 }
00357
00358 HitUnit(unit, goal, CalculateDamage(unit, goal));
00359
00360 return;
00361 }
00362
00363
00364 if (unit->Container) {
00365 x = unit->Container->X * TileSizeX + TileSizeX / 2;
00366 y = unit->Container->Y * TileSizeY + TileSizeY / 2;
00367 } else {
00368 x = unit->X * TileSizeX + TileSizeX / 2;
00369 y = unit->Y * TileSizeY + TileSizeY / 2;
00370 }
00371
00372 if (goal) {
00373 Assert(goal->Type);
00374
00375
00376
00377 if (MapDistanceBetweenUnits(unit, goal) < unit->Type->MinAttackRange) {
00378 DebugPrint("Missile target too near %d,%d\n" _C_
00379 MapDistanceBetweenUnits(unit, goal) _C_ unit->Type->MinAttackRange);
00380
00381 return;
00382 }
00383
00384
00385 if (unit->Container) {
00386 NearestOfUnit(goal, unit->Container->X, unit->Container->Y, &dx, &dy);
00387 } else {
00388 NearestOfUnit(goal, unit->X, unit->Y, &dx, &dy);
00389 }
00390 } else {
00391 dx = unit->Orders[0]->X;
00392 dy = unit->Orders[0]->Y;
00393
00394 }
00395
00396
00397 dx = dx * TileSizeX + TileSizeX / 2;
00398 dy = dy * TileSizeY + TileSizeY / 2;
00399 missile = MakeMissile(unit->Type->Missile.Missile, x, y, dx, dy);
00400
00401
00402
00403 if (goal) {
00404 missile->TargetUnit = goal;
00405 goal->RefsIncrease();
00406 }
00407 missile->SourceUnit = unit;
00408 unit->RefsIncrease();
00409 }
00410
00422 static void GetMissileMapArea(const Missile *missile, int *sx, int *sy,
00423 int *ex, int *ey)
00424 {
00425 #define BoundX(x) std::min(std::max(0, x), Map.Info.MapWidth - 1)
00426 #define BoundY(y) std::min(std::max(0, y), Map.Info.MapHeight - 1)
00427
00428 *sx = BoundX(missile->X / TileSizeX);
00429 *sy = BoundY(missile->Y / TileSizeY);
00430 *ex = BoundX((missile->X + missile->Type->Width + TileSizeX - 1) / TileSizeX);
00431 *ey = BoundY((missile->Y + missile->Type->Height + TileSizeY - 1) / TileSizeY);
00432
00433 #undef BoundX
00434 #undef BoundY
00435 }
00436
00445 static int MissileVisibleInViewport(const CViewport *vp, const Missile *missile)
00446 {
00447 int min_x;
00448 int max_x;
00449 int min_y;
00450 int max_y;
00451
00452 GetMissileMapArea(missile, &min_x, &min_y, &max_x, &max_y);
00453 if (!vp->AnyMapAreaVisibleInViewport(min_x, min_y, max_x, max_y)) {
00454 return 0;
00455 }
00456
00457 for (int x = min_x; x <= max_x; ++x) {
00458 for (int y = min_y; y <= max_y; ++y) {
00459 if (ReplayRevealMap || Map.IsFieldVisible(ThisPlayer, x, y)) {
00460 return 1;
00461 }
00462 }
00463 }
00464 return 0;
00465 }
00466
00474 void MissileType::DrawMissileType(int frame, int x, int y) const
00475 {
00476 if (this->Flip) {
00477 if (frame < 0) {
00478 if (this->Transparency == 50) {
00479 this->G->DrawFrameClipTransX(-frame - 1, x, y, 128);
00480 } else {
00481 this->G->DrawFrameClipX(-frame - 1, x, y);
00482 }
00483 } else {
00484 if (this->Transparency == 50) {
00485 this->G->DrawFrameClipTrans(frame, x, y, 128);
00486 } else {
00487 this->G->DrawFrameClip(frame, x, y);
00488 }
00489 }
00490 } else {
00491 int row;
00492
00493 row = this->NumDirections / 2 + 1;
00494 if (frame < 0) {
00495 frame = ((-frame - 1) / row) * this->NumDirections + this->NumDirections - (-frame - 1) % row;
00496 } else {
00497 frame = (frame / row) * this->NumDirections + frame % row;
00498 }
00499 if (this->Transparency == 50) {
00500 this->G->DrawFrameClipTrans(frame, x, y, 128);
00501 } else {
00502 this->G->DrawFrameClip(frame, x, y);
00503 }
00504 }
00505 }
00506
00510 void Missile::DrawMissile() const
00511 {
00512 int x;
00513 int y;
00514 const CViewport *vp;
00515
00516 Assert(this->Type);
00517 Assert(CurrentViewport);
00518
00519 vp = CurrentViewport;
00520 x = this->X - vp->MapX * TileSizeX + vp->X - vp->OffsetX;
00521 y = this->Y - vp->MapY * TileSizeY + vp->Y - vp->OffsetY;
00522 switch (this->Type->Class) {
00523 case MissileClassHit:
00524 VideoDrawNumberClip(x, y, GameFont, this->Damage);
00525 break;
00526 default:
00527 this->Type->DrawMissileType(this->SpriteFrame, x, y);
00528 break;
00529 }
00530 }
00531
00540 static int MissileDrawLevelCompare(const void *v1, const void *v2)
00541 {
00542 const Missile *c1;
00543 const Missile *c2;
00544
00545 c1 = *(Missile **)v1;
00546 c2 = *(Missile **)v2;
00547
00548 if (c1->Type->DrawLevel == c2->Type->DrawLevel) {
00549 return c1->Slot < c2->Slot ? -1 : 1;
00550 } else {
00551 return c1->Type->DrawLevel <= c2->Type->DrawLevel ? -1 : 1;
00552 }
00553 }
00563 int FindAndSortMissiles(const CViewport *vp, Missile **table, int tablesize)
00564 {
00565 int nmissiles;
00566 std::vector<Missile *>::const_iterator i;
00567
00568
00569
00570
00571 nmissiles = 0;
00572 for (i = GlobalMissiles.begin(); i != GlobalMissiles.end() && nmissiles < tablesize; ++i) {
00573 if ((*i)->Delay || (*i)->Hidden) {
00574 continue;
00575 }
00576
00577 if (MissileVisibleInViewport(vp, *i)) {
00578 table[nmissiles++] = *i;
00579 }
00580 }
00581
00582 for (i = LocalMissiles.begin(); i != LocalMissiles.end() && nmissiles < tablesize; ++i) {
00583 if ((*i)->Delay || (*i)->Hidden) {
00584 continue;
00585 }
00586
00587 table[nmissiles++] = *i;
00588 }
00589 if (nmissiles) {
00590 qsort((void *)table, nmissiles, sizeof(Missile *), MissileDrawLevelCompare);
00591 }
00592 return nmissiles;
00593 }
00594
00603 static void MissileNewHeadingFromXY(Missile *missile, int dx, int dy)
00604 {
00605 int dir;
00606 int nextdir;
00607 int neg;
00608
00609 if (missile->Type->NumDirections == 1 || (dx == 0 && dy == 0)) {
00610 return;
00611 }
00612
00613 if (missile->SpriteFrame < 0) {
00614 missile->SpriteFrame = -missile->SpriteFrame - 1;
00615 neg = 1;
00616 } else {
00617 neg = 0;
00618 }
00619 missile->SpriteFrame /= missile->Type->NumDirections / 2 + 1;
00620 missile->SpriteFrame *= missile->Type->NumDirections / 2 + 1;
00621
00622 nextdir = 256 / missile->Type->NumDirections;
00623 Assert(nextdir != 0);
00624 dir = ((DirectionToHeading(10 * dx, 10 * dy) + nextdir / 2) & 0xFF) / nextdir;
00625 if (dir <= LookingS / nextdir) {
00626 missile->SpriteFrame += dir;
00627 } else {
00628 missile->SpriteFrame += 256 / nextdir - dir;
00629 missile->SpriteFrame = -missile->SpriteFrame - 1;
00630 }
00631 }
00632
00640 static int MissileInitMove(Missile *missile)
00641 {
00642 int dx;
00643 int dy;
00644
00645 dx = missile->DX - missile->X;
00646 dy = missile->DY - missile->Y;
00647 MissileNewHeadingFromXY(missile, dx, dy);
00648 if (!(missile->State & 1)) {
00649 missile->CurrentStep = 0;
00650 missile->TotalStep = 0;
00651 if (dx == 0 && dy == 0) {
00652 return 1;
00653 }
00654
00655 missile->TotalStep = MapDistance(missile->SourceX, missile->SourceY, missile->DX, missile->DY);
00656 missile->State++;
00657 return 0;
00658 }
00659 Assert(missile->TotalStep != 0);
00660 missile->CurrentStep += missile->Type->Speed;
00661 if (missile->CurrentStep >= missile->TotalStep) {
00662 missile->X = missile->DX;
00663 missile->Y = missile->DY;
00664 return 1;
00665 }
00666 return 0;
00667 }
00668
00676 static int PointToPointMissile(Missile *missile)
00677 {
00678 int xstep;
00679 int ystep;
00680 int x;
00681 int y;
00682
00683 if (MissileInitMove(missile) == 1) {
00684 return 1;
00685 }
00686
00687 Assert(missile->Type != NULL);
00688 Assert(missile->TotalStep != 0);
00689 xstep = (missile->DX - missile->SourceX) * 1024 / missile->TotalStep;
00690 ystep = (missile->DY - missile->SourceY) * 1024 / missile->TotalStep;
00691 missile->X = missile->SourceX + xstep * missile->CurrentStep / 1024;
00692 missile->Y = missile->SourceY + ystep * missile->CurrentStep / 1024;
00693 if (missile->Type->SmokeMissile && missile->CurrentStep) {
00694 x = missile->X + missile->Type->Width / 2;
00695 y = missile->Y + missile->Type->Height / 2;
00696 MakeMissile(missile->Type->SmokeMissile, x, y, x, y);
00697 }
00698 return 0;
00699 }
00700
00710 static int ParabolicMissile(Missile *missile)
00711 {
00712 int orig_x;
00713 int orig_y;
00714 int xstep;
00715 int ystep;
00716 int k;
00717 int zprojToX;
00718 int zprojToY;
00719 int z;
00720 int x;
00721 int y;
00722
00723 k = -2048;
00724 zprojToX = 4;
00725 zprojToY = 1024;
00726 if (MissileInitMove(missile) == 1) {
00727 return 1;
00728 }
00729 Assert(missile->Type != NULL);
00730 orig_x = missile->X;
00731 orig_y = missile->Y;
00732 xstep = missile->DX - missile->SourceX;
00733 ystep = missile->DY - missile->SourceY;
00734 Assert(missile->TotalStep != 0);
00735 xstep = xstep * 1000 / missile->TotalStep;
00736 ystep = ystep * 1000 / missile->TotalStep;
00737 missile->X = missile->SourceX + xstep * missile->CurrentStep / 1000;
00738 missile->Y = missile->SourceY + ystep * missile->CurrentStep / 1000;
00739 Assert(k != 0);
00740 z = missile->CurrentStep * (missile->TotalStep - missile->CurrentStep) / k;
00741
00742 missile->X += z * zprojToX / 64;
00743 missile->Y += z * zprojToY / 64;
00744 MissileNewHeadingFromXY(missile, missile->X - orig_x, missile->Y - orig_y);
00745 if (missile->Type->SmokeMissile && missile->CurrentStep) {
00746 x = missile->X + missile->Type->Width / 2;
00747 y = missile->Y + missile->Type->Height / 2;
00748 MakeMissile(missile->Type->SmokeMissile, x, y, x, y);
00749 }
00750 return 0;
00751 }
00752
00760 static void MissileHitsGoal(const Missile *missile, CUnit *goal, int splash)
00761 {
00762 if (!missile->Type->CanHitOwner && goal == missile->SourceUnit) {
00763 return;
00764 }
00765
00766 if (goal->Orders[0]->Action != UnitActionDie) {
00767 if (missile->Damage) {
00768 HitUnit(missile->SourceUnit, goal, missile->Damage / splash);
00769 } else {
00770 Assert(missile->SourceUnit != NULL);
00771 HitUnit(missile->SourceUnit, goal,
00772 CalculateDamage(missile->SourceUnit, goal) / splash);
00773 }
00774 }
00775 }
00776
00782 void MissileHit(Missile *missile)
00783 {
00784 CUnit *goal;
00785 int x;
00786 int y;
00787 CUnit *table[UnitMax];
00788 int n;
00789 int i;
00790 int splash;
00791
00792 if (missile->Type->ImpactSound.Sound) {
00793 PlayMissileSound(missile, missile->Type->ImpactSound.Sound);
00794 }
00795
00796 x = missile->X + missile->Type->Width / 2;
00797 y = missile->Y + missile->Type->Height / 2;
00798
00799
00800
00801
00802 if (missile->Type->ImpactMissile) {
00803 MakeMissile(missile->Type->ImpactMissile, x, y, x, y);
00804 }
00805 if (missile->Type->ImpactParticle) {
00806 missile->Type->ImpactParticle->pushPreamble();
00807 missile->Type->ImpactParticle->pushInteger(x);
00808 missile->Type->ImpactParticle->pushInteger(y);
00809 missile->Type->ImpactParticle->run();
00810 }
00811
00812 if (!missile->SourceUnit) {
00813 return;
00814 }
00815
00816 x /= TileSizeX;
00817 y /= TileSizeY;
00818
00819 if (x < 0 || y < 0 || x >= Map.Info.MapWidth || y >= Map.Info.MapHeight) {
00820
00821 DebugPrint("Missile gone outside of map!\n");
00822 return;
00823 }
00824
00825
00826
00827
00828 if (!missile->Type->Range) {
00829 if (missile->TargetUnit) {
00830
00831
00832
00833 goal = missile->TargetUnit;
00834 if (goal->Destroyed) {
00835 goal->RefsDecrease();
00836 missile->TargetUnit = NoUnitP;
00837 return;
00838 }
00839 MissileHitsGoal(missile, goal, 1);
00840 return;
00841 }
00842 return;
00843 }
00844
00845
00846
00847
00848 i = missile->Type->Range;
00849 n = UnitCache.Select(x - i + 1, y - i + 1, x + i, y + i, table, UnitMax);
00850 Assert(missile->SourceUnit != NULL);
00851 for (i = 0; i < n; ++i) {
00852 goal = table[i];
00853
00854
00855
00856
00857 if (CanTarget(missile->SourceUnit->Type, goal->Type)) {
00858 splash = MapDistanceToUnit(x, y, goal);
00859 if (splash) {
00860 splash *= missile->Type->SplashFactor;
00861 } else {
00862 splash = 1;
00863 }
00864 MissileHitsGoal(missile, goal, splash);
00865 }
00866 }
00867 }
00868
00878 static int NextMissileFrame(Missile *missile, char sign, char longAnimation)
00879 {
00880 int neg;
00881 int animationIsFinished;
00882 int numDirections;
00883
00884
00885
00886
00887 neg = 0;
00888 animationIsFinished = 0;
00889 numDirections = missile->Type->NumDirections / 2 + 1;
00890 if (missile->SpriteFrame < 0) {
00891 neg = 1;
00892 missile->SpriteFrame = -missile->SpriteFrame - 1;
00893 }
00894 if (longAnimation) {
00895 int totalf;
00896 int df;
00897 int totalx;
00898 int dx;
00899
00900 totalx = MapDistance(missile->DX, missile->DY, missile->SourceX, missile->SourceY);
00901 dx = MapDistance(missile->X, missile->Y, missile->SourceX, missile->SourceY);
00902 totalf = missile->Type->SpriteFrames / numDirections;
00903 df = missile->SpriteFrame / numDirections;
00904 if ((sign == 1 && dx * totalf <= df * totalx) ||
00905 (sign == -1 && dx * totalf > df * totalx)) {
00906 return animationIsFinished;
00907 }
00908 }
00909 missile->SpriteFrame += sign * numDirections;
00910 if (sign > 0) {
00911 if (missile->SpriteFrame >= missile->Type->SpriteFrames) {
00912 missile->SpriteFrame -= missile->Type->SpriteFrames;
00913 animationIsFinished = 1;
00914 }
00915 } else {
00916 if (missile->SpriteFrame < 0) {
00917 missile->SpriteFrame += missile->Type->SpriteFrames;
00918 animationIsFinished = 1;
00919 }
00920 }
00921 if (neg) {
00922 missile->SpriteFrame = -missile->SpriteFrame - 1;
00923 }
00924
00925 return animationIsFinished;
00926 }
00927
00934 static void NextMissileFrameCycle(Missile *missile)
00935 {
00936 int neg;
00937 int totalx;
00938 int dx;
00939 int f;
00940
00941 neg = 0;
00942 if (missile->SpriteFrame < 0) {
00943 neg = 1;
00944 missile->SpriteFrame = -missile->SpriteFrame - 1;
00945 }
00946 totalx = abs(missile->DX - missile->SourceX);
00947 dx = abs(missile->X - missile->SourceX);
00948 f = missile->Type->SpriteFrames / (missile->Type->NumDirections / 2 + 1);
00949 f = 2 * f - 1;
00950 for (int i = 1, j = 1; i <= f; ++i) {
00951 if (dx * f / i < totalx) {
00952 if ((i - 1) * 2 < f) {
00953 j = i - 1;
00954 } else {
00955 j = f - i;
00956 }
00957 missile->SpriteFrame = missile->SpriteFrame % (missile->Type->NumDirections / 2 + 1) +
00958 j * (missile->Type->NumDirections / 2 + 1);
00959 break;
00960 }
00961 }
00962 if (neg) {
00963 missile->SpriteFrame = -missile->SpriteFrame - 1;
00964 }
00965 }
00966
00972 static void MissilesActionLoop(std::vector<Missile *> &missiles)
00973 {
00974
00975
00976
00977 for (size_t i = 0; i != missiles.size(); ) {
00978
00979 if (missiles[i]->Delay) {
00980 missiles[i]->Delay--;
00981 ++i;
00982 continue;
00983 }
00984
00985 if (missiles[i]->TTL > 0) {
00986 missiles[i]->TTL--;
00987 }
00988
00989 if (!missiles[i]->TTL) {
00990 FreeMissile(missiles, i);
00991 continue;
00992 }
00993
00994 Assert(missiles[i]->Wait);
00995 if (--missiles[i]->Wait) {
00996 ++i;
00997 continue;
00998 }
00999
01000 missiles[i]->Action();
01001
01002 if (!missiles[i]->TTL) {
01003 FreeMissile(missiles, i);
01004 continue;
01005 }
01006 ++i;
01007 }
01008 }
01009
01013 void MissileActions(void)
01014 {
01015 MissilesActionLoop(GlobalMissiles);
01016 MissilesActionLoop(LocalMissiles);
01017 }
01018
01026 int ViewPointDistanceToMissile(const Missile *missile)
01027 {
01028 int x = (missile->X + missile->Type->Width / 2) / TileSizeX;
01029 int y = (missile->Y + missile->Type->Height / 2) / TileSizeY;
01030
01031 return ViewPointDistance(x, y);
01032 }
01033
01041 MissileType *MissileBurningBuilding(int percent)
01042 {
01043 for (std::vector<BurningBuildingFrame *>::iterator i = BurningBuildingFrames.begin();
01044 i != BurningBuildingFrames.end(); ++i) {
01045 if (percent > (*i)->Percent) {
01046 return (*i)->Missile;
01047 }
01048 }
01049 return NULL;
01050 }
01051
01057 void Missile::SaveMissile(CFile *file) const
01058 {
01059 file->printf("Missile(\"type\", \"%s\",", this->Type->Ident.c_str());
01060 file->printf(" \"%s\",", this->Local ? "local" : "global");
01061 file->printf(" \"pos\", {%d, %d}, \"origin-pos\", {%d, %d}, \"goal\", {%d, %d},",
01062 this->X, this->Y, this->SourceX, this->SourceY, this->DX, this->DY);
01063 file->printf("\n \"frame\", %d, \"state\", %d, \"anim-wait\", %d, \"wait\", %d, \"delay\", %d,\n ",
01064 this->SpriteFrame, this->State, this->AnimWait, this->Wait, this->Delay);
01065
01066 if (this->SourceUnit) {
01067 file->printf(" \"source\", \"%s\",", UnitReference(this->SourceUnit).c_str());
01068 }
01069 if (this->TargetUnit) {
01070 file->printf(" \"target\", \"%s\",", UnitReference(this->TargetUnit).c_str());
01071 }
01072
01073 file->printf(" \"damage\", %d,", this->Damage);
01074
01075 file->printf(" \"ttl\", %d,", this->TTL);
01076 if (this->Hidden) {
01077 file->printf(" \"hidden\", ");
01078 }
01079
01080 file->printf(" \"step\", {%d, %d}", this->CurrentStep, this->TotalStep);
01081
01082
01083
01084 file->printf(")\n");
01085 }
01086
01092 void SaveMissiles(CFile *file)
01093 {
01094 std::vector<Missile *>::const_iterator i;
01095
01096 file->printf("\n--- -----------------------------------------\n");
01097 file->printf("--- MODULE: missiles\n\n");
01098
01099 for (i = GlobalMissiles.begin(); i != GlobalMissiles.end(); ++i) {
01100 (*i)->SaveMissile(file);
01101 }
01102 for (i = LocalMissiles.begin(); i != LocalMissiles.end(); ++i) {
01103 (*i)->SaveMissile(file);
01104 }
01105 }
01106
01110 void MissileType::Init(void)
01111 {
01112
01113
01114
01115 if (!this->FiredSound.Name.empty()) {
01116 this->FiredSound.Sound = SoundForName(this->FiredSound.Name);
01117 }
01118 if (!this->ImpactSound.Name.empty()) {
01119 this->ImpactSound.Sound = SoundForName(this->ImpactSound.Name);
01120 }
01121 this->ImpactMissile = MissileTypeByIdent(this->ImpactName);
01122 this->SmokeMissile = MissileTypeByIdent(this->SmokeName);
01123 }
01124
01128 void InitMissileTypes(void)
01129 {
01130 for (std::vector<MissileType*>::iterator i = MissileTypes.begin(); i != MissileTypes.end(); ++i) {
01131 (*i)->Init();
01132 }
01133 }
01134
01138 MissileType::MissileType(const std::string &ident) :
01139 Ident(ident), Transparency(0), Width(0), Height(0),
01140 DrawLevel(0), SpriteFrames(0), NumDirections(0),
01141 Flip(false), CanHitOwner(false), FriendlyFire(false),
01142 Class(), NumBounces(0), StartDelay(0), Sleep(0), Speed(0),
01143 Range(0), SplashFactor(0), ImpactMissile(NULL),
01144 SmokeMissile(NULL), ImpactParticle(NULL), G(NULL)
01145 {
01146 };
01147
01151 MissileType::~MissileType()
01152 {
01153 CGraphic::Free(this->G);
01154 delete ImpactParticle;
01155 }
01156
01160 void CleanMissileTypes(void)
01161 {
01162 for (std::vector<MissileType*>::iterator i = MissileTypes.begin(); i != MissileTypes.end(); ++i) {
01163 delete *i;
01164 }
01165 MissileTypes.clear();
01166 MissileTypeMap.clear();
01167 }
01168
01172 void InitMissiles(void)
01173 {
01174 }
01175
01179 void CleanMissiles(void)
01180 {
01181 std::vector<Missile*>::const_iterator i;
01182
01183 for (i = GlobalMissiles.begin(); i != GlobalMissiles.end(); ++i) {
01184 delete *i;
01185 }
01186 GlobalMissiles.clear();
01187 for (i = LocalMissiles.begin(); i != LocalMissiles.end(); ++i) {
01188 delete *i;
01189 }
01190 LocalMissiles.clear();
01191 }
01192
01193 #ifdef DEBUG
01194 void FreeBurningBuildingFrames()
01195 {
01196 for (std::vector<BurningBuildingFrame *>::iterator i = BurningBuildingFrames.begin();
01197 i != BurningBuildingFrames.end(); ++i) {
01198 delete *i;
01199 }
01200 BurningBuildingFrames.clear();
01201 }
01202 #endif
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01222 void MissileNone::Action()
01223 {
01224 this->Wait = this->Type->Sleep;
01225
01226 }
01227
01231 void MissilePointToPoint::Action()
01232 {
01233 this->Wait = this->Type->Sleep;
01234 if (PointToPointMissile(this)) {
01235 MissileHit(this);
01236 this->TTL = 0;
01237 } else {
01238 NextMissileFrame(this, 1, 0);
01239 }
01240 }
01241
01246 void MissilePointToPointWithHit::Action()
01247 {
01248 this->Wait = this->Type->Sleep;
01249 if (PointToPointMissile(this)) {
01250 if (NextMissileFrame(this, 1, 0)) {
01251 MissileHit(this);
01252 this->TTL = 0;
01253 }
01254 }
01255 }
01256
01260 void MissilePointToPointCycleOnce::Action()
01261 {
01262 this->Wait = this->Type->Sleep;
01263 if (PointToPointMissile(this)) {
01264 MissileHit(this);
01265 this->TTL = 0;
01266 } else {
01267 NextMissileFrameCycle(this);
01268 }
01269 }
01270
01274 void MissileStay::Action()
01275 {
01276 this->Wait = this->Type->Sleep;
01277 if (NextMissileFrame(this, 1, 0)) {
01278 MissileHit(this);
01279 this->TTL = 0;
01280 }
01281 }
01282
01286 void MissilePointToPointBounce::Action()
01287 {
01288 this->Wait = this->Type->Sleep;
01289 if (PointToPointMissile(this)) {
01290 if (this->State < 2 * this->Type->NumBounces - 1 && this->TotalStep) {
01291 int xstep;
01292 int ystep;
01293
01294 xstep = (this->DX - this->SourceX) * 1024 / this->TotalStep;
01295 ystep = (this->DY - this->SourceY) * 1024 / this->TotalStep;
01296 this->DX += xstep * (TileSizeX + TileSizeY) * 3 / 4 / 1024;
01297 this->DY += ystep * (TileSizeX + TileSizeY) * 3 / 4 / 1024;
01298
01299 this->State++;
01300 this->SourceX = this->X;
01301 this->SourceY = this->Y;
01302 PointToPointMissile(this);
01303
01304 MissileHit(this);
01305
01306
01307 } else {
01308 MissileHit(this);
01309 this->TTL = 0;
01310 }
01311 } else {
01312 NextMissileFrame(this, 1, 0);
01313 }
01314 }
01315
01320 void MissileCycleOnce::Action()
01321 {
01322 this->Wait = this->Type->Sleep;
01323 switch (this->State) {
01324 case 0:
01325 case 2:
01326 ++this->State;
01327 break;
01328 case 1:
01329 if (NextMissileFrame(this, 1, 0)) {
01330 ++this->State;
01331 }
01332 break;
01333 case 3:
01334 if (NextMissileFrame(this, -1, 0)) {
01335 MissileHit(this);
01336 this->TTL = 0;
01337 }
01338 break;
01339 }
01340 }
01341
01345 void MissileFire::Action()
01346 {
01347 CUnit *unit;
01348
01349 unit = this->SourceUnit;
01350 this->Wait = this->Type->Sleep;
01351 if (unit->Destroyed || unit->Orders[0]->Action == UnitActionDie) {
01352 this->TTL = 0;
01353 return;
01354 }
01355 if (NextMissileFrame(this, 1, 0)) {
01356 int f;
01357 MissileType *fire;
01358
01359 this->SpriteFrame = 0;
01360 f = (100 * unit->Variable[HP_INDEX].Value) / unit->Variable[HP_INDEX].Max;
01361 fire = MissileBurningBuilding(f);
01362 if (!fire) {
01363 this->TTL = 0;
01364 unit->Burning = 0;
01365 } else {
01366 if (this->Type != fire) {
01367 this->X += this->Type->Width / 2;
01368 this->Y += this->Type->Height / 2;
01369 this->Type = fire;
01370 this->X -= this->Type->Width / 2;
01371 this->Y -= this->Type->Height / 2;
01372 }
01373 }
01374 }
01375 }
01376
01380 void MissileHit::Action()
01381 {
01382 this->Wait = this->Type->Sleep;
01383 if (PointToPointMissile(this)) {
01384 ::MissileHit(this);
01385 this->TTL = 0;
01386 }
01387 }
01388
01392 void MissileParabolic::Action()
01393 {
01394 this->Wait = this->Type->Sleep;
01395 if (ParabolicMissile(this)) {
01396 MissileHit(this);
01397 this->TTL = 0;
01398 } else {
01399 NextMissileFrameCycle(this);
01400 }
01401 }
01402