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