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
00029
00030
00031
00032
00033
00034
00035
00036
00037
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #include <stdio.h>
00049 #include <stdlib.h>
00050 #include <string.h>
00051
00052 #include "stratagus.h"
00053
00054 #include "unittype.h"
00055 #include "unit_cache.h"
00056 #include "spells.h"
00057 #include "sound.h"
00058 #include "missile.h"
00059 #include "map.h"
00060 #include "ui.h"
00061 #include "actions.h"
00062
00063
00064
00065
00066
00070 std::vector<SpellType*> SpellTypeTable;
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00091 int Demolish::Cast(CUnit *caster, const SpellType *spell,
00092 CUnit *target, int x, int y)
00093 {
00094 int xmin;
00095 int ymin;
00096 int xmax;
00097 int ymax;
00098 int i;
00099 int n;
00100 CUnit *table[UnitMax];
00101
00102
00103
00104
00105 xmin = x - this->Range - 2;
00106 ymin = y - this->Range - 2;
00107 xmax = x + this->Range + 2;
00108 ymax = y + this->Range + 2;
00109 if (xmin < 0) {
00110 xmin = 0;
00111 }
00112 if (xmax > Map.Info.MapWidth - 1) {
00113 xmax = Map.Info.MapWidth - 1;
00114 }
00115 if (ymin < 0) {
00116 ymin = 0;
00117 }
00118 if (ymax > Map.Info.MapHeight - 1) {
00119 ymax = Map.Info.MapHeight - 1;
00120 }
00121
00122
00123
00124
00125 if (this->Damage) {
00126 n = UnitCache.Select(xmin, ymin, xmax + 1, ymax + 1, table, UnitMax);
00127 for (i = 0; i < n; ++i) {
00128 if (table[i]->Type->UnitType != UnitTypeFly && table[i]->Orders[0]->Action != UnitActionDie &&
00129 MapDistanceToUnit(x, y, table[i]) <= this->Range) {
00130
00131 HitUnit(caster, table[i], this->Damage);
00132 }
00133 }
00134 }
00135
00136 return 1;
00137 }
00138
00150 int SpawnPortal::Cast(CUnit *caster, const SpellType *spell,
00151 CUnit *target, int x, int y)
00152 {
00153
00154 CUnit *portal;
00155 CUnitType *ptype;
00156
00157 ptype = this->PortalType;
00158
00159 DebugPrint("Spawning a portal exit.\n");
00160 portal = caster->Goal;
00161 if (portal) {
00162 portal->MoveToXY(x, y);
00163 } else {
00164 portal = MakeUnitAndPlace(x, y, ptype, &Players[PlayerNumNeutral]);
00165 }
00166
00167 caster->Goal = portal;
00168 portal->RefsIncrease();
00169
00170 return 0;
00171 }
00172
00184 int AreaAdjustVitals::Cast(CUnit *caster, const SpellType *spell,
00185 CUnit *target, int x, int y)
00186 {
00187 CUnit *units[UnitMax];
00188 int nunits;
00189 int j;
00190 int hp;
00191 int mana;
00192
00193
00194 nunits = UnitCache.Select(x - spell->Range,
00195 y - spell->Range,
00196 x + spell->Range + caster->Type->Width,
00197 y + spell->Range + caster->Type->Height,
00198 units, UnitMax);
00199 hp = this->HP;
00200 mana = this->Mana;
00201 caster->Variable[MANA_INDEX].Value -= spell->ManaCost;
00202 for (j = 0; j < nunits; ++j) {
00203 target = units[j];
00204
00205 if (!CanCastSpell(caster, spell, target, x, y)) {
00206 continue;
00207 }
00208 if (hp < 0) {
00209 HitUnit(caster, target, -hp);
00210 } else {
00211 target->Variable[HP_INDEX].Value += hp;
00212 if (target->Variable[HP_INDEX].Value > target->Variable[HP_INDEX].Max) {
00213 target->Variable[HP_INDEX].Value = target->Variable[HP_INDEX].Max;
00214 }
00215 }
00216 target->Variable[MANA_INDEX].Value += mana;
00217 if (target->Variable[MANA_INDEX].Value < 0) {
00218 target->Variable[MANA_INDEX].Value = 0;
00219 }
00220 if (target->Variable[MANA_INDEX].Value > target->Variable[MANA_INDEX].Max) {
00221 target->Variable[MANA_INDEX].Value = target->Variable[MANA_INDEX].Max;
00222 }
00223 }
00224 return 0;
00225 }
00226
00240 int AreaBombardment::Cast(CUnit *caster, const SpellType *spell,
00241 CUnit *target, int x, int y)
00242 {
00243 int fields;
00244 int shards;
00245 int damage;
00246 ::Missile *mis;
00247 int offsetx;
00248 int offsety;
00249 int dx;
00250 int dy;
00251 int i;
00252 MissileType *missile;
00253
00254 mis = NULL;
00255
00256 fields = this->Fields;
00257 shards = this->Shards;
00258 damage = this->Damage;
00259 offsetx = this->StartOffsetX;
00260 offsety = this->StartOffsetY;
00261 missile = this->Missile;
00262 while (fields--) {
00263
00264 do {
00265
00266 dx = x + SyncRand() % 5 - 2;
00267 dy = y + SyncRand() % 5 - 2;
00268 } while (dx < 0 && dy < 0 && dx >= Map.Info.MapWidth && dy >= Map.Info.MapHeight);
00269 for (i = 0; i < shards; ++i) {
00270 mis = MakeMissile(missile,
00271 dx * TileSizeX + TileSizeX / 2 + offsetx,
00272 dy * TileSizeY + TileSizeY / 2 + offsety,
00273 dx * TileSizeX + TileSizeX / 2,
00274 dy * TileSizeY + TileSizeY / 2);
00275
00276
00277 if (mis->Type->Speed) {
00278 mis->Delay = i * mis->Type->Sleep * 2 * TileSizeX / mis->Type->Speed;
00279 } else {
00280 mis->Delay = i * mis->Type->Sleep * mis->Type->G->NumFrames;
00281 }
00282 mis->Damage = damage;
00283
00284
00285 mis->SourceUnit = caster;
00286 caster->RefsIncrease();
00287 }
00288 }
00289 return 1;
00290 }
00291
00303 static void EvaluateMissileLocation(const SpellActionMissileLocation *location,
00304 CUnit *caster, CUnit *target, int x, int y, int *resx, int *resy)
00305 {
00306 if (location->Base == LocBaseCaster) {
00307 *resx = caster->X * TileSizeX + TileSizeX / 2;
00308 *resy = caster->Y * TileSizeY + TileSizeY / 2;
00309 } else {
00310 if (target) {
00311 *resx = target->X * TileSizeX + TileSizeX / 2;
00312 *resy = target->Y * TileSizeY + TileSizeY / 2;
00313 } else {
00314 *resx = x * TileSizeX + TileSizeX / 2;
00315 *resy = y * TileSizeY + TileSizeY / 2;
00316 }
00317 }
00318 *resx += location->AddX;
00319 if (location->AddRandX) {
00320 *resx += SyncRand() % location->AddRandX;
00321 }
00322 *resy += location->AddY;
00323 if (location->AddRandY) {
00324 *resy += SyncRand() % location->AddRandY;
00325 }
00326 }
00327
00339 int SpawnMissile::Cast(CUnit *caster, const SpellType *spell,
00340 CUnit *target, int x, int y)
00341 {
00342 ::Missile *missile;
00343 int sx;
00344 int sy;
00345 int dx;
00346 int dy;
00347
00348 EvaluateMissileLocation(&this->StartPoint,
00349 caster, target, x, y, &sx, &sy);
00350 EvaluateMissileLocation(&this->EndPoint,
00351 caster, target, x, y, &dx, &dy);
00352
00353 missile = MakeMissile(this->Missile, sx, sy, dx, dy);
00354 missile->TTL = this->TTL;
00355 missile->Delay = this->Delay;
00356 missile->Damage = this->Damage;
00357 if (missile->Damage != 0) {
00358 missile->SourceUnit = caster;
00359 caster->RefsIncrease();
00360 }
00361 if ((missile->TargetUnit = target)) {
00362 target->RefsIncrease();
00363 }
00364 return 1;
00365 }
00366
00378 int AdjustVariable::Cast(CUnit *caster, const SpellType *spell,
00379 CUnit *target, int x, int y)
00380 {
00381 int i;
00382 CUnit *unit;
00383
00384 for (i = 0; i < UnitTypeVar.NumberVariable; ++i) {
00385 unit = (this->Var[i].TargetIsCaster) ? caster : target;
00386 if (!unit) {
00387 continue;
00388 }
00389
00390 if (this->Var[i].ModifEnable) {
00391 unit->Variable[i].Enable = this->Var[i].Enable;
00392 }
00393 unit->Variable[i].Enable ^= this->Var[i].InvertEnable;
00394
00395
00396 if (this->Var[i].ModifMax) {
00397 unit->Variable[i].Max = this->Var[i].Max;
00398 }
00399 unit->Variable[i].Max += this->Var[i].AddMax;
00400
00401
00402 if (this->Var[i].ModifIncrease) {
00403 unit->Variable[i].Increase = this->Var[i].Increase;
00404 }
00405 unit->Variable[i].Increase += this->Var[i].AddIncrease;
00406
00407
00408 if (this->Var[i].ModifValue) {
00409 unit->Variable[i].Value = this->Var[i].Value;
00410 }
00411 unit->Variable[i].Value += this->Var[i].AddValue;
00412 unit->Variable[i].Value += this->Var[i].IncreaseTime
00413 * unit->Variable[i].Increase;
00414
00415 if (unit->Variable[i].Value <= 0) {
00416 unit->Variable[i].Value = 0;
00417 } else if (unit->Variable[i].Value > unit->Variable[i].Max) {
00418 unit->Variable[i].Value = unit->Variable[i].Max;
00419 }
00420 }
00421 return 0;
00422 }
00423
00424
00436 int AdjustVitals::Cast(CUnit *caster, const SpellType *spell,
00437 CUnit *target, int x, int y)
00438 {
00439 int castcount;
00440 int diffHP;
00441 int diffMana;
00442 int hp;
00443 int mana;
00444 int manacost;
00445
00446 Assert(caster);
00447 Assert(spell);
00448 if (!target) {
00449 return 0;
00450 }
00451
00452 hp = this->HP;
00453 mana = this->Mana;
00454 manacost = spell->ManaCost;
00455
00456
00457 if (hp > 0) {
00458 diffHP = target->Variable[HP_INDEX].Max - target->Variable[HP_INDEX].Value;
00459 } else {
00460 diffHP = target->Variable[HP_INDEX].Value;
00461 }
00462 if (mana > 0) {
00463 diffMana = target->Stats->Variables[MANA_INDEX].Max - target->Variable[MANA_INDEX].Value;
00464 } else {
00465 diffMana = target->Variable[MANA_INDEX].Value;
00466 }
00467
00468
00469
00470
00471 castcount = 0;
00472 if (hp) {
00473 castcount = std::max(castcount, diffHP / abs(hp) + (((hp < 0) &&
00474 (diffHP % (-hp) > 0)) ? 1 : 0));
00475 }
00476 if (mana) {
00477 castcount = std::max(castcount, diffMana / abs(mana) + (((mana < 0) &&
00478 (diffMana % (-mana) > 0)) ? 1 : 0));
00479 }
00480 if (manacost) {
00481 castcount = std::min(castcount, caster->Variable[MANA_INDEX].Value / manacost);
00482 }
00483 if (this->MaxMultiCast) {
00484 castcount = std::min(castcount, this->MaxMultiCast);
00485 }
00486
00487 caster->Variable[MANA_INDEX].Value -= castcount * manacost;
00488 if (hp < 0) {
00489 HitUnit(caster, target, -(castcount * hp));
00490 } else {
00491 target->Variable[HP_INDEX].Value += castcount * hp;
00492 if (target->Variable[HP_INDEX].Value > target->Variable[HP_INDEX].Max) {
00493 target->Variable[HP_INDEX].Value = target->Variable[HP_INDEX].Max;
00494 }
00495 }
00496 target->Variable[MANA_INDEX].Value += castcount * mana;
00497 if (target->Variable[MANA_INDEX].Value < 0) {
00498 target->Variable[MANA_INDEX].Value = 0;
00499 }
00500 if (target->Variable[MANA_INDEX].Value > target->Variable[MANA_INDEX].Max) {
00501 target->Variable[MANA_INDEX].Value = target->Variable[MANA_INDEX].Max;
00502 }
00503
00504 return 0;
00505 }
00506
00518 int Polymorph::Cast(CUnit *caster, const SpellType *spell,
00519 CUnit *target, int x, int y)
00520 {
00521 int i;
00522 int j;
00523 CUnitType *type;
00524
00525 if (!target) {
00526 return 0;
00527 }
00528
00529 type = this->NewForm;
00530
00531 x = x - type->TileWidth / 2;
00532 y = y - type->TileHeight / 2;
00533
00534 caster->Player->Score += target->Type->Points;
00535 if (caster->IsEnemy(target)) {
00536 if (target->Type->Building) {
00537 caster->Player->TotalRazings++;
00538 } else {
00539 caster->Player->TotalKills++;
00540 }
00541 caster->Variable[KILL_INDEX].Value++;
00542 caster->Variable[KILL_INDEX].Max++;
00543 caster->Variable[KILL_INDEX].Enable = 1;
00544 }
00545
00546
00547 target->Remove(NULL);
00548 for (i = 0; i < type->TileWidth; ++i) {
00549 for (j = 0; j < type->TileHeight; ++j) {
00550 if (!UnitTypeCanBeAt(type, x + i, y + j)) {
00551 target->Place(target->X, target->Y);
00552 return 0;
00553 }
00554 }
00555 }
00556 caster->Variable[MANA_INDEX].Value -= spell->ManaCost;
00557 if (this->PlayerNeutral) {
00558 MakeUnitAndPlace(x, y, type, Players + PlayerNumNeutral);
00559 } else {
00560 MakeUnitAndPlace(x, y, type, target->Player);
00561 }
00562 UnitLost(target);
00563 UnitClearOrders(target);
00564 target->Release();
00565 return 1;
00566 }
00567
00579 int Capture::Cast(CUnit *caster, const SpellType *spell,
00580 CUnit *target, int x, int y)
00581 {
00582 if (!target || caster->Player == target->Player) {
00583 return 0;
00584 }
00585
00586 if (this->DamagePercent) {
00587 if ((100 * target->Variable[HP_INDEX].Value) /
00588 target->Variable[HP_INDEX].Max > this->DamagePercent &&
00589 target->Variable[HP_INDEX].Value > this->Damage) {
00590 HitUnit(caster, target, this->Damage);
00591 if (this->SacrificeEnable) {
00592
00593 caster->Remove(NULL);
00594 UnitLost(caster);
00595 UnitClearOrders(caster);
00596 }
00597 return 1;
00598 }
00599 }
00600 caster->Player->Score += target->Type->Points;
00601 if (caster->IsEnemy(target)) {
00602 if (target->Type->Building) {
00603 caster->Player->TotalRazings++;
00604 } else {
00605 caster->Player->TotalKills++;
00606 }
00607 caster->Variable[KILL_INDEX].Value++;
00608 caster->Variable[KILL_INDEX].Max++;
00609 caster->Variable[KILL_INDEX].Enable = 1;
00610 }
00611 target->ChangeOwner(caster->Player);
00612 if (this->SacrificeEnable) {
00613
00614 caster->Remove(NULL);
00615 UnitLost(caster);
00616 UnitClearOrders(caster);
00617 } else {
00618 caster->Variable[MANA_INDEX].Value -= spell->ManaCost;
00619 }
00620 UnitClearOrders(target);
00621 return 0;
00622 }
00623
00635 int Summon::Cast(CUnit *caster, const SpellType *spell,
00636 CUnit *target, int x, int y)
00637 {
00638 int ttl;
00639 int cansummon;
00640 int n;
00641 CUnit *table[UnitMax];
00642 CUnit *unit;
00643 CUnitType *unittype;
00644
00645 unittype = this->UnitType;
00646 ttl = this->TTL;
00647
00648 if (this->RequireCorpse) {
00649 n = UnitCache.Select(x - 1, y - 1, x + 2, y + 2, table, UnitMax);
00650 cansummon = 0;
00651 while (n) {
00652 n--;
00653 unit = table[n];
00654 if (unit->Orders[0]->Action == UnitActionDie && !unit->Type->Building) {
00655
00656
00657
00658 x = unit->X;
00659 y = unit->Y;
00660 unit->Remove(NULL);
00661 unit->Release();
00662 cansummon = 1;
00663 break;
00664 }
00665 }
00666 } else {
00667 cansummon = 1;
00668 }
00669
00670 if (cansummon) {
00671 DebugPrint("Summoning a %s\n" _C_ unittype->Name.c_str());
00672
00673
00674
00675
00676
00677 target = MakeUnit(unittype, caster->Player);
00678 if (target != NoUnitP) {
00679
00680 target->X = x + 1;
00681 target->Y = y;
00682 DropOutOnSide(target, LookingW, 0, 0);
00683
00684
00685
00686 if (ttl) {
00687 target->TTL = GameCycle + ttl;
00688 }
00689
00690 caster->Variable[MANA_INDEX].Value -= spell->ManaCost;
00691 } else {
00692 DebugPrint("Unable to allocate Unit");
00693 }
00694 return 1;
00695 }
00696 return 0;
00697 }
00698
00699
00700
00701
00702
00710 static Target *NewTargetUnit(CUnit *unit)
00711 {
00712 return new Target(TargetUnit, unit, 0, 0);
00713 }
00714
00715 #if 0
00716
00724 static Target *NewTargetPosition(int x, int y)
00725 {
00726 return new Target(TargetPosition, NULL, x, y);
00727 }
00728 #endif
00729
00730
00731
00732
00733
00746 static bool PassCondition(const CUnit *caster, const SpellType *spell, const CUnit *target,
00747 int x, int y, const ConditionInfo *condition)
00748 {
00749 if (caster->Variable[MANA_INDEX].Value < spell->ManaCost) {
00750 return false;
00751 }
00752 if (spell->Target == TargetUnit) {
00753 if ((!target) || target->Destroyed || target->Orders[0]->Action == UnitActionDie) {
00754 return false;
00755 }
00756 }
00757 if (!condition) {
00758 return true;
00759 }
00760
00761 for (int i = 0; i < UnitTypeVar.NumberVariable; i++) {
00762 const CUnit *unit;
00763
00764 unit = (condition->Variable[i].ConditionApplyOnCaster) ? caster : target;
00765
00766 if (unit == NULL) {
00767 continue;
00768 }
00769 if (condition->Variable[i].Enable != CONDITION_TRUE) {
00770 if ((condition->Variable[i].Enable == CONDITION_ONLY) ^ (unit->Variable[i].Enable)) {
00771 return false;
00772 }
00773 }
00774
00775 if (condition->Variable[i].MinValue >= unit->Variable[i].Value) {
00776 return false;
00777 }
00778 if (condition->Variable[i].MaxValue != -1 &&
00779 condition->Variable[i].MaxValue <= unit->Variable[i].Value) {
00780 return false;
00781 }
00782
00783 if (condition->Variable[i].MinMax >= unit->Variable[i].Max) {
00784 return false;
00785 }
00786
00787 if (!unit->Variable[i].Max) {
00788 continue;
00789 }
00790
00791 if (condition->Variable[i].MinValuePercent * unit->Variable[i].Max
00792 >= 100 * unit->Variable[i].Value) {
00793 return false;
00794 }
00795 if (condition->Variable[i].MaxValuePercent * unit->Variable[i].Max
00796 <= 100 * unit->Variable[i].Value) {
00797 return false;
00798 }
00799 }
00800
00801 if (!target) {
00802 return true;
00803 }
00804 if (condition->Alliance != CONDITION_TRUE) {
00805 if ((condition->Alliance == CONDITION_ONLY) ^
00806
00807 (caster->IsAllied(target) || target->Player == caster->Player)) {
00808 return false;
00809 }
00810 }
00811 if (condition->Opponent != CONDITION_TRUE) {
00812 if ((condition->Opponent == CONDITION_ONLY) ^
00813 (caster->IsEnemy(target) && 1)) {
00814 return false;
00815 }
00816 }
00817 if (condition->TargetSelf != CONDITION_TRUE) {
00818 if ((condition->TargetSelf == CONDITION_ONLY) ^ (caster == target)) {
00819 return false;
00820 }
00821 }
00822 if (condition->Building != CONDITION_TRUE) {
00823 if ((condition->Building == CONDITION_ONLY) ^ (target->Type->Building)) {
00824 return false;
00825 }
00826 }
00827 if (condition->Organic != CONDITION_TRUE) {
00828 if ((condition->Organic == CONDITION_ONLY) ^ (target->Type->Organic)) {
00829 return false;
00830 }
00831 }
00832
00833 return true;
00834 }
00835
00846 static Target *SelectTargetUnitsOfAutoCast(CUnit *caster, const SpellType *spell)
00847 {
00848 CUnit *table[UnitMax];
00849 int x;
00850 int y;
00851 int range;
00852 int nunits;
00853 int i;
00854 int j;
00855 int combat;
00856 AutoCastInfo *autocast;
00857
00858
00859 if (caster->Player->AiEnabled && spell->AICast) {
00860 autocast = spell->AICast;
00861 } else {
00862 autocast = spell->AutoCast;
00863 }
00864 Assert(autocast);
00865 x = caster->X;
00866 y = caster->Y;
00867 range = spell->AutoCast->Range;
00868
00869
00870
00871
00872 nunits = UnitCache.Select(caster->X - range, caster->Y - range,
00873 caster->X + range + caster->Type->TileWidth,
00874 caster->Y + range + caster->Type->TileHeight, table, UnitMax);
00875
00876
00877
00878 combat = 0;
00879 for (i = 0; i < nunits; ++i) {
00880 if (caster->IsEnemy(table[i]) && !table[i]->Type->Coward) {
00881 combat = 1;
00882 }
00883 }
00884
00885
00886
00887
00888 if (autocast->Combat != CONDITION_TRUE) {
00889 if ((autocast->Combat == CONDITION_ONLY) ^ (combat)) {
00890 return NULL;
00891 }
00892 }
00893
00894 switch (spell->Target) {
00895 case TargetSelf :
00896 if (PassCondition(caster, spell, caster, x, y, spell->Condition) &&
00897 PassCondition(caster, spell, caster, x, y, autocast->Condition)) {
00898 return NewTargetUnit(caster);
00899 }
00900 return NULL;
00901 case TargetPosition:
00902 return 0;
00903
00904
00905
00906
00907
00908
00909 case TargetUnit:
00910
00911
00912
00913
00914 for (i = 0, j = 0; i < nunits; ++i) {
00915
00916 if (caster == table[i]) {
00917 continue;
00918 }
00919
00920
00921 if (PassCondition(caster, spell, table[i], x, y, spell->Condition) &&
00922 PassCondition(caster, spell, table[i], x, y, autocast->Condition)) {
00923 table[j++] = table[i];
00924 }
00925 }
00926 nunits = j;
00927
00928
00929
00930
00931
00932
00933
00934 if (nunits != 0) {
00935 #if 0
00936
00937 sort(table, nb_units, spell->autocast->f_order);
00938 return NewTargetUnit(table[0]);
00939 #else
00940
00941 i = SyncRand() % nunits;
00942 return NewTargetUnit(table[i]);
00943 #endif
00944 }
00945 break;
00946 default:
00947
00948 DebugPrint("Spell is screwed up, unknown target type\n");
00949 Assert(0);
00950 return NULL;
00951 break;
00952 }
00953 return NULL;
00954 }
00955
00956
00957
00958
00959
00960
00961
00962
00963
00967 void InitSpells(void)
00968 {
00969 }
00970
00978 SpellType *SpellTypeByIdent(const std::string &ident)
00979 {
00980 for (std::vector<SpellType *>::iterator i = SpellTypeTable.begin(); i < SpellTypeTable.end(); ++i) {
00981 if ((*i)->Ident == ident) {
00982 return *i;
00983 }
00984 }
00985 return NULL;
00986 }
00987
00988
00989
00990
00991
00999 bool SpellIsAvailable(const CPlayer *player, int spellid)
01000 {
01001 int dependencyId;
01002 dependencyId = SpellTypeTable[spellid]->DependencyId;
01003
01004 return dependencyId == -1;
01005 }
01006
01019 bool CanCastSpell(const CUnit *caster, const SpellType *spell,
01020 const CUnit *target, int x, int y)
01021 {
01022 if (spell->Target == TargetUnit && target == NULL) {
01023 return false;
01024 }
01025 return PassCondition(caster, spell, target, x, y, spell->Condition);
01026 }
01027
01036 int AutoCastSpell(CUnit *caster, const SpellType *spell)
01037 {
01038 Target *target;
01039
01040
01041 if (!SpellIsAvailable(caster->Player, spell->Slot)
01042 || caster->Variable[MANA_INDEX].Value < spell->ManaCost) {
01043 return 0;
01044 }
01045 target = SelectTargetUnitsOfAutoCast(caster, spell);
01046 if (target == NULL) {
01047 return 0;
01048 } else {
01049
01050
01051 CommandSpellCast(caster, target->X, target->Y, target->Unit,
01052 (SpellType *)spell, FlushCommands);
01053 delete target;
01054 }
01055 return 1;
01056 }
01057
01069 int SpellCast(CUnit *caster, const SpellType *spell, CUnit *target,
01070 int x, int y)
01071 {
01072 int cont;
01073 int mustSubtractMana;
01074
01075 if (target) {
01076 x = target->X;
01077 y = target->Y;
01078 }
01079
01080
01081
01082 if (spell->Target == TargetSelf) {
01083 x = caster->X;
01084 y = caster->Y;
01085 target = caster;
01086 }
01087 DebugPrint("Spell cast: (%s), %s -> %s (%d,%d)\n" _C_ spell->Ident.c_str() _C_
01088 caster->Type->Name.c_str() _C_ target ? target->Type->Name.c_str() : "none" _C_ x _C_ y);
01089 if (CanCastSpell(caster, spell, target, x, y)) {
01090 cont = 1;
01091 mustSubtractMana = 1;
01092
01093
01094
01095 PlayGameSound(spell->SoundWhenCast.Sound, MaxSampleVolume);
01096 for (std::vector<SpellActionType*>::const_iterator act = spell->Action.begin();
01097 act != spell->Action.end(); ++act) {
01098 if ((*act)->ModifyManaCaster) {
01099 mustSubtractMana = 0;
01100 }
01101 cont = cont & (*act)->Cast(caster, spell, target, x, y);
01102 }
01103 if (mustSubtractMana) {
01104 caster->Variable[MANA_INDEX].Value -= spell->ManaCost;
01105 }
01106
01107
01108
01109
01110
01111
01112
01113 if (spell->RepeatCast && cont) {
01114 return CanCastSpell(caster, spell, target, x, y);
01115 }
01116 }
01117
01118
01119
01120 return 0;
01121 }
01122
01123
01127 SpellType::SpellType(int slot, const std::string &ident) :
01128 Ident(ident), Slot(slot), Target(), Action(),
01129 Range(0), ManaCost(0), RepeatCast(0),
01130 DependencyId(-1), Condition(NULL),
01131 AutoCast(NULL), AICast(NULL)
01132 {
01133 }
01134
01138 SpellType::~SpellType()
01139 {
01140 for (std::vector<SpellActionType *>::iterator act = Action.begin(); act != Action.end(); ++act) {
01141 delete *act;
01142 }
01143 Action.clear();
01144
01145 delete Condition;
01146
01147
01148
01149 delete AutoCast;
01150 delete AICast;
01151 }
01152
01153
01157 void CleanSpells(void)
01158 {
01159 DebugPrint("Cleaning spells.\n");
01160 for (std::vector<SpellType *>::iterator i = SpellTypeTable.begin(); i < SpellTypeTable.end(); ++i) {
01161 delete *i;
01162 }
01163 SpellTypeTable.clear();
01164 }
01165