1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: magic.cpp
5 	Desc: contains magic definitions
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "../main.hpp"
13 #include "../game.hpp"
14 #include "../stat.hpp"
15 #include "../entity.hpp"
16 #include "../interface/interface.hpp"
17 #include "../sound.hpp"
18 #include "../items.hpp"
19 #include "../net.hpp"
20 #include "../player.hpp"
21 #include "magic.hpp"
22 #include "../collision.hpp"
23 #include "../classdescriptions.hpp"
24 #include "../scores.hpp"
25 
freeSpells()26 void freeSpells()
27 {
28 	list_FreeAll(&spell_forcebolt.elements);
29 	list_FreeAll(&spell_magicmissile.elements);
30 	list_FreeAll(&spell_cold.elements);
31 	list_FreeAll(&spell_fireball.elements);
32 	list_FreeAll(&spell_lightning.elements);
33 	list_FreeAll(&spell_removecurse.elements);
34 	list_FreeAll(&spell_light.elements);
35 	list_FreeAll(&spell_identify.elements);
36 	list_FreeAll(&spell_magicmapping.elements);
37 	list_FreeAll(&spell_sleep.elements);
38 	list_FreeAll(&spell_confuse.elements);
39 	list_FreeAll(&spell_slow.elements);
40 	list_FreeAll(&spell_opening.elements);
41 	list_FreeAll(&spell_locking.elements);
42 	list_FreeAll(&spell_levitation.elements);
43 	list_FreeAll(&spell_invisibility.elements);
44 	list_FreeAll(&spell_teleportation.elements);
45 	list_FreeAll(&spell_healing.elements);
46 	list_FreeAll(&spell_extrahealing.elements);
47 	list_FreeAll(&spell_cureailment.elements);
48 	list_FreeAll(&spell_dig.elements);
49 	list_FreeAll(&spell_summon.elements);
50 	list_FreeAll(&spell_stoneblood.elements);
51 	list_FreeAll(&spell_bleed.elements);
52 	list_FreeAll(&spell_dominate.elements);
53 	list_FreeAll(&spell_reflectMagic.elements);
54 	list_FreeAll(&spell_acidSpray.elements);
55 	list_FreeAll(&spell_stealWeapon.elements);
56 	list_FreeAll(&spell_drainSoul.elements);
57 	list_FreeAll(&spell_vampiricAura.elements);
58 	list_FreeAll(&spell_charmMonster.elements);
59 	list_FreeAll(&spell_revertForm.elements);
60 	list_FreeAll(&spell_ratForm.elements);
61 	list_FreeAll(&spell_spiderForm.elements);
62 	list_FreeAll(&spell_trollForm.elements);
63 	list_FreeAll(&spell_impForm.elements);
64 	list_FreeAll(&spell_sprayWeb.elements);
65 	list_FreeAll(&spell_poison.elements);
66 	list_FreeAll(&spell_speed.elements);
67 	list_FreeAll(&spell_fear.elements);
68 	list_FreeAll(&spell_strike.elements);
69 	list_FreeAll(&spell_detectFood.elements);
70 	list_FreeAll(&spell_weakness.elements);
71 	list_FreeAll(&spell_amplifyMagic.elements);
72 	list_FreeAll(&spell_shadowTag.elements);
73 	list_FreeAll(&spell_telePull.elements);
74 	list_FreeAll(&spell_demonIllusion.elements);
75 	list_FreeAll(&spell_trollsBlood.elements);
76 	list_FreeAll(&spell_salvageItem.elements);
77 	list_FreeAll(&spell_flutter.elements);
78 	list_FreeAll(&spell_dash.elements);
79 	list_FreeAll(&spell_polymorph.elements);
80 }
81 
spell_magicMap(int player)82 void spell_magicMap(int player)
83 {
84 	if (players[player] == nullptr || players[player]->entity == nullptr)
85 	{
86 		return;
87 	}
88 
89 	if ( multiplayer == SERVER && player > 0 )
90 	{
91 		//Tell the client to map the magic.
92 		strcpy((char*)net_packet->data, "MMAP");
93 		net_packet->address.host = net_clients[player - 1].host;
94 		net_packet->address.port = net_clients[player - 1].port;
95 		net_packet->len = 4;
96 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
97 		return;
98 	}
99 
100 	messagePlayer(player, language[412]);
101 	mapLevel(player);
102 }
103 
spell_detectFoodEffectOnMap(int player)104 void spell_detectFoodEffectOnMap(int player)
105 {
106 	if ( players[player] == nullptr || players[player]->entity == nullptr )
107 	{
108 		return;
109 	}
110 
111 	if ( multiplayer == SERVER && player > 0 )
112 	{
113 		//Tell the client to map the food.
114 		strcpy((char*)net_packet->data, "MFOD");
115 		net_packet->address.host = net_clients[player - 1].host;
116 		net_packet->address.port = net_clients[player - 1].port;
117 		net_packet->len = 4;
118 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
119 		return;
120 	}
121 
122 	mapFoodOnLevel(player);
123 }
124 
spell_summonFamiliar(int player)125 void spell_summonFamiliar(int player)
126 {
127 	// server only function
128 	if ( players[player] == nullptr || players[player]->entity == nullptr )
129 	{
130 		return;
131 	}
132 
133 	Uint32 numCreatures = 1;
134 	Monster creature = RAT;
135 
136 	// spawn something really nasty
137 	/*numCreatures = 1;
138 	switch ( rand() % 4 )
139 	{
140 		case 0:
141 			creature = MINOTAUR;
142 			break;
143 		case 1:
144 			creature = DEMON;
145 			break;
146 		case 2:
147 			creature = CREATURE_IMP;
148 			break;
149 		case 3:
150 			creature = TROLL;
151 			break;
152 	}*/
153 	// spawn moderately nasty things
154 	//switch ( rand() % 6 )
155 	//{
156 	//	case 0:
157 	//		creature = GNOME;
158 	//		numCreatures = rand() % 3 + 1;
159 	//		break;
160 	//	case 1:
161 	//		creature = SPIDER;
162 	//		numCreatures = rand() % 2 + 1;
163 	//		break;
164 	//	case 2:
165 	//		creature = SUCCUBUS;
166 	//		numCreatures = rand() % 2 + 1;
167 	//		break;
168 	//	case 3:
169 	//		creature = SCORPION;
170 	//		numCreatures = rand() % 2 + 1;
171 	//		break;
172 	//	case 4:
173 	//		creature = GHOUL;
174 	//		numCreatures = rand() % 2 + 1;
175 	//		break;
176 	//	case 5:
177 	//		creature = GOBLIN;
178 	//		numCreatures = rand() % 2 + 1;
179 	//		break;
180 	//}
181 
182 	// spawn weak monster ally
183 	switch ( rand() % 3 )
184 	{
185 		case 0:
186 			creature = RAT;
187 			numCreatures = rand() % 3 + 1;
188 			break;
189 		case 1:
190 			creature = GHOUL;
191 			numCreatures = 1;
192 			break;
193 		case 2:
194 			creature = SLIME;
195 			numCreatures = rand() % 2 + 1;
196 			break;
197 	}
198 
199 	//// spawn humans
200 	//creature = HUMAN;
201 	//numCreatures = rand() % 3 + 1;
202 
203 	////Spawn many/neat allies
204 	//switch ( rand() % 2 )
205 	//{
206 	//	case 0:
207 	//		// summon zap brigadiers
208 	//		numCreatures = rand() % 2 + 4;
209 	//		creature = HUMAN;
210 	//		break;
211 	//	case 1:
212 	//		// summon demons
213 	//		numCreatures = rand() % 2 + 4;
214 	//		creature = DEMON;
215 	//		break;
216 	//
217 	//}
218 
219 	int i;
220 	bool spawnedMonster = false;
221 	for ( i = 0; i < numCreatures; ++i )
222 	{
223 		Entity* monster = summonMonster(creature, floor(players[player]->entity->x / 16) * 16 + 8, floor(players[player]->entity->y / 16) * 16 + 8);
224 
225 		if ( monster )
226 		{
227 			spawnedMonster = true;
228 
229 			Stat* monsterStats = monster->getStats();
230 			if ( monsterStats )
231 			{
232 				monsterStats->leader_uid = players[player]->entity->getUID();
233 				monster->flags[USERFLAG2] = true;
234 				monster->monsterAllyIndex = player;
235 				if ( multiplayer == SERVER )
236 				{
237 					serverUpdateEntitySkill(monster, 42); // update monsterAllyIndex for clients.
238 				}
239 
240 				// update followers for this player
241 				node_t* newNode = list_AddNodeLast(&stats[player]->FOLLOWERS);
242 				newNode->deconstructor = &defaultDeconstructor;
243 				Uint32* myuid = (Uint32*)malloc(sizeof(Uint32));
244 				newNode->element = myuid;
245 				*myuid = monster->getUID();
246 
247 				// update client followers
248 				if ( player > 0 && multiplayer == SERVER )
249 				{
250 					strcpy((char*)net_packet->data, "LEAD");
251 					SDLNet_Write32((Uint32)monster->getUID(), &net_packet->data[4]);
252 					strcpy((char*)(&net_packet->data[8]), monsterStats->name);
253 					net_packet->data[8 + strlen(monsterStats->name)] = 0;
254 					net_packet->address.host = net_clients[player - 1].host;
255 					net_packet->address.port = net_clients[player - 1].port;
256 					net_packet->len = 8 + strlen(monsterStats->name) + 1;
257 					sendPacketSafe(net_sock, -1, net_packet, player - 1);
258 
259 					serverUpdateAllyStat(player, monster->getUID(), monsterStats->LVL, monsterStats->HP, monsterStats->MAXHP, monsterStats->type);
260 				}
261 
262 				if ( !FollowerMenu.recentEntity && player == clientnum )
263 				{
264 					FollowerMenu.recentEntity = monster;
265 				}
266 			}
267 		}
268 	}
269 	if ( spawnedMonster )
270 	{
271 		if ( numCreatures <= 1 )
272 		{
273 			if ( creature < KOBOLD ) //Original monster count
274 			{
275 				messagePlayer(player, language[879], language[90 + creature]);
276 
277 			}
278 			else if ( creature >= KOBOLD ) //New monsters
279 			{
280 				messagePlayer(player, language[879], language[2000 + (creature - KOBOLD)]);
281 			}
282 			/*if ( item->beatitude >= 2 )
283 			{
284 				messagePlayer(player, language[880]);
285 			}*/
286 		}
287 		else
288 		{
289 			if ( creature < KOBOLD ) //Original monster count
290 			{
291 				messagePlayer(player, language[881], language[111 + creature]);
292 
293 			}
294 			else if ( creature >= KOBOLD ) //New monsters
295 			{
296 				messagePlayer(player, language[881], language[2050 + (creature - KOBOLD)]);
297 			}
298 			//if ( item->beatitude >= 2 )
299 			//{
300 			//	messagePlayer(player, language[882]);
301 			//}
302 		}
303 	}
304 }
305 
spellEffectDominate(Entity & my,spellElement_t & element,Entity & caster,Entity * parent)306 bool spellEffectDominate(Entity& my, spellElement_t& element, Entity& caster, Entity* parent)
307 {
308 	if ( !hit.entity )
309 	{
310 		return false;
311 	}
312 
313 	if ( hit.entity->behavior != &actMonster )
314 	{
315 		return false;
316 	}
317 
318 	Stat* hitstats = hit.entity->getStats();
319 	if ( !hitstats )
320 	{
321 		return false;
322 	}
323 
324 	//Abort if invalid creature (boss, shopkeep, etc).
325 	if ( hitstats->type ==  MINOTAUR
326 		|| hitstats->type == LICH
327 		|| hitstats->type == DEVIL
328 		|| hitstats->type == SHOPKEEPER
329 		|| hitstats->type == LICH_ICE
330 		|| hitstats->type == LICH_FIRE
331 		|| hitstats->type == SHADOW
332 		|| (hitstats->type == VAMPIRE && !strncmp(hitstats->name, "Bram Kindly", 11))
333 		|| (hitstats->type == COCKATRICE && !strncmp(map.name, "Cockatrice Lair", 15))
334 		)
335 	{
336 		Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
337 		if ( parent )
338 		{
339 			messagePlayerColor(parent->skill[2], color, language[2429]);
340 		}
341 		return false;
342 	}
343 
344 	playSoundEntity(hit.entity, 174, 64); //TODO: Dominate spell sound effect.
345 
346 	//Make the monster a follower.
347 	bool dominated = forceFollower(caster, *hit.entity);
348 
349 	if ( parent && dominated )
350 	{
351 		Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
352 		if ( parent->behavior == &actPlayer )
353 		{
354 			messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2428], language[2427], MSG_COMBAT);
355 		}
356 
357 		hit.entity->monsterAllyIndex = parent->skill[2];
358 		if ( multiplayer == SERVER )
359 		{
360 			serverUpdateEntitySkill(hit.entity, 42); // update monsterAllyIndex for clients.
361 		}
362 		// change the color of the hit entity.
363 
364 		hit.entity->flags[USERFLAG2] = true;
365 		serverUpdateEntityFlag(hit.entity, USERFLAG2);
366 		if ( monsterChangesColorWhenAlly(hitstats) )
367 		{
368 			int bodypart = 0;
369 			for ( node_t* node = (hit.entity)->children.first; node != nullptr; node = node->next )
370 			{
371 				if ( bodypart >= LIMB_HUMANOID_TORSO )
372 				{
373 					Entity* tmp = (Entity*)node->element;
374 					if ( tmp )
375 					{
376 						tmp->flags[USERFLAG2] = true;
377 						//serverUpdateEntityFlag(tmp, USERFLAG2);
378 					}
379 				}
380 				++bodypart;
381 			}
382 		}
383 
384 		caster.drainMP(hitstats->HP); //Drain additional MP equal to health of monster.
385 		Stat* casterStats = caster.getStats();
386 		if ( casterStats && casterStats->HP <= 0 )
387 		{
388 			// uh oh..
389 			if ( casterStats->amulet && casterStats->amulet->type == AMULET_LIFESAVING && casterStats->amulet->beatitude >= 0 )
390 			{
391 				// we're good!
392 				steamAchievementEntity(&caster, "BARONY_ACH_LIFE_FOR_A_LIFE");
393 			}
394 		}
395 	}
396 
397 	spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
398 	return true;
399 }
400 
spellEffectAcid(Entity & my,spellElement_t & element,Entity * parent,int resistance)401 void spellEffectAcid(Entity& my, spellElement_t& element, Entity* parent, int resistance)
402 {
403 	playSoundEntity(&my, 173, 128);
404 
405 	if ( hit.entity )
406 	{
407 		int damage = element.damage;
408 		damage += damage * ((my.actmagicSpellbookBonus / 100.f) + getBonusFromCasterOfSpellElement(parent, &element));
409 		//damage += ((element->mana - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->damage;
410 
411 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
412 		{
413 			Entity* parent = uidToEntity(my.parent);
414 			if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
415 			{
416 				// test for friendly fire
417 				if ( parent && parent->checkFriend(hit.entity) )
418 				{
419 					return;
420 				}
421 			}
422 			//playSoundEntity(&my, 173, 64);
423 			playSoundEntity(hit.entity, 249, 64);
424 			//playSoundEntity(hit.entity, 28, 64);
425 
426 			Stat* hitstats = hit.entity->getStats();
427 			if ( !hitstats )
428 			{
429 				return;
430 			}
431 			bool hasamulet = false;
432 			if ( (hitstats->amulet && hitstats->amulet->type == AMULET_POISONRESISTANCE) || hitstats->type == INSECTOID )
433 			{
434 				resistance += 2;
435 				hasamulet = true;
436 			}
437 			int oldHP = hitstats->HP;
438 			damage /= (1 + (int)resistance);
439 			damage *= hit.entity->getDamageTableMultiplier(*hitstats, DAMAGE_TABLE_MAGIC);
440 			hit.entity->modHP(-damage);
441 
442 			// write the obituary
443 			if ( parent )
444 			{
445 				parent->killedByMonsterObituary(hit.entity);
446 			}
447 
448 			if ( !hasamulet )
449 			{
450 				hitstats->EFFECTS[EFF_POISONED] = true;
451 				hitstats->EFFECTS_TIMERS[EFF_POISONED] = 300; // 6 seconds.
452 				hitstats->EFFECTS_TIMERS[EFF_POISONED] /= (1 + (int)resistance);
453 			}
454 			/*hitstats->EFFECTS[EFF_SLOW] = true;
455 			hitstats->EFFECTS_TIMERS[EFF_SLOW] = (element->duration * (((element->mana) / static_cast<double>(element->base_mana)) * element->overload_multiplier));
456 			hitstats->EFFECTS_TIMERS[EFF_SLOW] /= (1 + (int)resistance);*/
457 			if ( hit.entity->behavior == &actPlayer )
458 			{
459 				serverUpdateEffects(hit.entity->skill[2]);
460 			}
461 			// hit messages
462 			if ( parent )
463 			{
464 				Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
465 				if ( parent->behavior == &actPlayer )
466 				{
467 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2431], language[2430], MSG_COMBAT);
468 				}
469 			}
470 
471 			// update enemy bar for attacker
472 			if ( !strcmp(hitstats->name, "") )
473 			{
474 				if ( hitstats->type < KOBOLD ) //Original monster count
475 				{
476 					updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
477 				}
478 				else if ( hitstats->type >= KOBOLD ) //New monsters
479 				{
480 					updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
481 				}
482 			}
483 			else
484 			{
485 				updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
486 			}
487 
488 			if ( oldHP > 0 && hitstats->HP <= 0 && parent )
489 			{
490 				parent->awardXP(hit.entity, true, true);
491 			}
492 
493 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
494 
495 			int player = -1;
496 			if ( hit.entity->behavior == &actPlayer )
497 			{
498 				player = hit.entity->skill[2];
499 			}
500 			if ( player >= 0 )
501 			{
502 				messagePlayerColor(player, color, language[2432]);
503 			}
504 
505 			if ( hitstats->HP > 0 )
506 			{
507 				// damage armor
508 				Item* armor = nullptr;
509 				int armornum = -1;
510 				if ( hitstats->defending && (rand() % (8 + resistance) == 0) ) // 1 in 8 to corrode shield
511 				{
512 					armornum = hitstats->pickRandomEquippedItem(&armor, true, false, true, true);
513 				}
514 				else if ( !hitstats->defending && (rand() % (4 + resistance) == 0) ) // 1 in 4 to corrode armor
515 				{
516 					armornum = hitstats->pickRandomEquippedItem(&armor, true, false, false, false);
517 				}
518 				//messagePlayer(0, "armornum: %d", armornum);
519 				if ( armornum != -1 && armor != nullptr )
520 				{
521 					hit.entity->degradeArmor(*hitstats, *armor, armornum);
522 					//messagePlayerColor(player, color, "Armor piece: %s", armor->getName());
523 				}
524 			}
525 		}
526 		else if ( hit.entity->behavior == &actDoor )
527 		{
528 			hit.entity->doorHandleDamageMagic(damage, my, parent);
529 		}
530 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
531 	}
532 	else
533 	{
534 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
535 	}
536 }
537 
spellEffectPoison(Entity & my,spellElement_t & element,Entity * parent,int resistance)538 void spellEffectPoison(Entity& my, spellElement_t& element, Entity* parent, int resistance)
539 {
540 	playSoundEntity(&my, 173, 128);
541 	if ( hit.entity )
542 	{
543 		int damage = element.damage;
544 		damage += damage * ((my.actmagicSpellbookBonus / 100.f) + getBonusFromCasterOfSpellElement(parent, &element));
545 		//damage += ((element->mana - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->damage;
546 
547 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
548 		{
549 			Entity* parent = uidToEntity(my.parent);
550 			if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
551 			{
552 				// test for friendly fire
553 				if ( parent && parent->checkFriend(hit.entity) )
554 				{
555 					return;
556 				}
557 			}
558 			playSoundEntity(hit.entity, 249, 64);
559 
560 			Stat* hitstats = hit.entity->getStats();
561 			if ( !hitstats )
562 			{
563 				return;
564 			}
565 			bool hasamulet = false;
566 			if ( (hitstats->amulet && hitstats->amulet->type == AMULET_POISONRESISTANCE) || hitstats->type == INSECTOID )
567 			{
568 				resistance += 2;
569 				hasamulet = true;
570 			}
571 			damage /= (1 + (int)resistance);
572 			damage *= hit.entity->getDamageTableMultiplier(*hitstats, DAMAGE_TABLE_MAGIC);
573 			hit.entity->modHP(-damage);
574 
575 			// write the obituary
576 			if ( parent )
577 			{
578 				parent->killedByMonsterObituary(hit.entity);
579 			}
580 
581 			if ( !hasamulet )
582 			{
583 				if ( my.actmagicCastByMagicstaff == 1 )
584 				{
585 					hit.entity->setEffect(EFF_POISONED, true, 320, true); // 6 seconds.
586 				}
587 				else
588 				{
589 					hit.entity->setEffect(EFF_POISONED, true, std::max(200, 350 - hit.entity->getCON() * 5), true); // 4-7 seconds.
590 				}
591 				hitstats->poisonKiller = my.parent;
592 			}
593 
594 			if ( hit.entity->behavior == &actPlayer )
595 			{
596 				serverUpdateEffects(hit.entity->skill[2]);
597 			}
598 			// hit messages
599 			if ( parent )
600 			{
601 				Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
602 				if ( parent->behavior == &actPlayer )
603 				{
604 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3427], language[3426], MSG_COMBAT);
605 				}
606 			}
607 
608 			// update enemy bar for attacker
609 			if ( !strcmp(hitstats->name, "") )
610 			{
611 				if ( hitstats->type < KOBOLD ) //Original monster count
612 				{
613 					updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
614 				}
615 				else if ( hitstats->type >= KOBOLD ) //New monsters
616 				{
617 					updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
618 				}
619 			}
620 			else
621 			{
622 				updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
623 			}
624 
625 			if ( hitstats->HP <= 0 && parent )
626 			{
627 				parent->awardXP(hit.entity, true, true);
628 			}
629 
630 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
631 
632 			int player = -1;
633 			if ( hit.entity->behavior == &actPlayer )
634 			{
635 				player = hit.entity->skill[2];
636 			}
637 			if ( player >= 0 )
638 			{
639 				messagePlayerColor(player, color, language[3428]);
640 			}
641 		}
642 		else if ( hit.entity->behavior == &actDoor )
643 		{
644 			hit.entity->doorHandleDamageMagic(damage, my, parent);
645 		}
646 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
647 	}
648 	else
649 	{
650 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
651 	}
652 }
653 
spellEffectFear(Entity * my,spellElement_t & element,Entity * forceParent,Entity * target,int resistance)654 bool spellEffectFear(Entity* my, spellElement_t& element, Entity* forceParent, Entity* target, int resistance)
655 {
656 	if ( !target )
657 	{
658 		//spawnMagicEffectParticles(my.x, my.y, my.z, 863);
659 		return false;
660 	}
661 
662 	if ( target->behavior == &actMonster || target->behavior == &actPlayer )
663 	{
664 		Entity* parent = forceParent;
665 		if ( my && !parent )
666 		{
667 			parent = uidToEntity(my->parent);
668 		}
669 		if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
670 		{
671 			// test for friendly fire
672 			if ( parent && parent->checkFriend(target) )
673 			{
674 				return false;
675 			}
676 		}
677 
678 		Stat* hitstats = target->getStats();
679 		if ( !hitstats )
680 		{
681 			return false;
682 		}
683 
684 		int duration = 400; // 8 seconds
685 		duration = std::max(150, duration - TICKS_PER_SECOND * (hitstats->CON / 5)); // 3-8 seconds, depending on CON.
686 		duration /= (1 + resistance);
687 		if ( target->setEffect(EFF_FEAR, true, duration, true) )
688 		{
689 			playSoundEntity(target, 168, 128); // Healing.ogg
690 			Uint32 color = 0;
691 			if ( parent )
692 			{
693 				// update enemy bar for attacker
694 				if ( !strcmp(hitstats->name, "") )
695 				{
696 					if ( hitstats->type < KOBOLD ) //Original monster count
697 					{
698 						updateEnemyBar(parent, target, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
699 					}
700 					else if ( hitstats->type >= KOBOLD ) //New monsters
701 					{
702 						updateEnemyBar(parent, target, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
703 					}
704 				}
705 				else
706 				{
707 					updateEnemyBar(parent, target, hitstats->name, hitstats->HP, hitstats->MAXHP);
708 				}
709 				target->monsterAcquireAttackTarget(*parent, MONSTER_STATE_PATH);
710 				target->monsterFearfulOfUid = parent->getUID();
711 
712 				if ( parent->behavior == &actPlayer && parent->getStats() && parent->getStats()->type == TROLL )
713 				{
714 					serverUpdatePlayerGameplayStats(parent->skill[2], STATISTICS_FORUM_TROLL, AchievementObserver::FORUM_TROLL_FEAR);
715 				}
716 			}
717 		}
718 		else
719 		{
720 			// no effect.
721 			if ( parent )
722 			{
723 				Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
724 				if ( parent->behavior == &actPlayer )
725 				{
726 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2905], language[2906], MSG_COMBAT);
727 				}
728 			}
729 			return false;
730 		}
731 
732 		// hit messages
733 		if ( parent )
734 		{
735 			Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
736 			if ( parent->behavior == &actPlayer )
737 			{
738 				messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3434], language[3435], MSG_COMBAT);
739 			}
740 		}
741 
742 		Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
743 
744 		int player = -1;
745 		if ( target->behavior == &actPlayer )
746 		{
747 			player = target->skill[2];
748 		}
749 		if ( player >= 0 )
750 		{
751 			messagePlayerColor(player, color, language[3436]);
752 		}
753 	}
754 	spawnMagicEffectParticles(target->x, target->y, target->z, 863);
755 	return true;
756 }
757 
spellEffectSprayWeb(Entity & my,spellElement_t & element,Entity * parent,int resistance)758 void spellEffectSprayWeb(Entity& my, spellElement_t& element, Entity* parent, int resistance)
759 {
760 	if ( hit.entity )
761 	{
762 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
763 		{
764 			Entity* parent = uidToEntity(my.parent);
765 			if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
766 			{
767 				// test for friendly fire
768 				if ( parent && parent->checkFriend(hit.entity) )
769 				{
770 					return;
771 				}
772 			}
773 
774 			Stat* hitstats = hit.entity->getStats();
775 			if ( !hitstats )
776 			{
777 				return;
778 			}
779 
780 			bool spawnParticles = true;
781 			if ( hitstats->EFFECTS[EFF_WEBBED] )
782 			{
783 				spawnParticles = false;
784 			}
785 			int previousDuration = hitstats->EFFECTS_TIMERS[EFF_WEBBED];
786 			int duration = 400;
787 			duration /= (1 + resistance);
788 			if ( hit.entity->setEffect(EFF_WEBBED, true, 400, true) ) // 8 seconds.
789 			{
790 				if ( duration - previousDuration > 10 )
791 				{
792 					playSoundEntity(hit.entity, 396 + rand() % 3, 64); // play sound only if not recently webbed. (triple shot makes many noise)
793 				}
794 				hit.entity->creatureWebbedSlowCount = std::min(3, hit.entity->creatureWebbedSlowCount + 1);
795 				if ( hit.entity->behavior == &actPlayer )
796 				{
797 					serverUpdateEntitySkill(hit.entity, 52); // update player.
798 				}
799 				if ( spawnParticles )
800 				{
801 					createParticleAestheticOrbit(hit.entity, 863, 400, PARTICLE_EFFECT_SPELL_WEB_ORBIT);
802 					serverSpawnMiscParticles(hit.entity, PARTICLE_EFFECT_SPELL_WEB_ORBIT, 863);
803 				}
804 			}
805 			else
806 			{
807 				// no effect.
808 				if ( parent )
809 				{
810 					Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
811 					if ( parent->behavior == &actPlayer )
812 					{
813 						messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2905], language[2906], MSG_COMBAT);
814 					}
815 				}
816 				return;
817 			}
818 
819 			// hit messages
820 			if ( parent )
821 			{
822 				Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
823 				if ( parent->behavior == &actPlayer )
824 				{
825 					if ( duration - previousDuration > 10 ) // message if not recently webbed
826 					{
827 						messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3430], language[3429], MSG_COMBAT);
828 					}
829 				}
830 			}
831 
832 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
833 
834 			int player = -1;
835 			if ( hit.entity->behavior == &actPlayer )
836 			{
837 				player = hit.entity->skill[2];
838 			}
839 			if ( player >= 0 )
840 			{
841 				if ( duration - previousDuration > 10 ) // message if not recently webbed
842 				{
843 					messagePlayerColor(player, color, language[3431]);
844 				}
845 			}
846 		}
847 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, 863);
848 	}
849 	else
850 	{
851 		spawnMagicEffectParticles(my.x, my.y, my.z, 863);
852 	}
853 }
854 
spellEffectStealWeapon(Entity & my,spellElement_t & element,Entity * parent,int resistance)855 void spellEffectStealWeapon(Entity& my, spellElement_t& element, Entity* parent, int resistance)
856 {
857 	if ( hit.entity )
858 	{
859 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
860 		{
861 			Entity* parent = uidToEntity(my.parent);
862 			if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
863 			{
864 				// test for friendly fire
865 				if ( parent && parent->checkFriend(hit.entity) )
866 				{
867 					return;
868 				}
869 			}
870 
871 			Stat* hitstats = hit.entity->getStats();
872 			if ( !hitstats )
873 			{
874 				return;
875 			}
876 
877 			if ( hitstats->type == LICH || hitstats->type == LICH_FIRE || hitstats->type == LICH_ICE || hitstats->type == DEVIL )
878 			{
879 				return;
880 			}
881 
882 			if ( hit.entity->behavior == &actMonster
883 				&& (hit.entity->monsterAllySummonRank != 0
884 					|| (hitstats->type == INCUBUS && !strncmp(hitstats->name, "inner demon", strlen("inner demon"))))
885 				)
886 			{
887 				return;
888 			}
889 
890 			// update enemy bar for attacker
891 			if ( !strcmp(hitstats->name, "") )
892 			{
893 				if ( hitstats->type < KOBOLD ) //Original monster count
894 				{
895 					updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
896 				}
897 				else if ( hitstats->type >= KOBOLD ) //New monsters
898 				{
899 					updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
900 				}
901 			}
902 			else
903 			{
904 				updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
905 			}
906 
907 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
908 
909 			int player = -1;
910 			if ( hit.entity->behavior == &actPlayer )
911 			{
912 				player = hit.entity->skill[2];
913 			}
914 
915 			if ( hitstats->weapon )
916 			{
917 				Entity* spellEntity = createParticleSapCenter(parent, hit.entity, SPELL_STEAL_WEAPON, my.sprite, my.sprite);
918 				if ( spellEntity )
919 				{
920 					playSoundEntity(&my, 174, 128); // succeeded spell sound
921 					spellEntity->skill[7] = 1; // found weapon
922 
923 					// store weapon data
924 					spellEntity->skill[10] = hitstats->weapon->type;
925 					if ( itemCategory(hitstats->weapon) == SPELLBOOK )
926 					{
927 						spellEntity->skill[11] = DECREPIT;
928 					}
929 					else
930 					{
931 						spellEntity->skill[11] = hitstats->weapon->status;
932 					}
933 					spellEntity->skill[12] = hitstats->weapon->beatitude;
934 					spellEntity->skill[13] = hitstats->weapon->count;
935 					spellEntity->skill[14] = hitstats->weapon->appearance;
936 					spellEntity->skill[15] = hitstats->weapon->identified;
937 					spellEntity->itemOriginalOwner = hit.entity->getUID();
938 					// hit messages
939 					if ( player >= 0 )
940 					{
941 						color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
942 						messagePlayerColor(player, color, language[2435], hitstats->weapon->getName());
943 					}
944 
945 					if ( parent )
946 					{
947 						color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
948 						if ( parent->behavior == &actPlayer )
949 						{
950 							messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2434], language[2433], MSG_STEAL_WEAPON);
951 						}
952 					}
953 
954 					if ( hit.entity->behavior == &actMonster && itemCategory(hitstats->weapon) != SPELLBOOK )
955 					{
956 						free(hitstats->weapon);
957 						hitstats->weapon = nullptr;
958 					}
959 					else if ( hit.entity->behavior == &actPlayer )
960 					{
961 						// player.
962 						Item* weapon = hitstats->weapon;
963 						Item** slot = itemSlot(hitstats, weapon);
964 						if ( slot )
965 						{
966 							*slot = nullptr;
967 						}
968 						if ( weapon->node )
969 						{
970 							list_RemoveNode(weapon->node);
971 						}
972 						else
973 						{
974 							free(weapon);
975 						}
976 						if ( player > 0 && multiplayer == SERVER )
977 						{
978 							strcpy((char*)net_packet->data, "STLA");
979 							net_packet->data[4] = 5; // steal weapon index in STLA netcode.
980 							net_packet->address.host = net_clients[player - 1].host;
981 							net_packet->address.port = net_clients[player - 1].port;
982 							net_packet->len = 5;
983 							sendPacketSafe(net_sock, -1, net_packet, player - 1);
984 						}
985 					}
986 				}
987 			}
988 			else
989 			{
990 				playSoundEntity(&my, 163, 128); // failed spell sound
991 				// hit messages
992 				if ( player >= 0 )
993 				{
994 					color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
995 					messagePlayerColor(player, color, language[2438]);
996 				}
997 
998 				if ( parent )
999 				{
1000 					color = SDL_MapRGB(mainsurface->format, 255, 255, 255);
1001 					if ( parent->behavior == &actPlayer )
1002 					{
1003 						messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2437], language[2436], MSG_COMBAT);
1004 					}
1005 				}
1006 			}
1007 		}
1008 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
1009 	}
1010 	else
1011 	{
1012 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
1013 	}
1014 	return;
1015 }
1016 
spellEffectDrainSoul(Entity & my,spellElement_t & element,Entity * parent,int resistance)1017 void spellEffectDrainSoul(Entity& my, spellElement_t& element, Entity* parent, int resistance)
1018 {
1019 	if ( hit.entity )
1020 	{
1021 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
1022 		{
1023 			Entity* parent = uidToEntity(my.parent);
1024 			if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
1025 			{
1026 				// test for friendly fire
1027 				if ( parent && parent->checkFriend(hit.entity) )
1028 				{
1029 					return;
1030 				}
1031 			}
1032 
1033 			Stat* hitstats = hit.entity->getStats();
1034 			if ( !hitstats )
1035 			{
1036 				return;
1037 			}
1038 
1039 			int damage = element.damage;
1040 			damage += damage * ((my.actmagicSpellbookBonus / 100.f) + getBonusFromCasterOfSpellElement(parent, &element));
1041 			//damage += ((element->mana - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->damage;
1042 			damage /= (1 + (int)resistance);
1043 			damage *= hit.entity->getDamageTableMultiplier(*hitstats, DAMAGE_TABLE_MAGIC);
1044 
1045 			if ( parent )
1046 			{
1047 				Stat* casterStats = parent->getStats();
1048 				if ( casterStats && casterStats->type == LICH_ICE )
1049 				{
1050 					damage *= 2;
1051 				}
1052 			}
1053 
1054 			int damageHP = hitstats->HP;
1055 			int damageMP = hitstats->MP;
1056 			hit.entity->modHP(-damage);
1057 			if ( damage > hitstats->MP )
1058 			{
1059 				damage = hitstats->MP;
1060 			}
1061 			if ( parent && parent->behavior == &actPlayer )
1062 			{
1063 				damage /= 4; // reduced mana steal
1064 			}
1065 			hit.entity->drainMP(damage);
1066 
1067 			damageHP -= hitstats->HP;
1068 			damageMP -= hitstats->MP;
1069 
1070 			// write the obituary
1071 			if ( parent )
1072 			{
1073 				parent->killedByMonsterObituary(hit.entity);
1074 			}
1075 
1076 			// update enemy bar for attacker
1077 			if ( !strcmp(hitstats->name, "") )
1078 			{
1079 				if ( hitstats->type < KOBOLD ) //Original monster count
1080 				{
1081 					updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
1082 				}
1083 				else if ( hitstats->type >= KOBOLD ) //New monsters
1084 				{
1085 					updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
1086 				}
1087 			}
1088 			else
1089 			{
1090 				updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
1091 			}
1092 
1093 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1094 
1095 			int player = -1;
1096 			if ( hit.entity->behavior == &actPlayer )
1097 			{
1098 				player = hit.entity->skill[2];
1099 			}
1100 
1101 			if ( hitstats->HP <= 0 && parent )
1102 			{
1103 				parent->awardXP(hit.entity, true, true);
1104 			}
1105 
1106 			if ( damageHP > 0 && parent )
1107 			{
1108 				Entity* spellEntity = createParticleSapCenter(parent, hit.entity, SPELL_DRAIN_SOUL, my.sprite, my.sprite);
1109 				if ( spellEntity )
1110 				{
1111 					playSoundEntity(&my, 167, 128); // succeeded spell sound
1112 					playSoundEntity(&my, 28, 128); // damage
1113 					spellEntity->skill[7] = damageHP; // damage taken to HP
1114 					spellEntity->skill[8] = damageMP; // damage taken tp MP
1115 
1116 					// hit messages
1117 					if ( player >= 0 )
1118 					{
1119 						color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1120 						messagePlayerColor(player, color, language[2441]);
1121 					}
1122 
1123 					if ( parent )
1124 					{
1125 						color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1126 						if ( parent->behavior == &actPlayer )
1127 						{
1128 							messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2440], language[2439], MSG_COMBAT);
1129 						}
1130 					}
1131 				}
1132 			}
1133 			else
1134 			{
1135 				playSoundEntity(&my, 163, 128); // failed spell sound
1136 				// hit messages
1137 				if ( player >= 0 )
1138 				{
1139 					color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1140 					messagePlayerColor(player, color, language[2444]);
1141 				}
1142 
1143 				if ( parent )
1144 				{
1145 					color = SDL_MapRGB(mainsurface->format, 255, 255, 255);
1146 					if ( parent->behavior == &actPlayer )
1147 					{
1148 						messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2443], language[2442], MSG_COMBAT);
1149 					}
1150 				}
1151 			}
1152 		}
1153 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
1154 	}
1155 	else
1156 	{
1157 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
1158 	}
1159 	return;
1160 }
1161 
spellEffectVampiricAura(Entity * caster,spell_t * spell,int extramagic_to_use)1162 spell_t* spellEffectVampiricAura(Entity* caster, spell_t* spell, int extramagic_to_use)
1163 {
1164 	//Also refactor the duration determining code.
1165 	node_t* node = spell->elements.first;
1166 	if ( !node )
1167 	{
1168 		return nullptr;
1169 	}
1170 	spellElement_t* element = static_cast<spellElement_t*>(node->element);
1171 	if ( !element )
1172 	{
1173 		return nullptr;
1174 	}
1175 	Stat* myStats = caster->getStats();
1176 	if ( !myStats )
1177 	{
1178 		return nullptr;
1179 	}
1180 
1181 	bool newbie = caster->isSpellcasterBeginner();
1182 
1183 	int duration = element->duration; // duration in ticks.
1184 	duration += (((element->mana + extramagic_to_use) - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->duration;
1185 	node_t* spellnode = list_AddNodeLast(&myStats->magic_effects);
1186 	spellnode->element = copySpell(spell); //We need to save the spell since this is a channeled spell.
1187 	spell_t* channeled_spell = (spell_t*)(spellnode->element);
1188 	channeled_spell->magic_effects_node = spellnode;
1189 	spellnode->size = sizeof(spell_t);
1190 	((spell_t*)spellnode->element)->caster = caster->getUID();
1191 	spellnode->deconstructor = &spellDeconstructor;
1192 	//if ( newbie )
1193 	//{
1194 	//	//This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell.
1195 	//	int chance = rand() % 10;
1196 	//	// spellcasting power is 0 to 100, based on spellcasting and intelligence.
1197 	//	int spellcastingPower = std::min(std::max(0, myStats->PROFICIENCIES[PRO_SPELLCASTING] + statGetINT(myStats, caster)), 100);
1198 	//	if ( chance >= spellcastingPower / 10 )
1199 	//	{
1200 	//		duration -= rand() % (1000 / (spellcastingPower + 1)); // reduce the duration by 0-20 seconds
1201 	//	}
1202 	//	if ( duration < 50 )
1203 	//	{
1204 	//		duration = 50;    //Range checking.
1205 	//	}
1206 	//}
1207 	duration /= getCostOfSpell((spell_t*)spellnode->element);
1208 	channeled_spell->channel_duration = duration; //Tell the spell how long it's supposed to last so that it knows what to reset its timer to.
1209 	caster->setEffect(EFF_VAMPIRICAURA, true, duration, true);
1210 	for ( int i = 0; i < MAXPLAYERS; ++i )
1211 	{
1212 		if ( players[i] && caster && (caster == players[i]->entity) )
1213 		{
1214 			serverUpdateEffects(i);
1215 			Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1216 			messagePlayerColor(i, color, language[2477]);
1217 			playSoundPlayer(i, 403, 32);
1218 		}
1219 	}
1220 
1221 	playSoundEntity(caster, 167, 128);
1222 	createParticleDropRising(caster, 600, 0.7);
1223 	serverSpawnMiscParticles(caster, PARTICLE_EFFECT_VAMPIRIC_AURA, 600);
1224 	return channeled_spell;
1225 }
1226 
spellEffectCharmMonster(Entity & my,spellElement_t & element,Entity * parent,int resistance,bool magicstaff)1227 void spellEffectCharmMonster(Entity& my, spellElement_t& element, Entity* parent, int resistance, bool magicstaff)
1228 {
1229 	if ( hit.entity )
1230 	{
1231 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
1232 		{
1233 			if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
1234 			{
1235 				// test for friendly fire
1236 				if ( parent && parent->checkFriend(hit.entity) )
1237 				{
1238 					return;
1239 				}
1240 			}
1241 
1242 			Stat* hitstats = hit.entity->getStats();
1243 			if ( !hitstats )
1244 			{
1245 				return;
1246 			}
1247 
1248 			Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1249 
1250 			int player = -1;
1251 			if ( hit.entity->behavior == &actPlayer )
1252 			{
1253 				player = hit.entity->skill[2];
1254 			}
1255 
1256 			int difficulty = 0;
1257 			switch ( hitstats->type )
1258 			{
1259 				case HUMAN:
1260 				case RAT:
1261 				case SLIME:
1262 				case SPIDER:
1263 				case SKELETON:
1264 				case SCORPION:
1265 				case SHOPKEEPER:
1266 					difficulty = 0;
1267 					break;
1268 				case GOBLIN:
1269 				case TROLL:
1270 				case GHOUL:
1271 				case GNOME:
1272 				case SCARAB:
1273 				case AUTOMATON:
1274 				case SUCCUBUS:
1275 					difficulty = 1;
1276 					break;
1277 				case CREATURE_IMP:
1278 				case DEMON:
1279 				case KOBOLD:
1280 				case INCUBUS:
1281 				case INSECTOID:
1282 				case GOATMAN:
1283 					difficulty = 2;
1284 					break;
1285 				case CRYSTALGOLEM:
1286 				case VAMPIRE:
1287 					difficulty = 5;
1288 					break;
1289 				case COCKATRICE:
1290 				case SHADOW:
1291 				case LICH:
1292 				case DEVIL:
1293 				case LICH_ICE:
1294 				case LICH_FIRE:
1295 				case MINOTAUR:
1296 					difficulty = 666;
1297 					break;
1298 			}
1299 
1300 			int chance = 80;
1301 			bool allowStealFollowers = false;
1302 			Stat* casterStats = nullptr;
1303 			int currentCharmedFollowerCount = 0;
1304 
1305 			/************** CHANCE CALCULATION ***********/
1306 			if ( hitstats->EFFECTS[EFF_CONFUSED] || hitstats->EFFECTS[EFF_DRUNK] || player >= 0 )
1307 			{
1308 				difficulty -= 1; // players and confused/drunk monsters have lower resistance.
1309 			}
1310 			if ( strcmp(hitstats->name, "") && !monsterNameIsGeneric(*hitstats) )
1311 			{
1312 				difficulty += 1; // minibosses +1 difficulty.
1313 			}
1314 			chance -= difficulty * 30;
1315 			if ( parent )
1316 			{
1317 				casterStats = parent->getStats();
1318 				if ( casterStats )
1319 				{
1320 					if ( magicstaff )
1321 					{
1322 						chance += ((parent->getCHR() + casterStats->PROFICIENCIES[PRO_LEADERSHIP]) / 20) * 10;
1323 					}
1324 					else
1325 					{
1326 						chance += ((parent->getCHR() + casterStats->PROFICIENCIES[PRO_LEADERSHIP]) / 20) * 5;
1327 						chance += (parent->getINT() * 2);
1328 					}
1329 
1330 					if ( parent->behavior == &actMonster )
1331 					{
1332 						allowStealFollowers = true;
1333 						if ( casterStats->type == INCUBUS || casterStats->type == SUCCUBUS )
1334 						{
1335 							if ( hitstats->type == DEMON || hitstats->type == INCUBUS
1336 								|| hitstats->type == SUCCUBUS || hitstats->type == CREATURE_IMP
1337 								|| hitstats->type == GOATMAN )
1338 							{
1339 								chance = 100; // bonus for demons.
1340 							}
1341 							else if ( difficulty <= 2 )
1342 							{
1343 								chance = 80; // special base chance for monsters.
1344 							}
1345 						}
1346 						else if ( difficulty <= 2 )
1347 						{
1348 							chance = 60; // special base chance for monsters.
1349 						}
1350 					}
1351 					else if ( parent->behavior == &actPlayer )
1352 					{
1353 						// search followers for charmed.
1354 						for ( node_t* node = casterStats->FOLLOWERS.first; node != NULL; node = node->next )
1355 						{
1356 							Uint32* c = (Uint32*)node->element;
1357 							Entity* follower = nullptr;
1358 							if ( c )
1359 							{
1360 								follower = uidToEntity(*c);
1361 							}
1362 							if ( follower )
1363 							{
1364 								if ( Stat* followerStats = follower->getStats() )
1365 								{
1366 									if ( followerStats->monsterIsCharmed == 1 )
1367 									{
1368 										++currentCharmedFollowerCount;
1369 									}
1370 								}
1371 							}
1372 						}
1373 					}
1374 				}
1375 			}
1376 			if ( hit.entity->monsterState == MONSTER_STATE_WAIT )
1377 			{
1378 				chance += 10;
1379 			}
1380 			chance /= (1 + resistance);
1381 			/************** END CHANCE CALCULATION ***********/
1382 
1383 			// special cases:
1384 			if ( (hitstats->type == VAMPIRE && !strncmp(hitstats->name, "Bram Kindly", 11))
1385 				|| (hitstats->type == COCKATRICE && !strncmp(map.name, "Cockatrice Lair", 15))
1386 				)
1387 			{
1388 				chance = 0;
1389 			}
1390 			else if ( hit.entity->behavior == &actMonster
1391 				&& (hit.entity->monsterAllySummonRank != 0
1392 					|| (hitstats->type == INCUBUS && !strncmp(hitstats->name, "inner demon", strlen("inner demon"))))
1393 				)
1394 			{
1395 				chance = 0; // not allowed to control summons
1396 			}
1397 
1398 			if ( parent && hit.entity == parent )
1399 			{
1400 				// caster hit themselves somehow... get pacified.
1401 				int duration = element.duration;
1402 				duration /= (1 + resistance);
1403 				if ( hit.entity->setEffect(EFF_PACIFY, true, duration, true) )
1404 				{
1405 					playSoundEntity(hit.entity, 168, 128); // Healing.ogg
1406 					if ( player >= 0 )
1407 					{
1408 						color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1409 						messagePlayerColor(player, color, language[3144]);
1410 					}
1411 					if ( parent )
1412 					{
1413 						if ( parent->behavior == &actPlayer )
1414 						{
1415 							messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3139], language[3140], MSG_COMBAT);
1416 						}
1417 						// update enemy bar for attacker
1418 						if ( !strcmp(hitstats->name, "") )
1419 						{
1420 							if ( hitstats->type < KOBOLD ) //Original monster count
1421 							{
1422 								updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
1423 							}
1424 							else if ( hitstats->type >= KOBOLD ) //New monsters
1425 							{
1426 								updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
1427 							}
1428 						}
1429 						else
1430 						{
1431 							updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
1432 						}
1433 					}
1434 				}
1435 			}
1436 			else if ( chance <= 0 )
1437 			{
1438 				// no effect.
1439 				playSoundEntity(hit.entity, 163, 64); // FailedSpell1V1.ogg
1440 				if ( parent && parent->behavior == &actPlayer )
1441 				{
1442 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2905], language[2906], MSG_COMBAT);
1443 					steamAchievementClient(parent->skill[2], "BARONY_ACH_OFF_LIMITS");
1444 				}
1445 				if ( player >= 0 )
1446 				{
1447 					color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1448 					messagePlayerColor(player, color, language[3141]);
1449 				}
1450 			}
1451 			else if ( parent && rand() % 100 < chance
1452 				&& (hitstats->leader_uid == 0 || (allowStealFollowers && hitstats->leader_uid != parent->getUID()) )
1453 				&& player < 0
1454 				&& hitstats->type != SHOPKEEPER
1455 				&& currentCharmedFollowerCount == 0
1456 				)
1457 			{
1458 				// fully charmed. (players not affected here.)
1459 				// does not affect shopkeepers
1460 				// succubus/incubus can steal followers from others, checking to see if they don't already follow them.
1461 				Entity* whoToFollow = parent;
1462 				if ( parent->behavior == &actMonster && parent->monsterAllyGetPlayerLeader() )
1463 				{
1464 					whoToFollow = parent->monsterAllyGetPlayerLeader();
1465 				}
1466 
1467 				if ( forceFollower(*whoToFollow, *hit.entity) )
1468 				{
1469 					serverSpawnMiscParticles(hit.entity, PARTICLE_EFFECT_CHARM_MONSTER, 0);
1470 					createParticleCharmMonster(hit.entity);
1471 					playSoundEntity(hit.entity, 174, 64); // WeirdSpell.ogg
1472 					if ( whoToFollow->behavior == &actPlayer )
1473 					{
1474 						whoToFollow->increaseSkill(PRO_LEADERSHIP);
1475 						messagePlayerMonsterEvent(whoToFollow->skill[2], color, *hitstats, language[3137], language[3138], MSG_COMBAT);
1476 						hit.entity->monsterAllyIndex = whoToFollow->skill[2];
1477 						if ( multiplayer == SERVER )
1478 						{
1479 							serverUpdateEntitySkill(hit.entity, 42); // update monsterAllyIndex for clients.
1480 						}
1481 						if ( hit.entity->monsterTarget == whoToFollow->getUID() )
1482 						{
1483 							hit.entity->monsterReleaseAttackTarget();
1484 						}
1485 					}
1486 
1487 					// change the color of the hit entity.
1488 					hit.entity->flags[USERFLAG2] = true;
1489 					serverUpdateEntityFlag(hit.entity, USERFLAG2);
1490 					hitstats->monsterIsCharmed = 1;
1491 					if ( monsterChangesColorWhenAlly(hitstats) )
1492 					{
1493 						int bodypart = 0;
1494 						for ( node_t* node = (hit.entity)->children.first; node != nullptr; node = node->next )
1495 						{
1496 							if ( bodypart >= LIMB_HUMANOID_TORSO )
1497 							{
1498 								Entity* tmp = (Entity*)node->element;
1499 								if ( tmp )
1500 								{
1501 									tmp->flags[USERFLAG2] = true;
1502 									//serverUpdateEntityFlag(tmp, USERFLAG2);
1503 								}
1504 							}
1505 							++bodypart;
1506 						}
1507 					}
1508 					if ( whoToFollow->behavior == &actMonster )
1509 					{
1510 						if ( whoToFollow->monsterTarget == hit.entity->getUID() )
1511 						{
1512 							whoToFollow->monsterReleaseAttackTarget(); // monsters stop attacking their new friend.
1513 						}
1514 
1515 						// handle players losing their allies.
1516 						if ( hit.entity->monsterAllyIndex != -1 )
1517 						{
1518 							hit.entity->monsterAllyIndex = -1;
1519 							if ( multiplayer == SERVER )
1520 							{
1521 								serverUpdateEntitySkill(hit.entity, 42); // update monsterAllyIndex for clients.
1522 							}
1523 						}
1524 					}
1525 				}
1526 			}
1527 			else
1528 			{
1529 				// had a chance, or currently in service of another monster, or a player, or spell no parent, failed to completely charm.
1530 				// loses will to attack.
1531 				int duration = element.duration;
1532 				duration /= (1 + resistance);
1533 				if ( hitstats->type == SHOPKEEPER )
1534 				{
1535 					duration = 100;
1536 				}
1537 				if ( hit.entity->setEffect(EFF_PACIFY, true, duration, true) )
1538 				{
1539 					playSoundEntity(hit.entity, 168, 128); // Healing.ogg
1540 					if ( player >= 0 )
1541 					{
1542 						color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1543 						messagePlayerColor(player, color, language[3144]);
1544 					}
1545 					if ( parent )
1546 					{
1547 						if ( parent->behavior == &actPlayer )
1548 						{
1549 							messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3139], language[3140], MSG_COMBAT);
1550 							if ( currentCharmedFollowerCount > 0 )
1551 							{
1552 								messagePlayer(parent->skill[2], language[3327]);
1553 							}
1554 						}
1555 						// update enemy bar for attacker
1556 						if ( !strcmp(hitstats->name, "") )
1557 						{
1558 							if ( hitstats->type < KOBOLD ) //Original monster count
1559 							{
1560 								updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
1561 							}
1562 							else if ( hitstats->type >= KOBOLD ) //New monsters
1563 							{
1564 								updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
1565 							}
1566 						}
1567 						else
1568 						{
1569 							updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
1570 						}
1571 					}
1572 					if ( hitstats->type == SHOPKEEPER && player >= 0 )
1573 					{
1574 						// reverses shop keeper grudges.
1575 						swornenemies[SHOPKEEPER][HUMAN] = false;
1576 						swornenemies[SHOPKEEPER][AUTOMATON] = false;
1577 						monsterally[SHOPKEEPER][HUMAN] = true;
1578 						monsterally[SHOPKEEPER][AUTOMATON] = true;
1579 						hit.entity->monsterReleaseAttackTarget();
1580 					}
1581 				}
1582 				else
1583 				{
1584 					// resists the charm.
1585 					playSoundEntity(hit.entity, 163, 64); // FailedSpell1V1.ogg
1586 					if ( parent && parent->behavior == &actPlayer )
1587 					{
1588 						messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3142], language[3143], MSG_COMBAT);
1589 					}
1590 					if ( player >= 0 )
1591 					{
1592 						color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1593 						messagePlayerColor(player, color, language[3141]);
1594 					}
1595 				}
1596 			}
1597 		}
1598 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
1599 	}
1600 	else
1601 	{
1602 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
1603 	}
1604 	return;
1605 }
1606 
spellEffectPolymorph(Entity * target,Stat * targetStats,Entity * parent,bool fromMagicSpell,int customDuration)1607 Entity* spellEffectPolymorph(Entity* target, Stat* targetStats, Entity* parent, bool fromMagicSpell, int customDuration)
1608 {
1609 	int effectDuration = 0;
1610 	effectDuration = TICKS_PER_SECOND * 60 * (4 + rand() % 3); // 4-6 minutes
1611 	if ( customDuration > 0 )
1612 	{
1613 		effectDuration = customDuration;
1614 	}
1615 	if ( !target || !targetStats )
1616 	{
1617 		if ( parent && parent->behavior == &actPlayer )
1618 		{
1619 			messagePlayer(parent->skill[2], language[3191]); // had no effect
1620 		}
1621 		return nullptr;
1622 	}
1623 
1624 	if ( targetStats->type == LICH || targetStats->type == SHOPKEEPER || targetStats->type == DEVIL
1625 		|| targetStats->type == MINOTAUR || targetStats->type == LICH_FIRE || targetStats->type == LICH_ICE
1626 		|| (target->behavior == &actMonster && target->monsterAllySummonRank != 0)
1627 		|| (targetStats->type == INCUBUS && !strncmp(targetStats->name, "inner demon", strlen("inner demon")))
1628 		|| targetStats->type == SENTRYBOT || targetStats->type == SPELLBOT || targetStats->type == GYROBOT
1629 		|| targetStats->type == DUMMYBOT
1630 		)
1631 	{
1632 		if ( parent && parent->behavior == &actPlayer )
1633 		{
1634 			messagePlayer(parent->skill[2], language[3191]); // had no effect
1635 		}
1636 		return nullptr;
1637 	}
1638 
1639 	if ( target->behavior == &actMonster )
1640 	{
1641 		Monster monsterSummonType = static_cast<Monster>(rand() % NUMMONSTERS);
1642 		// pick a completely random monster (barring some exceptions).
1643 		// disable shadow spawning if the monster has a leader since it'll aggro the player and bad things
1644 		while ( monsterSummonType == LICH || monsterSummonType == SHOPKEEPER || monsterSummonType == DEVIL
1645 			|| monsterSummonType == MIMIC || monsterSummonType == BUGBEAR || monsterSummonType == OCTOPUS
1646 			|| monsterSummonType == MINOTAUR || monsterSummonType == LICH_FIRE || monsterSummonType == LICH_ICE
1647 			|| monsterSummonType == NOTHING || monsterSummonType == targetStats->type || monsterSummonType == HUMAN
1648 			|| (targetStats->leader_uid != 0 && monsterSummonType == SHADOW) || monsterSummonType == SENTRYBOT
1649 			|| monsterSummonType == SPELLBOT || monsterSummonType == GYROBOT || monsterSummonType == DUMMYBOT )
1650 		{
1651 			monsterSummonType = static_cast<Monster>(rand() % NUMMONSTERS);
1652 		}
1653 
1654 		if ( targetStats->type == SHADOW )
1655 		{
1656 			monsterSummonType = CREATURE_IMP; // shadows turn to imps
1657 		}
1658 
1659 		bool summonCanEquipItems = false;
1660 		bool hitMonsterCanTransferEquipment = false;
1661 
1662 		switch ( monsterSummonType )
1663 		{
1664 			case RAT:
1665 			case SLIME:
1666 			case TROLL:
1667 			case SPIDER:
1668 			case GHOUL:
1669 			case SCORPION:
1670 			case CREATURE_IMP:
1671 			case DEMON:
1672 			case SCARAB:
1673 			case CRYSTALGOLEM:
1674 			case SHADOW:
1675 			case COCKATRICE:
1676 				summonCanEquipItems = false;
1677 				break;
1678 			default:
1679 				summonCanEquipItems = true;
1680 				break;
1681 		}
1682 
1683 		switch ( targetStats->type )
1684 		{
1685 			case RAT:
1686 			case SLIME:
1687 			case TROLL:
1688 			case SPIDER:
1689 			case GHOUL:
1690 			case SCORPION:
1691 			case CREATURE_IMP:
1692 			case DEMON:
1693 			case SCARAB:
1694 			case CRYSTALGOLEM:
1695 			case SHADOW:
1696 			case COCKATRICE:
1697 				hitMonsterCanTransferEquipment = false;
1698 				break;
1699 			default:
1700 				hitMonsterCanTransferEquipment = true;
1701 				break;
1702 		}
1703 
1704 		bool fellToDeath = false;
1705 		bool tryReposition = false;
1706 		bool fellInLava = false;
1707 		bool fellInWater = false;
1708 
1709 		if ( targetStats->EFFECTS[EFF_LEVITATING]
1710 			&& (monsterSummonType != CREATURE_IMP && monsterSummonType != COCKATRICE && monsterSummonType != SHADOW) )
1711 		{
1712 			// check if there's a floor...
1713 			int x, y, u, v;
1714 			x = std::min(std::max<unsigned int>(1, target->x / 16), map.width - 2);
1715 			y = std::min(std::max<unsigned int>(1, target->y / 16), map.height - 2);
1716 			for ( u = x - 1; u <= x + 1; u++ )
1717 			{
1718 				for ( v = y - 1; v <= y + 1; v++ )
1719 				{
1720 					if ( entityInsideTile(target, u, v, 0) )   // no floor
1721 					{
1722 						if ( !map.tiles[0 + u * MAPLAYERS + v * MAPLAYERS * map.height] )
1723 						{
1724 							// no floor.
1725 							fellToDeath = true;
1726 							tryReposition = true;
1727 						}
1728 						else if ( lavatiles[map.tiles[0 + u * MAPLAYERS + v * MAPLAYERS * map.height]] )
1729 						{
1730 							fellInLava = true;
1731 							tryReposition = true;
1732 						}
1733 						else if ( swimmingtiles[map.tiles[0 + u * MAPLAYERS + v * MAPLAYERS * map.height]] )
1734 						{
1735 							fellInWater = true;
1736 							tryReposition = true;
1737 						}
1738 						else
1739 						{
1740 							tryReposition = true; // something else??
1741 						}
1742 						break;
1743 					}
1744 				}
1745 				if ( tryReposition )
1746 				{
1747 					break;
1748 				}
1749 			}
1750 		}
1751 
1752 		Entity* summonedEntity = nullptr;
1753 		if ( tryReposition )
1754 		{
1755 			summonedEntity = summonMonster(monsterSummonType, target->x, target->y);
1756 			if ( !summonedEntity && (fellToDeath || fellInLava) )
1757 			{
1758 				summonedEntity = summonMonster(monsterSummonType, target->x, target->y, true); // force try, kill monster later.
1759 			}
1760 		}
1761 		else
1762 		{
1763 			summonedEntity = summonMonster(monsterSummonType, target->x, target->y, true);
1764 		}
1765 
1766 		if ( !summonedEntity )
1767 		{
1768 			if ( parent && parent->behavior == &actPlayer )
1769 			{
1770 				if ( fellInWater )
1771 				{
1772 					messagePlayer(parent->skill[2], language[3192]); // water make no work :<
1773 				}
1774 				else
1775 				{
1776 					messagePlayer(parent->skill[2], language[3191]); // failed for some other reason
1777 				}
1778 			}
1779 			return nullptr;
1780 		}
1781 
1782 		Stat* summonedStats = summonedEntity->getStats();
1783 		if ( !summonedStats )
1784 		{
1785 			if ( parent && parent->behavior == &actPlayer )
1786 			{
1787 				messagePlayer(parent->skill[2], language[3191]);
1788 			}
1789 			return nullptr;
1790 		}
1791 
1792 		// remove equipment from new monster
1793 		if ( summonCanEquipItems )
1794 		{
1795 			// monster does not have generated equipment yet, disable from generating.
1796 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_WEAPON] = 0;
1797 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_SHIELD] = 0;
1798 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_ARMOR] = 0;
1799 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_HELM] = 0;
1800 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_GLOVES] = 0;
1801 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_BOOTS] = 0;
1802 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_RING] = 0;
1803 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_CLOAK] = 0;
1804 			summonedStats->EDITOR_ITEMS[ITEM_SLOT_AMULET] = 0;
1805 		}
1806 
1807 		for ( int x = ITEM_SLOT_INV_1; x <= ITEM_SLOT_INV_6; x = x + ITEM_SLOT_NUMPROPERTIES )
1808 		{
1809 			if ( summonedStats->EDITOR_ITEMS[x] == 1 && summonedStats->EDITOR_ITEMS[x + ITEM_SLOT_CATEGORY] == 0 )
1810 			{
1811 				summonedStats->EDITOR_ITEMS[x] = 0; //clear default item in inventory
1812 			}
1813 		}
1814 
1815 		// copy stats from target to new creature.
1816 		summonedStats->HP = targetStats->HP;
1817 		summonedStats->OLDHP = targetStats->OLDHP;
1818 		summonedStats->MP = targetStats->MP;
1819 		summonedStats->MAXHP = targetStats->MAXHP;
1820 		summonedStats->MAXMP = targetStats->MAXMP;
1821 		summonedStats->STR = targetStats->STR;
1822 		summonedStats->DEX = targetStats->DEX;
1823 		summonedStats->CON = targetStats->CON;
1824 		summonedStats->INT = targetStats->INT;
1825 		summonedStats->PER = targetStats->PER;
1826 		//summonedStats->CHR = targetStats->CHR;
1827 		summonedStats->LVL = targetStats->LVL;
1828 		summonedStats->GOLD = targetStats->GOLD;
1829 
1830 		// don't apply random stats again
1831 		summonedStats->RANDOM_HP = 0;
1832 		summonedStats->RANDOM_MP = 0;
1833 		summonedStats->RANDOM_MAXHP = 0;
1834 		summonedStats->RANDOM_MAXMP = 0;
1835 		summonedStats->RANDOM_STR = 0;
1836 		summonedStats->RANDOM_DEX = 0;
1837 		summonedStats->RANDOM_CON = 0;
1838 		summonedStats->RANDOM_INT = 0;
1839 		summonedStats->RANDOM_PER = 0;
1840 		summonedStats->RANDOM_CHR = 0;
1841 		summonedStats->RANDOM_LVL = 0;
1842 		summonedStats->RANDOM_GOLD = 0;
1843 		summonedStats->MISC_FLAGS[STAT_FLAG_MONSTER_DISABLE_HC_SCALING] = 1;
1844 		summonedStats->leader_uid = targetStats->leader_uid;
1845 		if ( summonedStats->leader_uid != 0 && summonedStats->type != SHADOW )
1846 		{
1847 			Entity* leader = uidToEntity(summonedStats->leader_uid);
1848 			if ( leader )
1849 			{
1850 				// lose old ally
1851 				if ( target->monsterAllyIndex != -1 )
1852 				{
1853 					int playerFollower = MAXPLAYERS;
1854 					for ( int c = 0; c < MAXPLAYERS; c++ )
1855 					{
1856 						if ( players[c] && players[c]->entity )
1857 						{
1858 							if ( targetStats->leader_uid == players[c]->entity->getUID() )
1859 							{
1860 								playerFollower = c;
1861 								if ( stats[c] )
1862 								{
1863 									for ( node_t* allyNode = stats[c]->FOLLOWERS.first; allyNode != nullptr; allyNode = allyNode->next )
1864 									{
1865 										if ( *((Uint32*)allyNode->element) == target->getUID() )
1866 										{
1867 											list_RemoveNode(allyNode);
1868 											if ( c != clientnum )
1869 											{
1870 												serverRemoveClientFollower(c, target->getUID());
1871 											}
1872 											else
1873 											{
1874 												if ( FollowerMenu.recentEntity && (FollowerMenu.recentEntity->getUID() == 0
1875 													|| FollowerMenu.recentEntity->getUID() == target->getUID()) )
1876 												{
1877 													FollowerMenu.recentEntity = nullptr;
1878 												}
1879 											}
1880 											break;
1881 										}
1882 									}
1883 								}
1884 								break;
1885 							}
1886 						}
1887 					}
1888 				}
1889 
1890 				if ( forceFollower(*leader, *summonedEntity) )
1891 				{
1892 					if ( leader->behavior == &actPlayer )
1893 					{
1894 						summonedEntity->monsterAllyIndex = leader->skill[2];
1895 						if ( multiplayer == SERVER )
1896 						{
1897 							serverUpdateEntitySkill(summonedEntity, 42); // update monsterAllyIndex for clients.
1898 						}
1899 
1900 					}
1901 					// change the color of the hit entity.
1902 					summonedEntity->flags[USERFLAG2] = true;
1903 					serverUpdateEntityFlag(summonedEntity, USERFLAG2);
1904 					if ( monsterChangesColorWhenAlly(summonedStats) )
1905 					{
1906 						int bodypart = 0;
1907 						for ( node_t* node = summonedEntity->children.first; node != nullptr; node = node->next )
1908 						{
1909 							if ( bodypart >= LIMB_HUMANOID_TORSO )
1910 							{
1911 								Entity* tmp = (Entity*)node->element;
1912 								if ( tmp )
1913 								{
1914 									tmp->flags[USERFLAG2] = true;
1915 									//serverUpdateEntityFlag(tmp, USERFLAG2);
1916 								}
1917 							}
1918 							++bodypart;
1919 						}
1920 					}
1921 				}
1922 			}
1923 		}
1924 		if ( targetStats->type == HUMAN )
1925 		{
1926 			strcpy(summonedStats->name, targetStats->name);
1927 		}
1928 
1929 		if ( hitMonsterCanTransferEquipment && summonCanEquipItems )
1930 		{
1931 			// weapon
1932 			Item** slot = itemSlot(targetStats, targetStats->weapon);
1933 			if ( slot )
1934 			{
1935 				summonedStats->weapon = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
1936 					(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
1937 			}
1938 
1939 			// shield
1940 			slot = itemSlot(targetStats, targetStats->shield);
1941 			if ( slot )
1942 			{
1943 				summonedStats->shield = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
1944 					(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
1945 			}
1946 
1947 			// breastplate
1948 			slot = itemSlot(targetStats, targetStats->breastplate);
1949 			if ( slot )
1950 			{
1951 				if ( monsterSummonType == KOBOLD || monsterSummonType == GNOME )
1952 				{
1953 					// kobold/gnomes can't equip breastplate, drop it!
1954 					Entity* dropped = dropItemMonster(targetStats->breastplate, target, targetStats);
1955 					if ( dropped )
1956 					{
1957 						dropped->flags[USERFLAG1] = true;
1958 					}
1959 				}
1960 				else
1961 				{
1962 					summonedStats->breastplate = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
1963 						(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
1964 				}
1965 			}
1966 
1967 			// shoes
1968 			slot = itemSlot(targetStats, targetStats->shoes);
1969 			if ( slot )
1970 			{
1971 				summonedStats->shoes = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
1972 					(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
1973 			}
1974 
1975 			// helm
1976 			slot = itemSlot(targetStats, targetStats->helmet);
1977 			if ( slot )
1978 			{
1979 				if ( monsterSummonType == KOBOLD || monsterSummonType == GNOME )
1980 				{
1981 					// kobold/gnomes can't equip non-hoods, drop the rest
1982 					if ( (*slot)->type == HAT_HOOD )
1983 					{
1984 						summonedStats->helmet = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
1985 							(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
1986 					}
1987 					else
1988 					{
1989 						Entity* dropped = dropItemMonster(targetStats->helmet, target, targetStats);
1990 						if ( dropped )
1991 						{
1992 							dropped->flags[USERFLAG1] = true;
1993 						}
1994 					}
1995 				}
1996 				else
1997 				{
1998 					summonedStats->helmet = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
1999 						(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
2000 				}
2001 			}
2002 
2003 			// amulet
2004 			slot = itemSlot(targetStats, targetStats->amulet);
2005 			if ( slot )
2006 			{
2007 				summonedStats->amulet = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
2008 					(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
2009 			}
2010 
2011 			// ring
2012 			slot = itemSlot(targetStats, targetStats->ring);
2013 			if ( slot )
2014 			{
2015 				summonedStats->ring = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
2016 					(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
2017 			}
2018 
2019 			// cloak
2020 			slot = itemSlot(targetStats, targetStats->cloak);
2021 			if ( slot )
2022 			{
2023 				summonedStats->cloak = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
2024 					(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
2025 			}
2026 
2027 			// gloves
2028 			slot = itemSlot(targetStats, targetStats->gloves);
2029 			if ( slot )
2030 			{
2031 				if ( monsterSummonType == KOBOLD || monsterSummonType == GNOME )
2032 				{
2033 					// kobold/gnomes can't equip gloves, drop it!
2034 					Entity* dropped = dropItemMonster(targetStats->gloves, target, targetStats);
2035 					if ( dropped )
2036 					{
2037 						dropped->flags[USERFLAG1] = true;
2038 					}
2039 				}
2040 				else
2041 				{
2042 					summonedStats->gloves = newItem((*slot)->type, (*slot)->status, (*slot)->beatitude,
2043 						(*slot)->count, (*slot)->appearance, (*slot)->identified, nullptr);
2044 				}
2045 			}
2046 		}
2047 		else if ( hitMonsterCanTransferEquipment && !summonCanEquipItems )
2048 		{
2049 			Entity* dropped = dropItemMonster(targetStats->weapon, target, targetStats);
2050 			if ( dropped )
2051 			{
2052 				dropped->flags[USERFLAG1] = true;
2053 			}
2054 			dropped = dropItemMonster(targetStats->shield, target, targetStats);
2055 			if ( dropped )
2056 			{
2057 				dropped->flags[USERFLAG1] = true;
2058 			}
2059 			dropped = dropItemMonster(targetStats->breastplate, target, targetStats);
2060 			if ( dropped )
2061 			{
2062 				dropped->flags[USERFLAG1] = true;
2063 			}
2064 			dropped = dropItemMonster(targetStats->shoes, target, targetStats);
2065 			if ( dropped )
2066 			{
2067 				dropped->flags[USERFLAG1] = true;
2068 			}
2069 			dropped = dropItemMonster(targetStats->gloves, target, targetStats);
2070 			if ( dropped )
2071 			{
2072 				dropped->flags[USERFLAG1] = true;
2073 			}
2074 			dropped = dropItemMonster(targetStats->ring, target, targetStats);
2075 			if ( dropped )
2076 			{
2077 				dropped->flags[USERFLAG1] = true;
2078 			}
2079 			dropped = dropItemMonster(targetStats->amulet, target, targetStats);
2080 			if ( dropped )
2081 			{
2082 				dropped->flags[USERFLAG1] = true;
2083 			}
2084 			dropped = dropItemMonster(targetStats->cloak, target, targetStats);
2085 			if ( dropped )
2086 			{
2087 				dropped->flags[USERFLAG1] = true;
2088 			}
2089 			dropped = dropItemMonster(targetStats->helmet, target, targetStats);
2090 			if ( dropped )
2091 			{
2092 				dropped->flags[USERFLAG1] = true;
2093 			}
2094 		}
2095 
2096 		node_t* nextnode = nullptr;
2097 		for ( node_t* node = targetStats->inventory.first; node; node = nextnode )
2098 		{
2099 			nextnode = node->next;
2100 			Item* item = (Item*)node->element;
2101 			if ( item && item->appearance != MONSTER_ITEM_UNDROPPABLE_APPEARANCE && itemSlot(targetStats, item) == nullptr )
2102 			{
2103 				Item* copiedItem = newItem(item->type, item->status, item->beatitude, item->count, item->appearance, item->identified, &summonedStats->inventory);
2104 				if ( item->node )
2105 				{
2106 					list_RemoveNode(item->node);
2107 				}
2108 				else
2109 				{
2110 					free(item);
2111 				}
2112 			}
2113 		}
2114 
2115 		if ( parent && parent->behavior == &actPlayer )
2116 		{
2117 			Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
2118 			bool namedMonsterAsGeneric = monsterNameIsGeneric(*targetStats);
2119 			// the %s polymorph into a %s!
2120 			if ( !strcmp((*targetStats).name, "") || namedMonsterAsGeneric )
2121 			{
2122 				if ( (*targetStats).type < KOBOLD ) //Original monster count
2123 				{
2124 					if ( summonedStats->type < KOBOLD )
2125 					{
2126 						messagePlayerColor(parent->skill[2], color, language[3187], language[90 + (*targetStats).type], language[90 + summonedStats->type]);
2127 					}
2128 					else
2129 					{
2130 						messagePlayerColor(parent->skill[2], color, language[3187], language[90 + (*targetStats).type], language[2000 + summonedStats->type - KOBOLD]);
2131 					}
2132 				}
2133 				else if ( (*targetStats).type >= KOBOLD ) //New monsters
2134 				{
2135 					if ( summonedStats->type < KOBOLD )
2136 					{
2137 						messagePlayerColor(parent->skill[2], color, language[3187], language[2000 + (*targetStats).type - KOBOLD], language[90 + summonedStats->type]);
2138 					}
2139 					else
2140 					{
2141 						messagePlayerColor(parent->skill[2], color, language[3187], language[2000 + (*targetStats).type - KOBOLD], language[2000 + summonedStats->type - KOBOLD]);
2142 					}
2143 				}
2144 			}
2145 			else
2146 			{
2147 				if ( summonedStats->type < KOBOLD )
2148 				{
2149 					messagePlayerColor(parent->skill[2], color, language[3188], (*targetStats).name, language[90 + summonedStats->type]);
2150 				}
2151 				else
2152 				{
2153 					messagePlayerColor(parent->skill[2], color, language[3188], (*targetStats).name, language[2000 + summonedStats->type - KOBOLD]);
2154 				}
2155 			}
2156 		}
2157 
2158 		playSoundEntity(target, 400, 92);
2159 		spawnExplosion(target->x, target->y, target->z);
2160 		createParticleDropRising(target, 593, 1.f);
2161 		serverSpawnMiscParticles(target, PARTICLE_EFFECT_RISING_DROP, 593);
2162 
2163 		if ( fellToDeath )
2164 		{
2165 			summonedEntity->setObituary(language[3010]); // fell to their death.
2166 			summonedStats->HP = 0; // kill me instantly
2167 		}
2168 		else if ( fellInLava )
2169 		{
2170 			summonedEntity->setObituary(language[1506]); // goes for a swim in some lava.
2171 			summonedStats->HP = 0; // kill me instantly
2172 		}
2173 		else
2174 		{
2175 			for ( node_t* node = map.creatures->first; node != nullptr; node = node->next )
2176 			{
2177 				Entity* creature = (Entity*)node->element;
2178 				if ( creature && creature->behavior == &actMonster && creature != target && creature != summonedEntity )
2179 				{
2180 					if ( creature->monsterTarget == target->getUID() )
2181 					{
2182 						if ( creature->checkEnemy(summonedEntity) )
2183 						{
2184 							creature->monsterAcquireAttackTarget(*summonedEntity, MONSTER_STATE_PATH); // re-acquire new target
2185 						}
2186 						else
2187 						{
2188 							creature->monsterReleaseAttackTarget(); // release if new target is ally.
2189 						}
2190 					}
2191 				}
2192 			}
2193 		}
2194 
2195 		list_RemoveNode(target->mynode);
2196 		target = nullptr;
2197 		return summonedEntity;
2198 	}
2199 	else if ( target->behavior == &actPlayer )
2200 	{
2201 		if ( target->setEffect(EFF_POLYMORPH, true, effectDuration, true) )
2202 		{
2203 			spawnExplosion(target->x, target->y, target->z);
2204 			playSoundEntity(target, 400, 92);
2205 			createParticleDropRising(target, 593, 1.f);
2206 			serverSpawnMiscParticles(target, PARTICLE_EFFECT_RISING_DROP, 593);
2207 
2208 			if ( targetStats->playerRace == RACE_HUMAN )
2209 			{
2210 				int roll = (RACE_HUMAN + 1) + rand() % 8;
2211 				if ( target->effectPolymorph == 0 )
2212 				{
2213 					target->effectPolymorph = target->getMonsterFromPlayerRace(roll);
2214 				}
2215 				else
2216 				{
2217 					while ( target->effectPolymorph == target->getMonsterFromPlayerRace(roll) )
2218 					{
2219 						roll = (RACE_HUMAN + 1) + rand() % 8; // re roll to not polymorph into the same thing
2220 					}
2221 					target->effectPolymorph = target->getMonsterFromPlayerRace(roll);
2222 				}
2223 			}
2224 			else if ( (targetStats->playerRace != RACE_HUMAN && targetStats->appearance == 0) )
2225 			{
2226 				target->effectPolymorph = 100 + rand() % NUMAPPEARANCES;
2227 			}
2228 			serverUpdateEntitySkill(target, 50);
2229 
2230 			Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
2231 			Monster race = NOTHING;
2232 			if ( target->effectPolymorph > NUMMONSTERS )
2233 			{
2234 				race = HUMAN;
2235 			}
2236 			else
2237 			{
2238 				race = static_cast<Monster>(target->effectPolymorph);
2239 			}
2240 			if ( race < KOBOLD )
2241 			{
2242 				messagePlayerColor(target->skill[2], color, language[3186], language[90 + race]);
2243 			}
2244 			else
2245 			{
2246 				messagePlayerColor(target->skill[2], color, language[3186], language[2000 + race - KOBOLD]);
2247 			}
2248 
2249 			// change player's type here, don't like this.. will get auto reset in actPlayer() though
2250 			// otherwise the below aggro check will still assume previous race since actPlayer() hasn't run yet.
2251 			targetStats->type = race;
2252 
2253 			for ( node_t* node = map.creatures->first; node != nullptr; node = node->next )
2254 			{
2255 				Entity* creature = (Entity*)node->element;
2256 				if ( creature && creature->behavior == &actMonster && creature != target )
2257 				{
2258 					if ( creature->monsterTarget == target->getUID() )
2259 					{
2260 						if ( creature->checkEnemy(target) )
2261 						{
2262 							creature->monsterAcquireAttackTarget(*target, MONSTER_STATE_PATH); // re-acquire new target
2263 						}
2264 						else
2265 						{
2266 							creature->monsterReleaseAttackTarget(); // release if new target is ally.
2267 						}
2268 					}
2269 				}
2270 			}
2271 		}
2272 		else
2273 		{
2274 			messagePlayer(target->skill[2], language[3189]);
2275 		}
2276 
2277 	}
2278 
2279 	return nullptr;
2280 }
2281 
spellEffectTeleportPull(Entity * my,spellElement_t & element,Entity * parent,Entity * target,int resistance)2282 bool spellEffectTeleportPull(Entity* my, spellElement_t& element, Entity* parent, Entity* target, int resistance)
2283 {
2284 	if ( !parent )
2285 	{
2286 		return false;
2287 	}
2288 	if ( target )
2289 	{
2290 		playSoundEntity(target, 173, 128);
2291 		//int damage = element.damage;
2292 		//damage += ((element->mana - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->damage;
2293 
2294 		if ( target->behavior == &actMonster || target->behavior == &actPlayer
2295 			/*|| target->behavior == &actDoor || target->behavior == &actChest*/ )
2296 		{
2297 			Stat* hitstats = target->getStats();
2298 			if ( hitstats )
2299 			{
2300 				if ( !(svFlags & SV_FLAG_FRIENDLYFIRE) )
2301 				{
2302 					// test for friendly fire
2303 					if ( parent && parent->checkFriend(target) )
2304 					{
2305 						return false;
2306 					}
2307 				}
2308 			}
2309 			//playSoundEntity(target, 249, 64);
2310 
2311 			if ( parent )
2312 			{
2313 				if ( target->behavior == &actPlayer )
2314 				{
2315 					if ( MFLAG_DISABLETELEPORT )
2316 					{
2317 						// can't teleport here.
2318 						Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 255);
2319 						messagePlayerColor(target->skill[2], color, language[2381]);
2320 						if ( parent->behavior == &actPlayer )
2321 						{
2322 							messagePlayerColor(parent->skill[2], color, language[3452]);
2323 						}
2324 						return false;
2325 					}
2326 				}
2327 				if ( target->behavior == &actMonster && target->isBossMonster() )
2328 				{
2329 					if ( parent->behavior == &actPlayer )
2330 					{
2331 						Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
2332 						if ( hitstats )
2333 						{
2334 							messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[2905], language[2906], MSG_COMBAT);
2335 						}
2336 					}
2337 					return false;
2338 				}
2339 
2340 				// try find a teleport location in front of the caster.
2341 				int tx = static_cast<int>(std::floor(parent->x + 32 * cos(parent->yaw))) >> 4;
2342 				int ty = static_cast<int>(std::floor(parent->y + 32 * sin(parent->yaw))) >> 4;
2343 				int dist = 2;
2344 				bool foundLocation = false;
2345 				int numlocations = 0;
2346 				std::vector<std::pair<int, int>> goodspots;
2347 				std::vector<std::pair<int, int>> spotsWithLineOfSight;
2348 				if ( !checkObstacle((tx << 4) + 8, (ty << 4) + 8, target, NULL) ) // try find directly infront of caster.
2349 				{
2350 					Entity* ohitentity = hit.entity;
2351 					real_t ox = target->x;
2352 					real_t oy = target->y;
2353 					target->x = (tx << 4) + 8;
2354 					target->y = (ty << 4) + 8;
2355 					TileEntityList.updateEntity(*target); // important - lineTrace needs the TileEntityListUpdated.
2356 
2357 					// pretend the target is in the supposed spawn locations and try linetrace from each position.
2358 					real_t tangent = atan2(target->y - parent->y, target->x - parent->x);
2359 					lineTraceTarget(parent, parent->x, parent->y, tangent, 92, 0, true, target);
2360 					if ( hit.entity == target )
2361 					{
2362 						foundLocation = true;
2363 					}
2364 
2365 					// reset the coordinates we messed with
2366 					target->x = ox;
2367 					target->y = oy;
2368 					TileEntityList.updateEntity(*target); // important - lineTrace needs the TileEntityListUpdated.
2369 					hit.entity = ohitentity;
2370 				}
2371 				if ( !foundLocation )
2372 				{
2373 					// otherwise, let's search in an area
2374 					for ( int iy = std::max(1, ty - dist); iy < std::min(ty + dist, static_cast<int>(map.height)); ++iy )
2375 					{
2376 						for ( int ix = std::max(1, tx - dist); ix < std::min(tx + dist, static_cast<int>(map.width)); ++ix )
2377 						{
2378 							if ( !checkObstacle((ix << 4) + 8, (iy << 4) + 8, target, NULL) )
2379 							{
2380 								Entity* ohitentity = hit.entity;
2381 								real_t ox = target->x;
2382 								real_t oy = target->y;
2383 								target->x = (ix << 4) + 8;
2384 								target->y = (iy << 4) + 8;
2385 								TileEntityList.updateEntity(*target); // important - lineTrace needs the TileEntityListUpdated.
2386 
2387 								// pretend the target is in the supposed spawn locations and try linetrace from each position.
2388 								real_t tangent = atan2(target->y - parent->y, target->x - parent->x);
2389 								lineTraceTarget(parent, parent->x, parent->y, tangent, 92, 0, false, target);
2390 								if ( hit.entity == target )
2391 								{
2392 									spotsWithLineOfSight.push_back(std::make_pair(ix, iy));
2393 								}
2394 								goodspots.push_back(std::make_pair(ix, iy));
2395 								numlocations++;
2396 								// reset the coordinates we messed with
2397 								target->x = ox;
2398 								target->y = oy;
2399 								TileEntityList.updateEntity(*target); // important - lineTrace needs the TileEntityListUpdated.
2400 								hit.entity = ohitentity;
2401 							}
2402 						}
2403 					}
2404 					if ( numlocations == 0 )
2405 					{
2406 						if ( parent->behavior == &actPlayer )
2407 						{
2408 							// no room to teleport!
2409 							messagePlayer(parent->skill[2], language[3453]);
2410 						}
2411 						return false;
2412 					}
2413 
2414 					if ( !spotsWithLineOfSight.empty() )
2415 					{
2416 						std::pair<int, int> tmpPair = spotsWithLineOfSight[rand() % spotsWithLineOfSight.size()];
2417 						tx = tmpPair.first;
2418 						ty = tmpPair.second;
2419 					}
2420 					else if ( !goodspots.empty() )
2421 					{
2422 						std::pair<int, int> tmpPair = goodspots[rand() % goodspots.size()];
2423 						tx = tmpPair.first;
2424 						ty = tmpPair.second;
2425 					}
2426 					else
2427 					{
2428 						if ( parent->behavior == &actPlayer )
2429 						{
2430 							// no room to teleport!
2431 							messagePlayer(parent->skill[2], language[3453]);
2432 						}
2433 						return false;
2434 					}
2435 				}
2436 
2437 				// this timer is the entity spawn location.
2438 				Entity* locationTimer = createParticleTimer(parent, 40, 593);
2439 				locationTimer->x = tx * 16.0 + 8;
2440 				locationTimer->y = ty * 16.0 + 8;
2441 				locationTimer->z = 0;
2442 				locationTimer->particleTimerCountdownAction = PARTICLE_EFFECT_TELEPORT_PULL_TARGET_LOCATION;
2443 				locationTimer->particleTimerCountdownSprite = 593;
2444 				locationTimer->particleTimerTarget = static_cast<Sint32>(target->getUID()); // get the target to teleport around.
2445 				locationTimer->particleTimerEndAction = PARTICLE_EFFECT_TELEPORT_PULL; // teleport behavior of timer.
2446 				locationTimer->particleTimerEndSprite = 593; // sprite to use for end of timer function.
2447 				locationTimer->flags[PASSABLE] = false; // so this location is reserved for teleporting the entity.
2448 				locationTimer->sizex = 4;
2449 				locationTimer->sizey = 4;
2450 				if ( !locationTimer->myTileListNode )
2451 				{
2452 					locationTimer->setUID(-2);
2453 					TileEntityList.addEntity(*locationTimer);
2454 					locationTimer->setUID(-3);
2455 				}
2456 
2457 				// set a coundown to spawn particles on the monster.
2458 				Entity* spellTimer = createParticleTimer(target, 40, 593);
2459 				spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
2460 				spellTimer->particleTimerCountdownSprite = 593;
2461 				spellTimer->particleTimerTarget = static_cast<Sint32>(parent->getUID()); // get the target to teleport around.
2462 
2463 
2464 				if ( multiplayer == SERVER )
2465 				{
2466 					serverSpawnMiscParticles(target, PARTICLE_EFFECT_TELEPORT_PULL, 593);
2467 					serverSpawnMiscParticlesAtLocation(tx, ty, 0, PARTICLE_EFFECT_TELEPORT_PULL_TARGET_LOCATION, 593);
2468 				}
2469 
2470 				Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
2471 				if ( parent->behavior == &actPlayer )
2472 				{
2473 					// play a sound for the player to confirm the hit.
2474 					playSoundPlayer(parent->skill[2], 251, 128);
2475 				}
2476 
2477 				// update enemy bar for attacker
2478 				if ( hitstats )
2479 				{
2480 					if ( !strcmp(hitstats->name, "") )
2481 					{
2482 						if ( hitstats->type < KOBOLD ) //Original monster count
2483 						{
2484 							updateEnemyBar(parent, target, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
2485 						}
2486 						else if ( hitstats->type >= KOBOLD ) //New monsters
2487 						{
2488 							updateEnemyBar(parent, target, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
2489 						}
2490 					}
2491 					else
2492 					{
2493 						updateEnemyBar(parent, target, hitstats->name, hitstats->HP, hitstats->MAXHP);
2494 					}
2495 				}
2496 			}
2497 			return true;
2498 		}
2499 		if ( my )
2500 		{
2501 			spawnMagicEffectParticles(target->x, target->y, target->z, my->sprite);
2502 		}
2503 	}
2504 	else if ( my )
2505 	{
2506 		spawnMagicEffectParticles(my->x, my->y, my->z, my->sprite);
2507 	}
2508 	return false;
2509 }
2510 
spellEffectShadowTag(Entity & my,spellElement_t & element,Entity * parent,int resistance)2511 void spellEffectShadowTag(Entity& my, spellElement_t& element, Entity* parent, int resistance)
2512 {
2513 	if ( hit.entity )
2514 	{
2515 		//int damage = element.damage;
2516 		//damage += ((element->mana - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->damage;
2517 
2518 		if ( hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer )
2519 		{
2520 			playSoundEntity(&my, 174, 128);
2521 			Stat* hitstats = hit.entity->getStats();
2522 			if ( !hitstats )
2523 			{
2524 				return;
2525 			}
2526 
2527 			if ( parent )
2528 			{
2529 				bool sameAsPrevious = false;
2530 				if ( parent->creatureShadowTaggedThisUid != 0 )
2531 				{
2532 					Entity* oldTarget = nullptr;
2533 					if ( oldTarget = uidToEntity(parent->creatureShadowTaggedThisUid) )
2534 					{
2535 						if ( oldTarget != hit.entity )
2536 						{
2537 							oldTarget->setEffect(EFF_SHADOW_TAGGED, false, 0, true);
2538 						}
2539 						else
2540 						{
2541 							sameAsPrevious = true;
2542 						}
2543 					}
2544 				}
2545 				if ( parent->checkFriend(hit.entity) )
2546 				{
2547 					hit.entity->setEffect(EFF_SHADOW_TAGGED, true, 60 * TICKS_PER_SECOND, true);
2548 				}
2549 				else
2550 				{
2551 					hit.entity->setEffect(EFF_SHADOW_TAGGED, true, 10 * TICKS_PER_SECOND, true);
2552 				}
2553 				parent->creatureShadowTaggedThisUid = hit.entity->getUID();
2554 				serverUpdateEntitySkill(parent, 54);
2555 				if ( !sameAsPrevious )
2556 				{
2557 					createParticleShadowTag(hit.entity, parent->getUID(), 60 * TICKS_PER_SECOND);
2558 					serverSpawnMiscParticles(hit.entity, PARTICLE_EFFECT_SHADOW_TAG, 870, parent->getUID());
2559 				}
2560 			}
2561 
2562 			// hit messages
2563 			if ( parent )
2564 			{
2565 				Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
2566 				if ( parent->behavior == &actPlayer )
2567 				{
2568 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3463], language[3464], MSG_COMBAT);
2569 				}
2570 			}
2571 
2572 			// update enemy bar for attacker
2573 			if ( !strcmp(hitstats->name, "") )
2574 			{
2575 				if ( hitstats->type < KOBOLD ) //Original monster count
2576 				{
2577 					updateEnemyBar(parent, hit.entity, language[90 + hitstats->type], hitstats->HP, hitstats->MAXHP);
2578 				}
2579 				else if ( hitstats->type >= KOBOLD ) //New monsters
2580 				{
2581 					updateEnemyBar(parent, hit.entity, language[2000 + (hitstats->type - KOBOLD)], hitstats->HP, hitstats->MAXHP);
2582 				}
2583 			}
2584 			else
2585 			{
2586 				updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP);
2587 			}
2588 
2589 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
2590 			int player = -1;
2591 			if ( hit.entity->behavior == &actPlayer )
2592 			{
2593 				player = hit.entity->skill[2];
2594 				if ( player >= 0 )
2595 				{
2596 					messagePlayerColor(player, color, language[3465]);
2597 				}
2598 			}
2599 		}
2600 		spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my.sprite);
2601 	}
2602 	else
2603 	{
2604 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
2605 	}
2606 }
2607 
spellEffectDemonIllusion(Entity & my,spellElement_t & element,Entity * parent,Entity * target,int resistance)2608 bool spellEffectDemonIllusion(Entity& my, spellElement_t& element, Entity* parent, Entity* target, int resistance)
2609 {
2610 	if ( target )
2611 	{
2612 		//int damage = element.damage;
2613 		//damage += ((element->mana - element->base_mana) / static_cast<double>(element->overload_multiplier)) * element->damage;
2614 
2615 		if ( target->behavior == &actMonster || target->behavior == &actPlayer )
2616 		{
2617 			Stat* hitstats = target->getStats();
2618 			if ( !hitstats )
2619 			{
2620 				return false;
2621 			}
2622 
2623 			if ( hitstats->type == INCUBUS || hitstats->type == SUCCUBUS
2624 				|| hitstats->type == AUTOMATON || hitstats->type == DEVIL || hitstats->type == DEMON || hitstats->type == CREATURE_IMP
2625 				|| hitstats->type == SHADOW
2626 				|| (hitstats->type == INCUBUS && !strncmp(hitstats->name, "inner demon", strlen("inner demon"))) )
2627 			{
2628 				if ( parent && parent->behavior == &actPlayer )
2629 				{
2630 					// unable to taunt!
2631 					Uint32 color = SDL_MapRGB(mainsurface->format, 255, 255, 255);
2632 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3472], language[3473], MSG_COMBAT);
2633 				}
2634 				return false;
2635 			}
2636 			else if ( hitstats->monsterDemonHasBeenExorcised != 0
2637 				&& target->behavior != &actPlayer )
2638 			{
2639 				if ( parent && parent->behavior == &actPlayer )
2640 				{
2641 					// already exorcised!
2642 					Uint32 color = SDL_MapRGB(mainsurface->format, 255, 255, 255);
2643 					messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3735], language[3736], MSG_COMBAT);
2644 				}
2645 				return false;
2646 			}
2647 
2648 			if ( parent )
2649 			{
2650 				// try find a summon location around the entity.
2651 				int tx = static_cast<int>(std::floor(target->x)) >> 4;
2652 				int ty = static_cast<int>(std::floor(target->y)) >> 4;
2653 				int dist = 3;
2654 				int numlocations = 0;
2655 				std::vector<std::pair<int, int>> goodspots;
2656 				for ( int iy = std::max(1, ty - dist); iy < std::min(ty + dist, static_cast<int>(map.height)); ++iy )
2657 				{
2658 					for ( int ix = std::max(1, tx - dist); ix < std::min(tx + dist, static_cast<int>(map.width)); ++ix )
2659 					{
2660 						if ( !checkObstacle((ix << 4) + 8, (iy << 4) + 8, target, NULL) )
2661 						{
2662 							Entity* ohitentity = hit.entity;
2663 							real_t ox = parent->x;
2664 							real_t oy = parent->y;
2665 							parent->x = (ix << 4) + 8;
2666 							parent->y = (iy << 4) + 8;
2667 							TileEntityList.updateEntity(*parent); // important - lineTrace needs the TileEntityListUpdated.
2668 
2669 							// pretend the parent is in the supposed spawn locations and try linetrace from each position.
2670 							real_t tangent = atan2(parent->y - target->y, parent->x - target->x);
2671 							lineTraceTarget(target, target->x, target->y, tangent, 64, 0, false, parent);
2672 							if ( hit.entity == parent )
2673 							{
2674 								goodspots.push_back(std::make_pair(ix, iy));
2675 								numlocations++;
2676 							}
2677 							// reset the coordinates we messed with
2678 							parent->x = ox;
2679 							parent->y = oy;
2680 							TileEntityList.updateEntity(*parent); // important - lineTrace needs the TileEntityListUpdated.
2681 							hit.entity = ohitentity;
2682 						}
2683 					}
2684 				}
2685 				if ( numlocations == 0 )
2686 				{
2687 					if ( parent->behavior == &actPlayer )
2688 					{
2689 						// no room to spawn!
2690 						messagePlayer(parent->skill[2], language[3471]);
2691 					}
2692 					return false;
2693 				}
2694 				std::pair<int, int> tmpPair = goodspots[rand() % goodspots.size()];
2695 				tx = tmpPair.first;
2696 				ty = tmpPair.second;
2697 
2698 				Entity* monster = summonMonster(INCUBUS, tx * 16.0 + 8, ty * 16.0 + 8, true);
2699 				if ( monster )
2700 				{
2701 					spawnExplosion(monster->x, monster->y, -1);
2702 					playSoundEntity(monster, 171, 128);
2703 					//playSoundEntity(&my, 178, 128);
2704 					createParticleErupt(monster, 983);
2705 					serverSpawnMiscParticles(monster, PARTICLE_EFFECT_ERUPT, 983);
2706 
2707 					monster->parent = parent->getUID();
2708 					monster->monsterIllusionTauntingThisUid = static_cast<Sint32>(target->getUID());
2709 					switch ( target->getRace() )
2710 					{
2711 						case LICH:
2712 						case LICH_FIRE:
2713 						case LICH_ICE:
2714 						case MINOTAUR:
2715 							break;
2716 						default:
2717 							target->monsterAcquireAttackTarget(*monster, MONSTER_STATE_PATH);
2718 							break;
2719 					}
2720 					monster->lookAtEntity(*target);
2721 					Stat* monsterStats = monster->getStats();
2722 					if ( monsterStats )
2723 					{
2724 						monsterStats->leader_uid = 0;
2725 						strcpy(monsterStats->name, "inner demon");
2726 						monster->setEffect(EFF_STUNNED, true, 20, false);
2727 						monster->flags[USERFLAG2] = true;
2728 						serverUpdateEntityFlag(monster, USERFLAG2);
2729 						if ( monsterChangesColorWhenAlly(monsterStats) )
2730 						{
2731 							int bodypart = 0;
2732 							for ( node_t* node = (monster)->children.first; node != nullptr; node = node->next )
2733 							{
2734 								if ( bodypart >= LIMB_HUMANOID_TORSO )
2735 								{
2736 									Entity* tmp = (Entity*)node->element;
2737 									if ( tmp )
2738 									{
2739 										tmp->flags[USERFLAG2] = true;
2740 										serverUpdateEntityFlag(tmp, USERFLAG2);
2741 									}
2742 								}
2743 								++bodypart;
2744 							}
2745 						}
2746 					}
2747 					Stat* parentStats = parent->getStats();
2748 					if ( parentStats )
2749 					{
2750 						if ( parent->behavior == &actPlayer )
2751 						{
2752 							Uint32 color = SDL_MapRGB(mainsurface->format, 255, 255, 0);
2753 							messagePlayerColor(parent->skill[2], color, language[621]);
2754 						}
2755 						parent->modHP(-(parentStats->MAXHP / 10));
2756 						if ( parentStats->sex == MALE )
2757 						{
2758 							parent->setObituary(language[1528]);
2759 						}
2760 						else
2761 						{
2762 							parent->setObituary(language[1529]);
2763 						}
2764 					}
2765 
2766 					hitstats->monsterDemonHasBeenExorcised++;
2767 
2768 					// hit messages
2769 					Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
2770 					if ( parent->behavior == &actPlayer )
2771 					{
2772 						messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, language[3469], language[3470], MSG_COMBAT);
2773 					}
2774 				}
2775 			}
2776 
2777 			Uint32 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
2778 			int player = -1;
2779 			if ( target->behavior == &actPlayer )
2780 			{
2781 				player = target->skill[2];
2782 				if ( player >= 0 )
2783 				{
2784 					messagePlayerColor(player, color, language[3468]);
2785 					if ( hitstats->monsterDemonHasBeenExorcised == 3 )
2786 					{
2787 						Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
2788 						messagePlayerColor(player, color, language[3468]);
2789 					}
2790 				}
2791 			}
2792 			spawnMagicEffectParticles(target->x, target->y, target->z, my.sprite);
2793 			return true;
2794 		}
2795 		spawnMagicEffectParticles(target->x, target->y, target->z, my.sprite);
2796 	}
2797 	else
2798 	{
2799 		spawnMagicEffectParticles(my.x, my.y, my.z, my.sprite);
2800 	}
2801 	return false;
2802 }