1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name spells.cpp - The spell cast action. */
12 //
13 // (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
14 // Jimmy Salmon, and Joris DAUPHIN
15 //
16 // This program is free software; you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation; only version 2 of the License.
19 //
20 // This program is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with this program; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 // 02111-1307, USA.
29 //
30
31 /*
32 ** And when we cast our final spell
33 ** And we meet in our dreams
34 ** A place that no one else can go
35 ** Don't ever let your love die
36 ** Don't ever go breaking this spell
37 */
38
39 //@{
40
41 /*----------------------------------------------------------------------------
42 -- Includes
43 ----------------------------------------------------------------------------*/
44
45 #include "stratagus.h"
46
47 #include "spells.h"
48
49 #include "actions.h"
50 #include "commands.h"
51 #include "map.h"
52 #include "sound.h"
53 #include "unit.h"
54 #include "unit_find.h"
55 #include "upgrade.h"
56
57 /*----------------------------------------------------------------------------
58 -- Variables
59 ----------------------------------------------------------------------------*/
60
61 /**
62 ** Define the names and effects of all im play available spells.
63 */
64 std::vector<SpellType *> SpellTypeTable;
65
66
67 /*----------------------------------------------------------------------------
68 -- Functions
69 ----------------------------------------------------------------------------*/
70
71 // ****************************************************************************
72 // Target constructor
73 // ****************************************************************************
74
75 /**
76 ** Target constructor for unit.
77 **
78 ** @param unit Target unit.
79 **
80 ** @return the new target.
81 */
NewTargetUnit(CUnit & unit)82 static Target *NewTargetUnit(CUnit &unit)
83 {
84 return new Target(TargetUnit, &unit, unit.tilePos);
85 }
86
87 // ****************************************************************************
88 // Main local functions
89 // ****************************************************************************
90
91 /**
92 ** Check the condition.
93 **
94 ** @param caster Pointer to caster unit.
95 ** @param spell Pointer to the spell to cast.
96 ** @param target Pointer to target unit, or 0 if it is a position spell.
97 ** @param goalPos position, or {-1, -1} if it is a unit spell.
98 ** @param condition Pointer to condition info.
99 **
100 ** @return true if passed, false otherwise.
101 */
PassCondition(const CUnit & caster,const SpellType & spell,const CUnit * target,const Vec2i & goalPos,const ConditionInfo * condition)102 static bool PassCondition(const CUnit &caster, const SpellType &spell, const CUnit *target,
103 const Vec2i &goalPos, const ConditionInfo *condition)
104 {
105 if (caster.Variable[MANA_INDEX].Value < spell.ManaCost) { // Check caster mana.
106 return false;
107 }
108 // check countdown timer
109 if (caster.SpellCoolDownTimers[spell.Slot]) { // Check caster mana.
110 return false;
111 }
112 // Check caster's resources
113 if (caster.Player->CheckCosts(spell.Costs, false)) {
114 return false;
115 }
116 if (spell.Target == TargetUnit) { // Casting a unit spell without a target.
117 if ((!target) || target->IsAlive() == false) {
118 return false;
119 }
120 }
121 if (!condition) { // no condition, pass.
122 return true;
123 }
124 for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); i++) { // for custom variables
125 const CUnit *unit;
126
127 if (!condition->Variable[i].Check) {
128 continue;
129 }
130
131 unit = (condition->Variable[i].ConditionApplyOnCaster) ? &caster : target;
132 // Spell should target location and have unit condition.
133 if (unit == NULL) {
134 continue;
135 }
136 if (condition->Variable[i].Enable != CONDITION_TRUE) {
137 if ((condition->Variable[i].Enable == CONDITION_ONLY) ^ (unit->Variable[i].Enable)) {
138 return false;
139 }
140 }
141 // Value and Max
142 if (condition->Variable[i].ExactValue != -1 &&
143 condition->Variable[i].ExactValue != unit->Variable[i].Value) {
144 return false;
145 }
146 if (condition->Variable[i].ExceptValue != -1 &&
147 condition->Variable[i].ExceptValue == unit->Variable[i].Value) {
148 return false;
149 }
150 if (condition->Variable[i].MinValue >= unit->Variable[i].Value) {
151 return false;
152 }
153 if (condition->Variable[i].MaxValue != -1 &&
154 condition->Variable[i].MaxValue <= unit->Variable[i].Value) {
155 return false;
156 }
157
158 if (condition->Variable[i].MinMax >= unit->Variable[i].Max) {
159 return false;
160 }
161
162 if (!unit->Variable[i].Max) {
163 continue;
164 }
165 // Percent
166 if (condition->Variable[i].MinValuePercent * unit->Variable[i].Max
167 >= 100 * unit->Variable[i].Value) {
168 return false;
169 }
170 if (condition->Variable[i].MaxValuePercent * unit->Variable[i].Max
171 <= 100 * unit->Variable[i].Value) {
172 return false;
173 }
174 }
175 if (target && !target->Type->CheckUserBoolFlags(condition->BoolFlag)) {
176 return false;
177 }
178
179 if (condition->CheckFunc) {
180 condition->CheckFunc->pushPreamble();
181 condition->CheckFunc->pushString(spell.Ident);
182 condition->CheckFunc->pushInteger(UnitNumber(caster));
183 condition->CheckFunc->pushInteger(goalPos.x);
184 condition->CheckFunc->pushInteger(goalPos.y);
185 condition->CheckFunc->pushInteger((target && target->IsAlive()) ? UnitNumber(*target) : -1);
186 condition->CheckFunc->run(1);
187 if (condition->CheckFunc->popBoolean() == false) {
188 return false;
189 }
190 }
191 if (!target) {
192 return true;
193 }
194
195 if (condition->Alliance != CONDITION_TRUE) {
196 if ((condition->Alliance == CONDITION_ONLY) ^
197 // own units could be not allied ?
198 (caster.IsAllied(*target) || target->Player == caster.Player)) {
199 return false;
200 }
201 }
202 if (condition->Opponent != CONDITION_TRUE) {
203 if ((condition->Opponent == CONDITION_ONLY) ^
204 (caster.IsEnemy(*target) && 1)) {
205 return false;
206 }
207 }
208 if (condition->TargetSelf != CONDITION_TRUE) {
209 if ((condition->TargetSelf == CONDITION_ONLY) ^ (&caster == target)) {
210 return false;
211 }
212 }
213 return true;
214 }
215
216 class AutoCastPrioritySort
217 {
218 public:
AutoCastPrioritySort(const CUnit & caster,const int var,const bool reverse)219 explicit AutoCastPrioritySort(const CUnit &caster, const int var, const bool reverse) :
220 caster(caster), variable(var), reverse(reverse) {}
operator ()(const CUnit * lhs,const CUnit * rhs) const221 bool operator()(const CUnit *lhs, const CUnit *rhs) const
222 {
223 if (variable == ACP_DISTANCE) {
224 if (reverse) {
225 return lhs->MapDistanceTo(caster) > rhs->MapDistanceTo(caster);
226 } else {
227 return lhs->MapDistanceTo(caster) < rhs->MapDistanceTo(caster);
228 }
229 } else {
230 if (reverse) {
231 return lhs->Variable[variable].Value > rhs->Variable[variable].Value;
232 } else {
233 return lhs->Variable[variable].Value < rhs->Variable[variable].Value;
234 }
235 }
236 }
237 private:
238 const CUnit &caster;
239 const int variable;
240 const bool reverse;
241 };
242
243 /**
244 ** Select the target for the autocast.
245 **
246 ** @param caster Unit who would cast the spell.
247 ** @param spell Spell-type pointer.
248 **
249 ** @return Target* chosen target or Null if spell can't be cast.
250 ** @todo FIXME: should be global (for AI) ???
251 ** @todo FIXME: write for position target.
252 */
SelectTargetUnitsOfAutoCast(CUnit & caster,const SpellType & spell)253 static Target *SelectTargetUnitsOfAutoCast(CUnit &caster, const SpellType &spell)
254 {
255 AutoCastInfo *autocast;
256
257 // Ai cast should be a lot better. Use autocast if not found.
258 if (caster.Player->AiEnabled && spell.AICast) {
259 autocast = spell.AICast;
260 } else {
261 autocast = spell.AutoCast;
262 }
263 Assert(autocast);
264 const Vec2i &pos = caster.tilePos;
265 int range = autocast->Range;
266 int minRange = autocast->MinRange;
267
268 // Select all units aroung the caster
269 std::vector<CUnit *> table;
270 SelectAroundUnit(caster, range, table, OutOfMinRange(minRange, caster.tilePos));
271 if (minRange == 0)
272 table.push_back(&caster); // Allow self as target (we check conditions later)
273
274 // Check generic conditions. FIXME: a better way to do this?
275 if (autocast->Combat != CONDITION_TRUE) {
276 // Check each unit if it is hostile.
277 bool inCombat = false;
278 for (size_t i = 0; i < table.size(); ++i) {
279 const CUnit &target = *table[i];
280
281 // Note that CanTarget doesn't take into account (offensive) spells...
282 if (target.IsVisibleAsGoal(*caster.Player) && caster.IsEnemy(target)
283 && (CanTarget(*caster.Type, *target.Type) || CanTarget(*target.Type, *caster.Type))) {
284 inCombat = true;
285 break;
286 }
287 }
288 if ((autocast->Combat == CONDITION_ONLY) ^ (inCombat)) {
289 return NULL;
290 }
291 }
292
293 switch (spell.Target) {
294 case TargetSelf :
295 if (PassCondition(caster, spell, &caster, pos, spell.Condition)
296 && PassCondition(caster, spell, &caster, pos, autocast->Condition)) {
297 return NewTargetUnit(caster);
298 }
299 return NULL;
300 case TargetPosition: {
301 if (autocast->PositionAutoCast && table.empty() == false) {
302 size_t count = 0;
303 for (size_t i = 0; i != table.size(); ++i) {
304 // Check for corpse
305 if (autocast->Corpse == CONDITION_ONLY) {
306 if (table[i]->CurrentAction() != UnitActionDie) {
307 continue;
308 }
309 } else if (autocast->Corpse == CONDITION_FALSE) {
310 if (table[i]->CurrentAction() == UnitActionDie || table[i]->IsAlive() == false) {
311 continue;
312 }
313 }
314 if (PassCondition(caster, spell, table[i], pos, spell.Condition)
315 && PassCondition(caster, spell, table[i], pos, autocast->Condition)) {
316 table[count++] = table[i];
317 }
318 }
319 if (count > 0) {
320 if (autocast->PriorytyVar != ACP_NOVALUE) {
321 std::sort(table.begin(), table.begin() + count,
322 AutoCastPrioritySort(caster, autocast->PriorytyVar, autocast->ReverseSort));
323 }
324 std::vector<int> array(count + 1);
325 for (size_t i = 1; i != count + 1; ++i) {
326 array[i] = UnitNumber(*table[i - 1]);
327 }
328 array[0] = UnitNumber(caster);
329 autocast->PositionAutoCast->pushPreamble();
330 autocast->PositionAutoCast->pushIntegers(array);
331 autocast->PositionAutoCast->run(2);
332 Vec2i resPos(autocast->PositionAutoCast->popInteger(), autocast->PositionAutoCast->popInteger());
333 if (Map.Info.IsPointOnMap(resPos)) {
334 Target *target = new Target(TargetPosition, NULL, resPos);
335 return target;
336 }
337 }
338 }
339 return 0;
340 }
341 case TargetUnit: {
342 // The units are already selected.
343 // Check every unit if it is a possible target
344
345 int n = 0;
346 for (size_t i = 0; i != table.size(); ++i) {
347 // Check if unit in battle
348 if (autocast->Attacker == CONDITION_ONLY) {
349 const int range = table[i]->Player->Type == PlayerPerson ? table[i]->Type->ReactRangePerson : table[i]->Type->ReactRangeComputer;
350 if ((table[i]->CurrentAction() != UnitActionAttack
351 && table[i]->CurrentAction() != UnitActionAttackGround
352 && table[i]->CurrentAction() != UnitActionSpellCast)
353 || table[i]->CurrentOrder()->HasGoal() == false
354 || table[i]->MapDistanceTo(table[i]->CurrentOrder()->GetGoalPos()) > range) {
355 continue;
356 }
357 }
358 // Check for corpse
359 if (autocast->Corpse == CONDITION_ONLY) {
360 if (table[i]->CurrentAction() != UnitActionDie) {
361 continue;
362 }
363 } else if (autocast->Corpse == CONDITION_FALSE) {
364 if (table[i]->CurrentAction() == UnitActionDie) {
365 continue;
366 }
367 }
368 if (PassCondition(caster, spell, table[i], pos, spell.Condition)
369 && PassCondition(caster, spell, table[i], pos, autocast->Condition)) {
370 table[n++] = table[i];
371 }
372 }
373 // Now select the best unit to target.
374 if (n != 0) {
375 // For the best target???
376 if (autocast->PriorytyVar != ACP_NOVALUE) {
377 std::sort(table.begin(), table.begin() + n,
378 AutoCastPrioritySort(caster, autocast->PriorytyVar, autocast->ReverseSort));
379 return NewTargetUnit(*table[0]);
380 } else { // Use the old behavior
381 return NewTargetUnit(*table[SyncRand() % n]);
382 }
383 }
384 break;
385 }
386 default:
387 // Something is wrong
388 DebugPrint("Spell is screwed up, unknown target type\n");
389 Assert(0);
390 return NULL;
391 break;
392 }
393 return NULL; // Can't spell the auto-cast.
394 }
395
396 // ****************************************************************************
397 // Public spell functions
398 // ****************************************************************************
399
400 // ****************************************************************************
401 // Constructor and destructor
402 // ****************************************************************************
403
404 /**
405 ** Spells constructor, inits spell id's and sounds
406 */
InitSpells()407 void InitSpells()
408 {
409 }
410
411 /**
412 ** Get spell-type struct pointer by string identifier.
413 **
414 ** @param ident Spell identifier.
415 **
416 ** @return spell-type struct pointer.
417 */
SpellTypeByIdent(const std::string & ident)418 SpellType *SpellTypeByIdent(const std::string &ident)
419 {
420 for (std::vector<SpellType *>::iterator i = SpellTypeTable.begin(); i < SpellTypeTable.end(); ++i) {
421 if ((*i)->Ident == ident) {
422 return *i;
423 }
424 }
425 return NULL;
426 }
427
428 // ****************************************************************************
429 // CanAutoCastSpell, CanCastSpell, AutoCastSpell, CastSpell.
430 // ****************************************************************************
431
432 /**
433 ** Check if spell is research for player \p player.
434 ** @param player player for who we want to know if he knows the spell.
435 ** @param spellid id of the spell to check.
436 **
437 ** @return 0 if spell is not available, else no null.
438 */
SpellIsAvailable(const CPlayer & player,int spellid)439 bool SpellIsAvailable(const CPlayer &player, int spellid)
440 {
441 const int dependencyId = SpellTypeTable[spellid]->DependencyId;
442
443 return dependencyId == -1 || UpgradeIdAllowed(player, dependencyId) == 'R';
444 }
445
446 /**
447 ** Check if unit can cast the spell.
448 **
449 ** @param caster Unit that casts the spell
450 ** @param spell Spell-type pointer
451 ** @param target Target unit that spell is addressed to
452 ** @param goalPos coord of target spot when/if target does not exist
453 **
454 ** @return =!0 if spell should/can casted, 0 if not
455 ** @note caster must know the spell, and spell must be researched.
456 */
CanCastSpell(const CUnit & caster,const SpellType & spell,const CUnit * target,const Vec2i & goalPos)457 bool CanCastSpell(const CUnit &caster, const SpellType &spell,
458 const CUnit *target, const Vec2i &goalPos)
459 {
460 if (spell.Target == TargetUnit && target == NULL) {
461 return false;
462 }
463 return PassCondition(caster, spell, target, goalPos, spell.Condition);
464 }
465
466 /**
467 ** Check if the spell can be auto cast and cast it.
468 **
469 ** @param caster Unit who can cast the spell.
470 ** @param spell Spell-type pointer.
471 **
472 ** @return 1 if spell is casted, 0 if not.
473 */
AutoCastSpell(CUnit & caster,const SpellType & spell)474 int AutoCastSpell(CUnit &caster, const SpellType &spell)
475 {
476 // Check for mana and cooldown time, trivial optimization.
477 if (!SpellIsAvailable(*caster.Player, spell.Slot)
478 || caster.Variable[MANA_INDEX].Value < spell.ManaCost
479 || caster.SpellCoolDownTimers[spell.Slot]) {
480 return 0;
481 }
482 Target *target = SelectTargetUnitsOfAutoCast(caster, spell);
483 if (target == NULL) {
484 return 0;
485 } else {
486 // Save previous order
487 COrder *savedOrder = NULL;
488 if (caster.CurrentAction() != UnitActionStill && caster.CanStoreOrder(caster.CurrentOrder())) {
489 savedOrder = caster.CurrentOrder()->Clone();
490 }
491 // Must move before ?
492 CommandSpellCast(caster, target->targetPos, target->Unit, spell, FlushCommands, true);
493 delete target;
494 if (savedOrder != NULL) {
495 caster.SavedOrder = savedOrder;
496 }
497 }
498 return 1;
499 }
500
501 /**
502 ** Spell cast!
503 **
504 ** @param caster Unit that casts the spell
505 ** @param spell Spell-type pointer
506 ** @param target Target unit that spell is addressed to
507 ** @param goalPos coord of target spot when/if target does not exist
508 **
509 ** @return !=0 if spell should/can continue or 0 to stop
510 */
SpellCast(CUnit & caster,const SpellType & spell,CUnit * target,const Vec2i & goalPos)511 int SpellCast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
512 {
513 Vec2i pos = goalPos;
514
515 caster.Variable[INVISIBLE_INDEX].Value = 0;// unit is invisible until attacks // FIXME: Must be configurable
516 if (target) {
517 pos = target->tilePos;
518 }
519 //
520 // For TargetSelf, you target.... YOURSELF
521 //
522 if (spell.Target == TargetSelf) {
523 pos = caster.tilePos;
524 target = &caster;
525 }
526 DebugPrint("Spell cast: (%s), %s -> %s (%d,%d)\n" _C_ spell.Ident.c_str() _C_
527 caster.Type->Name.c_str() _C_ target ? target->Type->Name.c_str() : "none" _C_ pos.x _C_ pos.y);
528 if (CanCastSpell(caster, spell, target, pos)) {
529 int cont = 1; // Should we recast the spell.
530 bool mustSubtractMana = true; // false if action which have their own calculation is present.
531 //
532 // Ugly hack, CastAdjustVitals makes it's own mana calculation.
533 //
534 if (spell.SoundWhenCast.Sound) {
535 if (spell.Target == TargetSelf) {
536 PlayUnitSound(caster, spell.SoundWhenCast.Sound);
537 } else {
538 PlayGameSound(spell.SoundWhenCast.Sound, CalculateVolume(false, ViewPointDistance(target ? target->tilePos : goalPos), spell.SoundWhenCast.Sound->Range));
539 }
540 }
541 for (std::vector<SpellActionType *>::const_iterator act = spell.Action.begin();
542 act != spell.Action.end(); ++act) {
543 if ((*act)->ModifyManaCaster) {
544 mustSubtractMana = false;
545 }
546 cont = cont & (*act)->Cast(caster, spell, target, pos);
547 }
548 if (mustSubtractMana) {
549 caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
550 }
551 caster.Player->SubCosts(spell.Costs);
552 caster.SpellCoolDownTimers[spell.Slot] = spell.CoolDown;
553 //
554 // Spells like blizzard are casted again.
555 // This is sort of confusing, we do the test again, to
556 // check if it will be possible to cast again. Otherwise,
557 // when you're out of mana the caster will try again ( do the
558 // anim but fail in this proc.
559 //
560 if (spell.RepeatCast && cont) {
561 return CanCastSpell(caster, spell, target, pos);
562 }
563 }
564 //
565 // Can't cast, STOP.
566 //
567 return 0;
568 }
569
570
571 /**
572 ** SpellType constructor.
573 */
SpellType(int slot,const std::string & ident)574 SpellType::SpellType(int slot, const std::string &ident) :
575 Ident(ident), Slot(slot), Target(), Action(),
576 Range(0), ManaCost(0), RepeatCast(0), CoolDown(0),
577 DependencyId(-1), Condition(NULL),
578 AutoCast(NULL), AICast(NULL), ForceUseAnimation(false)
579 {
580 memset(Costs, 0, sizeof(Costs));
581 }
582
583 /**
584 ** SpellType destructor.
585 */
~SpellType()586 SpellType::~SpellType()
587 {
588 for (std::vector<SpellActionType *>::iterator act = Action.begin(); act != Action.end(); ++act) {
589 delete *act;
590 }
591 Action.clear();
592
593 delete Condition;
594 //
595 // Free Autocast.
596 //
597 delete AutoCast;
598 delete AICast;
599 }
600
601
602 /**
603 ** Cleanup the spell subsystem.
604 */
CleanSpells()605 void CleanSpells()
606 {
607 DebugPrint("Cleaning spells.\n");
608 for (std::vector<SpellType *>::iterator i = SpellTypeTable.begin(); i < SpellTypeTable.end(); ++i) {
609 delete *i;
610 }
611 SpellTypeTable.clear();
612 }
613
614 //@}
615