1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
44 
45 #include "game/Spells.h"
46 
47 #include <algorithm>
48 #include <cmath>
49 #include <cstdio>
50 #include <cstdlib>
51 #include <cstring>
52 #include <limits>
53 #include <map>
54 #include <set>
55 #include <sstream>
56 #include <utility>
57 
58 #include <boost/foreach.hpp>
59 #include <boost/lexical_cast.hpp>
60 
61 #include "core/Application.h"
62 #include "core/Config.h"
63 #include "core/Core.h"
64 #include "core/GameTime.h"
65 #include "core/Localisation.h"
66 
67 #include "game/Damage.h"
68 #include "game/EntityManager.h"
69 #include "game/Equipment.h"
70 #include "game/NPC.h"
71 #include "game/Player.h"
72 #include "game/Inventory.h"
73 
74 #include "gui/Speech.h"
75 #include "gui/Menu.h"
76 #include "gui/Interface.h"
77 #include "gui/MiniMap.h"
78 
79 #include "graphics/BaseGraphicsTypes.h"
80 #include "graphics/Color.h"
81 #include "graphics/Draw.h"
82 #include "graphics/GraphicsTypes.h"
83 #include "graphics/Math.h"
84 #include "graphics/Renderer.h"
85 #include "graphics/Vertex.h"
86 #include "graphics/data/Mesh.h"
87 #include "graphics/data/TextureContainer.h"
88 #include "graphics/effects/SpellEffects.h"
89 #include "graphics/particle/ParticleEffects.h"
90 #include "graphics/particle/ParticleSystem.h"
91 #include "graphics/spells/Spells01.h"
92 #include "graphics/spells/Spells02.h"
93 #include "graphics/spells/Spells03.h"
94 #include "graphics/spells/Spells04.h"
95 #include "graphics/spells/Spells05.h"
96 #include "graphics/spells/Spells06.h"
97 #include "graphics/spells/Spells07.h"
98 #include "graphics/spells/Spells09.h"
99 #include "graphics/spells/Spells10.h"
100 
101 #include "input/Input.h"
102 
103 #include "io/resource/ResourcePath.h"
104 #include "io/log/Logger.h"
105 
106 #include "math/Angle.h"
107 #include "math/Vector2.h"
108 #include "math/Vector3.h"
109 
110 #include "physics/Collisions.h"
111 
112 #include "platform/Platform.h"
113 
114 #include "scene/Light.h"
115 #include "scene/Scene.h"
116 #include "scene/GameSound.h"
117 #include "scene/Interactive.h"
118 
119 #include "script/Script.h"
120 
121 using std::abs;
122 using std::string;
123 
124 static const float DEC_FOCAL = 50.0f;
125 static const float IMPROVED_FOCAL = 320.0f;
126 
127 void MakeSpCol();
128 extern bool TRUE_PLAYER_MOUSELOOK_ON;
129 long passwall=0;
130 long WILLRETURNTOFREELOOK = 0;
131 long GLOBAL_MAGIC_MODE=1;
132 bool bPrecastSpell = false;
133 extern std::string LAST_FAILED_SEQUENCE;
134 enum ARX_SPELLS_RuneDirection
135 {
136 	AUP,
137 	AUPRIGHT,
138 	ARIGHT,
139 	ADOWNRIGHT,
140 	ADOWN,
141 	ADOWNLEFT,
142 	ALEFT,
143 	AUPLEFT
144 };
145 long sp_arm=0;
146 long cur_arm=0;
147 long cur_sos=0;
148 static void ApplyPasswall();
149 static void ApplySPArm();
150 static void ApplySPuw();
151 static void ApplySPRf();
152 static void ApplySPMax();
153 static void ApplySPWep();
154 static void ApplySPBow();
155 static void ApplyCurPNux();
156 static void ApplyCurMr();
157 static void ApplyCurSOS();
158 
159 
160 extern long FistParticles;
161 extern long sp_max;
162 short uw_mode=0;
163 static short uw_mode_pos=0;
164 extern long MAGICMODE;
165 extern Entity * CURRENT_TORCH;
166 extern float GLOBAL_SLOWDOWN;
167 extern void ARX_SPSound();
168 extern float sp_max_y[64];
169 extern Color sp_max_col[64];
170 extern char	sp_max_ch[64];
171 extern long sp_max_nb;
172 long cur_mega=0;
173 float sp_max_start = 0;
174 long sp_wep=0;
175 
176 extern bool bRenderInCursorMode;
177 
178 bool bOldLookToggle;
179 extern float SLID_START;
180 
181 long BH_MODE = 0;
EERIE_OBJECT_SetBHMode()182 void EERIE_OBJECT_SetBHMode()
183 {
184 	if (BH_MODE)
185 		BH_MODE=0;
186 	else
187 	{
188 		BH_MODE=1;
189 		MakeCoolFx(&player.pos);
190 		MakeSpCol();
191 		strcpy(sp_max_ch,"!!!_Super-Deformed_!!!");
192 		sp_max_nb=strlen(sp_max_ch);
193 		sp_max_start=arxtime.get_updated();
194 			}
195 }
196 
197 struct SYMBOL_DRAW {
198 	unsigned long	starttime;
199 	Vec3f		lastpos;
200 	short			lasttim;
201 	short			duration;
202 	char			sequence[32];
203 	char			cPosStartX;
204 	char			cPosStartY;
205 };
206 
207 extern bool FrustrumsClipSphere(EERIE_FRUSTRUM_DATA * frustrums,EERIE_SPHERE * sphere);
208 extern bool bGToggleCombatModeWithKey;
209 void ARX_INTERFACE_Combat_Mode(long i);
210 
211 static float ARX_SPELLS_GetManaCost(Spell _lNumSpell,long _lNumSpellTab);
212 
213 ///////////////Spell Interpretation
214 SPELL spells[MAX_SPELLS];
215 short ARX_FLARES_broken(1);
216 long CurrPoint(0);
217 long cur_mx=0;
218 long cur_pnux=0;
219 long cur_pom=0;
220 long cur_rf=0;
221 long cur_mr=0;
222 long cur_sm=0;
223 long cur_bh=0;
224 
225 static float LASTTELEPORT(0.0F);
226 long snip=0;
227 static Vec2s Lm;
228 
229 static const long MAX_POINTS(200);
230 static Vec2s plist[MAX_POINTS];
231 static std::string SpellMoves;
232 Rune SpellSymbol[MAX_SPELL_SYMBOLS];
233 size_t CurrSpellSymbol=0;
234 
235 static long lMaxSymbolDrawSizeX;
236 static long lMaxSymbolDrawSizeY;
237 
238 unsigned char ucFlick=0;
239 
GetSpellPosition(Vec3f * pos,long i)240 bool GetSpellPosition(Vec3f * pos,long i)
241 {
242 
243 
244 	switch (spells[i].type)
245 	{
246 				//*********************************************************************************************
247 		// LEVEL 1 SPELLS -----------------------------------------------------------------------------
248 		case SPELL_MAGIC_SIGHT: // Launching MAGIC_SIGHT
249 		break;
250 		//----------------------------------------------------------------------------------------------
251 		case SPELL_MAGIC_MISSILE: // Launching MAGIC_MISSILE
252 		break;
253 		//----------------------------------------------------------------------------------------------
254 		case SPELL_IGNIT:// Launching IGNIT
255 		break;
256 		//---------------------------------------------------------------------------------------------
257 		case SPELL_DOUSE:// Launching DOUSE
258 		break;
259 		//---------------------------------------------------------------------------------------------
260 		case SPELL_ACTIVATE_PORTAL:// Launching ACTIVATE_PORTAL
261 		break;
262 		//*************************************************************************************************
263 		// LEVEL 2 SPELLS -----------------------------------------------------------------------------
264 		case SPELL_HEAL:// Launching HEAL
265 		break;
266 		//---------------------------------------------------------------------------------------------
267 		case SPELL_DETECT_TRAP:// Launching DETECT_TRAP
268 		break;
269 		//---------------------------------------------------------------------------------------------
270 		case SPELL_ARMOR:// Launching ARMOR
271 
272 			if (ValidIONum(spells[i].target))
273 			{
274 				*pos = entities[spells[i].target]->pos;
275 				return true;
276 			}
277 
278 		break;
279 		//------------------------------------------------------------------------------------------------
280 		case SPELL_LOWER_ARMOR:// Launching LOWER_ARMOR
281 
282 			if (ValidIONum(spells[i].target))
283 			{
284 				*pos = entities[spells[i].target]->pos;
285 				return true;
286 			}
287 
288 		break;
289 		//------------------------------------------------------------------------------------------------
290 		case SPELL_HARM:// Launching HARM
291 		break;
292 		//**********************************************************************************************
293 		// LEVEL 3 SPELLS -----------------------------------------------------------------------------
294 		case SPELL_SPEED:// Launching SPEED
295 
296 			if (ValidIONum(spells[i].target))
297 			{
298 				*pos = entities[spells[i].target]->pos;
299 				return true;
300 			}
301 
302 		break;
303 		//--------------------------------------------------------------------------------------------------
304 		case SPELL_DISPELL_ILLUSION:// Launching DISPELL_ILLUSION (REVEAL)
305 		break;
306 		//----------------------------------------------------------------------------------------------
307 		case SPELL_FIREBALL:// Launching FIREBALL
308 		break;
309 		//-------------------------------------------------------------------------------------------------
310 		case SPELL_CREATE_FOOD:// Launching CREATE_FOOD
311 		break;
312 		//----------------------------------------------------------------------------------------------
313 		case SPELL_ICE_PROJECTILE:// Launching ICE_PROJECTILE
314 		break;
315 		//***********************************************************************************************
316 		// LEVEL 4 SPELLS -----------------------------------------------------------------------------
317 		case SPELL_BLESS:// Launching BLESS
318 
319 			if (ValidIONum(spells[i].target))
320 			{
321 				*pos = entities[spells[i].target]->pos;
322 				return true;
323 			}
324 
325 		break;
326 		//-----------------------------------------------------------------------------------------------
327 		case SPELL_DISPELL_FIELD:// Launching DISPELL_FIELD
328 		break;
329 		//-----------------------------------------------------------------------------------------------
330 		case SPELL_FIRE_PROTECTION:// Launching FIRE_PROTECTION
331 
332 			if (ValidIONum(spells[i].target))
333 			{
334 				*pos = entities[spells[i].target]->pos;
335 				return true;
336 			}
337 
338 		break;
339 		//----------------------------------------------------------------------------------------------
340 		case SPELL_COLD_PROTECTION:// Launching COLD_PROTECTION
341 
342 			if (ValidIONum(spells[i].target))
343 			{
344 				*pos = entities[spells[i].target]->pos;
345 				return true;
346 			}
347 
348 		break;
349 		//----------------------------------------------------------------------------------------------
350 		case SPELL_TELEKINESIS:// Launching TELEKINESIS
351 		break;
352 		//-----------------------------------------------------------------------------------------------
353 		case SPELL_CURSE:// Launching CURSE
354 
355 			if (ValidIONum(spells[i].target))
356 			{
357 				*pos = entities[spells[i].target]->pos;
358 				return true;
359 			}
360 
361 		break;
362 		//*********************************************************************
363 		// LEVEL 5 SPELLS -----------------------------------------------------------------------------
364 		case SPELL_RUNE_OF_GUARDING:
365 		break;
366 		//----------------------------------------------------------------------------
367 		case SPELL_LEVITATE:
368 		break;
369 		//----------------------------------------------------------------------------
370 		case SPELL_CURE_POISON:
371 		break;
372 		//----------------------------------------------------------------------------
373 		case SPELL_REPEL_UNDEAD:
374 		break;
375 		//----------------------------------------------------------------------------
376 		case SPELL_POISON_PROJECTILE:
377 		break;
378 		//***************************************************************************
379 		// LEVEL 6 -----------------------------------------------------------------------------
380 		case SPELL_RISE_DEAD:
381 		break;
382 		//----------------------------------------------------------------------------
383 		case SPELL_PARALYSE:
384 
385 			if (ValidIONum(spells[i].target))
386 			{
387 				*pos = entities[spells[i].target]->pos;
388 				return true;
389 			}
390 
391 		break;
392 		//----------------------------------------------------------------------------
393 		case SPELL_CREATE_FIELD:
394 		break;
395 		//----------------------------------------------------------------------------
396 		case SPELL_DISARM_TRAP:
397 		break;
398 		//----------------------------------------------------------------------------
399 		case SPELL_SLOW_DOWN:
400 
401 			if (ValidIONum(spells[i].target))
402 			{
403 				*pos = entities[spells[i].target]->pos;
404 				return true;
405 			}
406 
407 		break;
408 		//****************************************************************************************
409 		// LEVEL 7 SPELLS -----------------------------------------------------------------------------
410 		case SPELL_FLYING_EYE:
411 		{
412 			*pos = eyeball.pos;
413 			return true;
414 		}
415 		break;
416 		//----------------------------------------------------------------------------
417 		case SPELL_FIRE_FIELD:
418 			CSpellFx *pCSpellFX;
419 			pCSpellFX = spells[i].pSpellFx;
420 
421 			if (pCSpellFX)
422 			{
423 				CFireField *pFireField = (CFireField *) pCSpellFX;
424 
425 				*pos = pFireField->pos;
426 				return true;
427 			}
428 
429 		break;
430 		//----------------------------------------------------------------------------
431 		case SPELL_ICE_FIELD:
432 		break;
433 		//----------------------------------------------------------------------------
434 		case SPELL_LIGHTNING_STRIKE:
435 		break;
436 		//----------------------------------------------------------------------------
437 		case SPELL_CONFUSE:
438 
439 			if (ValidIONum(spells[i].target))
440 			{
441 				*pos = entities[spells[i].target]->pos;
442 				return true;
443 			}
444 
445 		break;
446 		//*********************************************************************************
447 		// LEVEL 8 SPELLS -----------------------------------------------------------------------------
448 		case SPELL_INVISIBILITY:
449 
450 			if (ValidIONum(spells[i].target))
451 			{
452 				*pos = entities[spells[i].target]->pos;
453 				return true;
454 			}
455 
456 		break;
457 		//----------------------------------------------------------------------------
458 		case SPELL_MANA_DRAIN:
459 
460 			if (ValidIONum(spells[i].target))
461 			{
462 				*pos = entities[spells[i].target]->pos;
463 				return true;
464 			}
465 
466 		break;
467 		//----------------------------------------------------------------------------
468 		case SPELL_EXPLOSION:
469 		break;
470 		//----------------------------------------------------------------------------
471 		case SPELL_ENCHANT_WEAPON:
472 		break;
473 		//----------------------------------------------------------------------------
474 		case SPELL_LIFE_DRAIN:
475 
476 			if (ValidIONum(spells[i].target))
477 			{
478 				*pos = entities[spells[i].target]->pos;
479 				return true;
480 			}
481 
482 		break;
483 		//*****************************************************************************************
484 		// LEVEL 9 SPELLS -----------------------------------------------------------------------------
485 		case SPELL_SUMMON_CREATURE:
486 		break;
487 		//----------------------------------------------------------------------------
488 		case SPELL_NEGATE_MAGIC:
489 		break;
490 		//----------------------------------------------------------------------------
491 		case SPELL_INCINERATE:
492 
493 			if (ValidIONum(spells[i].target))
494 			{
495 				*pos = entities[spells[i].target]->pos;
496 				return true;
497 			}
498 
499 		break;
500 		//----------------------------------------------------------------------------
501 		case SPELL_MASS_PARALYSE:
502 		break;
503 		//----------------------------------------------------------------------------
504 		//********************************************************************************************
505 		// LEVEL 10 SPELLS -----------------------------------------------------------------------------
506 		case SPELL_MASS_LIGHTNING_STRIKE:
507 		break;
508 		//----------------------------------------------------------------------------
509 		case SPELL_CONTROL_TARGET:
510 		break;
511 		//----------------------------------------------------------------------------
512 		case SPELL_FREEZE_TIME:
513 		break;
514 		//----------------------------------------------------------------------------
515 		case SPELL_MASS_INCINERATE:
516 		break;
517 		//----------------------------------------------------------------------------
518 		case SPELL_TELEPORT:
519 		break;
520 		//----------------------------------------------------------------------------
521 		default: break;
522 	}
523 
524 	if (ValidIONum(spells[i].caster))
525 	{
526 		*pos = entities[spells[i].caster]->pos;
527 		return true;
528 	}
529 
530 	return false;
531 }
532 
LaunchAntiMagicField(size_t ident)533 void LaunchAntiMagicField(size_t ident) {
534 
535 	for(size_t n = 0 ; n < MAX_SPELLS; n++) {
536 
537 		if(!spells[n].exist || n == ident) {
538 			continue;
539 		}
540 
541 		if(spells[ident].caster_level < spells[n].caster_level) {
542 			continue;
543 		}
544 
545 		Vec3f pos;
546 		GetSpellPosition(&pos,n);
547 		if(closerThan(pos, entities[spells[ident].caster]->pos, 600.f)) {
548 			if(spells[n].type != SPELL_CREATE_FIELD) {
549 				spells[n].tolive = 0;
550 			} else if(spells[ident].caster == 0 && spells[n].caster == 0) {
551 				spells[n].tolive = 0;
552 			}
553 		}
554 	}
555 }
556 
557 //-----------------------------------------------------------------------------
ARX_SPELLS_AddSpellOn(const long & caster,const long & spell)558 void ARX_SPELLS_AddSpellOn(const long &caster, const long &spell)
559 {
560 	if (caster < 0 ||  spell < 0 || !entities[caster]) return;
561 
562 	Entity *io = entities[caster];
563 	void *ptr;
564 
565 	ptr = realloc(io->spells_on, sizeof(long) * (io->nb_spells_on + 1));
566 
567 	if (!ptr) return;
568 
569 	io->spells_on = (long *)ptr;
570 	io->spells_on[io->nb_spells_on] = spell;
571 	io->nb_spells_on++;
572 }
573 
574 //-----------------------------------------------------------------------------
ARX_SPELLS_GetSpellOn(const Entity * io,Spell spellid)575 long ARX_SPELLS_GetSpellOn(const Entity * io, Spell spellid)
576 {
577 	if (!io) return -1;
578 
579 	for (long i(0); i < io->nb_spells_on; i++)
580 	{
581 		if (	(spells[io->spells_on[i]].type == spellid)
582 			&&	(spells[io->spells_on[i]].exist)	)
583 			return io->spells_on[i];
584 	}
585 
586 	return -1;
587 }
588 
589 //-----------------------------------------------------------------------------
ARX_SPELLS_RemoveSpellOn(const long & caster,const long & spell)590 void ARX_SPELLS_RemoveSpellOn(const long &caster, const long &spell)
591 {
592 	if (caster < 0 || spell < 0) return;
593 
594 	Entity *io = entities[caster];
595 
596 	if (!io || !io->nb_spells_on) return;
597 
598 	if (io->nb_spells_on == 1)
599 	{
600 		free(io->spells_on);
601 		io->spells_on = NULL;
602 		io->nb_spells_on = 0;
603 		return;
604 	}
605 
606 	long i(0);
607 
608 	for (; i < io->nb_spells_on; i++)
609 		if (io->spells_on[i] == spell) break;
610 
611 	if ( i >= io->nb_spells_on) return;
612 
613 	io->nb_spells_on--;
614 	memcpy(&io->spells_on[i], &io->spells_on[i + 1], sizeof(long) * (io->nb_spells_on - i));
615 
616 	io->spells_on = (long *)realloc(io->spells_on, sizeof(long) * io->nb_spells_on);
617 }
618 
ARX_SPELLS_RemoveMultiSpellOn(long spell_id)619 void ARX_SPELLS_RemoveMultiSpellOn(long spell_id) {
620 	for(size_t i = 0; i < entities.size(); i++) {
621 		ARX_SPELLS_RemoveSpellOn(i, spells[spell_id].type);
622 	}
623 }
624 
ARX_SPELLS_RemoveAllSpellsOn(Entity * io)625 void ARX_SPELLS_RemoveAllSpellsOn(Entity *io) {
626 	free(io->spells_on), io->spells_on = NULL, io->nb_spells_on = 0;
627 }
628 
ARX_SPELLS_RequestSymbolDraw(Entity * io,const string & name,float duration)629 void ARX_SPELLS_RequestSymbolDraw(Entity *io, const string & name, float duration) {
630 
631 	const char * sequence;
632 	int iPosX = 0;
633 	int iPosY = 0;
634 
635 	if(name == "aam")              iPosX = 0, iPosY = 2, sequence = "6666";
636 	else if(name == "cetrius")     iPosX = 1, iPosY = 1, sequence = "33388886666";
637 	else if(name == "comunicatum") iPosX = 0, iPosY = 0, sequence = "6666622244442226666";
638 	else if(name == "cosum")       iPosX = 0, iPosY = 2, sequence = "66666222244448888";
639 	else if(name == "folgora")     iPosX = 0, iPosY = 3, sequence = "99993333";
640 	else if(name == "fridd")       iPosX = 0, iPosY = 4, sequence = "888886662222";
641 	else if(name == "kaom")        iPosX = 3, iPosY = 0, sequence = "44122366";
642 	else if(name == "mega")        iPosX = 2, iPosY = 4, sequence = "88888";
643 	else if(name == "morte")       iPosX = 0, iPosY = 2, sequence = "66666222";
644 	else if(name == "movis")       iPosX = 0, iPosY = 0, sequence = "666611116666";
645 	else if(name == "nhi")         iPosX = 4, iPosY = 2, sequence = "4444";
646 	else if(name == "rhaa")        iPosX = 2, iPosY = 0, sequence = "22222";
647 	else if(name == "spacium")     iPosX = 4, iPosY = 0, sequence = "44444222266688";
648 	else if(name == "stregum")     iPosX = 0, iPosY = 4, sequence = "8888833338888";
649 	else if(name == "taar")        iPosX = 0, iPosY = 1, sequence = "666222666";
650 	else if(name == "tempus")      iPosX = 0, iPosY = 4, sequence = "88886662226668866";
651 	else if(name == "tera")        iPosX = 0, iPosY = 3, sequence = "99922266";
652 	else if(name == "vista")       iPosX = 1, iPosY = 0, sequence = "333111";
653 	else if(name == "vitae")       iPosX = 0, iPosY = 2, sequence = "66666888";
654 	else if(name == "yok")         iPosX = 0, iPosY = 0, sequence = "222226666888";
655 	else if(name == "akbaa")       iPosX = 0, iPosY = 0, sequence = "22666772222";
656 	else return;
657 
658 	io->symboldraw = (SYMBOL_DRAW *)realloc(io->symboldraw, sizeof(SYMBOL_DRAW));
659 
660 	if (!io->symboldraw) return;
661 
662 	SYMBOL_DRAW *sd = io->symboldraw;
663 
664 	sd->duration = (short)std::max(1l, long(duration));
665 	strcpy(sd->sequence, sequence);
666 
667 	sd->starttime = (unsigned long)(arxtime);
668 	sd->lasttim = 0;
669 	sd->lastpos.x = io->pos.x - EEsin(radians(MAKEANGLE(io->angle.b - 45.0F + iPosX*2))) * 60.0F;
670 	sd->lastpos.y = io->pos.y - 120.0F - iPosY*5;
671 	sd->lastpos.z = io->pos.z + EEcos(radians(MAKEANGLE(io->angle.b - 45.0F + iPosX*2))) * 60.0F;
672 
673 	sd->cPosStartX = checked_range_cast<char>(iPosX);
674 	sd->cPosStartY = checked_range_cast<char>(iPosY);
675 
676 	io->gameFlags &= ~GFLAG_INVISIBILITY;
677 }
678 
ARX_SPELLS_RequestSymbolDraw2(Entity * io,Rune symb,float duration)679 static void ARX_SPELLS_RequestSymbolDraw2(Entity *io, Rune symb, float duration)
680 {
681 	const char * sequence;
682 	int iPosX = 0;
683 	int iPosY = 0;
684 
685 	switch (symb)
686 	{
687 		case RUNE_AAM   :
688 			iPosX = 0, iPosY = 2, sequence = "6666";
689 			break;
690 		case RUNE_CETRIUS:
691 			iPosX = 0, iPosY = 1, sequence = "33388886666";
692 			break;
693 		case RUNE_COMUNICATUM:
694 			iPosX = 0, iPosY = 0, sequence = "6666622244442226666";
695 			break;
696 		case RUNE_COSUM:
697 			iPosX = 0, iPosY = 2, sequence = "66666222244448888";
698 			break;
699 		case RUNE_FOLGORA:
700 			iPosX = 0, iPosY = 3, sequence = "99993333";
701 			break;
702 		case RUNE_FRIDD:
703 			iPosX = 0, iPosY = 4, sequence = "888886662222";
704 			break;
705 		case RUNE_KAOM:
706 			iPosX = 3, iPosY = 0, sequence = "44122366";
707 			break;
708 		case RUNE_MEGA:
709 			iPosX = 2, iPosY = 4, sequence = "88888";
710 			break;
711 		case RUNE_MORTE:
712 			iPosX = 0, iPosY = 2, sequence = "66666222";
713 			break;
714 		case RUNE_MOVIS:
715 			iPosX = 0, iPosY = 0, sequence = "666611116666";
716 			break;
717 		case RUNE_NHI:
718 			iPosX = 4, iPosY = 2, sequence = "4444";
719 			break;
720 		case RUNE_RHAA:
721 			iPosX = 2, iPosY = 0, sequence = "22222";
722 			break;
723 		case RUNE_SPACIUM:
724 			iPosX = 4, iPosY = 0, sequence = "44444222266688";
725 			break;
726 		case RUNE_STREGUM:
727 			iPosX = 0, iPosY = 4, sequence = "8888833338888";
728 			break;
729 		case RUNE_TAAR:
730 			iPosX = 0, iPosY = 1, sequence = "666222666";
731 			break;
732 		case RUNE_TEMPUS:
733 			iPosX = 0, iPosY = 4, sequence = "88886662226668866";
734 			break;
735 		case RUNE_TERA:
736 			iPosX = 0, iPosY = 3, sequence = "99922266";
737 			break;
738 		case RUNE_VISTA:
739 			iPosX = 1, iPosY = 0, sequence = "333111";
740 			break;
741 		case RUNE_VITAE:
742 			iPosX = 0, iPosY = 2, sequence = "66666888";
743 			break;
744 		case RUNE_YOK:
745 			iPosX = 0, iPosY = 0, sequence = "222226666888";
746 			break;
747 		default:
748 			return;
749 	}
750 
751 	SYMBOL_DRAW * ptr;
752 	ptr = (SYMBOL_DRAW *)realloc(io->symboldraw, sizeof(SYMBOL_DRAW));
753 
754 	if (!ptr) return;
755 
756 	io->symboldraw = ptr;
757 
758 	SYMBOL_DRAW *sd = io->symboldraw;
759 	sd->duration = duration < 1.0F ? 1 : (short)(long)duration;
760 	strcpy(sd->sequence, sequence);
761 	sd->starttime = (unsigned long)(arxtime);
762 	sd->lasttim = 0;
763 
764 	sd->lastpos.x = io->pos.x - EEsin(radians(MAKEANGLE(io->angle.b - 45.0F + iPosX*2))) * 60.0F;
765 	sd->lastpos.y = io->pos.y - 120.0F - iPosY*5;
766 	sd->lastpos.z = io->pos.z + EEcos(radians(MAKEANGLE(io->angle.b - 45.0F + iPosX*2))) * 60.0F;
767 
768 	sd->cPosStartX = checked_range_cast<char>(iPosX);
769 	sd->cPosStartY = checked_range_cast<char>(iPosY);
770 
771 	io->gameFlags &= ~GFLAG_INVISIBILITY;
772 
773 }
774 
775 //-----------------------------------------------------------------------------
ARX_SPELLS_RequestSymbolDraw3(const char * _pcName,char * _pcRes)776 void ARX_SPELLS_RequestSymbolDraw3(const char *_pcName,char *_pcRes)
777 {
778 	if		(!strcmp(_pcName, "aam"))		strcpy(_pcRes, "6666");
779 	else if (!strcmp(_pcName, "cetrius"))	strcpy(_pcRes, "33388886666");
780 	else if (!strcmp(_pcName, "comunicatum")) 	strcpy(_pcRes, "6666622244442226666");
781 	else if (!strcmp(_pcName, "cosum"))     strcpy(_pcRes, "66666222244448888");
782 	else if (!strcmp(_pcName, "folgora"))   strcpy(_pcRes, "99993333");
783 	else if (!strcmp(_pcName, "fridd"))		strcpy(_pcRes, "888886662222");
784 	else if (!strcmp(_pcName, "kaom"))		strcpy(_pcRes, "44122366");
785 	else if (!strcmp(_pcName, "mega"))		strcpy(_pcRes, "88888");
786 	else if (!strcmp(_pcName, "morte"))		strcpy(_pcRes, "66666222");
787 	else if (!strcmp(_pcName, "movis"))		strcpy(_pcRes, "666611116666");
788 	else if (!strcmp(_pcName, "nhi"))		strcpy(_pcRes, "4444");
789 	else if (!strcmp(_pcName, "rhaa"))		strcpy(_pcRes, "22222");
790 	else if (!strcmp(_pcName, "spacium"))	strcpy(_pcRes, "44444222266688");
791 	else if (!strcmp(_pcName, "stregum"))	strcpy(_pcRes, "8888833338888");
792 	else if (!strcmp(_pcName, "taar"))		strcpy(_pcRes, "666222666");
793 	else if (!strcmp(_pcName, "tempus"))	strcpy(_pcRes, "88886662226668866");
794 	else if (!strcmp(_pcName, "tera"))		strcpy(_pcRes, "99922266");
795 	else if (!strcmp(_pcName, "vista"))		strcpy(_pcRes, "333111");
796 	else if (!strcmp(_pcName, "vitae"))		strcpy(_pcRes, "66666888");
797 	else if (!strcmp(_pcName, "yok"))		strcpy(_pcRes, "222226666888");
798 	else if (!strcmp(_pcName, "akbaa"))		strcpy(_pcRes, "22666772222");
799 }
800 
801 #define OFFSET_X 8*2//0
802 #define OFFSET_Y 6*2//0
803 
804 //-----------------------------------------------------------------------------
GetSymbVector(char c,Vec2s * vec)805 void GetSymbVector(char c,Vec2s * vec)
806 {
807 	switch (c)
808 	{
809 		case '1' :
810 			vec->x = -OFFSET_X, vec->y =  OFFSET_Y;
811 			break;
812 		case '2' :
813 			vec->x =         0, vec->y =  OFFSET_Y;
814 			break;
815 		case '3' :
816 			vec->x =  OFFSET_X, vec->y =  OFFSET_Y;
817 			break;
818 		case '4' :
819 			vec->x = -OFFSET_X, vec->y =         0;
820 			break;
821 		case '6' :
822 			vec->x =  OFFSET_X, vec->y =         0;
823 			break;
824 		case '7' :
825 			vec->x = -OFFSET_X, vec->y = -OFFSET_Y;
826 			break;
827 		case '8' :
828 			vec->x =         0, vec->y = -OFFSET_Y;
829 			break;
830 		case '9' :
831 			vec->x =  OFFSET_X, vec->y = -OFFSET_Y;
832 			break;
833 		default  :
834 			vec->x =         0, vec->y =         0;
835 			break;
836 	}
837 }
838 
MakeSpellName(char * spell,Spell num)839 static bool MakeSpellName(char * spell, Spell num) {
840 
841 	// TODO(spells) use map
842 
843 	switch (num)
844 	{
845 		// Level 1
846 		case SPELL_MAGIC_SIGHT           :
847 			strcpy(spell, "magic_sight");
848 			break;
849 		case SPELL_MAGIC_MISSILE         :
850 			strcpy(spell, "magic_missile");
851 			break;
852 		case SPELL_IGNIT                 :
853 			strcpy(spell, "ignit");
854 			break;
855 		case SPELL_DOUSE                 :
856 			strcpy(spell, "douse");
857 			break;
858 		case SPELL_ACTIVATE_PORTAL       :
859 			strcpy(spell, "activate_portal");
860 			break;
861 
862 		// Level 2
863 		case SPELL_HEAL                  :
864 			strcpy(spell, "heal");
865 			break;
866 		case SPELL_DETECT_TRAP           :
867 			strcpy(spell, "detect_trap");
868 			break;
869 		case SPELL_ARMOR                 :
870 			strcpy(spell, "armor");
871 			break;
872 		case SPELL_LOWER_ARMOR           :
873 			strcpy(spell, "lower_armor");
874 			break;
875 		case SPELL_HARM                  :
876 			strcpy(spell, "harm");
877 			break;
878 
879 		// Level 3
880 		case SPELL_SPEED                 :
881 			strcpy(spell, "speed");
882 			break;
883 		case SPELL_DISPELL_ILLUSION      :
884 			strcpy(spell, "dispell_illusion");
885 			break;
886 		case SPELL_FIREBALL              :
887 			strcpy(spell, "fireball");
888 			break;
889 		case SPELL_CREATE_FOOD           :
890 			strcpy(spell, "create_food");
891 			break;
892 		case SPELL_ICE_PROJECTILE        :
893 			strcpy(spell, "ice_projectile");
894 			break;
895 
896 		// Level 4
897 		case SPELL_BLESS                 :
898 			strcpy(spell, "bless");
899 			break;
900 		case SPELL_DISPELL_FIELD         :
901 			strcpy(spell, "dispell_field");
902 			break;
903 		case SPELL_FIRE_PROTECTION       :
904 			strcpy(spell, "fire_protection");
905 			break;
906 		case SPELL_TELEKINESIS           :
907 			strcpy(spell, "telekinesis");
908 			break;
909 		case SPELL_CURSE                 :
910 			strcpy(spell, "curse");
911 			break;
912 		case SPELL_COLD_PROTECTION       :
913 			strcpy(spell, "cold_protection");
914 			break;
915 
916 		// Level 5
917 		case SPELL_RUNE_OF_GUARDING      :
918 			strcpy(spell, "rune_of_guarding");
919 			break;
920 		case SPELL_LEVITATE              :
921 			strcpy(spell, "levitate");
922 			break;
923 		case SPELL_CURE_POISON           :
924 			strcpy(spell, "cure_poison");
925 			break;
926 		case SPELL_REPEL_UNDEAD          :
927 			strcpy(spell, "repel_undead");
928 			break;
929 		case SPELL_POISON_PROJECTILE     :
930 			strcpy(spell, "poison_projectile");
931 			break;
932 
933 		// Level 6
934 		case SPELL_RISE_DEAD             :
935 			strcpy(spell, "raise_dead");
936 			break;
937 		case SPELL_PARALYSE              :
938 			strcpy(spell, "paralyse");
939 			break;
940 		case SPELL_CREATE_FIELD          :
941 			strcpy(spell, "create_field");
942 			break;
943 		case SPELL_DISARM_TRAP           :
944 			strcpy(spell, "disarm_trap");
945 			break;
946 		case SPELL_SLOW_DOWN             :
947 			strcpy(spell, "slowdown");
948 			break;
949 
950 		// Level 7
951 		case SPELL_FLYING_EYE            :
952 			strcpy(spell, "flying_eye");
953 			break;
954 		case SPELL_FIRE_FIELD            :
955 			strcpy(spell, "fire_field");
956 			break;
957 		case SPELL_ICE_FIELD             :
958 			strcpy(spell, "ice_field");
959 			break;
960 		case SPELL_LIGHTNING_STRIKE      :
961 			strcpy(spell, "lightning_strike");
962 			break;
963 		case SPELL_CONFUSE               :
964 			strcpy(spell, "confuse");
965 			break;
966 
967 		// Level 8
968 		case SPELL_INVISIBILITY          :
969 			strcpy(spell, "invisibility");
970 			break;
971 		case SPELL_MANA_DRAIN            :
972 			strcpy(spell, "mana_drain");
973 			break;
974 		case SPELL_EXPLOSION             :
975 			strcpy(spell, "explosion");
976 			break;
977 		case SPELL_ENCHANT_WEAPON        :
978 			strcpy(spell, "enchant_weapon");
979 			break;
980 		case SPELL_LIFE_DRAIN            :
981 			strcpy(spell, "life_drain");
982 			break;
983 
984 		// Level 9
985 		case SPELL_SUMMON_CREATURE       :
986 			strcpy(spell, "summon_creature");
987 			break;
988 		case SPELL_FAKE_SUMMON		     :
989 			strcpy(spell, "fake_summon");
990 			break;
991 		case SPELL_NEGATE_MAGIC          :
992 			strcpy(spell, "negate_magic");
993 			break;
994 		case SPELL_INCINERATE            :
995 			strcpy(spell, "incinerate");
996 			break;
997 		case SPELL_MASS_PARALYSE         :
998 			strcpy(spell, "mass_paralyse");
999 			break;
1000 
1001 		// Level 10
1002 		case SPELL_MASS_LIGHTNING_STRIKE :
1003 			strcpy(spell, "mass_lightning_strike");
1004 			break;
1005 		case SPELL_CONTROL_TARGET        :
1006 			strcpy(spell, "control");
1007 			break;
1008 		case SPELL_FREEZE_TIME           :
1009 			strcpy(spell, "freeze_time");
1010 			break;
1011 		case SPELL_MASS_INCINERATE       :
1012 			strcpy(spell, "mass_incinerate");
1013 			break;
1014 		default :
1015 			return false;
1016 	}
1017 
1018 	return true;
1019 }
1020 
SPELLCAST_Notify(long num)1021 void SPELLCAST_Notify(long num) {
1022 
1023 	if(num < 0) {
1024 		return;
1025 	}
1026 
1027 	if(size_t(num) >= MAX_SPELLS) {
1028 		return;
1029 	}
1030 
1031 	char spell[128];
1032 	long source = spells[num].caster;
1033 	if(!MakeSpellName(spell, spells[num].type)) {
1034 		return;
1035 	}
1036 
1037 	for(size_t i = 0; i < entities.size(); i++) {
1038 		if(entities[i] != NULL) {
1039 			EVENT_SENDER = (source >= 0) ? entities[source] : NULL;
1040 			char param[256];
1041 			sprintf(param, "%s %ld", spell, (long)spells[num].caster_level);
1042 			SendIOScriptEvent(entities[i], SM_SPELLCAST, param);
1043 		}
1044 	}
1045 }
1046 
1047 //-----------------------------------------------------------------------------
SPELLCAST_NotifyOnlyTarget(long num)1048 void SPELLCAST_NotifyOnlyTarget(long num)
1049 {
1050 	if (num < 0) return;
1051 
1052 	if ((size_t)num >= MAX_SPELLS) return;
1053 
1054 	if(spells[num].target<0) return;
1055 
1056 	char spell[128];
1057 	long source = spells[num].caster;
1058 
1059 	if (MakeSpellName(spell,spells[num].type))
1060 	{
1061 		if (source >= 0) EVENT_SENDER = entities[source];
1062 		else EVENT_SENDER = NULL;
1063 
1064 		char param[256];
1065 		sprintf(param,"%s %ld",spell,(long)spells[num].caster_level);
1066 		SendIOScriptEvent(entities[spells[num].target], SM_SPELLCAST, param);
1067 	}
1068 }
1069 
1070 //-----------------------------------------------------------------------------
SPELLEND_Notify(long num)1071 void SPELLEND_Notify(long num)
1072 {
1073 	if(num < 0 || (size_t)num >= MAX_SPELLS) {
1074 		return;
1075 	}
1076 
1077 	char spell[128];
1078 	long source=spells[num].caster;
1079 
1080 	if (spells[num].type==SPELL_CONFUSE)
1081 	{
1082 		if (ValidIONum(source))
1083 			EVENT_SENDER = entities[source];
1084 		else
1085 			EVENT_SENDER = NULL;
1086 
1087 		if(ValidIONum(spells[num].target)) {
1088 			if(MakeSpellName(spell,spells[num].type)) {
1089 				Entity * targ = entities[spells[num].target];
1090 				char param[128];
1091 				sprintf(param,"%s %ld",spell,(long)spells[num].caster_level);
1092 				SendIOScriptEvent(targ,SM_SPELLEND,param);
1093 			}
1094 		}
1095 		return;
1096 	}
1097 
1098 	// we only notify player spells end.
1099 	if(!MakeSpellName(spell,spells[num].type)) {
1100 		return;
1101 	}
1102 
1103 	for (size_t i = 0; i < entities.size(); i++) {
1104 		if(entities[i]) {
1105 			EVENT_SENDER = ValidIONum(source) ? entities[source] : NULL;
1106 			char param[128];
1107 			sprintf(param,"%s %ld",spell,(long)spells[num].caster_level);
1108 			SendIOScriptEvent(entities[i],SM_SPELLEND,param);
1109 		}
1110 	}
1111 }
1112 
ReCenterSequence(char * _pcSequence,int & _iMinX,int & _iMinY,int & _iMaxX,int & _iMaxY)1113 void ReCenterSequence(char *_pcSequence, int & _iMinX, int & _iMinY,
1114                       int & _iMaxX, int & _iMaxY) {
1115 
1116 	int iSizeX=0,iSizeY=0;
1117 	_iMinX=_iMinY=0;
1118 	_iMaxX=_iMaxY=0;
1119 	int iLenght=strlen(_pcSequence);
1120 
1121 	for(int iI=0;iI<iLenght;iI++)
1122 	{
1123 		Vec2s es2dVector;
1124 		GetSymbVector(_pcSequence[iI],&es2dVector);
1125 		iSizeX+=es2dVector.x;
1126 		iSizeY+=es2dVector.y;
1127 		_iMinX=std::min(_iMinX,iSizeX);
1128 		_iMinY=std::min(_iMinY,iSizeY);
1129 		_iMaxX=std::max(_iMaxX,iSizeX);
1130 		_iMaxY=std::max(_iMaxY,iSizeY);
1131 	}
1132 }
1133 
ARX_SPELLS_UpdateSymbolDraw()1134 void ARX_SPELLS_UpdateSymbolDraw() {
1135 
1136 	unsigned long curtime = (unsigned long)(arxtime);
1137 
1138 	for(size_t i = 0; i < entities.size(); i++) {
1139 		Entity * io = entities[i];
1140 		if (io)
1141 		{
1142 			if (io->spellcast_data.castingspell != SPELL_NONE)
1143 			{
1144 				if (io->symboldraw==NULL)
1145 				{
1146 					long tst=0;
1147 
1148 					if (!(io->spellcast_data.spell_flags & SPELLCAST_FLAG_NOANIM) &&  (io->ioflags & IO_NPC))
1149 					{
1150 						ANIM_USE * ause1=&io->animlayer[1];
1151 
1152 						if (ause1->cur_anim==io->anims[ANIM_CAST_START]  && (ause1->flags & EA_ANIMEND))
1153 						{
1154 							FinishAnim(io,ause1->cur_anim);
1155 							ANIM_Set(ause1,io->anims[ANIM_CAST_CYCLE]);
1156 							tst=1;
1157 						}
1158 						else if (ause1->cur_anim==io->anims[ANIM_CAST_CYCLE]) tst=1;
1159 						else if (ause1->cur_anim!=io->anims[ANIM_CAST_START])
1160 							io->spellcast_data.castingspell = SPELL_NONE;
1161 					}
1162 					else tst=1;
1163 
1164 					if ((io->spellcast_data.symb[0] != RUNE_NONE)  && tst )
1165 					{
1166 						Rune symb = io->spellcast_data.symb[0];
1167 
1168 						for (long j=0;j<3;j++)
1169 							io->spellcast_data.symb[j]=io->spellcast_data.symb[j+1];
1170 
1171 						io->spellcast_data.symb[3] = RUNE_NONE;
1172 						ARX_SPELLS_RequestSymbolDraw2(io, symb, (1000-(io->spellcast_data.spell_level*60))*std::max(io->speed_modif+io->basespeed,0.01f));
1173 						io->gameFlags &=~GFLAG_INVISIBILITY;
1174 					}
1175 					else if (tst)// cast spell !!!
1176 					{
1177 						io->gameFlags &=~GFLAG_INVISIBILITY;
1178 						ARX_SPELLS_Launch(io->spellcast_data.castingspell,i,io->spellcast_data.spell_flags,io->spellcast_data.spell_level,io->spellcast_data.target,io->spellcast_data.duration);
1179 
1180 						if (!(io->spellcast_data.spell_flags & SPELLCAST_FLAG_NOANIM)
1181 								&&  (io->ioflags & IO_NPC))
1182 						{
1183 							ANIM_USE * ause1=&io->animlayer[1];
1184 							AcquireLastAnim(io);
1185 							FinishAnim(io,ause1->cur_anim);
1186 							ANIM_Set(ause1,io->anims[ANIM_CAST]);
1187 						}
1188 
1189 						io->spellcast_data.castingspell = SPELL_NONE;
1190 					}
1191 				}
1192 			}
1193 
1194 			float rr=rnd();
1195 
1196 			if (io->flarecount)
1197 			{
1198 				if (io->dynlight==-1) io->dynlight=(short)GetFreeDynLight();
1199 
1200 				if (io->dynlight!=-1)
1201 				{
1202 					DynLight[io->dynlight].pos.x=io->pos.x-EEsin(radians(MAKEANGLE(io->angle.b-45.f)))*60.f;
1203 					DynLight[io->dynlight].pos.y=io->pos.y-120.f;
1204 					DynLight[io->dynlight].pos.z=io->pos.z+EEcos(radians(MAKEANGLE(io->angle.b-45.f)))*60.f;
1205 					DynLight[io->dynlight].fallstart=140.f+(float)io->flarecount*0.333333f+rr*5.f;
1206 					DynLight[io->dynlight].fallend=220.f+(float)io->flarecount*0.5f+rr*5.f;
1207 					DynLight[io->dynlight].intensity=1.6f;
1208 					DynLight[io->dynlight].exist=1;
1209 					DynLight[io->dynlight].rgb.r=0.01f*io->flarecount*2;
1210 					DynLight[io->dynlight].rgb.g=0.009f*io->flarecount*2;
1211 					DynLight[io->dynlight].rgb.b=0.008f*io->flarecount*2;
1212 				}
1213 			}
1214 			else if (io->dynlight>-1)
1215 			{
1216 				DynLight[io->dynlight].exist=0;
1217 				io->dynlight=-1;
1218 			}
1219 
1220 			if(io->symboldraw) {
1221 				SYMBOL_DRAW * sd = entities[i]->symboldraw;
1222 				long tim=curtime-sd->starttime;
1223 
1224 
1225 
1226 				if (tim>sd->duration)
1227 				{
1228 					if (io->dynlight!=-1)
1229 					{
1230 						DynLight[io->dynlight].time_creation = (unsigned long)(arxtime);
1231 						DynLight[io->dynlight].duration = 600;
1232 						io->dynlight=-1;
1233 					}
1234 
1235 					free(io->symboldraw);
1236 					io->symboldraw=NULL;
1237 					continue;
1238 				}
1239 
1240 				long nbcomponents=strlen(sd->sequence);
1241 
1242 				if (nbcomponents<=0)
1243 				{
1244 					free(io->symboldraw);
1245 					io->symboldraw=NULL;
1246 					continue;
1247 				}
1248 
1249 				float ti=((float)sd->duration/(float)nbcomponents);
1250 
1251 				if (ti<=0) ti=1;
1252 
1253 				Vec2s pos1, vect, old_pos;
1254 				long newtime=tim;
1255 				long oldtime=sd->lasttim;
1256 
1257 				if (oldtime>sd->duration) oldtime=sd->duration;
1258 
1259 				if (newtime>sd->duration) newtime=sd->duration;
1260 
1261 				sd->lasttim=(short)tim;
1262 
1263 				pos1.x = (short)subj.center.x - OFFSET_X * 2 + sd->cPosStartX * OFFSET_X;
1264 				pos1.y = (short)subj.center.y - OFFSET_Y * 2 + sd->cPosStartY * OFFSET_Y;
1265 
1266 				float div_ti=1.f/ti;
1267 
1268 				if (io != entities.player())
1269 				{
1270 					old_pos = pos1;
1271 
1272 					for (long j=0;j<nbcomponents;j++)
1273 					{
1274 						GetSymbVector(sd->sequence[j],&vect);
1275 						vect += vect / 2;
1276 
1277 						if(oldtime <= ti) {
1278 							float ratio = float(oldtime)*div_ti;
1279 							old_pos += (vect.to<float>() * ratio).to<short>();
1280 							break;
1281 						}
1282 
1283 						old_pos += vect;
1284 						oldtime-=(long)ti;
1285 					}
1286 
1287 					for (int j=0;j<nbcomponents;j++)
1288 					{
1289 						GetSymbVector(sd->sequence[j],&vect);
1290 						vect += vect / 2;
1291 
1292 						if (newtime<=ti)
1293 						{
1294 							float ratio = float(newtime) * div_ti;
1295 							pos1 += (vect.to<float>() * ratio).to<short>();
1296 							AddFlare(&pos1,0.1f,1,entities[i]);
1297 							FlareLine(&old_pos,&pos1,entities[i]);
1298 							break;
1299 						}
1300 
1301 						pos1 += vect;
1302 						newtime-=(long)ti;
1303 					}
1304 				}
1305 				else
1306 				{
1307 					int iMinX,iMinY,iMaxX,iMaxY;
1308 					int iSizeX,iSizeY;
1309 					ReCenterSequence(sd->sequence,iMinX,iMinY,iMaxX,iMaxY);
1310 					iSizeX=iMaxX-iMinX;
1311 					iSizeY=iMaxY-iMinY;
1312 					pos1.x = 97;
1313 					pos1.y = 64;
1314 
1315 
1316 					long lPosX	= (((513>>1)-lMaxSymbolDrawSizeX)>>1);
1317 					long lPosY	= (313-(((313*3/4)-lMaxSymbolDrawSizeY)>>1));
1318 
1319 					pos1.x = checked_range_cast<short>(pos1.x + lPosX);
1320 					pos1.y = checked_range_cast<short>(pos1.y + lPosY);
1321 
1322 
1323 
1324 					lPosX =  ((lMaxSymbolDrawSizeX-iSizeX)>>1);
1325 					lPosY =  ((lMaxSymbolDrawSizeY-iSizeY)>>1);
1326 
1327 					pos1.x = checked_range_cast<short>(pos1.x + lPosX);
1328 					pos1.y = checked_range_cast<short>(pos1.y + lPosY);
1329 
1330 
1331 
1332 					int iX = pos1.x-iMinX;
1333 					int iY = pos1.y-iMinY;
1334 
1335 					pos1.x = checked_range_cast<short>(iX);
1336 					pos1.y = checked_range_cast<short>(iY);
1337 
1338 
1339 					for (long j=0;j<nbcomponents;j++)
1340 					{
1341 
1342 						GetSymbVector(sd->sequence[j],&vect);
1343 
1344 						if (newtime<ti)
1345 						{
1346 							float ratio = (float)(newtime) * div_ti;
1347 
1348 
1349 							float fX = pos1.x + (ratio*vect.x)*0.5f;
1350 							float fY = pos1.y + (ratio*vect.y)*0.5f;
1351 
1352 							pos1.x = checked_range_cast<short>(fX);
1353 							pos1.y = checked_range_cast<short>(fY);
1354 
1355 
1356 							Vec2s pos;
1357 							pos.x=(short)(pos1.x*Xratio);
1358 							pos.y=(short)(pos1.y*Yratio);
1359 
1360 							if (io == entities.player())
1361 								AddFlare2(&pos,0.1f,1,entities[i]);
1362 							else
1363 								AddFlare(&pos,0.1f,1,entities[i]);
1364 
1365 
1366 							break;
1367 						}
1368 
1369 						pos1 += vect;
1370 
1371 						newtime-=(long)ti;
1372 					}
1373 				}
1374 			}
1375 		}
1376 	}
1377 }
1378 
ARX_SPELLS_ClearAllSymbolDraw()1379 void ARX_SPELLS_ClearAllSymbolDraw() {
1380 	BOOST_FOREACH(Entity * e, entities) {
1381 		if(e && e->symboldraw) {
1382 			free(e->symboldraw), e->symboldraw = NULL;
1383 		}
1384 	}
1385 }
1386 
ARX_SPELLS_AnalyseSYMBOL()1387 static void ARX_SPELLS_AnalyseSYMBOL() {
1388 
1389 	long sm = 0;
1390 	try {
1391 		sm = boost::lexical_cast<long>(SpellMoves);
1392 	} catch(...) {
1393 		LogDebug("bad spell moves: " << SpellMoves);
1394 	}
1395 
1396 	switch(sm) {
1397 
1398 		// COSUM
1399 		case 62148  :
1400 		case 632148 :
1401 		case 62498  :
1402 		case 62748  :
1403 		case 6248   :
1404 				SpellSymbol[CurrSpellSymbol++] = RUNE_COSUM;
1405 
1406 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1407 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1408 				}
1409 
1410 				ARX_SOUND_PlaySFX(SND_SYMB_COSUM);
1411 			break;
1412 
1413 		// COMUNICATUM
1414 		case 632426 :
1415 		case 627426 :
1416 		case 634236 :
1417 		case 624326 :
1418 		case 62426  :
1419 				SpellSymbol[CurrSpellSymbol++] = RUNE_COMUNICATUM;
1420 
1421 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1422 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1423 				}
1424 
1425 				ARX_SOUND_PlaySFX(SND_SYMB_COMUNICATUM);
1426 			break;
1427 
1428 		// FOLGORA
1429 		case 9823   :
1430 		case 9232   :
1431 		case 983    :
1432 		case 963    :
1433 		case 923    :
1434 		case 932    :
1435 		case 93     :
1436 				SpellSymbol[CurrSpellSymbol++] = RUNE_FOLGORA;
1437 
1438 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1439 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1440 				}
1441 
1442 				ARX_SOUND_PlaySFX(SND_SYMB_FOLGORA);
1443 			break;
1444 
1445 		// SPACIUM
1446 		case 42368  :
1447 		case 42678  :
1448 		case 42698  :
1449 		case 4268   :
1450 				SpellSymbol[CurrSpellSymbol++] = RUNE_SPACIUM;
1451 
1452 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1453 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1454 				}
1455 
1456 				ARX_SOUND_PlaySFX(SND_SYMB_SPACIUM);
1457 			break;
1458 
1459 		// TERA
1460 		case 9826   :
1461 		case 92126  :
1462 		case 9264   :
1463 		case 9296   :
1464 		case 926    :
1465 				SpellSymbol[CurrSpellSymbol++] = RUNE_TERA;
1466 
1467 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1468 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1469 				}
1470 
1471 				ARX_SOUND_PlaySFX(SND_SYMB_TERA);
1472 			break;
1473 
1474 		// CETRIUS
1475 		case 286   :
1476 		case 3286  :
1477 		case 23836 :
1478 		case 38636 :
1479 		case 2986  :
1480 		case 2386  :
1481 		case 386   :
1482 				SpellSymbol[CurrSpellSymbol++] = RUNE_CETRIUS;
1483 
1484 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1485 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1486 				}
1487 
1488 				ARX_SOUND_PlaySFX(SND_SYMB_CETRIUS);
1489 			break;
1490 
1491 		// RHAA
1492 		case 28    :
1493 		case 2     :
1494 				SpellSymbol[CurrSpellSymbol++] = RUNE_RHAA;
1495 
1496 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1497 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1498 				}
1499 
1500 				ARX_SOUND_PlaySFX(SND_SYMB_RHAA);
1501 			break;
1502 
1503 		// FRIDD
1504 		case 98362	:
1505 		case 8362	:
1506 		case 8632	:
1507 		case 8962	:
1508 		case 862	:
1509 				SpellSymbol[CurrSpellSymbol++] = RUNE_FRIDD;
1510 
1511 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1512 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1513 				}
1514 
1515 				ARX_SOUND_PlaySFX(SND_SYMB_FRIDD);
1516 			break;
1517 
1518 		// KAOM
1519 		case 41236	:
1520 		case 23		:
1521 		case 236	:
1522 		case 2369	:
1523 		case 136	:
1524 		case 12369	:
1525 		case 1236	:
1526 
1527 				if ((cur_arm>=0) && (cur_arm & 1) )
1528 				{
1529 					cur_arm++;
1530 
1531 					if (cur_arm>20)
1532 						ApplySPArm();
1533 				}
1534 				else
1535 					cur_arm=-1;
1536 
1537 				SpellSymbol[CurrSpellSymbol++] = RUNE_KAOM;
1538 
1539 				if((size_t)CurrSpellSymbol >= MAX_SPELL_SYMBOLS) {
1540 					CurrSpellSymbol = MAX_SPELL_SYMBOLS - 1;
1541 				}
1542 
1543 				ARX_SOUND_PlaySFX(SND_SYMB_KAOM);
1544 			break;
1545 
1546 		// STREGUM
1547 		case 82328 :
1548 		case 8328  :
1549 		case 2328  :
1550 		case 8938  :
1551 		case 8238  :
1552 		case 838   :
1553 				SpellSymbol[CurrSpellSymbol++] = RUNE_STREGUM;
1554 
1555 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1556 
1557 				ARX_SOUND_PlaySFX(SND_SYMB_STREGUM);
1558 			break;
1559 
1560 		// MORTE
1561 		case 628   :
1562 		case 621   :
1563 		case 62    :
1564 				SpellSymbol[CurrSpellSymbol++] = RUNE_MORTE;
1565 
1566 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1567 
1568 				ARX_SOUND_PlaySFX(SND_SYMB_MORTE);
1569 			break;
1570 
1571 		// TEMPUS
1572 		case 962686  :
1573 		case 862686  :
1574 		case 8626862 :
1575 				SpellSymbol[CurrSpellSymbol++] = RUNE_TEMPUS;
1576 
1577 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1578 
1579 				ARX_SOUND_PlaySFX(SND_SYMB_TEMPUS);
1580 			break;
1581 
1582 		// MOVIS
1583 		case 6316:
1584 		case 61236:
1585 		case 6146:
1586 		case 61216:
1587 		case 6216:
1588 		case 6416:
1589 		case 62126:
1590 		case 61264:
1591 		case 6126:
1592 		case 6136:
1593 		case 616:
1594 				SpellSymbol[CurrSpellSymbol++] = RUNE_MOVIS;
1595 
1596 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1597 
1598 				ARX_SOUND_PlaySFX(SND_SYMB_MOVIS);
1599 			break;
1600 
1601 		// NHI
1602 		case 46:
1603 		case 4:
1604 				SpellSymbol[CurrSpellSymbol++] = RUNE_NHI;
1605 
1606 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1607 
1608 				ARX_SOUND_PlaySFX(SND_SYMB_NHI);
1609 			break;
1610 
1611 		// AAM
1612 		case 64:
1613 		case 6:
1614 				SpellSymbol[CurrSpellSymbol++] = RUNE_AAM;
1615 
1616 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1617 
1618 				ARX_SOUND_PlaySFX(SND_SYMB_AAM);
1619 			break;
1620 
1621 		// YOK
1622 		case 412369:
1623 		case 2687:
1624 		case 2698:
1625 		case 2638:
1626 		case 26386:
1627 		case 2368:
1628 		case 2689:
1629 		case 268:
1630 				SpellSymbol[CurrSpellSymbol++] = RUNE_YOK;
1631 
1632 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1633 
1634 				ARX_SOUND_PlaySFX(SND_SYMB_YOK);
1635 			break;
1636 
1637 		// TAAR
1638 		case 6236:
1639 		case 6264:
1640 		case 626:
1641 				SpellSymbol[CurrSpellSymbol++] = RUNE_TAAR;
1642 
1643 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1644 
1645 				ARX_SOUND_PlaySFX(SND_SYMB_TAAR);
1646 			break;
1647 
1648 		// MEGA
1649 		case 82:
1650 		case 8:
1651 
1652 				if ((cur_arm>=0) && !(cur_arm & 1) )
1653 				{
1654 					cur_arm++;
1655 				}
1656 				else
1657 					cur_arm=-1;
1658 
1659 				SpellSymbol[CurrSpellSymbol++] = RUNE_MEGA;
1660 
1661 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1662 
1663 				ARX_SOUND_PlaySFX(SND_SYMB_MEGA);
1664 			break;
1665 
1666 		// VISTA
1667 		case 3614:
1668 		case 361:
1669 		case 341:
1670 		case 3212:
1671 		case 3214:
1672 		case 312:
1673 		case 314:
1674 		case 321:
1675 		case 31:
1676 				SpellSymbol[CurrSpellSymbol++] = RUNE_VISTA;
1677 
1678 				if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1679 
1680 				ARX_SOUND_PlaySFX(SND_SYMB_VISTA);
1681 			break;
1682 
1683 		// VITAE
1684 		case 698:
1685 		case 68:
1686 			SpellSymbol[CurrSpellSymbol++] = RUNE_VITAE;
1687 
1688 			if (CurrSpellSymbol>=MAX_SPELL_SYMBOLS) CurrSpellSymbol=MAX_SPELL_SYMBOLS-1;
1689 
1690 			ARX_SOUND_PlaySFX(SND_SYMB_VITAE);
1691 			break;
1692 
1693 //--------------------------------------------------------------------------------------------------------------------
1694 
1695 			// Special UW mode
1696 		case 238:
1697 		case 2398:
1698 		case 23898:
1699 		case 236987:
1700 		case 23698:
1701 
1702 			if (uw_mode_pos == 0) uw_mode_pos++;
1703 
1704 			goto failed;
1705 		break;
1706 		case 2382398:
1707 		case 2829:
1708 		case 23982398:
1709 		case 39892398:
1710 		case 2398938:
1711 		case 28239898:
1712 		case 238982398:
1713 		case 238923898:
1714 		case 28982398:
1715 		case 3923989:
1716 		case 292398:
1717 		case 398329:
1718 		case 38923898:
1719 		case 2398289:
1720 		case 289823898:
1721 		case 2989238:
1722 		case 29829:
1723 		case 2393239:
1724 		case 38239:
1725 		case 239829:
1726 		case 2898239:
1727 		case 28982898:
1728 		case 389389:
1729 		case 3892389:
1730 		case 289289:
1731 		case 289239:
1732 		case 239289:
1733 		case 2989298:
1734 		case 2392398:
1735 		case 238929:
1736 		case 28923898:
1737 		case 2929:
1738 		case 2398298:
1739 		case 239823898:
1740 		case 28238:
1741 		case 2892398:
1742 		case 28298:
1743 		case 298289:
1744 		case 38929:
1745 		case 289298989:
1746 		case 23892398:
1747 		case 238239:
1748 		case 29298:
1749 		case 2329298:
1750 		case 232389829:
1751 		case 2389829:
1752 		case 239239:
1753 		case 282398:
1754 		case 2389239:
1755 		case 2929898:
1756 		case 3292398:
1757 		case 23923298:
1758 		case 23898239:
1759 		case 3232929:
1760 		case 2982398:
1761 		case 238298:
1762 		case 3939:
1763 
1764 			if (uw_mode_pos == 1)
1765 			{
1766 				ApplySPuw();
1767 			}
1768 
1769 			goto failed;
1770 		break;
1771 		case 161:
1772 		case 1621:
1773 		case 1261:
1774 
1775 			if (cur_sm==0) cur_sm++;
1776 
1777 			if (cur_bh==0) cur_bh++;
1778 
1779 			if (cur_bh==2) cur_bh++;
1780 
1781 			if (cur_sos==0) cur_sos++;
1782 
1783 			if (cur_sos == 2)
1784 			{
1785 				cur_sos = 0;
1786 				ApplyCurSOS();
1787 			}
1788 
1789 			goto failed;
1790 			break;
1791 		case 83614:
1792 		case 8361:
1793 		case 8341:
1794 		case 83212:
1795 		case 83214:
1796 		case 8312:
1797 		case 8314:
1798 		case 8321:
1799 		case 831:
1800 		case 82341:
1801 		case 834:
1802 		case 823:
1803 		case 8234:
1804 		case 8231:
1805 
1806 			if (cur_pom==0) cur_pom++;
1807 
1808 			if (cur_pnux==0) cur_pnux++;
1809 
1810 			if (cur_pnux==2) cur_pnux++;
1811 
1812 			if (cur_bh == 1)
1813 			{
1814 				cur_bh++;
1815 			}
1816 
1817 			if (cur_bh == 3)
1818 			{
1819 				cur_bh = 0;
1820 				EERIE_OBJECT_SetBHMode();
1821 			}
1822 
1823 			goto failed;
1824 			break;
1825 		break;
1826 
1827 		case 83692:
1828 		case 823982:
1829 		case 83982:
1830 		case 82369892:
1831 		case 82392:
1832 		case 83892:
1833 		case 823282:
1834 		case 8392:
1835 		{
1836 			if (cur_sm == 2)
1837 			{
1838 				cur_sm++;
1839 				ApplySPBow();
1840 			}
1841 
1842 			if (cur_mx == 0)
1843 			{
1844 				cur_mx = 1;
1845 			}
1846 
1847 			if (cur_mr == 0)
1848 			{
1849 				cur_mr = 1;
1850 			}
1851 
1852 			if (cur_pom == 2)
1853 			{
1854 				cur_pom++;
1855 				ApplySPWep();
1856 			}
1857 
1858 				goto failed;
1859 			}
1860 			break;
1861 		case 98324:
1862 		case 92324:
1863 		case 89324:
1864 		case 9324:
1865 		case 9892324:
1866 		case 9234:
1867 		case 934:
1868 		{
1869 			if (cur_mr == 1)
1870 			{
1871 				cur_mr = 2;
1872 				MakeCoolFx(&player.pos);
1873 			}
1874 
1875 			if (cur_mx == 1)
1876 			{
1877 				cur_mx = 2;
1878 				MakeCoolFx(&player.pos);
1879 			}
1880 
1881 			if (cur_rf == 1)
1882 			{
1883 				cur_rf = 2;
1884 				MakeCoolFx(&player.pos);
1885 			}
1886 
1887 				if (cur_sm==1) cur_sm++;
1888 
1889 				goto failed;
1890 			}
1891 			break;
1892 		case 3249:
1893 		case 2349:
1894 		case 323489:
1895 		case 23249:
1896 		case 3489:
1897 		case 32498:
1898 		case 349:
1899 		{
1900 			if (cur_mx == 2)
1901 			{
1902 				cur_mx = 3;
1903 				ApplySPMax();
1904 			}
1905 
1906 				goto failed;
1907 			}
1908 		break;
1909 
1910 		case 26:
1911 		{
1912 			if (cur_pnux == 1)
1913 			{
1914 				cur_pnux++;
1915 				   }
1916 
1917 			if (cur_pnux == 3)
1918 		{
1919 				cur_pnux++;
1920 				ApplyCurPNux();
1921 			}
1922 
1923 			goto failed;
1924 		}
1925 		break;
1926 		case 9232187:
1927 		case 93187:
1928 		case 9234187:
1929 		case 831878:
1930 		case 923187:
1931 		case 932187:
1932 		case 93217:
1933 		case 9317:
1934 		{
1935 			if (cur_pom==1) cur_pom++;
1936 
1937 			if (cur_sos==1) cur_sos++;
1938 
1939 			goto failed;
1940 		}
1941 		break;
1942 		case 82313:
1943 		case 8343:
1944 		case 82343:
1945 		case 83413:
1946 		case 8313:
1947 		{
1948 			if (cur_mr == 2)
1949 			{
1950 				cur_mr = 3;
1951 				MakeCoolFx(&player.pos);
1952 				ApplyCurMr();
1953 			}
1954 
1955 			if (cur_rf == 0)
1956 			{
1957 				cur_rf = 1;
1958 			}
1959 
1960 			goto failed;
1961 			break;
1962 		}
1963 		case 86:
1964 
1965 			if (cur_rf == 2)
1966 		{
1967 				cur_rf = 3;
1968 				MakeCoolFx(&player.pos);
1969 				ApplySPRf();
1970 		}
1971 
1972 			goto failed;
1973 			break;
1974 
1975 		case 626262:
1976 		{
1977 			passwall++;
1978 
1979 			if (passwall==3)
1980 			{
1981 				passwall=0;
1982 				ApplyPasswall();
1983 			}
1984 		}
1985 		break;
1986 		case 828282:
1987 		{
1988 			player.skin++;
1989 
1990 			if ((player.skin==4) && (rnd()<0.9f))
1991 				player.skin++;
1992 
1993 			if (player.skin>5)
1994 				player.skin=0;
1995 
1996 			ARX_EQUIPMENT_RecreatePlayerMesh();
1997 			 goto failed;
1998 		}
1999 		break;
2000 
2001 		default:
2002 		{
2003 		failed:
2004 			;
2005 			std::string tex;
2006 
2007 			if (SpellMoves.length()>=127)
2008 				SpellMoves.resize(127);
2009 
2010 			LAST_FAILED_SEQUENCE = SpellMoves;
2011 
2012 			LogDebug("Unknown Symbol - " + SpellMoves);
2013 		}
2014 	}
2015 
2016 	bPrecastSpell = false;
2017 
2018 	// wanna precast?
2019 	if (GInput->actionPressed(CONTROLS_CUST_STEALTHMODE))
2020 	{
2021 		bPrecastSpell = true;
2022 	}
2023 }
2024 
2025 struct SpellDefinition {
2026 	SpellDefinition * next[RUNE_COUNT];
2027 	Spell spell;
SpellDefinitionSpellDefinition2028 	SpellDefinition() : spell(SPELL_NONE) {
2029 		for(size_t i = 0; i < RUNE_COUNT; i++) {
2030 			next[i] = NULL;
2031 		}
2032 	}
2033 
~SpellDefinitionSpellDefinition2034 	~SpellDefinition() {
2035 		for(size_t i = 0; i < RUNE_COUNT; i++) {
2036 			delete next[i];
2037 		}
2038 	}
2039 };
2040 
2041 static SpellDefinition definedSpells;
2042 typedef std::map<string, Spell> SpellNames;
2043 static SpellNames spellNames;
2044 
addSpell(const Rune symbols[MAX_SPELL_SYMBOLS],Spell spell,const string & name)2045 static void addSpell(const Rune symbols[MAX_SPELL_SYMBOLS], Spell spell, const string & name) {
2046 
2047 	typedef std::pair<SpellNames::const_iterator, bool> Res;
2048 	Res res = spellNames.insert(std::make_pair(name, spell));
2049 	if(!res.second) {
2050 		LogWarning << "Duplicate spell name: " + name;
2051 	}
2052 
2053 	if(symbols[0] == RUNE_NONE) {
2054 		return;
2055 	}
2056 
2057 	SpellDefinition * def = &definedSpells;
2058 
2059 	for(size_t i = 0; i < MAX_SPELL_SYMBOLS; i++) {
2060 		if(symbols[i] == RUNE_NONE) {
2061 			break;
2062 		}
2063 		arx_assert(symbols[i] >= 0 && (size_t)symbols[i] < RUNE_COUNT);
2064 		if(def->next[symbols[i]] == NULL) {
2065 			def->next[symbols[i]] = new SpellDefinition();
2066 		}
2067 		def = def->next[symbols[i]];
2068 	}
2069 
2070 	arx_assert(def->spell == SPELL_NONE);
2071 
2072 	def->spell = spell;
2073 }
2074 
getSpell(const Rune symbols[MAX_SPELL_SYMBOLS])2075 static Spell getSpell(const Rune symbols[MAX_SPELL_SYMBOLS]) {
2076 
2077 	const SpellDefinition * def = &definedSpells;
2078 
2079 	for(size_t i = 0; i < MAX_SPELL_SYMBOLS; i++) {
2080 		if(symbols[i] == RUNE_NONE) {
2081 			break;
2082 		}
2083 		arx_assert(symbols[i] >= 0 && (size_t)symbols[i] < RUNE_COUNT);
2084 		if(def->next[symbols[i]] == NULL) {
2085 			return SPELL_NONE;
2086 		}
2087 		def = def->next[symbols[i]];
2088 	}
2089 
2090 	return def->spell;
2091 }
2092 
GetSpellId(const string & spell)2093 Spell GetSpellId(const string & spell) {
2094 
2095 	SpellNames::const_iterator it = spellNames.find(spell);
2096 
2097 	return (it == spellNames.end()) ? SPELL_NONE : it->second;
2098 }
2099 
2100 struct RawSpellDefinition {
2101 	Rune symbols[MAX_SPELL_SYMBOLS];
2102 	Spell spell;
2103 	std::string name;
2104 };
2105 
2106 // TODO move to external file
2107 static const RawSpellDefinition allSpells[] = {
2108 	{{RUNE_RHAA, RUNE_STREGUM, RUNE_VITAE, RUNE_NONE}, SPELL_CURSE, "curse"}, // level 4
2109 	{{RUNE_RHAA, RUNE_TEMPUS, RUNE_NONE}, SPELL_FREEZE_TIME, "freeze_time"}, // level 10
2110 	{{RUNE_RHAA, RUNE_KAOM, RUNE_NONE}, SPELL_LOWER_ARMOR, "lower_armor"}, // level 2
2111 	{{RUNE_RHAA, RUNE_MOVIS, RUNE_NONE}, SPELL_SLOW_DOWN, "slowdown"}, // level 6
2112 	{{RUNE_RHAA, RUNE_VITAE, RUNE_NONE}, SPELL_HARM, "harm"}, // level 2
2113 	{{RUNE_RHAA, RUNE_VISTA, RUNE_NONE}, SPELL_CONFUSE, "confuse"}, // level 7
2114 	{{RUNE_MEGA, RUNE_NHI, RUNE_MOVIS, RUNE_NONE}, SPELL_MASS_PARALYSE, "mass_paralyse"}, // level 9
2115 	{{RUNE_MEGA, RUNE_KAOM, RUNE_NONE}, SPELL_ARMOR, "armor"}, // level 2
2116 	{{RUNE_MEGA, RUNE_VISTA, RUNE_NONE}, SPELL_MAGIC_SIGHT, "magic_sight"}, // level 1
2117 	{{RUNE_MEGA, RUNE_VITAE, RUNE_NONE}, SPELL_HEAL, "heal"}, // level 2
2118 	{{RUNE_MEGA, RUNE_MOVIS, RUNE_NONE}, SPELL_SPEED, "speed"}, // level 3
2119 	{{RUNE_MEGA, RUNE_STREGUM, RUNE_VITAE, RUNE_NONE}, SPELL_BLESS, "bless"}, // level 4
2120 	{{RUNE_MEGA, RUNE_STREGUM, RUNE_COSUM, RUNE_NONE}, SPELL_ENCHANT_WEAPON, "enchant_weapon"}, // level 8
2121 	{{RUNE_MEGA, RUNE_AAM, RUNE_MEGA, RUNE_YOK, RUNE_NONE}, SPELL_MASS_INCINERATE, "mass_incinerate"}, // level 10
2122 	{{RUNE_MEGA, RUNE_SPACIUM, RUNE_NONE}, SPELL_ACTIVATE_PORTAL, "activate_portal"}, // level ?
2123 	{{RUNE_MEGA, RUNE_SPACIUM, RUNE_MOVIS, RUNE_NONE}, SPELL_LEVITATE, "levitate"}, // level 5
2124 	{{RUNE_NHI, RUNE_MOVIS, RUNE_NONE}, SPELL_PARALYSE, "paralyse"}, // level 6
2125 	{{RUNE_NHI, RUNE_CETRIUS, RUNE_NONE}, SPELL_CURE_POISON, "cure_poison"}, // level 5
2126 	{{RUNE_NHI, RUNE_YOK, RUNE_NONE}, SPELL_DOUSE, "douse"}, // level 1
2127 	{{RUNE_NHI, RUNE_STREGUM, RUNE_VISTA, RUNE_NONE}, SPELL_DISPELL_ILLUSION, "dispell_illusion"}, // level 3
2128 	{{RUNE_NHI, RUNE_STREGUM, RUNE_SPACIUM, RUNE_NONE}, SPELL_NEGATE_MAGIC, "negate_magic"}, // level 9
2129 	{{RUNE_NHI, RUNE_SPACIUM, RUNE_NONE}, SPELL_DISPELL_FIELD, "dispell_field"}, // level 4
2130 	{{RUNE_NHI, RUNE_MORTE, RUNE_COSUM, RUNE_NONE}, SPELL_DISARM_TRAP, "disarm_trap"}, // level 6
2131 	{{RUNE_NHI, RUNE_VISTA, RUNE_NONE}, SPELL_INVISIBILITY, "invisibility"}, // level ?
2132 	{{RUNE_VISTA, RUNE_MOVIS, RUNE_NONE}, SPELL_FLYING_EYE, "flying_eye"}, // level 7
2133 	{{RUNE_MORTE, RUNE_KAOM, RUNE_NONE}, SPELL_REPEL_UNDEAD, "repel_undead"}, // level 5
2134 	{{RUNE_MORTE, RUNE_COSUM, RUNE_VISTA, RUNE_NONE}, SPELL_DETECT_TRAP, "detect_trap"}, // level 2
2135 	{{RUNE_MOVIS, RUNE_COMUNICATUM, RUNE_NONE}, SPELL_CONTROL_TARGET, "control"}, // level 10
2136 	{{RUNE_STREGUM, RUNE_MOVIS, RUNE_NONE}, SPELL_MANA_DRAIN, "mana_drain"}, // level 8
2137 	{{RUNE_AAM, RUNE_MEGA, RUNE_YOK, RUNE_NONE}, SPELL_INCINERATE, "incinerate"}, // level 9
2138 	{{RUNE_AAM, RUNE_MEGA, RUNE_MORTE, RUNE_NONE}, SPELL_EXPLOSION, "explosion"}, // level 8
2139 	{{RUNE_AAM, RUNE_KAOM, RUNE_SPACIUM, RUNE_NONE}, SPELL_CREATE_FIELD, "create_field"}, // level 6
2140 	{{RUNE_AAM, RUNE_MORTE, RUNE_VITAE, RUNE_NONE}, SPELL_RISE_DEAD, "raise_dead"}, // level 6
2141 	{{RUNE_AAM, RUNE_MORTE, RUNE_COSUM, RUNE_NONE}, SPELL_RUNE_OF_GUARDING, "rune_of_guarding"}, // level 5
2142 	{{RUNE_AAM, RUNE_VITAE, RUNE_TERA, RUNE_NONE}, SPELL_SUMMON_CREATURE, "summon_creature"}, // level 9
2143 	{{RUNE_AAM, RUNE_VITAE, RUNE_COSUM, RUNE_NONE}, SPELL_CREATE_FOOD, "create_food"}, // level 3
2144 	{{RUNE_AAM, RUNE_FOLGORA, RUNE_TAAR, RUNE_NONE}, SPELL_LIGHTNING_STRIKE, "lightning_strike"}, // level 7
2145 	{{RUNE_AAM, RUNE_FOLGORA, RUNE_SPACIUM, RUNE_NONE}, SPELL_MASS_LIGHTNING_STRIKE, "mass_lightning_strike"}, // level 10
2146 	{{RUNE_AAM, RUNE_YOK, RUNE_NONE}, SPELL_IGNIT, "ignit"}, // level 1
2147 	{{RUNE_AAM, RUNE_YOK, RUNE_SPACIUM, RUNE_NONE}, SPELL_FIRE_FIELD, "fire_field"}, // level 7
2148 	{{RUNE_AAM, RUNE_YOK, RUNE_TAAR, RUNE_NONE}, SPELL_FIREBALL, "fireball"}, // level 3
2149 	{{RUNE_AAM, RUNE_FRIDD, RUNE_SPACIUM, RUNE_NONE}, SPELL_ICE_FIELD, "ice_field"}, // level 7
2150 	{{RUNE_AAM, RUNE_FRIDD, RUNE_TAAR, RUNE_NONE}, SPELL_ICE_PROJECTILE, "ice_projectile"}, // level 3
2151 	{{RUNE_AAM, RUNE_CETRIUS, RUNE_TAAR, RUNE_NONE}, SPELL_POISON_PROJECTILE, "poison_projectile"}, // level 5
2152 	{{RUNE_AAM, RUNE_TAAR, RUNE_NONE}, SPELL_MAGIC_MISSILE, "magic_missile"}, // level 1
2153 	{{RUNE_YOK, RUNE_KAOM, RUNE_NONE}, SPELL_FIRE_PROTECTION, "fire_protection"}, // level 4
2154 	{{RUNE_FRIDD, RUNE_KAOM, RUNE_NONE}, SPELL_COLD_PROTECTION, "cold_protection"}, // level 4
2155 	{{RUNE_VITAE, RUNE_MOVIS, RUNE_NONE}, SPELL_LIFE_DRAIN, "life_drain"}, // level 8
2156 	{{RUNE_SPACIUM, RUNE_COMUNICATUM, RUNE_NONE}, SPELL_TELEKINESIS, "telekinesis"}, // level 4
2157 	{{RUNE_NONE}, SPELL_FAKE_SUMMON, "fake_summon"}
2158 };
2159 
2160 //! Plays the sound of Fizzling spell
ARX_SPELLS_Fizzle(long num)2161 static void ARX_SPELLS_Fizzle(long num) {
2162 	if(num < 0) {
2163 		ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE); // player fizzle
2164 	} else {
2165 		spells[num].tolive = 0;
2166 
2167 		if(spells[num].caster >= 0) {
2168 			ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE, &spells[num].caster_pos);
2169 		}
2170 	}
2171 }
2172 
ARX_SPELLS_AnalyseSPELL()2173 static bool ARX_SPELLS_AnalyseSPELL() {
2174 
2175 	long caster = 0; // Local Player
2176 	SpellcastFlags flags = 0;
2177 
2178 	if(GInput->actionPressed(CONTROLS_CUST_STEALTHMODE) || bPrecastSpell) {
2179 		flags |= SPELLCAST_FLAG_PRECAST;
2180 	}
2181 
2182 	bPrecastSpell = false;
2183 
2184 	Spell spell;
2185 
2186 	if(SpellSymbol[0] == RUNE_MEGA && SpellSymbol[1] == RUNE_MEGA
2187 	   && SpellSymbol[2] == RUNE_MEGA && SpellSymbol[3] == RUNE_AAM
2188 	   && SpellSymbol[4] == RUNE_VITAE && SpellSymbol[5] == RUNE_TERA) {
2189 		cur_mega = 10;
2190 		spell = SPELL_SUMMON_CREATURE;
2191 	} else {
2192 		spell = getSpell(SpellSymbol);
2193 	}
2194 
2195 	if(spell == SPELL_NONE) {
2196 
2197 		ARX_SPELLS_Fizzle(-1);
2198 
2199 		if(player.SpellToMemorize.bSpell) {
2200 			CurrSpellSymbol = 0;
2201 			player.SpellToMemorize.bSpell = false;
2202 		}
2203 
2204 		return false;
2205 	}
2206 
2207 	return ARX_SPELLS_Launch(spell, caster, flags);
2208 
2209 }
2210 
2211 
No_MagicAllowed()2212 bool No_MagicAllowed()
2213 {
2214 	ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE);
2215 	return false;
2216 }
2217 extern long PLAYER_PARALYSED;
2218 
2219 
ARX_SPELLS_Analyse()2220 static void ARX_SPELLS_Analyse() {
2221 
2222 	unsigned char dirs[MAX_POINTS];
2223 	unsigned char lastdir = 255;
2224 	long cdir = 0;
2225 
2226 	for(long i = 1; i < CurrPoint ; i++) {
2227 
2228 		Vec2s d = plist[i-1] - plist[i];
2229 
2230 		if(d.lengthSqr() > 100) {
2231 
2232 			float a = (float)abs(d.x);
2233 			float b = (float)abs(d.y);
2234 
2235 			if(b != 0.f && a / b > 0.4f && a / b < 2.5f) {
2236 				// Diagonal movemement.
2237 
2238 				if(d.x < 0 && d.y < 0) {
2239 					if(lastdir != ADOWNRIGHT) {
2240 						lastdir = dirs[cdir++] = ADOWNRIGHT;
2241 					}
2242 				} else if(d.x > 0 && d.y < 0) {
2243 					if(lastdir != ADOWNLEFT) {
2244 						lastdir = dirs[cdir++] = ADOWNLEFT;
2245 					}
2246 				} else if(d.x < 0 && d.y > 0) {
2247 					if(lastdir != AUPRIGHT) {
2248 						lastdir = dirs[cdir++] = AUPRIGHT;
2249 					}
2250 				} else if(d.x > 0 && d.y > 0) {
2251 					if(lastdir != AUPLEFT) {
2252 						lastdir = dirs[cdir++] = AUPLEFT;
2253 					}
2254 				}
2255 
2256 			} else if(a > b) {
2257 				// Horizontal movement.
2258 
2259 				if(d.x < 0) {
2260 					if(lastdir != ARIGHT) {
2261 						lastdir = dirs[cdir++] = ARIGHT;
2262 					}
2263 				} else {
2264 					if(lastdir != ALEFT) {
2265 						lastdir = dirs[cdir++] = ALEFT;
2266 					}
2267 				}
2268 
2269 			} else {
2270 				// Vertical movement.
2271 
2272 				if(d.y < 0) {
2273 					if(lastdir != ADOWN) {
2274 						lastdir = dirs[cdir++] = ADOWN;
2275 					}
2276 				} else {
2277 					if(lastdir != AUP) {
2278 						lastdir = dirs[cdir++] = AUP;
2279 					}
2280 				}
2281 			}
2282 		}
2283 	}
2284 
2285 	SpellMoves.clear();
2286 
2287 	if ( cdir > 0 )
2288 	{
2289 
2290 		for (long i = 0 ; i < cdir ; i++ )
2291 		{
2292 			switch ( dirs[i] )
2293 			{
2294 				case AUP:
2295 					SpellMoves += "8"; //uses PAD values
2296 					break;
2297 
2298 				case ADOWN:
2299 					SpellMoves += "2";
2300 					break;
2301 
2302 				case ALEFT:
2303 					SpellMoves += "4";
2304 					break;
2305 
2306 				case ARIGHT:
2307 					SpellMoves += "6";
2308 					break;
2309 
2310 				case AUPRIGHT:
2311 					SpellMoves += "9";
2312 					break;
2313 
2314 				case ADOWNRIGHT:
2315 					SpellMoves += "3";
2316 					break;
2317 
2318 				case AUPLEFT:
2319 					SpellMoves += "7";
2320 					break;
2321 
2322 				case ADOWNLEFT:
2323 					SpellMoves += "1";
2324 					break;
2325 			}
2326 		}
2327 	}
2328 }
2329 
ARX_SPELLS_ManageMagic()2330 void ARX_SPELLS_ManageMagic()
2331 {
2332 	if (ARXmenu.currentmode!=AMCM_OFF)
2333 		return;
2334 
2335 	Entity * io=entities.player();
2336 
2337 	if (!io) return;
2338 
2339 	if ((io->animlayer[1].cur_anim == io->anims[ANIM_BARE_UNREADY]) ||
2340 		(io->animlayer[1].cur_anim == io->anims[ANIM_DAGGER_UNREADY_PART_1]) ||
2341 		(io->animlayer[1].cur_anim == io->anims[ANIM_1H_UNREADY_PART_1]) ||
2342 		(io->animlayer[1].cur_anim == io->anims[ANIM_2H_UNREADY_PART_1]) ||
2343 		(io->animlayer[1].cur_anim == io->anims[ANIM_MISSILE_UNREADY_PART_1]) ||
2344 		(io->animlayer[1].cur_anim == io->anims[ANIM_DAGGER_UNREADY_PART_2]) ||
2345 		(io->animlayer[1].cur_anim == io->anims[ANIM_1H_UNREADY_PART_2]) ||
2346 		(io->animlayer[1].cur_anim == io->anims[ANIM_2H_UNREADY_PART_2]) ||
2347 		(io->animlayer[1].cur_anim == io->anims[ANIM_MISSILE_UNREADY_PART_2]))
2348 		return;
2349 
2350 	snip++;
2351 
2352 	if ((!(player.Current_Movement & PLAYER_CROUCH)) && (!BLOCK_PLAYER_CONTROLS &&
2353 		(GInput->actionPressed(CONTROLS_CUST_MAGICMODE))) && (!PLAYER_PARALYSED))
2354 	{
2355 
2356 		if (player.Interface & INTER_COMBATMODE)
2357 		{
2358 			WILLRETURNTOCOMBATMODE=1;
2359 
2360 			ARX_INTERFACE_Combat_Mode(0);
2361 			bGToggleCombatModeWithKey=false;
2362 
2363 
2364 			ResetAnim(&entities.player()->animlayer[1]);
2365 			entities.player()->animlayer[1].flags&=~EA_LOOP;
2366 		}
2367 
2368 		if (TRUE_PLAYER_MOUSELOOK_ON)
2369 		{
2370 			WILLRETURNTOFREELOOK = 1;
2371 			TRUE_PLAYER_MOUSELOOK_ON = false;
2372 		}
2373 
2374 		if (player.doingmagic!=2)
2375 		{
2376 			player.doingmagic=2;
2377 
2378 			if (entities.player()->anims[ANIM_CAST_START])
2379 			{
2380 				AcquireLastAnim(entities.player());
2381 				ANIM_Set(&entities.player()->animlayer[1],entities.player()->anims[ANIM_CAST_START]);
2382 				MAGICMODE = 1;
2383 			}
2384 		}
2385 
2386 		if (snip >= 2)
2387 		{
2388 			if ((!EERIEMouseButton & 1) && (ARX_FLARES_broken==0)) // TODO should this be !(EERIEMouseButton & 1)?
2389 			{
2390 				ARX_FLARES_broken=2;
2391 				PIPOrgb++;
2392 
2393 				if (PIPOrgb>2) PIPOrgb=0;
2394 			}
2395 
2396 			if(EERIEMouseButton & 1) {
2397 
2398 				Vec2s pos = DANAEMouse;
2399 				if(TRUE_PLAYER_MOUSELOOK_ON) {
2400 					pos = MemoMouse;
2401 				}
2402 
2403 				Vec2s pos2 = Lm;
2404 
2405 				if (!ARX_FLARES_broken) FlareLine(&pos2,&pos);
2406 
2407 				if (rnd()>0.6) AddFlare(&pos,1.f,-1);
2408 				else AddFlare(&pos,1.f,3);
2409 
2410 				OPIPOrgb = PIPOrgb;
2411 
2412 				Lm = DANAEMouse;
2413 				if(TRUE_PLAYER_MOUSELOOK_ON) {
2414 					Lm = MemoMouse;
2415 				}
2416 
2417 				ARX_FLARES_broken=0;
2418 
2419 				if (!ARX_SOUND_IsPlaying(SND_MAGIC_DRAW))
2420 					ARX_SOUND_PlaySFX(SND_MAGIC_DRAW, NULL, 1.0F, ARX_SOUND_PLAY_LOOPED);
2421 			}
2422 			else
2423 			{
2424 				ARX_SOUND_Stop(SND_MAGIC_DRAW);
2425 			}
2426 
2427 			snip=0;
2428 		}
2429 	}
2430 	else
2431 	{
2432 		ARX_FLARES_broken=1;
2433 		PIPOrgb++;
2434 
2435 		if (PIPOrgb>2) PIPOrgb=0;
2436 
2437 		if (player.doingmagic!=0)//==2)
2438 		{
2439 			player.doingmagic=0;//1
2440 
2441 			if (entities.player()->anims[ANIM_CAST_END])
2442 			{
2443 				AcquireLastAnim(entities.player());
2444 				ANIM_Set(&entities.player()->animlayer[1],entities.player()->anims[ANIM_CAST_END]);
2445 			}
2446 
2447 			ARX_FLARES_broken=3;
2448 		}
2449 	}
2450 
2451 
2452 	if (ARX_FLARES_broken==3)
2453 	{
2454 		cur_arm=0;
2455 		cur_mega=0;
2456 		passwall=0;
2457 
2458 		if (cur_mr!=3)
2459 			cur_mr=0;
2460 
2461 		if (cur_mx!=3)
2462 			cur_mx=0;
2463 
2464 		if (cur_rf!=3)
2465 			cur_rf=0;
2466 
2467 		if (cur_pom!=3)
2468 			cur_pom=0;
2469 
2470 		if (cur_pnux<3)
2471 			cur_pnux=0;
2472 
2473 		if (cur_sm<3)
2474 			cur_sm=0;
2475 
2476 		cur_bh=0;
2477 		cur_sos=0;
2478 
2479 		if (CurrSpellSymbol != 0)
2480 		{
2481 			if (!ARX_SPELLS_AnalyseSPELL())
2482 			{
2483 				if (entities.player()->anims[ANIM_CAST])
2484 				{
2485 					AcquireLastAnim(entities.player());
2486 					ANIM_Set(&entities.player()->animlayer[1],entities.player()->anims[ANIM_CAST]);
2487 				}
2488 			}
2489 		}
2490 
2491 		ARX_FLARES_broken=1;
2492 
2493 		if (WILLRETURNTOCOMBATMODE)
2494 		{
2495 			player.Interface|=INTER_COMBATMODE;
2496 			player.Interface|=INTER_NO_STRIKE;
2497 
2498 			ARX_EQUIPMENT_LaunchPlayerReadyWeapon();
2499 			player.doingmagic=0;
2500 			WILLRETURNTOCOMBATMODE=0;
2501 
2502 			TRUE_PLAYER_MOUSELOOK_ON = true;
2503 			bRenderInCursorMode=false;
2504 		}
2505 
2506 		if (WILLRETURNTOFREELOOK)
2507 		{
2508 			TRUE_PLAYER_MOUSELOOK_ON = true;
2509 			WILLRETURNTOFREELOOK = 0;
2510 		}
2511 
2512 		ARX_SPELLS_ResetRecognition();
2513 	}
2514 	else if (ARX_FLARES_broken==2)
2515 	{
2516 		ARX_SPELLS_Analyse();
2517 
2518 		if (!SpellMoves.empty())
2519 		 ARX_SPELLS_AnalyseSYMBOL();
2520 
2521 		ARX_FLARES_broken = 1;
2522 	}
2523 }
2524 
2525 /*!
2526  * Plays the sound of Fizzling spell plus "NO MANA" speech
2527  */
ARX_SPELLS_FizzleNoMana(long num)2528 static void ARX_SPELLS_FizzleNoMana(long num) {
2529 	if(num < 0) {
2530 		return;
2531 	}
2532 	if(spells[num].caster >= 0) {
2533 		spells[num].tolive = 0;
2534 		ARX_SPELLS_Fizzle(num);
2535 	}
2536 }
2537 
CanPayMana(long num,float cost,bool _bSound=true)2538 long CanPayMana(long num, float cost, bool _bSound = true) {
2539 
2540 	if (num<0) return 0;
2541 
2542 	if (spells[num].flags & SPELLCAST_FLAG_NOMANA) return 1;
2543 
2544 	if (spells[num].caster==0)
2545 	{
2546 		if (player.mana<cost)
2547 		{
2548 			ARX_SPELLS_FizzleNoMana(num);
2549 
2550 			if(_bSound) {
2551 				ARX_SPEECH_Add(getLocalised("player_cantcast"));
2552 				ARX_SPEECH_AddSpeech(entities.player(), "player_cantcast", ANIM_TALK_NEUTRAL);
2553 			}
2554 
2555 			return 0;
2556 		}
2557 
2558 		player.mana -= cost;
2559 		return 1;
2560 	}
2561 	else if(ValidIONum(spells[num].caster)) {
2562 		if(entities[spells[num].caster]->ioflags & IO_NPC) {
2563 			if(entities[spells[num].caster]->_npcdata->mana < cost) {
2564 				ARX_SPELLS_FizzleNoMana(num);
2565 				return 0;
2566 			}
2567 			entities[spells[num].caster]->_npcdata->mana -= cost;
2568 			return 1;
2569 		}
2570 	}
2571 
2572 	return 0;
2573 }
2574 
2575 //-----------------------------------------------------------------------------
2576 // Resets Spell Recognition
ARX_SPELLS_ResetRecognition()2577 void ARX_SPELLS_ResetRecognition() {
2578 
2579 	for(size_t i = 0; i < MAX_SPELL_SYMBOLS; i++) {
2580 		SpellSymbol[i] = RUNE_NONE;
2581 	}
2582 
2583 	for(size_t i = 0; i < 6; i++) {
2584 		player.SpellToMemorize.iSpellSymbols[i] = RUNE_NONE;
2585 	}
2586 
2587 	CurrSpellSymbol = 0;
2588 }
2589 
2590 // Adds a 2D point to currently drawn spell symbol
ARX_SPELLS_AddPoint(const Vec2s & pos)2591 void ARX_SPELLS_AddPoint(const Vec2s & pos) {
2592 	plist[CurrPoint] = pos;
2593 	CurrPoint++;
2594 	if(CurrPoint >= MAX_POINTS) {
2595 		CurrPoint = MAX_POINTS - 1;
2596 	}
2597 }
2598 
TemporaryGetSpellTarget(const Vec3f * from)2599 long TemporaryGetSpellTarget(const Vec3f * from) {
2600 
2601 	float mindist = std::numeric_limits<float>::max();
2602 	long found = 0;
2603 	for(size_t i = 1; i < entities.size(); i++) {
2604 		if(entities[i] && entities[i]->ioflags & IO_NPC) {
2605 			float dist = distSqr(*from, entities[i]->pos);
2606 			if(dist < mindist) {
2607 				found = i;
2608 				mindist = dist;
2609 			}
2610 		}
2611 	}
2612 
2613 	return found;
2614 }
2615 
ARX_SPEELS_GetMaxRect(const char * _pcName)2616 static void ARX_SPEELS_GetMaxRect(const char *_pcName)
2617 {
2618 	char tcTxt[32];
2619 	int iMinX,iMinY,iMaxX,iMaxY;
2620 	long iSizeX,iSizeY;
2621 
2622 	ARX_SPELLS_RequestSymbolDraw3(_pcName,tcTxt);
2623 	ReCenterSequence(tcTxt,iMinX,iMinY,iMaxX,iMaxY);
2624 	iSizeX=iMaxX-iMinX;
2625 	iSizeY=iMaxY-iMinY;
2626 	lMaxSymbolDrawSizeX=std::max(iSizeX,lMaxSymbolDrawSizeX);
2627 	lMaxSymbolDrawSizeY=std::max(iSizeY,lMaxSymbolDrawSizeY);
2628 }
2629 //-----------------------------------------------------------------------------
2630 // Initializes Spell engine (Called once at DANAE startup)
ARX_SPELLS_Init_Rects()2631 void ARX_SPELLS_Init_Rects() {
2632 	lMaxSymbolDrawSizeX = std::numeric_limits<long>::min();
2633 	lMaxSymbolDrawSizeY = std::numeric_limits<long>::min();
2634 
2635 	ARX_SPEELS_GetMaxRect("aam");
2636 	ARX_SPEELS_GetMaxRect("cetrius");
2637 	ARX_SPEELS_GetMaxRect("comunicatum");
2638 	ARX_SPEELS_GetMaxRect("cosum");
2639 	ARX_SPEELS_GetMaxRect("folgora");
2640 	ARX_SPEELS_GetMaxRect("fridd");
2641 	ARX_SPEELS_GetMaxRect("kaom");
2642 	ARX_SPEELS_GetMaxRect("mega");
2643 	ARX_SPEELS_GetMaxRect("morte");
2644 	ARX_SPEELS_GetMaxRect("movis");
2645 	ARX_SPEELS_GetMaxRect("nhi");
2646 	ARX_SPEELS_GetMaxRect("rhaa");
2647 	ARX_SPEELS_GetMaxRect("spacium");
2648 	ARX_SPEELS_GetMaxRect("stregum");
2649 	ARX_SPEELS_GetMaxRect("taar");
2650 	ARX_SPEELS_GetMaxRect("tempus");
2651 	ARX_SPEELS_GetMaxRect("tera");
2652 	ARX_SPEELS_GetMaxRect("vista");
2653 	ARX_SPEELS_GetMaxRect("vitae");
2654 	ARX_SPEELS_GetMaxRect("yok");
2655 	ARX_SPEELS_GetMaxRect("akbaa");
2656 }
2657 
2658 //-----------------------------------------------------------------------------
ARX_SPELLS_Init()2659 void ARX_SPELLS_Init() {
2660 
2661 	for(size_t i = 0; i < MAX_SPELLS; i++) {
2662 		spells[i].tolive = 0;
2663 		spells[i].exist = false;
2664 		spells[i].pSpellFx = NULL;
2665 	}
2666 
2667 	for(size_t i = 0; i < ARRAY_SIZE(allSpells); i++) {
2668 		addSpell(allSpells[i].symbols, allSpells[i].spell, allSpells[i].name);
2669 	}
2670 
2671 }
2672 
2673 // Clears All Spells.
ARX_SPELLS_ClearAll()2674 void ARX_SPELLS_ClearAll() {
2675 
2676 	for(size_t i = 0; i < MAX_SPELLS; i++) {
2677 		if(spells[i].exist) {
2678 			spells[i].tolive = 0;
2679 			spells[i].exist = false;
2680 			delete spells[i].pSpellFx, spells[i].pSpellFx = NULL;
2681 		}
2682 	}
2683 
2684 	BOOST_FOREACH(Entity * e, entities) {
2685 		if(e) {
2686 			ARX_SPELLS_RemoveAllSpellsOn(e);
2687 		}
2688 	}
2689 }
2690 
2691 // Obtains a Free Spell slot
ARX_SPELLS_GetFree()2692 static long ARX_SPELLS_GetFree() {
2693 
2694 	for(size_t i = 0; i < MAX_SPELLS; i++) {
2695 		if(!spells[i].exist) {
2696 			spells[i].longinfo = spells[i].longinfo2 = -1;
2697 			spells[i].misc = NULL;
2698 			return i;
2699 		}
2700 	}
2701 
2702 	return -1;
2703 }
2704 
ARX_SPELLS_GetInstance(Spell typ)2705 long ARX_SPELLS_GetInstance(Spell typ) {
2706 
2707 	for(size_t i = 0; i < MAX_SPELLS; i++) {
2708 		if(spells[i].exist && spells[i].type == typ) {
2709 			return i;
2710 		}
2711 	}
2712 
2713 	return -1;
2714 }
2715 
2716 // Checks for an existing instance of this spelltype
ARX_SPELLS_ExistAnyInstance(Spell typ)2717 bool ARX_SPELLS_ExistAnyInstance(Spell typ) {
2718 	return (ARX_SPELLS_GetInstance(typ) != -1);
2719 }
2720 
ARX_SPELLS_GetInstanceForThisCaster(Spell typ,long caster)2721 long ARX_SPELLS_GetInstanceForThisCaster(Spell typ, long caster) {
2722 
2723 	for(size_t i = 0; i < MAX_SPELLS; i++) {
2724 		if(spells[i].exist && spells[i].type == typ && spells[i].caster == caster) {
2725 			return i;
2726 		}
2727 	}
2728 
2729 	return -1;
2730 }
2731 
ARX_SPELLS_ExistAnyInstanceForThisCaster(Spell typ,long caster)2732 static bool ARX_SPELLS_ExistAnyInstanceForThisCaster(Spell typ, long caster) {
2733 	return (ARX_SPELLS_GetInstanceForThisCaster(typ, caster) != -1);
2734 }
2735 
2736 // Plays the sound of aborted spell
ARX_SPELLS_AbortSpellSound()2737 void ARX_SPELLS_AbortSpellSound() {
2738 	ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE);
2739 }
2740 
ARX_SPELLS_FizzleAllSpellsFromCaster(long num_caster)2741 void ARX_SPELLS_FizzleAllSpellsFromCaster(long num_caster) {
2742 
2743 	for(size_t i = 0; i < MAX_SPELLS; i++) {
2744 		if(spells[i].exist && spells[i].caster == num_caster) {
2745 			spells[i].tolive = 0;
2746 		}
2747 	}
2748 }
2749 
2750 PRECAST_STRUCT Precast[MAX_PRECAST];
2751 
ARX_SPELLS_Precast_Reset()2752 void ARX_SPELLS_Precast_Reset() {
2753 	for(size_t i = 0; i < MAX_PRECAST; i++) {
2754 		Precast[i].typ = SPELL_NONE;
2755 	}
2756 }
2757 
ARX_SPELLS_Precast_Add(Spell typ,long _level,SpellcastFlags flags,long duration)2758 void ARX_SPELLS_Precast_Add(Spell typ, long _level, SpellcastFlags flags, long duration) {
2759 
2760 	long found = -1;
2761 
2762 	for(size_t i = 0; i < MAX_PRECAST; i++) {
2763 		if(Precast[i].typ == SPELL_NONE) {
2764 			found = i;
2765 			break;
2766 		}
2767 	}
2768 
2769 	if(found == -1) {
2770 		for(size_t i = 1; i < MAX_PRECAST; i++) {
2771 			memcpy(&Precast[i - 1], &Precast[i], sizeof(PRECAST_STRUCT));
2772 		}
2773 
2774 		found = MAX_PRECAST - 1;
2775 	}
2776 
2777 	Precast[found].typ = typ;
2778 	Precast[found].level = _level;
2779 	Precast[found].launch_time = 0;
2780 	Precast[found].flags = flags;
2781 	Precast[found].duration = duration;
2782 }
2783 
2784 unsigned long LAST_PRECAST_TIME=0;
PrecastCheckCanPayMana(long num,float cost,bool _bSound=true)2785 long PrecastCheckCanPayMana(long num, float cost, bool _bSound = true)
2786 {
2787 	if (num<0) return 0;
2788 
2789 	if (Precast[num].flags & SPELLCAST_FLAG_NOMANA) return 1;
2790 
2791 		if (player.mana>=cost)
2792 		{
2793 			return 1;
2794 		}
2795 
2796 	ARX_SPELLS_FizzleNoMana(num);
2797 
2798 	if(_bSound) {
2799 		ARX_SPEECH_Add(getLocalised("player_cantcast"));
2800 		ARX_SPEECH_AddSpeech(entities.player(), "player_cantcast", ANIM_TALK_NEUTRAL);
2801 	}
2802 
2803 	return 0;
2804 }
2805 
ARX_SPELLS_Precast_Launch(long num)2806 void ARX_SPELLS_Precast_Launch(long num) {
2807 
2808 	if (float(arxtime) >= LAST_PRECAST_TIME+1000)
2809 	{
2810 		Spell iNumSpells=Precast[num].typ;
2811 		float cost=ARX_SPELLS_GetManaCost(iNumSpells,-1);
2812 
2813 		if(		(iNumSpells != SPELL_NONE)
2814 			&&	(!PrecastCheckCanPayMana(num,cost)	)  )
2815 			return;
2816 
2817 		LAST_PRECAST_TIME = (unsigned long)(arxtime);
2818 
2819 		if ((Precast[num].typ != SPELL_NONE) && (Precast[num].launch_time==0))
2820 		{
2821 			Precast[num].launch_time = (unsigned long)(arxtime);
2822 			ARX_SOUND_PlaySFX(SND_SPELL_CREATE_FIELD);
2823 		}
2824 	}
2825 }
ARX_SPELLS_Precast_Check()2826 void ARX_SPELLS_Precast_Check()
2827 {
2828 	for (size_t i = 0; i < MAX_PRECAST; i++)
2829 	{
2830 		if ((Precast[i].typ != SPELL_NONE) && (Precast[i].launch_time>0) &&(float(arxtime) >= Precast[i].launch_time))
2831 		{
2832 			ANIM_USE *ause1 = &entities.player()->animlayer[1];
2833 
2834 			if (player.Interface & INTER_COMBATMODE)
2835 			{
2836 				WILLRETURNTOCOMBATMODE=1;
2837 				ARX_INTERFACE_Combat_Mode(0);
2838 				bGToggleCombatModeWithKey=false;
2839 				ResetAnim(&entities.player()->animlayer[1]);
2840 				entities.player()->animlayer[1].flags&=~EA_LOOP;
2841 			}
2842 
2843 			if ((ause1->cur_anim) && (ause1->cur_anim==entities.player()->anims[ANIM_CAST]))
2844 			{
2845 				if (ause1->ctime>ause1->cur_anim->anims[ause1->altidx_cur]->anim_time-550)
2846 				{
2847 					ARX_SPELLS_Launch(	Precast[i].typ,
2848 										0,
2849 										Precast[i].flags | SPELLCAST_FLAG_LAUNCHPRECAST,
2850 										Precast[i].level,
2851 										-1,
2852 										Precast[i].duration);
2853 					Precast[i].typ = SPELL_NONE;
2854 
2855 					for (size_t li=i; li < MAX_PRECAST - 1; li++)
2856 					{
2857 						if (Precast[li + 1].typ != SPELL_NONE)
2858 						{
2859 							memcpy(&Precast[li], &Precast[li + 1], sizeof(PRECAST_STRUCT));
2860 							Precast[li + 1].typ = SPELL_NONE;
2861 						}
2862 					}
2863 				}
2864 			} else {
2865 				ANIM_USE * ause1 = &entities.player()->animlayer[1];
2866 				AcquireLastAnim(entities.player());
2867 				FinishAnim(entities.player(), ause1->cur_anim);
2868 				ANIM_Set(ause1, entities.player()->anims[ANIM_CAST]);
2869 			}
2870 		}
2871 	}
2872 }
2873 
2874 struct TARGETING_SPELL {
2875 	Spell typ;
2876 	long source;
2877 	SpellcastFlags flags;
2878 	long level;
2879 	long target;
2880 	long duration;
2881 };
2882 
2883 static TARGETING_SPELL t_spell;
2884 
2885 long LOOKING_FOR_SPELL_TARGET=0;
2886 unsigned long LOOKING_FOR_SPELL_TARGET_TIME=0;
ARX_SPELLS_CancelSpellTarget()2887 void ARX_SPELLS_CancelSpellTarget() {
2888 	t_spell.typ = SPELL_NONE;
2889 	LOOKING_FOR_SPELL_TARGET=0;
2890 }
2891 
ARX_SPELLS_LaunchSpellTarget(Entity * io)2892 void ARX_SPELLS_LaunchSpellTarget(Entity * io) {
2893 	if(io) {
2894 		ARX_SPELLS_Launch(t_spell.typ, t_spell.source, t_spell.flags, t_spell.level, io->index(), t_spell.duration);
2895 	}
2896 }
2897 
ARX_SPELLS_GetManaCost(Spell spell,long index)2898 static float ARX_SPELLS_GetManaCost(Spell spell, long index) {
2899 
2900 	// Calculate the player's magic level
2901 	float playerCasterLevel = player.Full_Skill_Casting + player.Full_Attribute_Mind;
2902 	playerCasterLevel = clamp(playerCasterLevel * 0.1f, 1.f, 10.f);
2903 
2904 	float casterLevel = ((index < 0) ? playerCasterLevel : spells[index].caster_level);
2905 
2906 	// TODO this data should not be hardcoded
2907 
2908 	switch(spell)  {
2909 
2910 		default:                          return   0.f;
2911 
2912 		case SPELL_TELEKINESIS:           return   0.001f;
2913 		case SPELL_CURSE:                 return   0.001f;
2914 		case SPELL_ARMOR:                 return   0.01f;
2915 		case SPELL_LOWER_ARMOR:           return   0.01f;
2916 		case SPELL_SPEED:                 return   0.01f;
2917 		case SPELL_BLESS:                 return   0.01f;
2918 		case SPELL_DETECT_TRAP:           return   0.03f;
2919 		case SPELL_MAGIC_SIGHT:           return   0.3f;
2920 		case SPELL_HARM:                  return   0.4f;
2921 		case SPELL_MANA_DRAIN:            return   0.4f;
2922 		case SPELL_IGNIT:                 return   1.f;
2923 		case SPELL_DOUSE:                 return   1.f;
2924 		case SPELL_FIRE_PROTECTION:       return   1.f;
2925 		case SPELL_COLD_PROTECTION:       return   1.f;
2926 		case SPELL_LEVITATE:              return   1.f;
2927 		case SPELL_CREATE_FIELD:          return   1.2f;
2928 		case SPELL_SLOW_DOWN:             return   1.2f;
2929 		case SPELL_ACTIVATE_PORTAL:       return   2.f;
2930 		case SPELL_NEGATE_MAGIC:          return   2.f;
2931 		case SPELL_INVISIBILITY:          return   3.f;
2932 		case SPELL_LIFE_DRAIN:            return   3.f;
2933 		case SPELL_HEAL:                  return   4.f;
2934 		case SPELL_FLYING_EYE:            return   4.f;
2935 		case SPELL_CREATE_FOOD:           return   5.f;
2936 		case SPELL_DISPELL_ILLUSION:      return   7.f;
2937 		case SPELL_DISPELL_FIELD:         return   7.f;
2938 		case SPELL_RUNE_OF_GUARDING:      return   9.f;
2939 		case SPELL_CURE_POISON:           return  10.f;
2940 		case SPELL_TELEPORT:              return  10.f;
2941 		case SPELL_RISE_DEAD:             return  12.f;
2942 		case SPELL_DISARM_TRAP:           return  15.f;
2943 		case SPELL_FIRE_FIELD:            return  15.f;
2944 		case SPELL_ICE_FIELD:             return  15.f;
2945 		case SPELL_REPEL_UNDEAD:          return  18.f;
2946 		case SPELL_ENCHANT_WEAPON:        return  35.f;
2947 		case SPELL_INCINERATE:            return  40.f;
2948 		case SPELL_CONTROL_TARGET:        return  40.f;
2949 		case SPELL_EXPLOSION:             return  45.f;
2950 		case SPELL_FREEZE_TIME:           return  60.f;
2951 		case SPELL_MASS_INCINERATE:       return 160.f;
2952 
2953 		case SPELL_CONFUSE:               return casterLevel * 0.1f;
2954 		case SPELL_MAGIC_MISSILE:         return casterLevel * 1.f;
2955 		case SPELL_ICE_PROJECTILE:        return casterLevel * 1.5f;
2956 		case SPELL_POISON_PROJECTILE:     return casterLevel * 2.f;
2957 		case SPELL_FIREBALL:              return casterLevel * 3.f;
2958 		case SPELL_PARALYSE:              return casterLevel * 3.f;
2959 		case SPELL_MASS_PARALYSE:         return casterLevel * 3.f;
2960 		case SPELL_LIGHTNING_STRIKE:      return casterLevel * 6.f;
2961 		case SPELL_MASS_LIGHTNING_STRIKE: return casterLevel * 8.f;
2962 
2963 		case SPELL_SUMMON_CREATURE:       return (casterLevel < 9) ? 20.f : 80.f;
2964 		case SPELL_FAKE_SUMMON:           return (casterLevel < 9) ? 20.f : 80.f;
2965 
2966 	}
2967 }
2968 
ARX_SPELLS_Launch(Spell typ,long source,SpellcastFlags flagss,long levell,long target,long duration)2969 bool ARX_SPELLS_Launch(Spell typ, long source, SpellcastFlags flagss, long levell, long target, long duration) {
2970 
2971 	SpellcastFlags flags = flagss;
2972 	long level = levell;
2973 
2974 	if ( cur_rf == 3 )
2975 	{
2976 		flags |= SPELLCAST_FLAG_NOCHECKCANCAST | SPELLCAST_FLAG_NOMANA;
2977 	}
2978 
2979 	if ( sp_max )
2980 	{
2981 		level = std::max( level, 15L );
2982 	}
2983 
2984 	if(source == 0 && !(flags & SPELLCAST_FLAG_NOCHECKCANCAST)) {
2985 		for(size_t i = 0; i < MAX_SPELL_SYMBOLS; i++) {
2986 			if(SpellSymbol[i] != RUNE_NONE) {
2987 				if(!( player.rune_flags & (RuneFlag)(1 << SpellSymbol[i]))) {
2988 					ARX_SOUND_PlaySpeech("player_cantcast");
2989 					CurrSpellSymbol = 0;
2990 					ARX_SPELLS_ResetRecognition();
2991 					return false;
2992 				}
2993 			}
2994 		}
2995 	}
2996 
2997 	float Player_Magic_Level = 0;
2998 
2999 	if ( !source )
3000 	{
3001 		ARX_SPELLS_ResetRecognition();
3002 
3003 		if ( player.SpellToMemorize.bSpell )
3004 		{
3005 			CurrSpellSymbol					= 0;
3006 			player.SpellToMemorize.bSpell	= false;
3007 		}
3008 
3009 		ARX_PLAYER_ComputePlayerFullStats();
3010 
3011 		if ( level == -1 )
3012 		{
3013 			Player_Magic_Level = (float) player.Full_Skill_Casting + player.Full_Attribute_Mind;
3014 			Player_Magic_Level = clamp(Player_Magic_Level * 0.1f, 1.0f, 10.0f);
3015 		}
3016 		else
3017 		{
3018 			Player_Magic_Level = static_cast<float>(level);
3019 		}
3020 	}
3021 
3022 	arx_assert( !( source && (flags & SPELLCAST_FLAG_PRECAST) ) );
3023 
3024 
3025 	if ( flags & SPELLCAST_FLAG_PRECAST )
3026 	{
3027 		int l = level;
3028 
3029 		if(l <= 0) {
3030 			l = checked_range_cast<int>(Player_Magic_Level);
3031 		}
3032 
3033 		SpellcastFlags flgs=flags;
3034 		flgs&=~SPELLCAST_FLAG_PRECAST;
3035 		ARX_SPELLS_Precast_Add( typ, l, flgs, duration);
3036 		return true;
3037 	}
3038 
3039 	static TextureContainer * tc4 = TextureContainer::Load("graph/particles/smoke");
3040 
3041 
3042 	if ((target < 0)
3043 		&&	( source == 0 )	)
3044 	switch ( typ )
3045 	{
3046 		case SPELL_LOWER_ARMOR:
3047 		case SPELL_CURSE:
3048 		case SPELL_PARALYSE:
3049 		case SPELL_INCINERATE:
3050 		case SPELL_SLOW_DOWN:
3051 		case SPELL_CONFUSE:
3052 		{
3053 				LOOKING_FOR_SPELL_TARGET_TIME	= (unsigned long)(arxtime);
3054 			LOOKING_FOR_SPELL_TARGET		= 1;
3055 			t_spell.typ						= typ;
3056 			t_spell.source					= source;
3057 			t_spell.flags					= flags;
3058 			t_spell.level					= level;
3059 			t_spell.target					= target;
3060 			t_spell.duration				= duration;
3061 			return false;
3062 		}
3063 		case SPELL_ENCHANT_WEAPON:
3064 		{
3065 				LOOKING_FOR_SPELL_TARGET_TIME	= (unsigned long)(arxtime);
3066 			LOOKING_FOR_SPELL_TARGET		= 2;
3067 			t_spell.typ						= typ;
3068 			t_spell.source					= source;
3069 			t_spell.flags					= flags;
3070 			t_spell.level					= level;
3071 			t_spell.target					= target;
3072 			t_spell.duration				= duration;
3073 			return false;
3074 		}
3075 		break;
3076 		case SPELL_CONTROL_TARGET:
3077 		{
3078 			long		tcount = 0;
3079 
3080 			if ( !ValidIONum( source ) )
3081 				return false;
3082 
3083 			Vec3f cpos = entities[source]->pos;
3084 
3085 			for(size_t ii = 1 ; ii < entities.size(); ii++) {
3086 				Entity * ioo = entities[ii];
3087 				if(ioo && (ioo->ioflags & IO_NPC) && ioo->_npcdata->life > 0.f
3088 				   && ioo->show == SHOW_FLAG_IN_SCENE
3089 				   && ioo->groups.find("demon") != ioo->groups.end()
3090 				   && closerThan(ioo->pos, cpos, 900.f)) {
3091 					tcount++;
3092 				}
3093 			}
3094 
3095 			if ( tcount == 0 )
3096 			{
3097 				ARX_SOUND_PlaySFX( SND_MAGIC_FIZZLE, &cpos );
3098 				return false;
3099 			}
3100 
3101 			ARX_SOUND_PlaySpeech("player_follower_attack");
3102 				LOOKING_FOR_SPELL_TARGET_TIME	= (unsigned long)(arxtime);
3103 			LOOKING_FOR_SPELL_TARGET		= 1;
3104 			t_spell.typ						= typ;
3105 			t_spell.source					= source;
3106 			t_spell.flags					= flags;
3107 			t_spell.level					= level;
3108 			t_spell.target					= target;
3109 			t_spell.duration				= duration;
3110 			return false;
3111 		}
3112 		break;
3113 		default: break;
3114 	}
3115 
3116 	if ( source == 0 )
3117 	{
3118 		ARX_SPELLS_CancelSpellTarget();
3119 	}
3120 
3121 
3122 	// Try to create a new spell instance
3123 	long i = ARX_SPELLS_GetFree();
3124 
3125 	if ( i < 0 )
3126 	{
3127 		return false;
3128 	}
3129 
3130 	if(ValidIONum(source) && spellicons[typ].bAudibleAtStart) {
3131 		ARX_NPC_SpawnAudibleSound(&entities[source]->pos, entities[source]);
3132 	}
3133 
3134 	spells[i].caster = source;	// Caster...
3135 	spells[i].target = target;	// No target if <0
3136 
3137 	if ( target < 0 )
3138 		spells[i].target = TemporaryGetSpellTarget( &entities[spells[i].caster]->pos );
3139 
3140 	// Create hand position if a hand is defined
3141 	if ( spells[i].caster == 0 )
3142 	{
3143 		spells[i].hand_group = entities[spells[i].caster]->obj->fastaccess.primary_attach;
3144 	}
3145 	else
3146 	{
3147 		spells[i].hand_group = entities[spells[i].caster]->obj->fastaccess.left_attach;
3148 	}
3149 
3150 	if(spells[i].hand_group != -1) {
3151 		spells[i].hand_pos = entities[spells[i].caster]->obj->vertexlist3[spells[i].hand_group].v;
3152 	}
3153 
3154 	if(source == 0) {
3155 		// Player source
3156 		spells[i].caster_level = Player_Magic_Level; // Level of caster
3157 		spells[i].caster_pos = player.pos;
3158 	} else {
3159 		// IO source
3160 		spells[i].caster_level = (float)clamp(level, 1l, 10l);
3161 		spells[i].caster_pos = entities[source]->pos;
3162 	}
3163 
3164 	if(flags & SPELLCAST_FLAG_LAUNCHPRECAST) {
3165 		spells[i].caster_level = static_cast<float>(level);
3166 	}
3167 
3168 	if(cur_rf == 3) {
3169 		spells[i].caster_level += 2;
3170 	}
3171 
3172 	// Checks target TODO if ( target < 0 ) is already handled above!
3173 	if (target<0) // no target... targeted by sight
3174 	{
3175 		if (source==0) // no target... player spell targeted by sight
3176 		{
3177 			spells[i].target_pos.x=player.pos.x-EEsin(radians(player.angle.b))*60.f;
3178 			spells[i].target_pos.y=player.pos.y+EEsin(radians(player.angle.a))*60.f;
3179 			spells[i].target_pos.z=player.pos.z+EEcos(radians(player.angle.b))*60.f;
3180 		}
3181 		else
3182 		{
3183 			// TODO entities[target] with target < 0 ??? - uh oh!
3184 			spells[i].target_pos.x=entities[target]->pos.x-EEsin(radians(entities[target]->angle.b))*60.f;
3185 			spells[i].target_pos.y=entities[target]->pos.y-120.f;
3186 			spells[i].target_pos.z=entities[target]->pos.z+EEcos(radians(entities[target]->angle.b))*60.f;
3187 		}
3188 	} else if (target==0) {
3189 		// player target
3190 		spells[i].target_pos = player.pos;
3191 	} else {
3192 		// IO target
3193 		spells[i].target_pos = entities[target]->pos;
3194 	}
3195 
3196 	spells[i].flags=flags;
3197 	spells[i].pSpellFx=NULL;
3198 	spells[i].type = typ;
3199 	spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
3200 
3201 
3202 	// Check spell-specific preconditions
3203 	switch(typ) {
3204 
3205 		case SPELL_MAGIC_SIGHT: {
3206 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3207 				return false;
3208 			}
3209 			break;
3210 		}
3211 
3212 		case SPELL_HEAL: {
3213 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3214 				return false;
3215 			}
3216 			break;
3217 		}
3218 
3219 		case SPELL_BLESS: {
3220 			if(ARX_SPELLS_ExistAnyInstance(typ)) {
3221 				return false;
3222 			}
3223 			break;
3224 		}
3225 
3226 		case SPELL_TELEKINESIS: {
3227 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3228 				return false;
3229 			}
3230 			break;
3231 		}
3232 
3233 		case SPELL_FLYING_EYE: {
3234 			if(eyeball.exist) {
3235 				return false;
3236 			}
3237 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ,spells[i].caster)) {
3238 				return false;
3239 			}
3240 			break;
3241 		}
3242 
3243 		case SPELL_INVISIBILITY: {
3244 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3245 				return false;
3246 			}
3247 			break;
3248 		}
3249 
3250 		case SPELL_MANA_DRAIN: {
3251 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3252 				return false;
3253 			}
3254 			break;
3255 		}
3256 
3257 		case SPELL_LIFE_DRAIN: {
3258 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3259 				return false;
3260 			}
3261 			break;
3262 		}
3263 
3264 		case SPELL_CONTROL_TARGET: {
3265 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3266 				return false;
3267 			}
3268 			break;
3269 		}
3270 
3271 		case SPELL_FREEZE_TIME: {
3272 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3273 				return false;
3274 			}
3275 			break;
3276 		}
3277 
3278 		case SPELL_TELEPORT: {
3279 			if(ARX_SPELLS_ExistAnyInstanceForThisCaster(typ, spells[i].caster)) {
3280 				return false;
3281 			}
3282 			break;
3283 		}
3284 
3285 		default: break; // no preconditions to check
3286 	}
3287 
3288 	if(!CanPayMana(i, ARX_SPELLS_GetManaCost(typ, i))) {
3289 		return false;
3290 	}
3291 
3292 	if(!GLOBAL_MAGIC_MODE) {
3293 		return No_MagicAllowed();
3294 	}
3295 
3296 	bool notifyAll = true;
3297 
3298 	switch(typ) {
3299 
3300 		case SPELL_NONE: return true;
3301 
3302 		// level 1 spells
3303 
3304 		case SPELL_MAGIC_SIGHT: {
3305 
3306 			spells[i].exist = true;
3307 			spells[i].fManaCostPerSecond = 0.36f;
3308 			spells[i].bDuration = true;
3309 			spells[i].tolive = (duration > -1) ? duration : 6000000l;
3310 
3311 			ARX_SOUND_PlaySFX(SND_SPELL_VISION_START, &spells[i].caster_pos);
3312 
3313 			if(spells[i].caster == 0) {
3314 				Project.improve = 1;
3315 				spells[i].snd_loop = SND_SPELL_VISION_LOOP;
3316 				ARX_SOUND_PlaySFX(spells[i].snd_loop, &spells[i].caster_pos, 1.f,
3317 				                  ARX_SOUND_PLAY_LOOPED);
3318 			}
3319 
3320 			break;
3321 		}
3322 
3323 		case SPELL_MAGIC_MISSILE: {
3324 
3325 			spells[i].exist = true;
3326 			spells[i].tolive = 20000; // TODO probably never read
3327 
3328 			long number;
3329 			if(sp_max || cur_rf == 3) {
3330 				number = long(spells[i].caster_level);
3331 			} else {
3332 				number = clamp(long(spells[i].caster_level + 1) / 2, 1l, 5l);
3333 			}
3334 
3335 			CMultiMagicMissile * effect = new CMultiMagicMissile(number);
3336 			effect->spellinstance = i;
3337 			effect->SetDuration(6000ul);
3338 			effect->Create();
3339 			spells[i].pSpellFx = effect;
3340 			spells[i].tolive = effect->GetDuration();
3341 
3342 			break;
3343 		}
3344 
3345 		case SPELL_IGNIT: {
3346 
3347 			spells[i].exist = true;
3348 			spells[i].tolive = 20000; // TODO probably never read
3349 
3350 			CIgnit * effect = new CIgnit();
3351 			effect->spellinstance = i;
3352 
3353 			Vec3f target;
3354 			if(spells[i].hand_group != -1) {
3355 				target = spells[i].hand_pos;
3356 			} else {
3357 				target = spells[i].caster_pos - Vec3f(0.f, 50.f, 0.f);
3358 			}
3359 
3360 			long id = GetFreeDynLight();
3361 			if(id != -1) {
3362 				DynLight[id].exist     = 1;
3363 				DynLight[id].intensity = 1.8f;
3364 				DynLight[id].fallend   = 450.f;
3365 				DynLight[id].fallstart = 380.f;
3366 				DynLight[id].rgb       = Color3f(1.f, 0.75f, 0.5f);
3367 				DynLight[id].pos       = target;
3368 				DynLight[id].duration  = 300;
3369 			}
3370 
3371 			float fPerimeter = 400.f + spells[i].caster_level * 30.f;
3372 
3373 			effect->Create(&target, fPerimeter, 500);
3374 			CheckForIgnition(&target, fPerimeter, 1, 1);
3375 
3376 			for(size_t ii = 0; ii < MAX_LIGHTS; ii++) {
3377 
3378 				if(!GLight[ii] || !(GLight[ii]->extras & EXTRAS_EXTINGUISHABLE)) {
3379 					continue;
3380 				}
3381 
3382 				if(spells[i].caster == 0 && (GLight[ii]->extras & EXTRAS_NO_IGNIT)) {
3383 					continue;
3384 				}
3385 
3386 				if(!(GLight[ii]->extras & EXTRAS_SEMIDYNAMIC)
3387 				  && !(GLight[ii]->extras & EXTRAS_SPAWNFIRE)
3388 				  && !(GLight[ii]->extras & EXTRAS_SPAWNSMOKE)) {
3389 					continue;
3390 				}
3391 
3392 				if(GLight[ii]->status) {
3393 					continue;
3394 				}
3395 
3396 				if(!fartherThan(target, GLight[ii]->pos, effect->GetPerimetre())) {
3397 					effect->AddLight(ii);
3398 				}
3399 			}
3400 
3401 			for(size_t n = 0; n < MAX_SPELLS; n++) {
3402 				if(!spells[n].exist) {
3403 					continue;
3404 				}
3405 				if(spells[n].type == SPELL_FIREBALL) {
3406 					CSpellFx * pCSpellFX = spells[n].pSpellFx;
3407 					if(pCSpellFX) {
3408 						CFireBall * pCF = (CFireBall *)pCSpellFX;
3409 						float radius = std::max(spells[i].caster_level * 2.f, 12.f);
3410 						if(closerThan(target, pCF->eCurPos,
3411 						              effect->GetPerimetre() + radius)) {
3412 							spells[n].caster_level += 1;
3413 						}
3414 					}
3415 				}
3416 			}
3417 
3418 			spells[i].pSpellFx = effect;
3419 			spells[i].tolive = effect->GetDuration();
3420 
3421 			break;
3422 		}
3423 
3424 		case SPELL_DOUSE: {
3425 
3426 			spells[i].exist = true;
3427 			spells[i].tolive = 20000;
3428 
3429 			CDoze * effect = new CDoze();
3430 			effect->spellinstance = i;
3431 
3432 			Vec3f target;
3433 			if(spells[i].hand_group >= 0) {
3434 				target = spells[i].hand_pos;
3435 			} else {
3436 				target = spells[i].caster_pos;
3437 				target.y -= 50.f;
3438 			}
3439 
3440 			float fPerimeter = 400.f + spells[i].caster_level * 30.f;
3441 			effect->CreateDoze(&target, fPerimeter, 500);
3442 			CheckForIgnition(&target, fPerimeter, 0, 1);
3443 
3444 			for(size_t ii = 0; ii < MAX_LIGHTS; ii++) {
3445 
3446 				if(!GLight[ii] || !(GLight[ii]->extras & EXTRAS_EXTINGUISHABLE)) {
3447 					continue;
3448 				}
3449 
3450 				if(!(GLight[ii]->extras & EXTRAS_SEMIDYNAMIC)
3451 				  && !(GLight[ii]->extras & EXTRAS_SPAWNFIRE)
3452 				  && !(GLight[ii]->extras & EXTRAS_SPAWNSMOKE)) {
3453 					continue;
3454 				}
3455 
3456 				if(!GLight[ii]->status) {
3457 					continue;
3458 				}
3459 
3460 				if(!fartherThan(target, GLight[ii]->pos, effect->GetPerimetre())) {
3461 					effect->AddLightDoze(ii);
3462 				}
3463 			}
3464 
3465 			if(CURRENT_TORCH
3466 			   && closerThan(target, player.pos, effect->GetPerimetre())) {
3467 				ARX_PLAYER_ClickedOnTorch(CURRENT_TORCH);
3468 			}
3469 
3470 			for(size_t n = 0; n < MAX_SPELLS; n++) {
3471 
3472 				if(!spells[n].exist) {
3473 					continue;
3474 				}
3475 
3476 				switch(spells[n].type) {
3477 
3478 					case SPELL_FIREBALL: {
3479 						CSpellFx * pCSpellFX = spells[n].pSpellFx;
3480 						if(pCSpellFX) {
3481 							CFireBall * pCF = (CFireBall *)pCSpellFX;
3482 							float radius = std::max(spells[i].caster_level * 2.f, 12.f);
3483 							if(closerThan(target, pCF->eCurPos,
3484 							              effect->GetPerimetre() + radius)) {
3485 								spells[n].caster_level -= spells[i].caster_level;
3486 								if(spells[n].caster_level < 1) {
3487 									spells[n].tolive = 0;
3488 								}
3489 							}
3490 						}
3491 						break;
3492 					}
3493 
3494 					case SPELL_FIRE_FIELD: {
3495 						Vec3f pos;
3496 						if(GetSpellPosition(&pos, n)) {
3497 							if(closerThan(target, pos, effect->GetPerimetre() + 200)) {
3498 								spells[n].caster_level -= spells[i].caster_level;
3499 								if(spells[n].caster_level < 1) {
3500 									spells[n].tolive=0;
3501 								}
3502 							}
3503 						}
3504 						break;
3505 					}
3506 
3507 					default: break;
3508 				}
3509 			}
3510 
3511 			spells[i].pSpellFx = effect;
3512 			spells[i].tolive = effect->GetDuration();
3513 
3514 			break;
3515 		}
3516 
3517 		case SPELL_ACTIVATE_PORTAL: {
3518 
3519 			ARX_SOUND_PlayInterface(SND_SPELL_ACTIVATE_PORTAL);
3520 			spells[i].exist = true;
3521 			spells[i].tolive = 20;
3522 
3523 			break;
3524 		}
3525 
3526 		// level 2 spells
3527 
3528 		case SPELL_HEAL: {
3529 
3530 			if(!(spells[i].flags & SPELLCAST_FLAG_NOSOUND)) {
3531 				ARX_SOUND_PlaySFX(SND_SPELL_HEALING, &spells[i].caster_pos);
3532 			}
3533 
3534 			spells[i].exist = true;
3535 			spells[i].bDuration = true;
3536 			spells[i].fManaCostPerSecond = 0.4f * spells[i].caster_level;
3537 			spells[i].tolive = (duration > -1) ? duration : 3500;
3538 
3539 			CHeal * effect = new CHeal();
3540 			effect->spellinstance = i;
3541 			effect->Create();
3542 			effect->SetDuration(spells[i].tolive);
3543 
3544 			spells[i].pSpellFx = effect;
3545 			spells[i].tolive = effect->GetDuration();
3546 
3547 			break;
3548 		}
3549 
3550 		case SPELL_DETECT_TRAP: {
3551 
3552 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].caster);
3553 			if(iCancel > -1) {
3554 				spells[iCancel].tolive = 0;
3555 			}
3556 
3557 			if(spells[i].caster == 0) {
3558 				spells[i].target = spells[i].caster;
3559 				if(!(spells[i].flags & SPELLCAST_FLAG_NOSOUND)) {
3560 					ARX_SOUND_PlayInterface(SND_SPELL_DETECT_TRAP);
3561 				}
3562 			}
3563 
3564 			spells[i].snd_loop = SND_SPELL_DETECT_TRAP_LOOP;
3565 			if(spells[i].caster == 0 && !(spells[i].flags & SPELLCAST_FLAG_NOSOUND)) {
3566 				ARX_SOUND_PlaySFX(spells[i].snd_loop, &spells[i].caster_pos, 1.f,
3567 				                  ARX_SOUND_PLAY_LOOPED);
3568 			}
3569 
3570 			spells[i].exist = true;
3571 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
3572 			spells[i].tolive = 60000;
3573 			spells[i].fManaCostPerSecond = 0.4f;
3574 			spells[i].bDuration = true;
3575 
3576 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
3577 
3578 			break;
3579 		}
3580 
3581 		case SPELL_ARMOR: {
3582 
3583 			long idx = ARX_SPELLS_GetSpellOn(entities[spells[i].target], typ);
3584 			if(idx >= 0) {
3585 				spells[idx].tolive = 0;
3586 			}
3587 
3588 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_LOWER_ARMOR,
3589 			                                                   spells[i].caster);
3590 			if(iCancel > -1) {
3591 				spells[iCancel].tolive = 0;
3592 			}
3593 
3594 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_FIRE_PROTECTION,
3595 			                                              spells[i].caster);
3596 			if(iCancel > -1) {
3597 				spells[iCancel].tolive = 0;
3598 			}
3599 
3600 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_COLD_PROTECTION,
3601 			                                              spells[i].caster);
3602 			if(iCancel > -1) {
3603 				spells[iCancel].tolive = 0;
3604 			}
3605 
3606 			if(!(spells[i].flags & SPELLCAST_FLAG_NOSOUND)) {
3607 				ARX_SOUND_PlaySFX(SND_SPELL_ARMOR_START, &spells[i].target_pos);
3608 			}
3609 
3610 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_ARMOR_LOOP,
3611 			                                       &spells[i].caster_pos, 1.f,
3612 			                                       ARX_SOUND_PLAY_LOOPED);
3613 
3614 			spells[i].exist = true;
3615 			if(duration > -1) {
3616 				spells[i].tolive = duration;
3617 			} else {
3618 				spells[i].tolive = (spells[i].caster == 0) ? 20000000 : 20000;
3619 			}
3620 
3621 			if(spells[i].caster == 0) {
3622 				spells[i].target = spells[i].caster;
3623 			}
3624 
3625 			spells[i].bDuration = true;
3626 			spells[i].fManaCostPerSecond = 0.2f * spells[i].caster_level;
3627 
3628 			CArmor * effect = new CArmor();
3629 			effect->spellinstance = i;
3630 			effect->Create(spells[i].tolive);
3631 
3632 			spells[i].pSpellFx = effect;
3633 			spells[i].tolive = effect->GetDuration();
3634 
3635 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
3636 
3637 			break;
3638 		}
3639 
3640 		case SPELL_LOWER_ARMOR: {
3641 
3642 			long idx = ARX_SPELLS_GetSpellOn(entities[spells[i].target], typ);
3643 			if(idx >= 0) {
3644 				spells[idx].tolive = 0;
3645 			}
3646 
3647 			if(spells[i].target < 0) {
3648 				return false;
3649 			}
3650 
3651 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_ARMOR,
3652 			                                                   spells[i].caster);
3653 			if(iCancel > -1) {
3654 				spells[iCancel].tolive = 0;
3655 			}
3656 
3657 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_FIRE_PROTECTION,
3658 			                                              spells[i].caster);
3659 			if(iCancel > -1) {
3660 				spells[iCancel].tolive = 0;
3661 			}
3662 
3663 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_COLD_PROTECTION,
3664 			                                              spells[i].caster);
3665 			if(iCancel > -1) {
3666 				spells[iCancel].tolive = 0;
3667 			}
3668 
3669 			if(!(spells[i].flags & SPELLCAST_FLAG_NOSOUND)) {
3670 				ARX_SOUND_PlaySFX(SND_SPELL_LOWER_ARMOR, &spells[i].caster_pos);
3671 			}
3672 
3673 			spells[i].exist = true;
3674 			if(duration > -1) {
3675 				spells[i].tolive = duration;
3676 			} else {
3677 				spells[i].tolive = (spells[i].caster == 0) ? 20000000 : 20000;
3678 			}
3679 
3680 			spells[i].bDuration = true;
3681 			spells[i].fManaCostPerSecond = 0.2f * spells[i].caster_level;
3682 
3683 			CLowerArmor * effect = new CLowerArmor();
3684 			effect->spellinstance = i;
3685 			effect->Create(spells[i].tolive);
3686 
3687 			spells[i].pSpellFx = effect;
3688 			spells[i].tolive = effect->GetDuration();
3689 
3690 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
3691 
3692 			break;
3693 		}
3694 
3695 		case SPELL_HARM: {
3696 
3697 			if(!(spells[i].flags & SPELLCAST_FLAG_NOSOUND)) {
3698 				ARX_SOUND_PlaySFX(SND_SPELL_HARM, &spells[i].caster_pos);
3699 			}
3700 
3701 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_SHIELD,
3702 			                                       &spells[i].caster_pos, 1.f,
3703 			                                       ARX_SOUND_PLAY_LOOPED);
3704 
3705 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_LIFE_DRAIN,
3706 			                                                   spells[i].caster);
3707 			if(iCancel > -1) {
3708 				spells[iCancel].tolive = 0;
3709 			}
3710 
3711 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_MANA_DRAIN,
3712 			                                              spells[i].caster);
3713 			if(iCancel > -1) {
3714 				spells[iCancel].tolive = 0;
3715 			}
3716 
3717 			spells[i].exist = true;
3718 			spells[i].tolive = (duration >-1) ? duration : 6000000;
3719 			spells[i].bDuration = true;
3720 			spells[i].fManaCostPerSecond = 0.4f;
3721 
3722 			spells[i].longinfo = ARX_DAMAGES_GetFree();
3723 			if(spells[i].longinfo != -1) {
3724 				damages[spells[i].longinfo].radius = 150.f;
3725 				damages[spells[i].longinfo].damages = 4.f;
3726 				damages[spells[i].longinfo].area = DAMAGE_FULL;
3727 				damages[spells[i].longinfo].duration = 100000000;
3728 				damages[spells[i].longinfo].source = spells[i].caster;
3729 				damages[spells[i].longinfo].flags = DAMAGE_FLAG_DONT_HURT_SOURCE
3730 				                                    | DAMAGE_FLAG_FOLLOW_SOURCE
3731 				                                    | DAMAGE_FLAG_ADD_VISUAL_FX;
3732 				damages[spells[i].longinfo].type = DAMAGE_TYPE_FAKEFIRE
3733 				                                   | DAMAGE_TYPE_MAGICAL;
3734 				damages[spells[i].longinfo].exist = true;
3735 			}
3736 
3737 			spells[i].longinfo2 = GetFreeDynLight();
3738 			if(spells[i].longinfo2 != -1) {
3739 				long id = spells[i].longinfo2;
3740 				DynLight[id].exist = 1;
3741 				DynLight[id].intensity = 2.3f;
3742 				DynLight[id].fallend = 700.f;
3743 				DynLight[id].fallstart = 500.f;
3744 				DynLight[id].rgb = Color3f::red;
3745 				DynLight[id].pos = spells[i].caster_pos;
3746 			}
3747 
3748 			break;
3749 		}
3750 
3751 		// level 3 spells
3752 
3753 		case SPELL_SPEED: {
3754 
3755 			spells[i].bDuration = true;
3756 			spells[i].fManaCostPerSecond = 2.f;
3757 
3758 			ARX_SOUND_PlaySFX(SND_SPELL_SPEED_START, &spells[i].caster_pos);
3759 
3760 			if(spells[i].caster == 0) {
3761 				spells[i].target = spells[i].caster;
3762 				spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_SPEED_LOOP,
3763 				                                       &spells[i].caster_pos, 1.f,
3764 				                                       ARX_SOUND_PLAY_LOOPED);
3765 			}
3766 
3767 			spells[i].exist = true;
3768 			spells[i].tolive = (spells[i].caster == 0) ? 200000000 : 20000;
3769 			if(duration > -1) {
3770 				spells[i].tolive = duration;
3771 			}
3772 
3773 			CSpeed * effect = new CSpeed();
3774 			effect->spellinstance = i;
3775 			effect->Create(spells[i].target, spells[i].tolive);
3776 
3777 			spells[i].pSpellFx = effect;
3778 			spells[i].tolive = effect->GetDuration();
3779 
3780 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
3781 
3782 			if(spells[i].caster >= 0 && spells[i].target < long(entities.size())) {
3783 				Entity * t = entities[spells[i].target];
3784 				if(t) {
3785 					t->speed_modif += spells[i].caster_level * 0.1f;
3786 				}
3787 			}
3788 
3789 			break;
3790 		}
3791 
3792 		case SPELL_DISPELL_ILLUSION: {
3793 
3794 			ARX_SOUND_PlaySFX(SND_SPELL_DISPELL_ILLUSION);
3795 			spells[i].exist = true;
3796 			spells[i].tolive = 1000;
3797 
3798 			for(size_t n = 0; n < MAX_SPELLS; n++) {
3799 
3800 				if(!spells[n].exist || spells[n].target == spells[i].caster) {
3801 					continue;
3802 				}
3803 
3804 				if(spells[n].caster_level > spells[i].caster_level) {
3805 					continue;
3806 				}
3807 
3808 				if(spells[n].type == SPELL_INVISIBILITY) {
3809 					if(ValidIONum(spells[n].target) && ValidIONum(spells[i].caster)) {
3810 						if(closerThan(entities[spells[n].target]->pos,
3811 						   entities[spells[i].caster]->pos, 1000.f)) {
3812 							spells[n].tolive = 0;
3813 						}
3814 					}
3815 				}
3816 			}
3817 
3818 			break;
3819 		}
3820 
3821 		case SPELL_FIREBALL: {
3822 
3823 			spells[i].exist = true;
3824 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
3825 			spells[i].tolive = 20000; // TODO probably never read
3826 
3827 			CFireBall * effect = new CFireBall();
3828 			effect->spellinstance = i;
3829 
3830 			if(spells[i].caster != 0) {
3831 				spells[i].hand_group = -1;
3832 			}
3833 
3834 			Vec3f target;
3835 			if(spells[i].hand_group >= 0) {
3836 				target = spells[i].hand_pos;
3837 			} else {
3838 				target = spells[i].caster_pos;
3839 				if(ValidIONum(spells[i].caster)) {
3840 					Entity * c = entities[spells[i].caster];
3841 					if(c->ioflags & IO_NPC) {
3842 						target.x -= EEsin(radians(c->angle.b)) * 30.f;
3843 						target.y -= 80.f;
3844 						target.z += EEcos(radians(c->angle.b)) * 30.f;
3845 					}
3846 				}
3847 			}
3848 
3849 			effect->SetDuration(6000ul);
3850 
3851 			float anglea = 0, angleb;
3852 			if(spells[i].caster == 0) {
3853 				anglea = player.angle.a, angleb = player.angle.b;
3854 			} else {
3855 
3856 				Vec3f start = entities[spells[i].caster]->pos;
3857 				if(ValidIONum(spells[i].caster)
3858 				   && (entities[spells[i].caster]->ioflags & IO_NPC)) {
3859 					start.y -= 80.f;
3860 				}
3861 
3862 				Entity * _io = entities[spells[i].caster];
3863 				if(ValidIONum(_io->targetinfo)) {
3864 					const Vec3f & end = entities[_io->targetinfo]->pos;
3865 					float d = dist(Vec2f(end.x, end.z), Vec2f(start.x, start.z));
3866 					anglea = degrees(getAngle(start.y, start.z, end.y, end.z + d));
3867 				}
3868 
3869 				angleb = entities[spells[i].caster]->angle.b;
3870 			}
3871 
3872 			effect->Create(target, MAKEANGLE(angleb), anglea, spells[i].caster_level);
3873 
3874 			spells[i].pSpellFx = effect;
3875 			spells[i].tolive = effect->GetDuration();
3876 
3877 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_LAUNCH, &spells[i].caster_pos);
3878 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_FIRE_WIND,
3879 			                                       &spells[i].caster_pos, 1.f,
3880 			                                       ARX_SOUND_PLAY_LOOPED);
3881 
3882 			break;
3883 		}
3884 
3885 		case SPELL_CREATE_FOOD: {
3886 
3887 			ARX_SOUND_PlaySFX(SND_SPELL_CREATE_FOOD, &spells[i].caster_pos);
3888 			spells[i].exist = true;
3889 			spells[i].tolive = (duration > -1) ? duration : 3500;
3890 
3891 			if(spells[i].caster == 0 || spells[i].target == 0) {
3892 				player.hunger = 100;
3893 			}
3894 
3895 			CCreateFood * effect = new CCreateFood();
3896 			effect->spellinstance = i;
3897 			effect->Create();
3898 			effect->SetDuration(spells[i].tolive);
3899 			spells[i].pSpellFx = effect;
3900 			spells[i].tolive = effect->GetDuration();
3901 
3902 			break;
3903 		}
3904 
3905 		case SPELL_ICE_PROJECTILE: {
3906 
3907 			ARX_SOUND_PlaySFX(SND_SPELL_ICE_PROJECTILE_LAUNCH, &spells[i].caster_pos);
3908 			spells[i].exist = true;
3909 			spells[i].tolive = 4200;
3910 
3911 			CIceProjectile * effect = new CIceProjectile();
3912 			effect->spellinstance = i;
3913 
3914 			Vec3f target;
3915 			float angleb;
3916 			if(spells[i].caster == 0) {
3917 				target = player.pos + Vec3f(0.f, 160.f, 0.f);
3918 				angleb = player.angle.b;
3919 			} else {
3920 				target = entities[spells[i].caster]->pos;
3921 				angleb = entities[spells[i].caster]->angle.b;
3922 			}
3923 			angleb = MAKEANGLE(angleb);
3924 			target.x -= EEsin(radians(angleb)) * 150.0f;
3925 			target.z += EEcos(radians(angleb)) * 150.0f;
3926 			effect->Create(target, angleb, spells[i].caster_level);
3927 
3928 			effect->SetDuration(spells[i].tolive);
3929 			spells[i].pSpellFx = effect;
3930 			spells[i].tolive = effect->GetDuration();
3931 
3932 			break;
3933 		}
3934 
3935 		// level 4 spells
3936 
3937 		case SPELL_BLESS: {
3938 
3939 			if(spells[i].caster == 0) {
3940 				spells[i].target = 0;
3941 			}
3942 
3943 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].target);
3944 			if(iCancel > -1) {
3945 				spells[iCancel].tolive = 0;
3946 			}
3947 
3948 			ARX_SOUND_PlaySFX(SND_SPELL_BLESS);
3949 			spells[i].exist = true;
3950 			// TODO this tolive value is probably never read
3951 			spells[i].tolive = (duration > -1) ? duration : 2000000;
3952 			spells[i].bDuration = true;
3953 			spells[i].fManaCostPerSecond = 0.5f * spells[i].caster_level * 0.6666f;
3954 
3955 			CBless * effect = new CBless();
3956 			effect->spellinstance = i;
3957 			Vec3f target = entities[spells[i].caster]->pos;
3958 			effect->Create(target, MAKEANGLE(player.angle.b));
3959 			effect->SetDuration(20000);
3960 			spells[i].pSpellFx = effect;
3961 			spells[i].tolive = effect->GetDuration();
3962 
3963 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
3964 
3965 			break;
3966 		}
3967 
3968 		case SPELL_DISPELL_FIELD: {
3969 
3970 			spells[i].tolive = 10;
3971 
3972 			long valid = 0, dispelled = 0;
3973 
3974 			for(size_t n = 0; n < MAX_SPELLS; n++) {
3975 
3976 				if(!spells[n].exist || !spells[n].pSpellFx) {
3977 					continue;
3978 				}
3979 
3980 				bool cancel = false;
3981 				Vec3f pos;
3982 
3983 				switch(spells[n].type) {
3984 
3985 					case SPELL_CREATE_FIELD: {
3986 						if(spells[i].caster != 0 || spells[n].caster == 0) {
3987 							pos = static_cast<CCreateField *>(spells[n].pSpellFx)->eSrc;
3988 							cancel = true;
3989 						}
3990 						break;
3991 					}
3992 
3993 					case SPELL_FIRE_FIELD: {
3994 						pos = static_cast<CFireField *>(spells[n].pSpellFx)->pos;
3995 						cancel = true;
3996 						break;
3997 					}
3998 
3999 					case SPELL_ICE_FIELD: {
4000 						pos = static_cast<CIceField *>(spells[n].pSpellFx)->eSrc;
4001 						cancel = true;
4002 						break;
4003 					}
4004 
4005 					default: break;
4006 				}
4007 
4008 				Entity * caster = entities[spells[i].caster];
4009 				if(cancel && closerThan(pos, caster->pos, 400.f)) {
4010 					valid++;
4011 					if(spells[n].caster_level <= spells[i].caster_level) {
4012 						spells[n].tolive = 0;
4013 						dispelled++;
4014 					}
4015 				}
4016 			}
4017 
4018 			if(valid > dispelled) {
4019 				// Some fileds could not be dispelled
4020 				ARX_SPEECH_AddSpeech(entities.player(), "player_not_skilled_enough",
4021 				                     ANIM_TALK_NEUTRAL, ARX_SPEECH_FLAG_NOTEXT);
4022 			}
4023 
4024 			if(dispelled > 0) {
4025 				ARX_SOUND_PlaySFX(SND_SPELL_DISPELL_FIELD);
4026 			} else {
4027 				ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE, &spells[i].caster_pos);
4028 			}
4029 
4030 			break;
4031 		}
4032 
4033 		case SPELL_FIRE_PROTECTION: {
4034 
4035 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_PROTECTION);
4036 
4037 			long idx = ARX_SPELLS_GetSpellOn(entities[spells[i].target], typ);
4038 			if(idx >= 0) {
4039 				spells[idx].tolive = 0;
4040 			}
4041 
4042 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_ARMOR,
4043 			                                                   spells[i].caster);
4044 			if(iCancel > -1) {
4045 				spells[iCancel].tolive = 0;
4046 			}
4047 
4048 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_LOWER_ARMOR,
4049 			                                              spells[i].caster);
4050 			if(iCancel > -1) {
4051 				spells[iCancel].tolive = 0;
4052 			}
4053 
4054 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_COLD_PROTECTION,
4055 			                                              spells[i].caster);
4056 			if(iCancel > -1) {
4057 				spells[iCancel].tolive = 0;
4058 			}
4059 
4060 			spells[i].exist = true;
4061 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4062 			if(duration > -1) {
4063 				spells[i].tolive = duration;
4064 			} else {
4065 				spells[i].tolive = (spells[i].caster == 0) ? 2000000 : 20000;
4066 			}
4067 
4068 			if(spells[i].caster == 0) {
4069 				spells[i].target = 0;
4070 			}
4071 
4072 			spells[i].bDuration = true;
4073 			spells[i].fManaCostPerSecond = 1.f;
4074 
4075 			CFireProtection * effect = new CFireProtection();
4076 			effect->spellinstance = i;
4077 			effect->Create(spells[i].tolive);
4078 			spells[i].pSpellFx = effect;
4079 			spells[i].tolive = effect->GetDuration();
4080 
4081 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4082 
4083 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_FIRE_PROTECTION_LOOP,
4084 			                                       &spells[i].caster_pos, 1.f,
4085 			                                       ARX_SOUND_PLAY_LOOPED);
4086 
4087 			break;
4088 		}
4089 
4090 		case SPELL_COLD_PROTECTION: {
4091 
4092 			long idx = ARX_SPELLS_GetSpellOn(entities[spells[i].target], typ);
4093 			if(idx >= 0) {
4094 				spells[idx].tolive = 0;
4095 			}
4096 
4097 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_ARMOR,
4098 			                                                   spells[i].caster);
4099 			if(iCancel > -1) {
4100 				spells[iCancel].tolive = 0;
4101 			}
4102 
4103 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_LOWER_ARMOR,
4104 			                                              spells[i].caster);
4105 			if(iCancel > -1) {
4106 				spells[iCancel].tolive = 0;
4107 			}
4108 
4109 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_FIRE_PROTECTION,
4110 			                                              spells[i].caster);
4111 			if(iCancel > -1) {
4112 				spells[iCancel].tolive = 0;
4113 			}
4114 
4115 			ARX_SOUND_PlaySFX(SND_SPELL_COLD_PROTECTION_START);
4116 
4117 			spells[i].exist = true;
4118 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4119 			if(duration > -1) {
4120 				spells[i].tolive = duration;
4121 			} else {
4122 				spells[i].tolive = (spells[i].caster == 0) ? 2000000 : 20000;
4123 			}
4124 
4125 			if(spells[i].caster == 0) {
4126 				spells[i].target = 0;
4127 			}
4128 
4129 			spells[i].bDuration = true;
4130 			spells[i].fManaCostPerSecond = 1.f;
4131 
4132 			CColdProtection * effect = new CColdProtection();
4133 			effect->spellinstance=i;
4134 			effect->Create(spells[i].tolive, spells[i].target);
4135 			spells[i].pSpellFx = effect;
4136 			spells[i].tolive = effect->GetDuration();
4137 
4138 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_COLD_PROTECTION_LOOP,
4139 			                                       &spells[i].caster_pos, 1.f,
4140 			                                       ARX_SOUND_PLAY_LOOPED);
4141 
4142 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4143 
4144 			break;
4145 		}
4146 
4147 		case SPELL_TELEKINESIS: {
4148 
4149 			spells[i].exist = true;
4150 			spells[i].tolive = (duration > -1) ? duration : 6000000;
4151 			spells[i].bDuration = true;
4152 			spells[i].fManaCostPerSecond = 0.9f;
4153 
4154 			if(spells[i].caster == 0) {
4155 				Project.telekinesis = 1;
4156 			}
4157 
4158 			ARX_SOUND_PlaySFX(SND_SPELL_TELEKINESIS_START, &spells[i].caster_pos);
4159 
4160 			break;
4161 		}
4162 
4163 		case SPELL_CURSE: {
4164 
4165 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].target);
4166 			if(iCancel > -1) {
4167 				spells[iCancel].tolive = 0;
4168 			}
4169 
4170 			ARX_SOUND_PlaySFX(SND_SPELL_CURSE, &spells[i].caster_pos);
4171 
4172 			spells[i].exist = true;
4173 			spells[i].tolive = (duration > -1) ? duration : 2000000;
4174 			spells[i].bDuration = true;
4175 			spells[i].fManaCostPerSecond = 0.5f * spells[i].caster_level;
4176 
4177 			CCurse * effect = new CCurse();
4178 			effect->spellinstance = i;
4179 
4180 			Vec3f target = spells[i].target_pos;
4181 			if(spells[i].target == 0) {
4182 				target.y -= 200.f;
4183 			} else if(spells[i].target > 0 && entities[spells[i].target]) {
4184 				target.y += entities[spells[i].target]->physics.cyl.height - 50.f;
4185 			}
4186 
4187 			effect->Create(target, MAKEANGLE(player.angle.b));
4188 			effect->SetDuration(spells[i].tolive);
4189 			spells[i].pSpellFx = effect;
4190 			spells[i].tolive = effect->GetDuration();
4191 
4192 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4193 
4194 			break;
4195 		}
4196 
4197 		// level 5 spells
4198 
4199 		case SPELL_RUNE_OF_GUARDING: {
4200 
4201 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].caster);
4202 			if(iCancel > -1) {
4203 				spells[iCancel].tolive = 0;
4204 			}
4205 
4206 			ARX_SOUND_PlaySFX(SND_SPELL_RUNE_OF_GUARDING);
4207 			spells[i].exist = true;
4208 			spells[i].tolive = (duration > -1) ? duration : 99999999;
4209 
4210 			CRuneOfGuarding * effect = new CRuneOfGuarding();
4211 			effect->spellinstance = i;
4212 			effect->Create(entities[spells[i].caster]->pos, 0);
4213 			effect->SetDuration(spells[i].tolive);
4214 			spells[i].pSpellFx = effect;
4215 			spells[i].tolive = effect->GetDuration();
4216 
4217 			break;
4218 		}
4219 
4220 		case SPELL_LEVITATE: {
4221 
4222 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].caster);
4223 			if(iCancel > -1) {
4224 				spells[iCancel].tolive = 0;
4225 			}
4226 
4227 			ARX_SOUND_PlaySFX(SND_SPELL_LEVITATE_START);
4228 			spells[i].exist = true;
4229 			spells[i].tolive = (duration > -1) ? duration : 2000000000;
4230 			spells[i].bDuration = true;
4231 			spells[i].fManaCostPerSecond = 1.f;
4232 
4233 			CLevitate * effect = new CLevitate();
4234 			effect->spellinstance = i;
4235 
4236 			Vec3f target;
4237 			if(spells[i].caster == 0 || spells[i].target == 0) {
4238 				target = player.pos + Vec3f(0.f, 150.f, 0.f);
4239 				spells[i].target = 0;
4240 				spells[i].tolive = 200000000;
4241 				player.levitate = 1;
4242 			} else {
4243 				target = entities[spells[i].target]->pos;
4244 			}
4245 
4246 			effect->Create(16, 50.f, 100.f, 80.f, &target, spells[i].tolive);
4247 			spells[i].pSpellFx = effect;
4248 			spells[i].tolive = effect->GetDuration();
4249 
4250 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4251 
4252 			break;
4253 		}
4254 
4255 		case SPELL_CURE_POISON: {
4256 
4257 			if(spells[i].caster == 0) {
4258 				spells[i].target = 0;
4259 			}
4260 
4261 			float cure = spells[i].caster_level * 10;
4262 			if(spells[i].target == 0) {
4263 				player.poison -= std::min(player.poison, cure);
4264 				ARX_SOUND_PlaySFX(SND_SPELL_CURE_POISON);
4265 			} else if (ValidIONum(spells[i].target)) {
4266 				Entity * io = entities[spells[i].target];
4267 				if(io->ioflags & IO_NPC) {
4268 					io->_npcdata->poisonned -= std::min(io->_npcdata->poisonned, cure);
4269 				}
4270 				ARX_SOUND_PlaySFX(SND_SPELL_CURE_POISON, &io->pos);
4271 			}
4272 
4273 			spells[i].exist = true;
4274 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4275 			spells[i].tolive = 3500;
4276 
4277 			CCurePoison * effect = new CCurePoison();
4278 			effect->spellinstance = i;
4279 			effect->Create();
4280 			effect->SetDuration(spells[i].tolive);
4281 			spells[i].pSpellFx = effect;
4282 			spells[i].tolive = effect->GetDuration();
4283 
4284 			break;
4285 		}
4286 
4287 		case SPELL_REPEL_UNDEAD: {
4288 
4289 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_REPEL_UNDEAD, spells[i].caster);
4290 			if(iCancel > -1) {
4291 				spells[iCancel].tolive = 0;
4292 			}
4293 
4294 			if(spells[i].caster == 0) {
4295 				spells[i].target = 0;
4296 			}
4297 
4298 			ARX_SOUND_PlaySFX(SND_SPELL_REPEL_UNDEAD);
4299 			if(spells[i].target == 0) {
4300 				spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_REPEL_UNDEAD_LOOP,
4301 				                                       &spells[i].caster_pos, 1.f,
4302 				                                       ARX_SOUND_PLAY_LOOPED);
4303 			}
4304 
4305 			spells[i].exist = true;
4306 			spells[i].tolive = (duration > -1) ? duration : 20000000;
4307 			spells[i].bDuration = true;
4308 			spells[i].fManaCostPerSecond = 1.f;
4309 
4310 			CRepelUndead * effect = new CRepelUndead();
4311 			effect->spellinstance = i;
4312 			effect->Create(player.pos, MAKEANGLE(player.angle.b));
4313 			effect->SetDuration(spells[i].tolive);
4314 			spells[i].pSpellFx = effect;
4315 			spells[i].tolive = effect->GetDuration();
4316 
4317 			break;
4318 		}
4319 
4320 		case SPELL_POISON_PROJECTILE: {
4321 
4322 			ARX_SOUND_PlaySFX(SND_SPELL_POISON_PROJECTILE_LAUNCH,
4323 			                  &spells[i].caster_pos);
4324 
4325 			spells[i].exist = true;
4326 			spells[i].tolive = 900000000; // TODO probably never read
4327 
4328 			long level = std::max(long(spells[i].caster_level), 1l);
4329 			CMultiPoisonProjectile * effect = new CMultiPoisonProjectile(level);
4330 			effect->spellinstance = i;
4331 			effect->SetDuration(8000ul);
4332 			float ang;
4333 			if(spells[i].caster == 0) {
4334 				ang = player.angle.b;
4335 			} else {
4336 				ang = entities[spells[i].caster]->angle.b;
4337 			}
4338 			effect->Create(Vec3f::ZERO, MAKEANGLE(ang));
4339 			spells[i].pSpellFx = effect;
4340 			spells[i].tolive = effect->GetDuration();
4341 
4342 			break;
4343 		}
4344 
4345 		// level 6 spells
4346 
4347 		case SPELL_RISE_DEAD: {
4348 
4349 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].caster);
4350 			if(iCancel > -1) {
4351 				spells[iCancel].tolive = 0;
4352 			}
4353 
4354 			float beta;
4355 			Vec3f target;
4356 			bool displace = true;
4357 			if(spells[i].caster == 0) {
4358 				target = player.basePosition();
4359 				beta = MAKEANGLE(player.angle.b);
4360 			} else {
4361 				target = entities[spells[i].caster]->pos;
4362 				beta = MAKEANGLE(entities[spells[i].caster]->angle.b);
4363 				displace = (entities[spells[i].caster]->ioflags & IO_NPC) == IO_NPC;
4364 			}
4365 			if(displace) {
4366 				target.x -= EEsin(radians(beta)) * 300.f;
4367 				target.z += EEcos(radians(beta)) * 300.f;
4368 			}
4369 			if(!ARX_INTERACTIVE_ConvertToValidPosForIO(NULL, &target)) {
4370 				ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE);
4371 				return false;
4372 			}
4373 
4374 			ARX_SOUND_PlaySFX(SND_SPELL_RAISE_DEAD, &spells[i].caster_pos);
4375 
4376 			spells[i].target_pos = target;
4377 			spells[i].exist = true;
4378 			// TODO this tolive value is probably never read
4379 			spells[i].tolive = (duration > -1) ? duration : 2000000;
4380 			spells[i].bDuration = true;
4381 			spells[i].fManaCostPerSecond = 1.2f;
4382 			spells[i].longinfo = -1;
4383 
4384 			CRiseDead * effect = new CRiseDead();
4385 			effect->spellinstance = i;
4386 			effect->Create(target, beta);
4387 			effect->SetDuration(2000, 500, 1800);
4388 			effect->SetColorBorder(0.5, 0.5, 0.5);
4389 			effect->SetColorRays1(0.5, 0.5, 0.5);
4390 			effect->SetColorRays2(1, 0, 0);
4391 
4392 			if(effect->lLightId == -1) {
4393 				effect->lLightId = GetFreeDynLight();
4394 			}
4395 			if(effect->lLightId != -1) {
4396 				long id = effect->lLightId;
4397 				DynLight[id].exist = 1;
4398 				DynLight[id].intensity = 1.3f;
4399 				DynLight[id].fallend = 450.f;
4400 				DynLight[id].fallstart = 380.f;
4401 				DynLight[id].rgb = Color3f::black;
4402 				DynLight[id].pos = target - Vec3f(0.f, 100.f, 0.f);
4403 				DynLight[id].duration = 200;
4404 				DynLight[id].time_creation = (unsigned long)(arxtime);
4405 			}
4406 
4407 			spells[i].pSpellFx = effect;
4408 			spells[i].tolive = effect->GetDuration();
4409 
4410 			break;
4411 		}
4412 
4413 		case SPELL_PARALYSE: {
4414 
4415 			ARX_SOUND_PlaySFX(SND_SPELL_PARALYSE, &spells[i].caster_pos);
4416 
4417 			spells[i].exist = true;
4418 			spells[i].tolive = (duration > -1) ? duration : 5000;
4419 
4420 			float resist_magic = 0.f;
4421 			if(spells[i].target == 0 && spells[i].caster_level <= player.level) {
4422 				resist_magic = player.resist_magic;
4423 			} else if(entities[spells[i].target]->ioflags & IO_NPC) {
4424 				resist_magic = entities[spells[i].target]->_npcdata->resist_magic;
4425 			}
4426 			if(rnd() * 100.f < resist_magic) {
4427 				float mul = max(0.5f, 1.f - (resist_magic * 0.005f));
4428 				spells[i].tolive = long(spells[i].tolive * mul);
4429 			}
4430 
4431 			entities[spells[i].target]->ioflags |= IO_FREEZESCRIPT;
4432 
4433 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4434 			ARX_NPC_Kill_Spell_Launch(entities[spells[i].target]);
4435 
4436 			break;
4437 		}
4438 
4439 		case SPELL_CREATE_FIELD: {
4440 
4441 			spells[i].exist = true;
4442 
4443 			unsigned long start = (unsigned long)(arxtime);
4444 			if(flags & SPELLCAST_FLAG_RESTORE) {
4445 				start -= std::min(start, 4000ul);
4446 			}
4447 			spells[i].lastupdate = spells[i].timcreation = start;
4448 
4449 			spells[i].tolive = (duration > -1) ? duration : 800000;
4450 			spells[i].bDuration = true;
4451 			spells[i].fManaCostPerSecond = 1.2f;
4452 
4453 			Vec3f target;
4454 			float beta;
4455 			bool displace = false;
4456 			if(spells[i].caster == 0) {
4457 				target = entities.player()->pos;
4458 				beta = player.angle.b;
4459 				displace = true;
4460 			} else {
4461 				if(ValidIONum(spells[i].caster)) {
4462 					Entity * io = entities[spells[i].caster];
4463 					target = io->pos;
4464 					beta = io->angle.b;
4465 					displace = (io->ioflags & IO_NPC) == IO_NPC;
4466 				} else {
4467 					ARX_DEAD_CODE();
4468 				}
4469 			}
4470 			if(displace) {
4471 				target.x -= EEsin(radians(MAKEANGLE(beta))) * 250.f;
4472 				target.z += EEcos(radians(MAKEANGLE(beta))) * 250.f;
4473 			}
4474 
4475 			ARX_SOUND_PlaySFX(SND_SPELL_CREATE_FIELD, &target);
4476 
4477 			CCreateField * effect  = new CCreateField();
4478 			effect->spellinstance = i;
4479 
4480 			res::path cls = "graph/obj3d/interactive/fix_inter/blue_cube/blue_cube";
4481 			Entity * io = AddFix(cls, -1, IO_IMMEDIATELOAD);
4482 			if(io) {
4483 
4484 				ARX_INTERACTIVE_HideGore(io);
4485 				RestoreInitialIOStatusOfIO(io);
4486 				spells[i].longinfo = io->index();
4487 				io->scriptload = 1;
4488 				io->ioflags |= IO_NOSAVE | IO_FIELD;
4489 				io->initpos = io->pos = target;
4490 				SendInitScriptEvent(io);
4491 
4492 				effect->Create(target, 0);
4493 				effect->SetDuration(spells[i].tolive);
4494 				effect->lLightId = GetFreeDynLight();
4495 
4496 				if(effect->lLightId != -1) {
4497 					long id = effect->lLightId;
4498 					DynLight[id].exist = 1;
4499 					DynLight[id].intensity = 0.7f + 2.3f;
4500 					DynLight[id].fallend = 500.f;
4501 					DynLight[id].fallstart = 400.f;
4502 					DynLight[id].rgb = Color3f(0.8f, 0.0f, 1.0f);
4503 					DynLight[id].pos = effect->eSrc - Vec3f(0.f, 150.f, 0.f);
4504 				}
4505 
4506 				spells[i].pSpellFx = effect;
4507 				spells[i].tolive = effect->GetDuration();
4508 
4509 				if(flags & SPELLCAST_FLAG_RESTORE) {
4510 					effect->Update(4000);
4511 				}
4512 
4513 			} else {
4514 				spells[i].tolive = 0;
4515 			}
4516 
4517 			break;
4518 		}
4519 
4520 		case SPELL_DISARM_TRAP: {
4521 
4522 			ARX_SOUND_PlaySFX(SND_SPELL_DISARM_TRAP);
4523 
4524 			spells[i].exist = true;
4525 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4526 			spells[i].tolive = 1;
4527 
4528 			CDisarmTrap * effect = new CDisarmTrap();
4529 			effect->spellinstance = i;
4530 
4531 			effect->Create(player.pos, MAKEANGLE(player.angle.b));
4532 			effect->SetDuration(spells[i].tolive);
4533 			spells[i].pSpellFx = effect;
4534 			spells[i].tolive = effect->GetDuration();
4535 
4536 			EERIE_SPHERE sphere;
4537 			sphere.origin = player.pos;
4538 			sphere.radius = 400.f;
4539 
4540 			for(size_t n = 0; n < MAX_SPELLS; n++) {
4541 
4542 				if(!spells[n].exist || spells[n].type != SPELL_RUNE_OF_GUARDING) {
4543 					continue;
4544 				}
4545 
4546 				if(!spells[n].pSpellFx) {
4547 					continue;
4548 				}
4549 
4550 				CSpellFx * effect = spells[n].pSpellFx;
4551 				if(sphere.contains(static_cast<CRuneOfGuarding *>(effect)->eSrc)) {
4552 					spells[n].caster_level -= spells[i].caster_level;
4553 					if(spells[n].caster_level <= 0) {
4554 						spells[n].tolive = 0;
4555 					}
4556 				}
4557 			}
4558 
4559 			break;
4560 		}
4561 
4562 		case SPELL_SLOW_DOWN: {
4563 
4564 			long target = spells[i].target;
4565 
4566 			Entity * io = entities[target];
4567 			for(int il = 0; il < io->nb_spells_on; il++) {
4568 				if(spells[io->spells_on[il]].type == SPELL_SLOW_DOWN) {
4569 					spells[i].exist = false;
4570 					return false;
4571 				}
4572 			}
4573 
4574 			ARX_SOUND_PlaySFX(SND_SPELL_SLOW_DOWN, &spells[i].caster_pos);
4575 
4576 			spells[i].exist = true;
4577 			spells[i].tolive = (spells[i].caster == 0) ? 10000000 : 10000;
4578 			if(duration > -1) {
4579 				spells[i].tolive=duration;
4580 			}
4581 			spells[i].pSpellFx = NULL;
4582 			spells[i].bDuration = true;
4583 			spells[i].fManaCostPerSecond = 1.2f;
4584 
4585 			CSlowDown * effect = new CSlowDown();
4586 			effect->spellinstance = i;
4587 			effect->Create(spells[i].target_pos, MAKEANGLE(player.angle.b));
4588 			effect->SetDuration(spells[i].tolive);
4589 			spells[i].pSpellFx = effect;
4590 			spells[i].tolive = effect->GetDuration();
4591 
4592 			ARX_SPELLS_AddSpellOn(target, i);
4593 
4594 			if(ValidIONum(target)) {
4595 				entities[target]->speed_modif -= spells[i].caster_level * 0.05f;
4596 			}
4597 
4598 			break;
4599 		}
4600 
4601 		// level 7 spells
4602 
4603 		case SPELL_FLYING_EYE: {
4604 
4605 			if(spells[i].caster == 0) {
4606 				spells[i].target = 0;
4607 			}
4608 
4609 			if(spells[i].target != 0) {
4610 				return false;
4611 			}
4612 
4613 			ARX_SOUND_PlaySFX(SND_SPELL_EYEBALL_IN);
4614 
4615 			spells[i].exist = true;
4616 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4617 			spells[i].tolive = 1000000;
4618 			spells[i].bDuration = true;
4619 			spells[i].fManaCostPerSecond = 3.2f;
4620 			eyeball.exist = 1;
4621 			float angleb = MAKEANGLE(player.angle.b);
4622 			eyeball.pos.x = player.pos.x - EEsin(radians(angleb)) * 200.f;
4623 			eyeball.pos.y = player.pos.y + 50.f;
4624 			eyeball.pos.z = player.pos.z + EEcos(radians(angleb)) * 200.f;
4625 			eyeball.angle = player.angle;
4626 
4627 			for(long n = 0; n < 12; n++) {
4628 
4629 				PARTICLE_DEF * pd = createParticle();
4630 				if(!pd) {
4631 					continue;
4632 				}
4633 
4634 				pd->ov = eyeball.pos + randomVec(-5.f, 5.f);
4635 				pd->move = randomVec(-2.f, 2.f);
4636 				pd->siz = 28.f;
4637 				pd->tolive = Random::get(2000, 6000);
4638 				pd->scale = Vec3f::repeat(12.f);
4639 				pd->tc = tc4;
4640 				pd->special = FADE_IN_AND_OUT | ROTATING | MODULATE_ROTATION
4641 				              | DISSIPATING;
4642 				pd->fparam = 0.0000001f;
4643 				pd->rgb = Color3f(0.7f, 0.7f, 1.f);
4644 			}
4645 
4646 			TRUE_PLAYER_MOUSELOOK_ON = true;
4647 			SLID_START = float(arxtime);
4648 			bOldLookToggle = config.input.mouseLookToggle;
4649 			config.input.mouseLookToggle = true;
4650 
4651 			break;
4652 		}
4653 
4654 		case SPELL_FIRE_FIELD: {
4655 
4656 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].caster);
4657 			if(iCancel > -1) {
4658 				spells[iCancel].tolive = 0;
4659 			}
4660 
4661 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_FIELD_START);
4662 
4663 			spells[i].exist = true;
4664 			spells[i].tolive = (duration > -1) ? duration : 100000;
4665 			spells[i].bDuration = true;
4666 			spells[i].fManaCostPerSecond = 2.8f;
4667 			spells[i].longinfo2 = -1;
4668 
4669 			CFireField * effect = new CFireField();
4670 			effect->spellinstance = i;
4671 
4672 			Vec3f target;
4673 			float beta;
4674 			float displace = false;
4675 			if(spells[i].caster == 0) {
4676 				target = player.basePosition();
4677 				beta = player.angle.b;
4678 				displace = true;
4679 			} else {
4680 				if(ValidIONum(spells[i].caster)) {
4681 					Entity * io = entities[spells[i].caster];
4682 					target = io->pos;
4683 					beta = io->angle.b;
4684 					displace = (io->ioflags & IO_NPC);
4685 				} else {
4686 					ARX_DEAD_CODE();
4687 				}
4688 			}
4689 			if(displace) {
4690 				target.x -= EEsin(radians(MAKEANGLE(beta))) * 250.f;
4691 				target.z += EEcos(radians(MAKEANGLE(beta))) * 250.f;
4692 			}
4693 
4694 			spells[i].longinfo = ARX_DAMAGES_GetFree();
4695 			if(spells[i].longinfo != -1) {
4696 				damages[spells[i].longinfo].radius = 150.f;
4697 				damages[spells[i].longinfo].damages = 10.f;
4698 				damages[spells[i].longinfo].area = DAMAGE_FULL;
4699 				damages[spells[i].longinfo].duration = 100000000;
4700 				damages[spells[i].longinfo].source = spells[i].caster;
4701 				damages[spells[i].longinfo].flags = 0;
4702 				damages[spells[i].longinfo].type = DAMAGE_TYPE_MAGICAL
4703 				                                   | DAMAGE_TYPE_FIRE
4704 				                                   | DAMAGE_TYPE_FIELD;
4705 				damages[spells[i].longinfo].exist = true;
4706 				damages[spells[i].longinfo].pos = target;
4707 			}
4708 
4709 			effect->Create(200.f, &target, spells[i].tolive);
4710 			spells[i].pSpellFx = effect;
4711 			spells[i].tolive = effect->GetDuration();
4712 
4713 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_FIRE_FIELD_LOOP,
4714 			                                       &target, 1.f,
4715 			                                       ARX_SOUND_PLAY_LOOPED);
4716 
4717 			break;
4718 		}
4719 
4720 		case SPELL_ICE_FIELD: {
4721 
4722 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(typ, spells[i].caster);
4723 			if(iCancel > -1) {
4724 				spells[iCancel].tolive = 0;
4725 			}
4726 
4727 			ARX_SOUND_PlaySFX(SND_SPELL_ICE_FIELD);
4728 
4729 			spells[i].exist = true;
4730 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4731 			spells[i].tolive = (duration > -1) ? duration : 100000;
4732 			spells[i].bDuration = true;
4733 			spells[i].fManaCostPerSecond = 2.8f;
4734 			spells[i].longinfo2 =-1;
4735 
4736 			CIceField * effect = new CIceField();
4737 			effect->spellinstance = i;
4738 
4739 			Vec3f target;
4740 			float beta;
4741 			float displace = false;
4742 			if(spells[i].caster == 0) {
4743 				target = player.basePosition();
4744 				beta = player.angle.b;
4745 				displace = true;
4746 			} else {
4747 				if(ValidIONum(spells[i].caster)) {
4748 					Entity * io = entities[spells[i].caster];
4749 					target = io->pos;
4750 					beta = io->angle.b;
4751 					displace = (io->ioflags & IO_NPC);
4752 				} else {
4753 					ARX_DEAD_CODE();
4754 				}
4755 			}
4756 			if(displace) {
4757 				target.x -= EEsin(radians(MAKEANGLE(beta))) * 250.f;
4758 				target.z += EEcos(radians(MAKEANGLE(beta))) * 250.f;
4759 			}
4760 
4761 			spells[i].longinfo = ARX_DAMAGES_GetFree();
4762 			if(spells[i].longinfo != -1) {
4763 				damages[spells[i].longinfo].radius = 150.f;
4764 				damages[spells[i].longinfo].damages = 10.f;
4765 				damages[spells[i].longinfo].area = DAMAGE_FULL;
4766 				damages[spells[i].longinfo].duration = 100000000;
4767 				damages[spells[i].longinfo].source = spells[i].caster;
4768 				damages[spells[i].longinfo].flags = 0;
4769 				damages[spells[i].longinfo].type = DAMAGE_TYPE_MAGICAL
4770 				                                   | DAMAGE_TYPE_COLD
4771 				                                   | DAMAGE_TYPE_FIELD;
4772 				damages[spells[i].longinfo].exist = true;
4773 				damages[spells[i].longinfo].pos = target;
4774 			}
4775 
4776 			effect->Create(target, MAKEANGLE(player.angle.b));
4777 			effect->SetDuration(spells[i].tolive);
4778 			spells[i].pSpellFx = effect;
4779 			spells[i].tolive = effect->GetDuration();
4780 
4781 			spells[i].snd_loop = ARX_SOUND_PlaySFX( SND_SPELL_ICE_FIELD_LOOP,
4782 			                                       &target, 1.f,
4783 			                                       ARX_SOUND_PLAY_LOOPED );
4784 
4785 			break;
4786 		}
4787 
4788 		case SPELL_LIGHTNING_STRIKE: {
4789 
4790 			spells[i].exist = true;
4791 
4792 			CLightning * effect = new CLightning();
4793 			effect->spellinstance = i;
4794 			Vec3f target(0.f, 0.f, -500.f);
4795 			effect->Create(Vec3f::ZERO, target, MAKEANGLE(player.angle.b));
4796 			effect->SetDuration(long(500 * spells[i].caster_level));
4797 			effect->lSrc = 0;
4798 			spells[i].pSpellFx = effect;
4799 			spells[i].tolive = effect->GetDuration();
4800 
4801 			ARX_SOUND_PlaySFX(SND_SPELL_LIGHTNING_START, &spells[i].caster_pos);
4802 
4803 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_LIGHTNING_LOOP,
4804 			                                       &spells[i].caster_pos, 1.f,
4805 			                                       ARX_SOUND_PLAY_LOOPED);
4806 
4807 			break;
4808 		}
4809 
4810 		case SPELL_CONFUSE: {
4811 
4812 			ARX_SOUND_PlaySFX(SND_SPELL_CONFUSE);
4813 
4814 			spells[i].exist = true;
4815 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4816 			spells[i].bDuration = true;
4817 			spells[i].fManaCostPerSecond = 1.5f;
4818 			if(duration > -1) {
4819 				spells[i].tolive = duration;
4820 			} else {
4821 				// TODO what then?
4822 			}
4823 
4824 			CConfuse * effect = new CConfuse();
4825 			effect->spellinstance = i;
4826 			effect->Create(player.pos, MAKEANGLE(player.angle.b));
4827 			effect->SetDuration(spells[i].tolive);
4828 			spells[i].pSpellFx = effect;
4829 			spells[i].tolive = effect->GetDuration();
4830 
4831 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4832 
4833 			notifyAll = false; // TODO inconsistent use of the SM_SPELLCAST event
4834 
4835 			break;
4836 		}
4837 
4838 		// level 8 spells
4839 
4840 		case SPELL_INVISIBILITY: {
4841 
4842 			spells[i].exist = true;
4843 			spells[i].tolive = (duration > -1) ? duration : 6000000;
4844 			spells[i].bDuration = true;
4845 			spells[i].fManaCostPerSecond = 3.f;
4846 
4847 			if(spells[i].caster == 0) {
4848 				spells[i].target = 0;
4849 			}
4850 
4851 			entities[spells[i].target]->gameFlags |= GFLAG_INVISIBILITY;
4852 			entities[spells[i].target]->invisibility = 0.f;
4853 
4854 			ARX_SOUND_PlaySFX(SND_SPELL_INVISIBILITY_START, &spells[i].caster_pos);
4855 
4856 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
4857 
4858 			break;
4859 		}
4860 
4861 		case SPELL_MANA_DRAIN: {
4862 
4863 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_LIFE_DRAIN,
4864 			                                                   spells[i].caster);
4865 			if(iCancel > -1) {
4866 				spells[iCancel].tolive = 0;
4867 			}
4868 
4869 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_HARM,
4870 			                                              spells[i].caster);
4871 			if(iCancel > -1) {
4872 				spells[iCancel].tolive = 0;
4873 			}
4874 
4875 			spells[i].exist = true;
4876 			spells[i].tolive = (duration > -1) ? duration : 6000000;
4877 			spells[i].bDuration = true;
4878 			spells[i].fManaCostPerSecond = 2.f;
4879 
4880 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_SHIELD,
4881 			                                       &spells[i].caster_pos, 1.2f,
4882 			                                       ARX_SOUND_PLAY_LOOPED);
4883 
4884 			spells[i].longinfo = ARX_DAMAGES_GetFree();
4885 			if(spells[i].longinfo != -1) {
4886 				damages[spells[i].longinfo].radius = 150.f;
4887 				damages[spells[i].longinfo].damages = 8.f;
4888 				damages[spells[i].longinfo].area = DAMAGE_FULL;
4889 				damages[spells[i].longinfo].duration = 100000000;
4890 				damages[spells[i].longinfo].source = spells[i].caster;
4891 				damages[spells[i].longinfo].flags = DAMAGE_FLAG_DONT_HURT_SOURCE
4892 				                                    | DAMAGE_FLAG_FOLLOW_SOURCE
4893 				                                    | DAMAGE_FLAG_ADD_VISUAL_FX;
4894 				damages[spells[i].longinfo].type = DAMAGE_TYPE_FAKEFIRE
4895 				                                   | DAMAGE_TYPE_MAGICAL
4896 				                                   | DAMAGE_TYPE_DRAIN_MANA;
4897 				damages[spells[i].longinfo].exist = true;
4898 			}
4899 
4900 			spells[i].longinfo2 = GetFreeDynLight();
4901 			if(spells[i].longinfo2 != -1) {
4902 				long id = spells[i].longinfo2;
4903 				DynLight[id].exist = 1;
4904 				DynLight[id].intensity = 2.3f;
4905 				DynLight[id].fallend = 700.f;
4906 				DynLight[id].fallstart = 500.f;
4907 				DynLight[id].rgb = Color3f::blue;
4908 				DynLight[id].pos = spells[i].caster_pos;
4909 				DynLight[id].duration=900;
4910 			}
4911 
4912 			break;
4913 		}
4914 
4915 		case SPELL_EXPLOSION: {
4916 
4917 			ARX_SOUND_PlaySFX(SND_SPELL_EXPLOSION);
4918 
4919 			spells[i].exist = true;
4920 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
4921 			spells[i].tolive = 2000;
4922 
4923 			Vec3f target = entities[spells[i].caster]->pos;
4924 			if(spells[i].caster == 0) {
4925 				target.y += 60.f;
4926 			} else {
4927 				target.y -= 60.f;
4928 			}
4929 
4930 			spells[i].longinfo = ARX_DAMAGES_GetFree();
4931 			if(spells[i].longinfo != -1) {
4932 				damages[spells[i].longinfo].radius = 350.f;
4933 				damages[spells[i].longinfo].damages = 10.f;
4934 				damages[spells[i].longinfo].area = DAMAGE_AREA;
4935 				damages[spells[i].longinfo].duration = spells[i].tolive;
4936 				damages[spells[i].longinfo].source = spells[i].caster;
4937 				damages[spells[i].longinfo].flags = DAMAGE_FLAG_DONT_HURT_SOURCE
4938 				                                    | DAMAGE_FLAG_FOLLOW_SOURCE
4939 				                                    | DAMAGE_FLAG_ADD_VISUAL_FX;
4940 				damages[spells[i].longinfo].type = DAMAGE_TYPE_FAKEFIRE
4941 				                                    | DAMAGE_TYPE_MAGICAL;
4942 				damages[spells[i].longinfo].exist = true;
4943 				damages[spells[i].longinfo].pos = target;
4944 			}
4945 
4946 			spells[i].longinfo2 = GetFreeDynLight();
4947 			if(spells[i].longinfo2 != -1) {
4948 				long id = spells[i].longinfo2;
4949 				DynLight[id].exist = 1;
4950 				DynLight[id].intensity = 2.3f;
4951 				DynLight[id].fallend = 700.f;
4952 				DynLight[id].fallstart = 500.f;
4953 				DynLight[id].rgb.r = 0.1f + rnd() * (1.f / 3);
4954 				DynLight[id].rgb.g = 0.1f + rnd() * (1.f / 3);
4955 				DynLight[id].rgb.b = 0.8f + rnd() * (1.f / 5);
4956 				DynLight[id].pos = target;
4957 				DynLight[id].duration = 200;
4958 			}
4959 
4960 			AddQuakeFX(300, 2000, 400, 1);
4961 
4962 			for(long i_angle = 0 ; i_angle < 360 ; i_angle += 12) {
4963 				for(long j = -100 ; j < 100 ; j += 50) {
4964 					float rr = radians(float(i_angle));
4965 					Vec3f pos(target.x - EEsin(rr) * 360.f, target.y,
4966 					          target.z + EEcos(rr) * 360.f);
4967 					Vec3f dir = Vec3f(pos.x - target.x, 0.f,
4968 					                  pos.z - target.z).getNormalized() * 60.f;
4969 					Color3f rgb(0.1f + rnd() * (1.f/3), 0.1f + rnd() * (1.f/3),
4970 					            0.8f + rnd() * (1.f/5));
4971 					Vec3f posi = target + Vec3f(0.f, j * 2, 0.f);
4972 					LaunchFireballBoom(&posi, 16, &dir, &rgb);
4973 				}
4974 			}
4975 
4976 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_WIND);
4977 
4978 			break;
4979 		}
4980 
4981 		case SPELL_ENCHANT_WEAPON: {
4982 
4983 			spells[i].exist = true;
4984 			spells[i].tolive = 20;
4985 
4986 			notifyAll = false; // TODO inconsistent use of the SM_SPELLCAST event
4987 
4988 			break;
4989 		}
4990 
4991 		case SPELL_LIFE_DRAIN: {
4992 
4993 			long iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_HARM,
4994 			                                                   spells[i].caster);
4995 			if(iCancel > -1) {
4996 				spells[iCancel].tolive = 0;
4997 			}
4998 
4999 			iCancel = ARX_SPELLS_GetInstanceForThisCaster(SPELL_MANA_DRAIN,
5000 			                                              spells[i].caster);
5001 			if(iCancel > -1) {
5002 				spells[iCancel].tolive = 0;
5003 			}
5004 
5005 			spells[i].exist = true;
5006 			spells[i].tolive = (duration > -1) ? duration : 6000000;
5007 			spells[i].bDuration = true;
5008 			spells[i].fManaCostPerSecond = 12.f;
5009 
5010 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_SHIELD,
5011 			                                       &spells[i].caster_pos, 0.8f,
5012 			                                       ARX_SOUND_PLAY_LOOPED);
5013 
5014 			spells[i].longinfo = ARX_DAMAGES_GetFree();
5015 			if(spells[i].longinfo != -1) {
5016 				long id = spells[i].longinfo;
5017 				damages[id].radius = 150.f;
5018 				damages[id].damages = spells[i].caster_level * 0.08f;
5019 				damages[id].area = DAMAGE_AREA;
5020 				damages[id].duration = 100000000;
5021 				damages[id].source = spells[i].caster;
5022 				damages[id].flags = DAMAGE_FLAG_DONT_HURT_SOURCE
5023 				                    | DAMAGE_FLAG_FOLLOW_SOURCE
5024 				                    | DAMAGE_FLAG_ADD_VISUAL_FX;
5025 				damages[id].type = DAMAGE_TYPE_FAKEFIRE | DAMAGE_TYPE_MAGICAL
5026 				                   | DAMAGE_TYPE_DRAIN_LIFE;
5027 				damages[id].exist = true;
5028 			}
5029 
5030 			spells[i].longinfo2 = GetFreeDynLight();
5031 			if(spells[i].longinfo2 != -1) {
5032 				long id = spells[i].longinfo2;
5033 				DynLight[id].exist = 1;
5034 				DynLight[id].intensity = 2.3f;
5035 				DynLight[id].fallend = 700.f;
5036 				DynLight[id].fallstart = 500.f;
5037 				DynLight[id].rgb = Color3f::red;
5038 				DynLight[id].pos = spells[i].caster_pos;
5039 				DynLight[id].duration = 900;
5040 			}
5041 
5042 			break;
5043 		}
5044 
5045 		// level 9 spells
5046 
5047 		case SPELL_SUMMON_CREATURE: {
5048 
5049 			ARX_SOUND_PlaySFX(SND_SPELL_SUMMON_CREATURE);
5050 
5051 			spells[i].exist = true;
5052 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5053 			spells[i].bDuration = true;
5054 			spells[i].fManaCostPerSecond = 1.9f;
5055 			spells[i].longinfo = 0;
5056 			spells[i].longinfo2 = 0;
5057 			spells[i].tolive = (duration > -1) ? duration : 2000000;
5058 
5059 			Vec3f target;
5060 			float beta;
5061 			bool displace = false;
5062 			if(spells[i].caster == 0) {
5063 				target = player.basePosition();
5064 				beta = player.angle.b;
5065 				displace = true;
5066 			} else {
5067 				target = entities[spells[i].caster]->pos;
5068 				beta = entities[spells[i].caster]->angle.b;
5069 				displace = (entities[spells[i].caster]->ioflags & IO_NPC) == IO_NPC;
5070 			}
5071 			if(displace) {
5072 				target.x -= EEsin(radians(MAKEANGLE(beta))) * 300.f;
5073 				target.z += EEcos(radians(MAKEANGLE(beta))) * 300.f;
5074 			}
5075 
5076 			if(!ARX_INTERACTIVE_ConvertToValidPosForIO(NULL, &target)) {
5077 				spells[i].exist = false;
5078 				ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE);
5079 				return false;
5080 			}
5081 
5082 			spells[i].fdata = (spells[i].caster == 0 && cur_mega == 10) ? 1.f : 0.f;
5083 			spells[i].target_pos = target;
5084 
5085 			CSummonCreature * effect = new CSummonCreature();
5086 			effect->spellinstance = i;
5087 			effect->Create(target, MAKEANGLE(player.angle.b));
5088 			effect->SetDuration(2000, 500, 1500);
5089 			effect->SetColorBorder(Color3f::red);
5090 			effect->SetColorRays1(Color3f::red);
5091 			effect->SetColorRays2(Color3f::yellow * .5f);
5092 
5093 			effect->lLightId = GetFreeDynLight();
5094 			if(effect->lLightId > -1) {
5095 				long id = effect->lLightId;
5096 				DynLight[id].exist = 1;
5097 				DynLight[id].intensity = 0.3f;
5098 				DynLight[id].fallend = 500.f;
5099 				DynLight[id].fallstart = 400.f;
5100 				DynLight[id].rgb = Color3f::red;
5101 				DynLight[id].pos = effect->eSrc;
5102 			}
5103 
5104 			spells[i].pSpellFx = effect;
5105 
5106 			break;
5107 		}
5108 
5109 		case SPELL_FAKE_SUMMON: {
5110 
5111 			if(spells[i].caster <= 0 || !ValidIONum(spells[i].target)) {
5112 				return false;
5113 			}
5114 
5115 			ARX_SOUND_PlaySFX(SND_SPELL_SUMMON_CREATURE);
5116 
5117 			spells[i].exist = true;
5118 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5119 			spells[i].bDuration = true;
5120 			spells[i].fManaCostPerSecond = 1.9f;
5121 			spells[i].tolive = 4000;
5122 
5123 			Vec3f target = entities[spells[i].target]->pos;
5124 			if(spells[i].target != 0) {
5125 				target.y += player.baseHeight();
5126 			}
5127 			spells[i].target_pos = target;
5128 
5129 			CSummonCreature * effect = new CSummonCreature();
5130 			effect->spellinstance = i;
5131 			effect->Create(target, MAKEANGLE(player.angle.b));
5132 			effect->SetDuration(2000, 500, 1500);
5133 			effect->SetColorBorder(Color3f::red);
5134 			effect->SetColorRays1(Color3f::red);
5135 			effect->SetColorRays2(Color3f::yellow * .5f);
5136 
5137 			effect->lLightId = GetFreeDynLight();
5138 
5139 			if(effect->lLightId > -1) {
5140 				long id = effect->lLightId;
5141 				DynLight[id].exist = 1;
5142 				DynLight[id].intensity = 0.3f;
5143 				DynLight[id].fallend = 500.f;
5144 				DynLight[id].fallstart = 400.f;
5145 				DynLight[id].rgb = Color3f::red;
5146 				DynLight[id].pos = effect->eSrc;
5147 			}
5148 
5149 			spells[i].pSpellFx = effect;
5150 
5151 			break;
5152 		}
5153 
5154 		case SPELL_NEGATE_MAGIC: {
5155 
5156 			ARX_SOUND_PlaySFX(SND_SPELL_NEGATE_MAGIC);
5157 
5158 			spells[i].exist = true;
5159 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5160 			spells[i].bDuration = true;
5161 			spells[i].fManaCostPerSecond = 2.f;
5162 			spells[i].tolive = (duration > -1) ? duration : 1000000;
5163 
5164 			CNegateMagic * effect = new CNegateMagic();
5165 			effect->spellinstance = i;
5166 			effect->Create(player.pos, MAKEANGLE(player.angle.b));
5167 			effect->SetDuration(spells[i].tolive);
5168 			spells[i].pSpellFx = effect;
5169 			spells[i].tolive = effect->GetDuration();
5170 
5171 			if(spells[i].caster == 0) {
5172 				spells[i].target = 0;
5173 			}
5174 
5175 			if(ValidIONum(spells[i].target)) {
5176 				LaunchAntiMagicField(i);
5177 			}
5178 
5179 			break;
5180 		}
5181 
5182 		case SPELL_INCINERATE: {
5183 
5184 			Entity * tio = entities[spells[i].target];
5185 			if((tio->ioflags & IO_NPC) && tio->_npcdata->life <= 0.f) {
5186 				return false;
5187 			}
5188 
5189 			ARX_SOUND_PlaySFX(SND_SPELL_INCINERATE);
5190 
5191 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_INCINERATE_LOOP,
5192 			                                       &spells[i].caster_pos, 1.f,
5193 			                                       ARX_SOUND_PLAY_LOOPED);
5194 
5195 			spells[i].exist = true;
5196 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5197 			spells[i].tolive = 20000;
5198 
5199 			tio->sfx_flag |= SFX_TYPE_YLSIDE_DEATH | SFX_TYPE_INCINERATE;
5200 			tio->sfx_time = (unsigned long)(arxtime);
5201 
5202 			ARX_SPELLS_AddSpellOn(spells[i].target, i);
5203 
5204 			break;
5205 		}
5206 
5207 		case SPELL_MASS_PARALYSE: {
5208 
5209 			ARX_SOUND_PlaySFX(SND_SPELL_MASS_PARALYSE);
5210 
5211 			spells[i].exist = true;
5212 			spells[i].tolive = (duration > -1) ? duration : 10000;
5213 			spells[i].longinfo2 = 0;
5214 
5215 			for(size_t ii = 0; ii < entities.size(); ii++) {
5216 
5217 				Entity * tio = entities[ii];
5218 				if(long(ii) == spells[i].caster || !tio || !(tio->ioflags & IO_NPC)) {
5219 					continue;
5220 				}
5221 
5222 				if(tio->show != SHOW_FLAG_IN_SCENE) {
5223 					continue;
5224 				}
5225 
5226 				if(tio->ioflags & IO_FREEZESCRIPT) {
5227 					continue;
5228 				}
5229 
5230 				if(fartherThan(tio->pos, entities[spells[i].caster]->pos, 500.f)) {
5231 					continue;
5232 				}
5233 
5234 				tio->ioflags |= IO_FREEZESCRIPT;
5235 
5236 				ARX_NPC_Kill_Spell_Launch(tio);
5237 				ARX_SPELLS_AddSpellOn(ii, i);
5238 
5239 				spells[i].longinfo2++;
5240 				spells[i].misc = realloc(spells[i].misc,
5241 				                         sizeof(long) * spells[i].longinfo2);
5242 				long * ptr = (long *)spells[i].misc;
5243 				ptr[spells[i].longinfo2 - 1] = ii;
5244 			}
5245 
5246 			break;
5247 		}
5248 
5249 		// level 10 spells
5250 
5251 		case SPELL_MASS_LIGHTNING_STRIKE: {
5252 
5253 			for(size_t ii = 0; ii < MAX_SPELLS; ii++) {
5254 				if(spells[ii].exist && spells[ii].type == typ) {
5255 					if(spells[ii].longinfo != -1) {
5256 						DynLight[spells[ii].longinfo].exist = 0;
5257 					}
5258 					spells[ii].longinfo = -1;
5259 					spells[ii].tolive = 0;
5260 				}
5261 			}
5262 
5263 			spells[i].exist = true;
5264 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5265 			spells[i].tolive = 5000; // TODO probably never read
5266 			spells[i].siz = 0;
5267 
5268 			spells[i].longinfo = GetFreeDynLight();
5269 			if(spells[i].longinfo != -1) {
5270 				long id = spells[i].longinfo;
5271 				DynLight[id].exist = 1;
5272 				DynLight[id].intensity = 1.8f;
5273 				DynLight[id].fallend = 450.f;
5274 				DynLight[id].fallstart = 380.f;
5275 				DynLight[id].rgb = Color3f(1.f, 0.75f, 0.75f);
5276 				DynLight[id].pos = spells[i].vsource;
5277 			}
5278 
5279 			long count = std::max(long(spells[i].caster_level), 1l);
5280 			CMassLightning * effect = new CMassLightning(count);
5281 			effect->spellinstance=i;
5282 
5283 			Vec3f target;
5284 			float beta;
5285 			if(spells[i].caster == 0) {
5286 				target = player.pos + Vec3f(0.f, 150.f, 0.f);
5287 				beta = player.angle.b;
5288 			} else {
5289 				Entity * io = entities[spells[i].caster];
5290 				target = io->pos + Vec3f(0.f, -20.f, 0.f);
5291 				beta = io->angle.b;
5292 			}
5293 			target.x -= EEsin(radians(MAKEANGLE(beta))) * 500.f;
5294 			target.z += EEcos(radians(MAKEANGLE(beta))) * 500.f;
5295 
5296 			effect->SetDuration(long(500 * spells[i].caster_level));
5297 			effect->Create(target, MAKEANGLE(player.angle.b));
5298 			spells[i].pSpellFx = effect;
5299 			spells[i].tolive = effect->GetDuration();
5300 
5301 			ARX_SOUND_PlaySFX(SND_SPELL_LIGHTNING_START);
5302 			spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_LIGHTNING_LOOP, &target,
5303 			                                       1.f, ARX_SOUND_PLAY_LOOPED);
5304 
5305 			// Draws White Flash on Screen
5306 			GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
5307 			GRenderer->SetRenderState(Renderer::AlphaBlending, true);
5308 			EERIEDrawBitmap(0.f, 0.f, float(DANAESIZX), float(DANAESIZY), 0.00009f,
5309 			                NULL, Color::white);
5310 			GRenderer->SetRenderState(Renderer::AlphaBlending, false);
5311 
5312 			break;
5313 		}
5314 
5315 		case SPELL_CONTROL_TARGET: {
5316 
5317 			if(!ValidIONum(spells[i].target)) {
5318 				return false;
5319 			}
5320 
5321 			long tcount = 0;
5322 			for(size_t ii = 1; ii < entities.size(); ii++) {
5323 
5324 				Entity * ioo = entities[ii];
5325 				if(!ioo || !(ioo->ioflags & IO_NPC)) {
5326 					continue;
5327 				}
5328 
5329 				if(ioo->_npcdata->life <= 0.f || ioo->show != SHOW_FLAG_IN_SCENE) {
5330 					continue;
5331 				}
5332 
5333 				if(ioo->groups.find("demon") == ioo->groups.end()) {
5334 					continue;
5335 				}
5336 
5337 				if(closerThan(ioo->pos, spells[i].caster_pos, 900.f)) {
5338 					tcount++;
5339 					std::ostringstream oss;
5340 					oss << entities[spells[i].target]->long_name();
5341 					oss << ' ' << long(spells[i].caster_level);
5342 					SendIOScriptEvent(ioo, SM_NULL, oss.str(), "npc_control");
5343 				}
5344 			}
5345 			if(tcount == 0) {
5346 				return false;
5347 			}
5348 
5349 			ARX_SOUND_PlaySFX(SND_SPELL_CONTROL_TARGET);
5350 
5351 			spells[i].exist = true;
5352 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5353 			spells[i].tolive = 1000;
5354 
5355 			CControlTarget * effect = new CControlTarget();
5356 			effect->spellinstance = i;
5357 			effect->Create(player.pos, MAKEANGLE(player.angle.b));
5358 			effect->SetDuration(spells[i].tolive);
5359 			spells[i].pSpellFx = effect;
5360 
5361 			break;
5362 		}
5363 
5364 		case SPELL_FREEZE_TIME: {
5365 
5366 			ARX_SOUND_PlaySFX(SND_SPELL_FREEZETIME);
5367 
5368 			float max_slowdown = std::max(0.f, GLOBAL_SLOWDOWN - 0.01f);
5369 			spells[i].siz = clamp(spells[i].caster_level * 0.08f, 0.f, max_slowdown);
5370 			GLOBAL_SLOWDOWN -= spells[i].siz;
5371 
5372 			spells[i].exist = true;
5373 			spells[i].tolive = (duration > -1) ? duration : 200000;
5374 			spells[i].bDuration = true;
5375 			spells[i].fManaCostPerSecond = 30.f * spells[i].siz;
5376 			spells[i].longinfo = (long)arxtime.get_updated();
5377 
5378 			break;
5379 		}
5380 
5381 		case SPELL_MASS_INCINERATE: {
5382 
5383 			ARX_SOUND_PlaySFX(SND_SPELL_MASS_INCINERATE);
5384 
5385 			spells[i].exist = true;
5386 			spells[i].lastupdate = spells[i].timcreation = (unsigned long)(arxtime);
5387 			spells[i].tolive = 20000;
5388 
5389 			long nb_targets=0;
5390 			for(size_t ii = 0; ii < entities.size(); ii++) {
5391 
5392 				Entity * tio = entities[ii];
5393 				if(long(ii) == spells[i].caster || !tio || !(tio->ioflags & IO_NPC)) {
5394 					continue;
5395 				}
5396 
5397 				if(tio->_npcdata->life <= 0.f || tio->show != SHOW_FLAG_IN_SCENE) {
5398 					continue;
5399 				}
5400 
5401 				if(fartherThan(tio->pos, entities[spells[i].caster]->pos, 500.f)) {
5402 					continue;
5403 				}
5404 
5405 				tio->sfx_flag |= SFX_TYPE_YLSIDE_DEATH | SFX_TYPE_INCINERATE;
5406 				tio->sfx_time = (unsigned long)(arxtime);
5407 				nb_targets++;
5408 				ARX_SPELLS_AddSpellOn(ii, i);
5409 			}
5410 
5411 			if(nb_targets) {
5412 				spells[i].snd_loop = ARX_SOUND_PlaySFX(SND_SPELL_INCINERATE_LOOP,
5413 				                                       &spells[i].caster_pos, 1.f,
5414 				                                       ARX_SOUND_PLAY_LOOPED);
5415 			} else {
5416 				spells[i].snd_loop = -1;
5417 			}
5418 
5419 			break;
5420 		}
5421 
5422 		case SPELL_TELEPORT: {
5423 
5424 			spells[i].exist = true;
5425 			spells[i].tolive = 7000;
5426 
5427 			ARX_SOUND_PlaySFX(SND_SPELL_TELEPORT, &spells[i].caster_pos);
5428 
5429 			if(spells[i].caster == 0) {
5430 				LASTTELEPORT = 0.f;
5431 			}
5432 
5433 			break;
5434 		}
5435 	}
5436 
5437 	if(notifyAll) {
5438 		SPELLCAST_Notify(i);
5439 	} else {
5440 		SPELLCAST_NotifyOnlyTarget(i);
5441 	}
5442 
5443 	return true;
5444 }
5445 
5446 // Used for specific Spell-End FX
ARX_SPELLS_Kill(long i)5447 void ARX_SPELLS_Kill(long i) {
5448 
5449 	static TextureContainer * tc4=TextureContainer::Load("graph/particles/smoke");
5450 
5451 	if (!spells[i].exist) return;
5452 
5453 	spells[i].exist=false;
5454 
5455 	// All Levels - Kill Light
5456 	if (spells[i].pSpellFx && spells[i].pSpellFx->lLightId != -1)
5457 	{
5458 		DynLight[spells[i].pSpellFx->lLightId].duration = 500;
5459 		DynLight[spells[i].pSpellFx->lLightId].time_creation = (unsigned long)(arxtime);
5460 	}
5461 
5462 	switch(spells[i].type) {
5463 
5464 		case SPELL_FIREBALL: {
5465 
5466 			if(spells[i].longinfo != -1) {
5467 				DynLight[spells[i].longinfo].duration = 500;
5468 				DynLight[spells[i].longinfo].time_creation = (unsigned long)(arxtime);
5469 			}
5470 			spells[i].longinfo = -1;
5471 
5472 			break;
5473 		}
5474 
5475 		case SPELL_LIGHTNING_STRIKE: {
5476 
5477 			if(spells[i].longinfo != -1) {
5478 				DynLight[spells[i].longinfo].duration = 200;
5479 				DynLight[spells[i].longinfo].time_creation = (unsigned long)(arxtime);
5480 			}
5481 			spells[i].longinfo = -1;
5482 
5483 			ARX_SOUND_Stop(spells[i].snd_loop);
5484 			ARX_SOUND_PlaySFX(SND_SPELL_LIGHTNING_END);
5485 
5486 			break;
5487 		}
5488 
5489 		case SPELL_MASS_LIGHTNING_STRIKE: {
5490 
5491 			if(spells[i].longinfo != -1) {
5492 				DynLight[spells[i].longinfo].duration = 200;
5493 				DynLight[spells[i].longinfo].time_creation = (unsigned long)(arxtime);
5494 			}
5495 			spells[i].longinfo = -1;
5496 
5497 			ARX_SOUND_Stop(spells[i].snd_loop);
5498 			ARX_SOUND_PlaySFX(SND_SPELL_LIGHTNING_END);
5499 
5500 			break;
5501 		}
5502 
5503 		case SPELL_REPEL_UNDEAD: {
5504 
5505 			if(spells[i].longinfo != -1) {
5506 				DynLight[spells[i].longinfo].duration = 200;
5507 				DynLight[spells[i].longinfo].time_creation = (unsigned long)(arxtime);
5508 			}
5509 			spells[i].longinfo = -1;
5510 
5511 			ARX_SOUND_Stop(spells[i].snd_loop);
5512 
5513 			break;
5514 		}
5515 
5516 		case SPELL_HARM:
5517 		case SPELL_LIFE_DRAIN:
5518 		case SPELL_MANA_DRAIN: {
5519 
5520 			if(spells[i].longinfo != -1) {
5521 				damages[spells[i].longinfo].exist = false;
5522 			}
5523 
5524 			if(spells[i].longinfo2 != -1) {
5525 				DynLight[spells[i].longinfo2].time_creation = (unsigned long)(arxtime);
5526 				DynLight[spells[i].longinfo2].duration = 600;
5527 			}
5528 
5529 			ARX_SOUND_Stop(spells[i].snd_loop);
5530 			break;
5531 		}
5532 
5533 		case SPELL_FLYING_EYE : {
5534 
5535 			ARX_SOUND_PlaySFX(SND_SPELL_EYEBALL_OUT);
5536 			eyeball.exist = -100;
5537 
5538 			for(long n = 0; n < 12; n++) {
5539 
5540 				PARTICLE_DEF * pd = createParticle();
5541 				if(!pd) {
5542 					break;
5543 				}
5544 
5545 				pd->ov = eyeball.pos + randomVec(-5.f, 5.f);
5546 				pd->move = randomVec(-2.f, 2.f);
5547 				pd->siz = 28.f;
5548 				pd->tolive = Random::get(2000, 6000);
5549 				pd->scale = Vec3f::repeat(12.f);
5550 				pd->timcreation = spells[i].lastupdate;
5551 				pd->tc = tc4;
5552 				pd->special = FADE_IN_AND_OUT | ROTATING | MODULATE_ROTATION | DISSIPATING;
5553 				pd->fparam = 0.0000001f;
5554 				pd->rgb = Color3f(0.7f, 0.7f, 1.f);
5555 			}
5556 
5557 			config.input.mouseLookToggle = bOldLookToggle;
5558 
5559 			break;
5560 		}
5561 
5562 		// Level 06
5563 
5564 		case SPELL_PARALYSE: {
5565 			ARX_SOUND_PlaySFX(SND_SPELL_PARALYSE_END);
5566 			break;
5567 		}
5568 
5569 		// Level 7
5570 
5571 		case SPELL_FIRE_FIELD: {
5572 			ARX_SOUND_Stop(spells[i].snd_loop);
5573 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_FIELD_END);
5574 			break;
5575 		}
5576 
5577 		case SPELL_ICE_FIELD: {
5578 			ARX_SOUND_Stop(spells[i].snd_loop);
5579 			ARX_SOUND_PlaySFX(SND_SPELL_ICE_FIELD_END);
5580 			break;
5581 		}
5582 
5583 		case SPELL_MASS_PARALYSE: {
5584 			ARX_SOUND_PlaySFX(SND_SPELL_PARALYSE_END);
5585 			break;
5586 		}
5587 
5588 		case SPELL_SUMMON_CREATURE: {
5589 
5590 			if(spells[i].pSpellFx->lLightId > -1) {
5591 				long id = spells[i].pSpellFx->lLightId;
5592 				DynLight[id].exist = 0;
5593 				spells[i].pSpellFx->lLightId=-1;
5594 			}
5595 
5596 			if(ValidIONum(spells[i].longinfo2) && spells[i].longinfo2 != 0) {
5597 
5598 				if(entities[spells[i].longinfo2]->scriptload
5599 				   && (entities[spells[i].longinfo2]->ioflags & IO_NOSAVE)) {
5600 
5601 					AddRandomSmoke(entities[spells[i].longinfo2], 100);
5602 					Vec3f posi = entities[spells[i].longinfo2]->pos;
5603 					posi.y -= 100.f;
5604 					MakeCoolFx(&posi);
5605 
5606 					long nn = GetFreeDynLight();
5607 					if(nn >= 0) {
5608 						DynLight[nn].exist = 1;
5609 						DynLight[nn].intensity = 0.7f + 2.f * rnd();
5610 						DynLight[nn].fallend = 600.f;
5611 						DynLight[nn].fallstart = 400.f;
5612 						DynLight[nn].rgb = Color3f(1.0f, 0.8f, 0.0f);
5613 						DynLight[nn].pos = posi;
5614 						DynLight[nn].duration = 600;
5615 					}
5616 
5617 					ARX_INTERACTIVE_DestroyIO(entities[spells[i].longinfo2]);
5618 				}
5619 			}
5620 
5621 			spells[i].longinfo2 = 0;
5622 			break;
5623 		}
5624 
5625 		case SPELL_FAKE_SUMMON: {
5626 
5627 			if(spells[i].pSpellFx->lLightId > -1) {
5628 				long id = spells[i].pSpellFx->lLightId;
5629 				DynLight[id].exist = 0;
5630 				spells[i].pSpellFx->lLightId = -1;
5631 			}
5632 
5633 			break;
5634 		}
5635 
5636 		default: break;
5637 	}
5638 
5639 	delete spells[i].pSpellFx, spells[i].pSpellFx = NULL;
5640 }
5641 
5642 
5643 EYEBALL_DEF eyeball;
5644 
5645 Anglef cabalangle;
5646 Vec3f cabalpos;
5647 Vec3f cabalscale;
5648 Color3f cabalcolor;
5649 
5650 
ARX_SPELLS_ApplyFireProtection(Entity * io,float damages)5651 float ARX_SPELLS_ApplyFireProtection(Entity * io,float damages)
5652 {
5653 	if (io)
5654 	{
5655 		long idx=ARX_SPELLS_GetSpellOn(io,SPELL_FIRE_PROTECTION);
5656 
5657 		if (idx>=0)
5658 		{
5659 			float modif=1.f-((float)spells[idx].caster_level*( 1.0f / 10 ));
5660 
5661 			if (modif>1.f) modif=1.f;
5662 			else if (modif<0.f) modif=0.f;
5663 
5664 			damages*=modif;
5665 		}
5666 
5667 		if (io->ioflags & IO_NPC)
5668 		{
5669 			damages-=io->_npcdata->resist_fire*( 1.0f / 100 )*damages;
5670 
5671 			if (damages<0.f) damages=0.f;
5672 		}
5673 	}
5674 
5675 	return damages;
5676 }
ARX_SPELLS_ApplyColdProtection(Entity * io,float damages)5677 float ARX_SPELLS_ApplyColdProtection(Entity * io,float damages)
5678 {
5679 	long idx=ARX_SPELLS_GetSpellOn(io,SPELL_COLD_PROTECTION);
5680 
5681 	if (idx>=0)
5682 	{
5683 		float modif=1.f-((float)spells[idx].caster_level*( 1.0f / 10 ));
5684 
5685 		if (modif>1.f) modif=1.f;
5686 		else if (modif<0.f) modif=0.f;
5687 
5688 		damages*=modif;
5689 	}
5690 
5691 	return damages;
5692 }
5693 
5694 //*************************************************************************************
5695 // Updates all currently working spells.
5696 //*************************************************************************************
ARX_SPELLS_Update()5697 void ARX_SPELLS_Update()
5698 {
5699 
5700 	unsigned long tim;
5701 	long framediff,framediff3;
5702 
5703 	ucFlick++;
5704 
5705 	tim = (unsigned long)(arxtime);
5706 
5707 	for(size_t i = 0; i < MAX_SPELLS; i++) {
5708 
5709 		if (!GLOBAL_MAGIC_MODE) spells[i].tolive=0;
5710 
5711 		if (spells[i].exist)
5712 		{
5713 			if (spells[i].bDuration && !CanPayMana(i,spells[i].fManaCostPerSecond * (float)FrameDiff * ( 1.0f / 1000 ), false))
5714 				ARX_SPELLS_Fizzle(i);
5715 
5716 			framediff=spells[i].timcreation+spells[i].tolive-tim;
5717 			framediff3=tim-spells[i].lastupdate;
5718 
5719 			if (framediff<0)
5720 			{
5721 				SPELLEND_Notify(i);
5722 
5723 				switch (spells[i].type)
5724 				{
5725 				//----------------------------------------------------------------------------
5726 				case SPELL_TELEPORT:
5727 					ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE, &spells[i].caster_pos);
5728 				break;
5729 				//----------------------------------------------------------------------------
5730 
5731 				//****************************************************************************
5732 				// LEVEL 1 SPELLS -----------------------------------------------------------------------------
5733 				//----------------------------------------------------------------------------
5734 				case SPELL_MAGIC_SIGHT:
5735 
5736 					if (spells[i].caster==0)
5737 					{
5738 						Project.improve=0;
5739 						ARX_SOUND_Stop(spells[i].snd_loop);
5740 					}
5741 
5742 					ARX_SOUND_PlaySFX(SND_SPELL_VISION_START, &spells[i].caster_pos);
5743 				break;
5744 				//-----------------------------------------------------------------------------
5745 				case SPELL_MAGIC_MISSILE:
5746 				{
5747 					if (spells[i].longinfo!=-1)
5748 						DynLight[spells[i].longinfo].exist=0;
5749 				}
5750 				break;
5751 				//----------------------------------------------------------------------------
5752 				case SPELL_IGNIT:
5753 				{
5754 					CIgnit *pIgnit;
5755 					pIgnit=(CIgnit *)spells[i].pSpellFx;
5756 					pIgnit->Action(1);
5757 				}
5758 				break;
5759 				//----------------------------------------------------------------------------
5760 				case SPELL_DOUSE:
5761 				{
5762 					CDoze *pDoze;
5763 					pDoze=(CDoze *)spells[i].pSpellFx;
5764 					pDoze->Action(0);
5765 				}
5766 				break;
5767 				//----------------------------------------------------------------------------------
5768 				//**********************************************************************************
5769 				// LEVEL 2 -------------------------------------------------------------------------
5770 				//----------------------------------------------------------------------------------
5771 				case SPELL_DETECT_TRAP:
5772 
5773 					if (spells[i].caster==0)
5774 					{
5775 						ARX_SOUND_Stop(spells[i].snd_loop);
5776 					}
5777 
5778 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5779 
5780 				break;
5781 				//----------------------------------------------------------------------------
5782 				case SPELL_ARMOR:
5783 				{
5784 					ARX_SOUND_Stop(spells[i].snd_loop);
5785 					ARX_SOUND_PlaySFX(SND_SPELL_ARMOR_END, &spells[i].caster_pos);
5786 					Entity * io=entities[spells[i].target];
5787 
5788 					if (spells[i].longinfo)
5789 					{
5790 						io->halo.flags&=~HALO_ACTIVE;
5791 						ARX_HALO_SetToNative(io);
5792 					}
5793 
5794 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5795 				}
5796 				break;
5797 				//----------------------------------------------------------------------------
5798 				case SPELL_LOWER_ARMOR:
5799 				{
5800 					Entity * io=entities[spells[i].target];
5801 
5802 					if (spells[i].longinfo)
5803 					{
5804 						io->halo.flags&=~HALO_ACTIVE;
5805 						ARX_HALO_SetToNative(io);
5806 					}
5807 
5808 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5809 				}
5810 				break;
5811 				//----------------------------------------------------------------------------------
5812 				//**********************************************************************************
5813 				// LEVEL 3 -------------------------------------------------------------------------
5814 				//----------------------------------------------------------------------------------
5815 				case SPELL_SPEED:
5816 
5817 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5818 
5819 					if(spells[i].target >= 0 && spells[i].target < long(entities.size()))
5820 					{
5821 						if (entities[spells[i].target])
5822 							entities[spells[i].target]->speed_modif-=spells[i].caster_level*( 1.0f / 10 );
5823 					}
5824 
5825 					if (spells[i].caster == 0) ARX_SOUND_Stop(spells[i].snd_loop);
5826 
5827 					ARX_SOUND_PlaySFX(SND_SPELL_SPEED_END, &spells[i].caster_pos);
5828 				break;
5829 				//----------------------------------------------------------------------------------
5830 				case SPELL_FIREBALL:
5831 					ARX_SOUND_Stop(spells[i].snd_loop);
5832 				break;
5833 				//----------------------------------------------------------------------------------
5834 				//**********************************************************************************
5835 				// LEVEL 4 -------------------------------------------------------------------------
5836 				//----------------------------------------------------------------------------------
5837 				case SPELL_BLESS:
5838 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5839 				break;
5840 				case SPELL_CURSE:
5841 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5842 				break;
5843 				//----------------------------------------------------------------------------
5844 				case SPELL_TELEKINESIS:
5845 
5846 					if (spells[i].caster==0)
5847 						Project.telekinesis=0;
5848 
5849 					ARX_SOUND_PlaySFX(SND_SPELL_TELEKINESIS_END, &spells[i].caster_pos);
5850 				break;
5851 				case SPELL_FIRE_PROTECTION:
5852 					ARX_SOUND_Stop(spells[i].snd_loop);
5853 					ARX_SOUND_PlaySFX(SND_SPELL_FIRE_PROTECTION_END);
5854 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);;
5855 
5856 					if (ValidIONum(spells[i].target))
5857 						ARX_HALO_SetToNative(entities[spells[i].target]);
5858 
5859 				break;
5860 				case SPELL_COLD_PROTECTION:
5861 					ARX_SOUND_Stop(spells[i].snd_loop);
5862 					ARX_SOUND_PlaySFX(SND_SPELL_COLD_PROTECTION_END);
5863 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);;
5864 
5865 					if (ValidIONum(spells[i].target))
5866 						ARX_HALO_SetToNative(entities[spells[i].target]);
5867 
5868 				break;
5869 				//----------------------------------------------------------------------------------
5870 				//**********************************************************************************
5871 				// LEVEL 5 -------------------------------------------------------------------------
5872 				//----------------------------------------------------------------------------------
5873 				//----------------------------------------------------------------------------
5874 				case SPELL_LEVITATE:
5875 				{
5876 
5877 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5878 
5879 					if (spells[i].target==0)
5880 						player.levitate=0;
5881 				}
5882 				break;
5883 				//----------------------------------------------------------------------------------
5884 				//**********************************************************************************
5885 				// LEVEL 6 SPELLS ------------------------------------------------------------------
5886 				//----------------------------------------------------------------------------------
5887 				case SPELL_PARALYSE:
5888 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5889 					entities[spells[i].target]->ioflags&=~IO_FREEZESCRIPT;
5890 				break;
5891 				//----------------------------------------------------------------------------------
5892 				case SPELL_RISE_DEAD:
5893 					ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &spells[i].caster_pos);
5894 
5895 					if (ValidIONum(spells[i].longinfo) && spells[i].longinfo!=0)
5896 					{
5897 						if (	(entities[spells[i].longinfo]->scriptload)
5898 							&&	(entities[spells[i].longinfo]->ioflags & IO_NOSAVE)	)
5899 						{
5900 							AddRandomSmoke(entities[spells[i].longinfo],100);
5901 							Vec3f posi = entities[spells[i].longinfo]->pos;
5902 							posi.y-=100.f;
5903 							MakeCoolFx(&posi);
5904 							long nn=GetFreeDynLight();
5905 
5906 							if(nn >= 0) {
5907 								DynLight[nn].exist=1;
5908 								DynLight[nn].intensity = 0.7f + 2.f*rnd();
5909 								DynLight[nn].fallend = 600.f;
5910 								DynLight[nn].fallstart = 400.f;
5911 								DynLight[nn].rgb = Color3f(1.0f, 0.8f, 0.f);
5912 								DynLight[nn].pos = posi;
5913 								DynLight[nn].duration = 600;
5914 							}
5915 
5916 							ARX_INTERACTIVE_DestroyIO(entities[spells[i].longinfo]);
5917 						}
5918 					}
5919 
5920 				break;
5921 				case SPELL_CREATE_FIELD:
5922 					CCreateField * pCreateField;
5923 					pCreateField = (CCreateField *) spells[i].pSpellFx;
5924 
5925 					if (	(pCreateField)
5926 						&&	(pCreateField->lLightId != -1)	)
5927 					{
5928 						long id=pCreateField->lLightId;
5929 						DynLight[id].duration=800;
5930 					}
5931 
5932 					if(ValidIONum(spells[i].longinfo)) {
5933 						delete entities[spells[i].longinfo];
5934 					}
5935 
5936 				break;
5937 				//----------------------------------------------------------------------------
5938 				case SPELL_SLOW_DOWN:
5939 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5940 
5941 					if(spells[i].target >= 0 && spells[i].target < long(entities.size()))
5942 					{
5943 						if (entities[spells[i].target])
5944 							entities[spells[i].target]->speed_modif+=spells[i].caster_level*( 1.0f / 20 );
5945 					}
5946 
5947 				break;
5948 				//----------------------------------------------------------------------------------
5949 				//**********************************************************************************
5950 				// LEVEL 7 -------------------------------------------------------------------------
5951 				//----------------------------------------------------------------------------------
5952 				case SPELL_ICE_FIELD:
5953 
5954 					if (spells[i].longinfo!=-1)
5955 						damages[spells[i].longinfo].exist=false;
5956 
5957 				break;
5958 				case SPELL_FIRE_FIELD:
5959 
5960 					if (spells[i].longinfo!=-1)
5961 						damages[spells[i].longinfo].exist=false;
5962 
5963 				break;
5964 				//----------------------------------------------------------------------------
5965 				case SPELL_LIGHTNING_STRIKE:
5966 					ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &spells[i].caster_pos);
5967 				break;
5968 				//----------------------------------------------------------------------------
5969 				case SPELL_FLYING_EYE:
5970 					ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE, &entities[spells[i].caster]->pos);
5971 				break;
5972 				case SPELL_CONFUSE:
5973 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5974 				break;
5975 				//----------------------------------------------------------------------------------
5976 				//**********************************************************************************
5977 				// LEVEL 8 -------------------------------------------------------------------------
5978 				//----------------------------------------------------------------------------------
5979 				case SPELL_EXPLOSION:
5980 				break;
5981 				//----------------------------------------------------------------------------
5982 				case SPELL_INVISIBILITY:
5983 					{
5984 						if (ValidIONum(spells[i].target))
5985 						{
5986 							entities[spells[i].target]->gameFlags&=~GFLAG_INVISIBILITY;
5987 							ARX_SOUND_PlaySFX(SND_SPELL_INVISIBILITY_END, &entities[spells[i].target]->pos);
5988 							ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
5989 						}
5990 					}
5991 				break;
5992 				//----------------------------------------------------------------------------------
5993 				//**********************************************************************************
5994 				// LEVEL 9 -------------------------------------------------------------------------
5995 				//----------------------------------------------------------------------------------
5996 				//----------------------------------------------------------------------------
5997 				case SPELL_MASS_PARALYSE:
5998 					{
5999 						long * ptr=(long *)spells[i].misc;
6000 
6001 						for (long in=0;in<spells[i].longinfo2;in++)
6002 						{
6003 
6004 							if (ValidIONum(ptr[in]))
6005 							{
6006 								ARX_SPELLS_RemoveSpellOn(ptr[in],i);
6007 								entities[ptr[in]]->ioflags&=~IO_FREEZESCRIPT;
6008 							}
6009 						}
6010 
6011 						if (ptr) free(spells[i].misc);
6012 
6013 						spells[i].misc=NULL;
6014 					}
6015 				break;
6016 				case SPELL_SUMMON_CREATURE :
6017 						ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &spells[i].caster_pos);
6018 
6019 						if (spells[i].pSpellFx->lLightId > -1)
6020 					{
6021 						long id = spells[i].pSpellFx->lLightId;
6022 						DynLight[id].exist = 0;
6023 						spells[i].pSpellFx->lLightId=-1;
6024 					}
6025 
6026 						// need to killio
6027 				break;
6028 				case SPELL_FAKE_SUMMON :
6029 					ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &spells[i].caster_pos);
6030 
6031 					if (spells[i].pSpellFx->lLightId > -1)
6032 					{
6033 						long id = spells[i].pSpellFx->lLightId;
6034 						DynLight[id].exist = 0;
6035 						spells[i].pSpellFx->lLightId=-1;
6036 					}
6037 
6038 				break;
6039 				case SPELL_INCINERATE:
6040 					ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
6041 					ARX_SOUND_Stop(spells[i].snd_loop);
6042 					ARX_SOUND_PlaySFX(SND_SPELL_INCINERATE_END, &spells[i].caster_pos);
6043 				break;
6044 				//----------------------------------------------------------------------------------
6045 				//**********************************************************************************
6046 				// LEVEL 10 ------------------------------------------------------------------------
6047 				//----------------------------------------------------------------------------------
6048 				case SPELL_FREEZE_TIME: {
6049 					GLOBAL_SLOWDOWN += spells[i].siz;
6050 					ARX_SOUND_PlaySFX(SND_SPELL_TELEKINESIS_END, &spells[i].caster_pos);
6051 					break;
6052 				}
6053 				case SPELL_MASS_INCINERATE:
6054 					ARX_SPELLS_RemoveMultiSpellOn(i);
6055 					ARX_SOUND_Stop(spells[i].snd_loop);
6056 					ARX_SOUND_PlaySFX(SND_SPELL_INCINERATE_END, &spells[i].caster_pos);
6057 				break;
6058 				default: break;
6059 				//----------------------------------------------------------------------------------
6060 			}
6061 
6062 			ARX_SPELLS_Kill(i);
6063 			continue;
6064 		}
6065 
6066 		//******************************************************************************************
6067 		//******************************************************************************************
6068 		//******************************************************************************************
6069 		//******************************************************************************************
6070 		//******************************************************************************************
6071 		//******************************************************************************************
6072 
6073 	if (spells[i].exist)
6074 		switch (spells[i].type)
6075 		{
6076 			case SPELL_DISPELL_FIELD: break;
6077 			case SPELL_NONE: break;
6078 			//**************************************************************************************
6079 			// LEVEL 1 -----------------------------------------------------------------------------
6080 			case SPELL_MAGIC_MISSILE:
6081 				{
6082 					CSpellFx *pCSpellFX = spells[i].pSpellFx;
6083 
6084 					if (pCSpellFX)
6085 					{
6086 						CMultiMagicMissile *pMMM = (CMultiMagicMissile *) pCSpellFX;
6087 							pMMM->CheckCollision();
6088 
6089 						// Update
6090 						pCSpellFX->Update(FrameDiff);
6091 
6092 						if (pCSpellFX->Render()==-1)
6093 							spells[i].tolive=0;
6094 					}
6095 				}
6096 			break;
6097 			//---------------------------------------------------------------------------------------
6098 			case SPELL_IGNIT:
6099 			case SPELL_DOUSE:
6100 			{
6101 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6102 
6103 				if (pCSpellFX)
6104 				{
6105 					pCSpellFX->Update(FrameDiff);
6106 				}
6107 			}
6108 			break;
6109 			//---------------------------------------------------------------------------------------
6110 			case SPELL_ACTIVATE_PORTAL:
6111 			{
6112 			}
6113 			break;
6114 			//---------------------------------------------------------------------------------------
6115 			//***************************************************************************************
6116 			// LEVEL 2 -----------------------------------------------------------------------------
6117 			case SPELL_HEAL: // guérit les ennemis collés
6118 			{
6119 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6120 
6121 				if (pCSpellFX)
6122 				{
6123 					pCSpellFX->Update(FrameDiff);
6124 					pCSpellFX->Render();
6125 				}
6126 
6127 				CHeal * ch=(CHeal *)pCSpellFX;
6128 
6129 				if (ch)
6130 				for(size_t ii = 0; ii < entities.size(); ii++) {
6131 					if ((entities[ii])
6132 						&& (entities[ii]->show==SHOW_FLAG_IN_SCENE)
6133 						&& (entities[ii]->gameFlags & GFLAG_ISINTREATZONE)
6134 								        && (entities[ii]->ioflags & IO_NPC)
6135 						&& (entities[ii]->_npcdata->life>0.f)
6136 						)
6137 					{
6138 						float dist;
6139 
6140 						if (long(ii) == spells[i].caster) dist=0;
6141 						else dist=fdist(ch->eSrc, entities[ii]->pos);
6142 
6143 						if (dist<300.f)
6144 						{
6145 							float gain=((rnd()*1.6f+0.8f)*spells[i].caster_level)*(300.f-dist)*( 1.0f / 300 )*framedelay*( 1.0f / 1000 );
6146 
6147 							if (ii==0)
6148 							{
6149 								if (!BLOCK_PLAYER_CONTROLS)
6150 									player.life=std::min(player.life+gain,player.Full_maxlife);
6151 							}
6152 							else entities[ii]->_npcdata->life=std::min(entities[ii]->_npcdata->life+gain,entities[ii]->_npcdata->maxlife);
6153 						}
6154 					}
6155 				}
6156 			}
6157 			break;
6158 			//------------------------------------------------------------------------------------
6159 			case SPELL_DETECT_TRAP:
6160 			{
6161 				if (spells[i].caster == 0)
6162 				{
6163 					ARX_SOUND_RefreshPosition(spells[i].snd_loop);
6164 				}
6165 
6166 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6167 
6168 				if (pCSpellFX)
6169 				{
6170 					pCSpellFX->Update(FrameDiff);
6171 					pCSpellFX->Render();
6172 				}
6173 			}
6174 			break;
6175 			//------------------------------------------------------------------------------------
6176 			case SPELL_ARMOR:
6177 			case SPELL_LOWER_ARMOR:
6178 			{
6179 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6180 
6181 				if (pCSpellFX)
6182 				{
6183 					pCSpellFX->Update(FrameDiff);
6184 					pCSpellFX->Render();
6185 				}
6186 			}
6187 			break;
6188 			//--------------------------------------------------------------------------------------
6189 			case SPELL_HARM:
6190 			{
6191 				if ( (cabal!=NULL) )
6192 				{
6193 					float refpos;
6194 					float scaley;
6195 
6196 					if (spells[i].caster==0) scaley=90.f;
6197 					else scaley=EEfabs(entities[spells[i].caster]->physics.cyl.height*( 1.0f / 2 ))+30.f;
6198 
6199 
6200 					float mov=EEsin((float)arxtime.get_frame_time()*( 1.0f / 800 ))*scaley;
6201 
6202 					if (spells[i].caster==0)
6203 					{
6204 								cabalpos.x = player.pos.x;
6205 						cabalpos.y=player.pos.y+60.f-mov;
6206 								cabalpos.z = player.pos.z;
6207 						refpos=player.pos.y+60.f;
6208 					}
6209 					else
6210 					{
6211 								cabalpos.x = entities[spells[i].caster]->pos.x;
6212 						cabalpos.y=entities[spells[i].caster]->pos.y-scaley-mov;
6213 								cabalpos.z = entities[spells[i].caster]->pos.z;
6214 						refpos=entities[spells[i].caster]->pos.y-scaley;
6215 					}
6216 
6217 					float Es=EEsin((float)arxtime.get_frame_time()*( 1.0f / 800 ) + radians(scaley));
6218 
6219 					if (spells[i].longinfo2!=-1)
6220 					{
6221 						DynLight[spells[i].longinfo2].pos.x = cabalpos.x;
6222 						DynLight[spells[i].longinfo2].pos.y = refpos;
6223 						DynLight[spells[i].longinfo2].pos.z = cabalpos.z;
6224 						DynLight[spells[i].longinfo2].rgb.r=rnd()*0.2f+0.8f;
6225 						DynLight[spells[i].longinfo2].rgb.g=rnd()*0.2f+0.6f;
6226 						DynLight[spells[i].longinfo2].fallstart=Es*1.5f;
6227 					}
6228 
6229 					GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
6230 					GRenderer->SetRenderState(Renderer::AlphaBlending, true);
6231 					GRenderer->SetRenderState(Renderer::DepthWrite, false);
6232 					cabalangle.b=spells[i].fdata+(float)FrameDiff*0.1f;
6233 					spells[i].fdata=cabalangle.b;
6234 
6235 							cabalangle.g = 0.f;
6236 							cabalcolor.r = 0.8f;
6237 							cabalcolor.g = 0.4f;
6238 							cabalcolor.b = 0.f;
6239 					cabalscale = Vec3f::repeat(Es);
6240 					DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
6241 					mov=EEsin((float)(arxtime.get_frame_time()-30.f)*( 1.0f / 800 ))*scaley;
6242 					cabalpos.y = refpos - mov;
6243 							cabalcolor.b = 0.f;
6244 							cabalcolor.g = 3.f;
6245 							cabalcolor.r = 0.5f;
6246 					DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
6247 					mov=EEsin((float)(arxtime.get_frame_time()-60.f)*( 1.0f / 800 ))*scaley;
6248 					cabalpos.y=refpos-mov;
6249 							cabalcolor.b = 0.f;
6250 							cabalcolor.g = 0.1f;
6251 							cabalcolor.r = 0.25f;
6252 					DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
6253 					mov=EEsin((float)(arxtime.get_frame_time()-120.f)*( 1.0f / 800 ))*scaley;
6254 					cabalpos.y=refpos-mov;
6255 							cabalcolor.b = 0.f;
6256 							cabalcolor.g = 0.1f;
6257 							cabalcolor.r = 0.15f;
6258 					DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
6259 					GRenderer->SetRenderState(Renderer::AlphaBlending, false);
6260 					GRenderer->SetRenderState(Renderer::DepthWrite, true);
6261 				}
6262 			}
6263 			break;
6264 			//--------------------------------------------------------------------------------------
6265 			//**************************************************************************************
6266 			// LEVEL 3 SPELLS -----------------------------------------------------------------------------
6267 			case SPELL_FIREBALL:
6268 			{
6269 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6270 
6271 				if (pCSpellFX)
6272 				{
6273 					CFireBall *pCF = (CFireBall*) pCSpellFX;
6274 
6275 					if (spells[i].longinfo==-1) spells[i].longinfo=GetFreeDynLight();
6276 
6277 					if (spells[i].longinfo!=-1)
6278 					{
6279 						long id=spells[i].longinfo;
6280 						EERIE_LIGHT * el=&DynLight[id];
6281 						el->exist=1;
6282 						el->pos = pCF->eCurPos;
6283 						el->intensity = 2.2f;
6284 						el->fallend = 500.f;
6285 						el->fallstart = 400.f;
6286 						el->rgb.r = 1.0f-rnd()*0.3f;
6287 						el->rgb.g = 0.6f-rnd()*0.1f;
6288 						el->rgb.b = 0.3f-rnd()*0.1f;
6289 					}
6290 
6291 					EERIE_SPHERE sphere;
6292 					sphere.origin = pCF->eCurPos;
6293 					sphere.radius=std::max(spells[i].caster_level*2.f,12.f);
6294 					#define MIN_TIME_FIREBALL 2000
6295 
6296 					if (pCF->pPSFire.iParticleNbMax)
6297 					{
6298 						if (pCF->ulCurrentTime > MIN_TIME_FIREBALL)
6299 						{
6300 							SpawnFireballTail(&pCF->eCurPos,&pCF->eMove,(float)spells[i].caster_level,0);
6301 						}
6302 						else
6303 						{
6304 							if (rnd()<0.9f)
6305 							{
6306 								Vec3f move = Vec3f::ZERO;
6307 								float dd=(float)pCF->ulCurrentTime / (float)MIN_TIME_FIREBALL*10;
6308 
6309 								if (dd>spells[i].caster_level) dd=spells[i].caster_level;
6310 
6311 								if (dd<1) dd=1;
6312 
6313 								SpawnFireballTail(&pCF->eCurPos,&move,(float)dd,1);
6314 							}
6315 						}
6316 					}
6317 
6318 					if (pCF->bExplo == false)
6319 					if (CheckAnythingInSphere(&sphere,spells[i].caster,CAS_NO_SAME_GROUP))
6320 					{
6321 						ARX_BOOMS_Add(&pCF->eCurPos);
6322 						LaunchFireballBoom(&pCF->eCurPos,(float)spells[i].caster_level);
6323 						pCF->pPSFire.iParticleNbMax = 0;
6324 						pCF->pPSFire2.iParticleNbMax = 0;
6325 						pCF->eMove *= 0.5f;
6326 						pCF->pPSSmoke.iParticleNbMax = 0;
6327 						pCF->SetTTL(1500);
6328 						pCF->bExplo = true;
6329 
6330 						DoSphericDamage(&pCF->eCurPos,3.f*spells[i].caster_level,30.f*spells[i].caster_level,DAMAGE_AREA,DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL,spells[i].caster);
6331 						spells[i].tolive=0;
6332 						ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, &sphere.origin);
6333 						ARX_NPC_SpawnAudibleSound(&sphere.origin, entities[spells[i].caster]);
6334 								}
6335 
6336 					pCSpellFX->Update(FrameDiff);
6337 					ARX_SOUND_RefreshPosition(spells[i].snd_loop, &pCF->eCurPos);
6338 				}
6339 			}
6340 			break;
6341 			//-----------------------------------------------------------------------------------------
6342 			case SPELL_SPEED:
6343 
6344 			if (spells[i].pSpellFx)
6345 			{
6346 				spells[i].pSpellFx->Update(FrameDiff);
6347 				spells[i].pSpellFx->Render();
6348 
6349 				if (spells[i].caster == 0) ARX_SOUND_RefreshPosition(spells[i].snd_loop);
6350 			}
6351 
6352 			break;
6353 			//-----------------------------------------------------------------------------------------
6354 			case SPELL_CREATE_FOOD:
6355 			case SPELL_ICE_PROJECTILE:
6356 			case SPELL_DISPELL_ILLUSION:
6357 
6358 			if (spells[i].pSpellFx)
6359 			{
6360 				spells[i].pSpellFx->Update(FrameDiff);
6361 				spells[i].pSpellFx->Render();
6362 			}
6363 
6364 			break;
6365 			//-----------------------------------------------------------------------------------------
6366 			//*****************************************************************************************
6367 			// LEVEL 4 SPELLS -----------------------------------------------------------------------------
6368 			case SPELL_BLESS:
6369 			{
6370 				if (spells[i].pSpellFx)
6371 				{
6372 					CBless * pBless=(CBless *)spells[i].pSpellFx;
6373 
6374 					if (pBless)
6375 					{
6376 						if (ValidIONum(spells[i].target))
6377 						{
6378 							pBless->eSrc = entities[spells[i].target]->pos;
6379 							Anglef angle = Anglef::ZERO;
6380 
6381 							if (spells[i].target==0)
6382 								angle.b=player.angle.b;
6383 							else
6384 								angle.b=entities[spells[i].target]->angle.b;
6385 
6386 							pBless->Set_Angle(angle);
6387 						}
6388 					}
6389 
6390 					spells[i].pSpellFx->Update(FrameDiff);
6391 					spells[i].pSpellFx->Render();
6392 				}
6393 			}
6394 			break;
6395 			//-----------------------------------------------------------------------------------------
6396 			case SPELL_CURSE:
6397 
6398 			if (spells[i].pSpellFx)
6399 			{
6400 				CCurse * curse=(CCurse *)spells[i].pSpellFx;
6401 				Vec3f target = Vec3f::ZERO;
6402 
6403 				if ((spells[i].target>=0) && (entities[spells[i].target]))
6404 				{
6405 					target = entities[spells[i].target]->pos;
6406 
6407 					if (spells[i].target==0) target.y-=200.f;
6408 					else target.y+=entities[spells[i].target]->physics.cyl.height-30.f;
6409 				}
6410 
6411 				curse->Update(checked_range_cast<unsigned long>(FrameDiff));
6412 
6413 				curse->eTarget = target;
6414 				curse->Render();
6415 				GRenderer->SetCulling(Renderer::CullNone);
6416 			}
6417 
6418 			break;
6419 			//-----------------------------------------------------------------------------------------
6420 			case SPELL_FIRE_PROTECTION:
6421 				spells[i].pSpellFx->Update(FrameDiff);
6422 				spells[i].pSpellFx->Render();
6423 			break;
6424 			//-----------------------------------------------------------------------------------------
6425 			case SPELL_COLD_PROTECTION:
6426 				spells[i].pSpellFx->Update(FrameDiff);
6427 				spells[i].pSpellFx->Render();
6428 			break;
6429 			//-----------------------------------------------------------------------------------------
6430 			//*****************************************************************************************
6431 			// LEVEL 5 SPELLS -----------------------------------------------------------------------------
6432 			//-----------------------------------------------------------------------------------------
6433 			case SPELL_CURE_POISON:
6434 			{
6435 				if (spells[i].pSpellFx)
6436 				{
6437 					spells[i].pSpellFx->Update(FrameDiff);
6438 					spells[i].pSpellFx->Render();
6439 				}
6440 			}
6441 			break;
6442 			//-----------------------------------------------------------------------------------------
6443 			case SPELL_RUNE_OF_GUARDING:
6444 			{
6445 				if (spells[i].pSpellFx)
6446 				{
6447 					spells[i].pSpellFx->Update(FrameDiff);
6448 					spells[i].pSpellFx->Render();
6449 					CRuneOfGuarding * pCRG=(CRuneOfGuarding *)spells[i].pSpellFx;
6450 
6451 					if (pCRG)
6452 					{
6453 						EERIE_SPHERE sphere;
6454 						sphere.origin = pCRG->eSrc;
6455 						sphere.radius=std::max(spells[i].caster_level*15.f,50.f);
6456 
6457 						if (CheckAnythingInSphere(&sphere,spells[i].caster,CAS_NO_SAME_GROUP | CAS_NO_BACKGROUND_COL | CAS_NO_ITEM_COL| CAS_NO_FIX_COL | CAS_NO_DEAD_COL))
6458 						{
6459 							ARX_BOOMS_Add(&pCRG->eSrc);
6460 							LaunchFireballBoom(&pCRG->eSrc,(float)spells[i].caster_level);
6461 							DoSphericDamage(&pCRG->eSrc,4.f*spells[i].caster_level,30.f*spells[i].caster_level,DAMAGE_AREA,DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL,spells[i].caster);
6462 							spells[i].tolive=0;
6463 							ARX_SOUND_PlaySFX(SND_SPELL_RUNE_OF_GUARDING_END, &sphere.origin);
6464 						}
6465 					}
6466 				}
6467 			}
6468 			break;
6469 			case SPELL_REPEL_UNDEAD:
6470 			{
6471 				if (spells[i].pSpellFx)
6472 				{
6473 					spells[i].pSpellFx->Update(FrameDiff);
6474 					spells[i].pSpellFx->Render();
6475 
6476 					if (spells[i].target == 0)
6477 						ARX_SOUND_RefreshPosition(spells[i].snd_loop);
6478 				}
6479 			}
6480 			break;
6481 			//-----------------------------------------------------------------------------------------
6482 			case SPELL_POISON_PROJECTILE:
6483 
6484 			if (spells[i].pSpellFx)
6485 			{
6486 				spells[i].pSpellFx->Update(FrameDiff);
6487 				spells[i].pSpellFx->Render();
6488 						}
6489 
6490 			break;
6491 			//-----------------------------------------------------------------------------------------
6492 			case SPELL_LEVITATE:
6493 			{
6494 				CLevitate *pLevitate=(CLevitate *)spells[i].pSpellFx;
6495 				Vec3f target;
6496 
6497 				if (spells[i].target==0)
6498 				{
6499 					target.x=player.pos.x;
6500 					target.y=player.pos.y+150.f;
6501 					target.z=player.pos.z;
6502 					player.levitate=1;
6503 				}
6504 				else
6505 				{
6506 					target.x=entities[spells[i].caster]->pos.x;
6507 							target.y = entities[spells[i].caster]->pos.y;
6508 					target.z=entities[spells[i].caster]->pos.z;
6509 				}
6510 
6511 				pLevitate->ChangePos(&target);
6512 
6513 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6514 
6515 				if (pCSpellFX)
6516 				{
6517 					pCSpellFX->Update(FrameDiff);
6518 					pCSpellFX->Render();
6519 					GRenderer->SetCulling(Renderer::CullNone);
6520 				}
6521 			}
6522 			break;
6523 			//-----------------------------------------------------------------------------------------
6524 			//*****************************************************************************************
6525 			// LEVEL 6 SPELLS -----------------------------------------------------------------------------
6526 			case SPELL_RISE_DEAD:
6527 			{
6528 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6529 
6530 				if (pCSpellFX)
6531 				{
6532 					if (spells[i].longinfo==-2)
6533 					{
6534 						pCSpellFX->lLightId=-1;
6535 						break;
6536 					}
6537 
6538 					spells[i].tolive+=200;
6539 
6540 					pCSpellFX->Update(FrameDiff);
6541 							pCSpellFX->Render();
6542 
6543 					if (pCSpellFX->lLightId > -1)
6544 					{
6545 						long id=pCSpellFX->lLightId;
6546 						DynLight[id].exist=1;
6547 								DynLight[id].intensity = 0.7f + 2.3f;
6548 						DynLight[id].fallend = 500.f;
6549 						DynLight[id].fallstart = 400.f;
6550 						DynLight[id].rgb.r = 0.8f;
6551 						DynLight[id].rgb.g = 0.2f;
6552 						DynLight[id].rgb.b = 0.2f;
6553 						DynLight[id].duration=800;
6554 								DynLight[id].time_creation = (unsigned long)(arxtime);
6555 					}
6556 
6557 					unsigned long tim=pCSpellFX->getCurrentTime();
6558 
6559 					if ((tim>3000) && (spells[i].longinfo==-1))
6560 					{
6561 						ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &spells[i].caster_pos);
6562 						CRiseDead *prise;
6563 						prise= (CRiseDead *)spells[i].pSpellFx;
6564 
6565 						if (prise)
6566 						{
6567 							EERIE_CYLINDER phys;
6568 							phys.height=-200;
6569 							phys.radius=50;
6570 							phys.origin=spells[i].target_pos;
6571 
6572 									float anything = CheckAnythingInCylinder(&phys, NULL, CFLAG_JUST_TEST);
6573 
6574 							if(EEfabs(anything) < 30) {
6575 
6576 								const char * cls =
6577 									"graph/obj3d/interactive/npc/undead_base/undead_base";
6578 								Entity * io = AddNPC(cls, -1, IO_IMMEDIATELOAD);
6579 
6580 								if(io) {
6581 
6582 									ARX_INTERACTIVE_HideGore(io);
6583 									RestoreInitialIOStatusOfIO(io);
6584 
6585 									long lSpellsCaster = spells[i].caster;
6586 									io->summoner = checked_range_cast<short>(lSpellsCaster);
6587 
6588 									io->ioflags|=IO_NOSAVE;
6589 									spells[i].longinfo = io->index();
6590 									io->scriptload=1;
6591 
6592 									ARX_INTERACTIVE_Teleport(io,&phys.origin,0);
6593 									SendInitScriptEvent(io);
6594 
6595 									if(ValidIONum(spells[i].caster)) {
6596 										EVENT_SENDER = entities[spells[i].caster];
6597 									} else {
6598 										EVENT_SENDER = NULL;
6599 									}
6600 
6601 									SendIOScriptEvent(io,SM_SUMMONED);
6602 
6603 									Vec3f pos;
6604 										{
6605 											pos.x=prise->eSrc.x+rnd()*100.f-50.f;
6606 											pos.y=prise->eSrc.y+100+rnd()*100.f-50.f;
6607 											pos.z=prise->eSrc.z+rnd()*100.f-50.f;
6608 											MakeCoolFx(&pos);
6609 										}
6610 									}
6611 
6612 									pCSpellFX->lLightId=-1;
6613 								}
6614 								else
6615 								{
6616 									ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE);
6617 									spells[i].longinfo=-2;
6618 									spells[i].tolive=0;
6619 								}
6620 							}
6621 
6622 						} else if(!arxtime.is_paused() && tim < 4000) {
6623 						  if(rnd() > 0.95f) {
6624 								CRiseDead *pRD = (CRiseDead*)pCSpellFX;
6625 								Vec3f pos = pRD->eSrc;
6626 								MakeCoolFx(&pos);
6627 							}
6628 						}
6629 
6630 					}
6631 			}
6632 			break;
6633 
6634 			//-----------------------------------------------------------------------------------------
6635 			case SPELL_SLOW_DOWN:
6636 			{
6637 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6638 
6639 				if (pCSpellFX)
6640 				{
6641 					pCSpellFX->Update(FrameDiff);
6642 					pCSpellFX->Render();
6643 				}
6644 			}
6645 			break;
6646 			case SPELL_DISARM_TRAP:
6647 			{
6648 			}
6649 			break;
6650 			case SPELL_PARALYSE:
6651 			break;
6652 			case SPELL_CREATE_FIELD:
6653 			{
6654 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6655 
6656 				if (pCSpellFX)
6657 				{
6658 					if (ValidIONum(spells[i].longinfo))
6659 					{
6660 						Entity * io=entities[spells[i].longinfo];
6661 						CCreateField * ccf=(CCreateField *)pCSpellFX;
6662 						io->pos = ccf->eSrc;
6663 
6664 						if (IsAnyNPCInPlatform(io))
6665 						{
6666 							spells[i].tolive=0;
6667 						}
6668 
6669 						pCSpellFX->Update(FrameDiff);
6670 
6671 						if (VisibleSphere(ccf->eSrc.x,ccf->eSrc.y-120.f,ccf->eSrc.z,400.f))
6672 							pCSpellFX->Render();
6673 					}
6674 				}
6675 			}
6676 			break;
6677 
6678 			//-----------------------------------------------------------------------------------------
6679 			//*****************************************************************************************
6680 			// LEVEL 7 SPELLS -----------------------------------------------------------------------------
6681 			case SPELL_CONFUSE:
6682 			{
6683 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6684 
6685 				if (pCSpellFX)
6686 				{
6687 					pCSpellFX->Update(FrameDiff);
6688 					pCSpellFX->Render();
6689 				}
6690 			}
6691 			break;
6692 
6693 			case SPELL_FIRE_FIELD: {
6694 
6695 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6696 
6697 				if (pCSpellFX)
6698 				{
6699 					CFireField *pf = (CFireField *) pCSpellFX;
6700 					pCSpellFX->Update(FrameDiff);
6701 
6702 					if (spells[i].longinfo2==-1)
6703 						spells[i].longinfo2=GetFreeDynLight();
6704 
6705 					if (spells[i].longinfo2!=-1)
6706 					{
6707 						EERIE_LIGHT * el=&DynLight[spells[i].longinfo2];
6708 
6709 						el->pos.x = pf->pos.x;
6710 						el->pos.y = pf->pos.y-120.f;
6711 						el->pos.z = pf->pos.z;
6712 						el->exist = 1;
6713 						el->intensity = 4.6f;
6714 						el->fallstart = 150.f+rnd()*30.f;
6715 						el->fallend   = 290.f+rnd()*30.f;
6716 						el->rgb.r = 1.f-rnd()*( 1.0f / 10 );
6717 						el->rgb.g = 0.8f;
6718 						el->rgb.b = 0.6f;
6719 						el->duration = 600;
6720 						el->extras=0;
6721 					}
6722 
6723 					if(VisibleSphere(pf->pos.x, pf->pos.y - 120.f, pf->pos.z, 350.f)) {
6724 
6725 						pCSpellFX->Render();
6726 						float fDiff = FrameDiff / 8.f;
6727 						int nTime = checked_range_cast<int>(fDiff);
6728 
6729 						for(long nn=0;nn<=nTime+1;nn++) {
6730 
6731 							PARTICLE_DEF * pd = createParticle();
6732 							if(!pd) {
6733 								break;
6734 							}
6735 
6736 							float t = rnd() * (PI * 2.f) - PI;
6737 							float ts = EEsin(t);
6738 							float tc = EEcos(t);
6739 							pd->ov = pf->pos + Vec3f(120.f * ts, 15.f * ts, 120.f * tc) * randomVec();
6740 							pd->move = Vec3f(2.f - 4.f * rnd(), 1.f - 8.f * rnd(), 2.f - 4.f * rnd());
6741 							pd->siz = 7.f;
6742 							pd->tolive = Random::get(500, 1500);
6743 							pd->tc = fire2;
6744 							pd->special = ROTATING | MODULATE_ROTATION | FIRE_TO_SMOKE;
6745 							pd->fparam = 0.1f - rnd() * 0.2f;
6746 							pd->scale = Vec3f::repeat(-8.f);
6747 
6748 							PARTICLE_DEF * pd2 = createParticle();
6749 							if(!pd2) {
6750 								break;
6751 							}
6752 
6753 							*pd2 = *pd;
6754 							pd2->delay = Random::get(60, 210);
6755 						}
6756 
6757 					}
6758 				}
6759 
6760 				break;
6761 			}
6762 
6763 			case SPELL_ICE_FIELD:
6764 			{
6765 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6766 
6767 				if (pCSpellFX)
6768 				{
6769 					pCSpellFX->Update(FrameDiff);
6770 
6771 					CIceField *pf = (CIceField *) pCSpellFX;
6772 
6773 					if (spells[i].longinfo2==-1)
6774 						spells[i].longinfo2=GetFreeDynLight();
6775 
6776 					if (spells[i].longinfo2!=-1)
6777 					{
6778 						EERIE_LIGHT * el=&DynLight[spells[i].longinfo2];
6779 
6780 						el->pos.x = pf->eSrc.x;
6781 						el->pos.y = pf->eSrc.y-120.f;
6782 						el->pos.z = pf->eSrc.z;
6783 						el->exist = 1;
6784 						el->intensity = 4.6f;
6785 						el->fallstart = 150.f+rnd()*30.f;
6786 						el->fallend   = 290.f+rnd()*30.f;
6787 						el->rgb.r = 0.76f;
6788 						el->rgb.g = 0.76f;
6789 						el->rgb.b = 1.0f-rnd()*( 1.0f / 10 );
6790 						el->duration = 600;
6791 						el->extras=0;
6792 					}
6793 
6794 
6795 
6796 					if (VisibleSphere(pf->eSrc.x,pf->eSrc.y-120.f,pf->eSrc.z,350.f))
6797 					{
6798 						pCSpellFX->Render();
6799 					}
6800 				}
6801 
6802 				GRenderer->SetCulling(Renderer::CullNone);
6803 			}
6804 			break;
6805 			//-----------------------------------------------------------------------------------------
6806 			case SPELL_LIGHTNING_STRIKE:
6807 				{
6808 					CSpellFx *pCSpellFX = spells[i].pSpellFx;
6809 
6810 					if (pCSpellFX)
6811 					{
6812 						pCSpellFX->Update(FrameDiff);
6813 						pCSpellFX->Render();
6814 					}
6815 						}
6816 			break;
6817 			//-----------------------------------------------------------------------------------------
6818 			//*****************************************************************************************
6819 			// LEVEL 8 SPELLS -----------------------------------------------------------------------------
6820 			case SPELL_ENCHANT_WEAPON:
6821 			{
6822 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
6823 
6824 				if (pCSpellFX)
6825 				{
6826 					pCSpellFX->Update(FrameDiff);
6827 					pCSpellFX->Render();
6828 				}
6829 			}
6830 			case SPELL_EXPLOSION:
6831 			{
6832 				if (spells[i].longinfo2 == -1)
6833 					spells[i].longinfo2=GetFreeDynLight();
6834 
6835 				if (spells[i].longinfo2 != -1)
6836 				{
6837 					long id = spells[i].longinfo2;
6838 					DynLight[id].rgb.r = 0.1f+rnd()*( 1.0f / 3 );;
6839 					DynLight[id].rgb.g = 0.1f+rnd()*( 1.0f / 3 );;
6840 					DynLight[id].rgb.b = 0.8f+rnd()*( 1.0f / 5 );;
6841 					DynLight[id].duration=200;
6842 
6843 					float rr,r2;
6844 					Vec3f pos;
6845 
6846 					float choice = rnd();
6847 					if(choice > .8f) {
6848 						long lvl = Random::get(9, 13);
6849 						rr=radians(rnd()*360.f);
6850 						r2=radians(rnd()*360.f);
6851 						pos.x=DynLight[id].pos.x-EEsin(rr)*260;
6852 						pos.y=DynLight[id].pos.y-EEsin(r2)*260;
6853 						pos.z=DynLight[id].pos.z+EEcos(rr)*260;
6854 						Color3f rgb(0.1f + rnd()*(1.f/3), 0.1f + rnd()*(1.0f/3), 0.8f + rnd()*(1.0f/5));
6855 						LaunchFireballBoom(&pos, static_cast<float>(lvl), NULL, &rgb);
6856 					} else if(choice > .6f) {
6857 						rr=radians(rnd()*360.f);
6858 						r2=radians(rnd()*360.f);
6859 						pos.x=DynLight[id].pos.x-EEsin(rr)*260;
6860 						pos.y=DynLight[id].pos.y-EEsin(r2)*260;
6861 						pos.z=DynLight[id].pos.z+EEcos(rr)*260;
6862 						MakeCoolFx(&pos);
6863 					} else if(choice > 0.4f) {
6864 						rr=radians(rnd()*360.f);
6865 						r2=radians(rnd()*360.f);
6866 						pos.x=DynLight[id].pos.x-EEsin(rr)*160;
6867 						pos.y=DynLight[id].pos.y-EEsin(r2)*160;
6868 						pos.z=DynLight[id].pos.z+EEcos(rr)*160;
6869 						ARX_PARTICLES_Add_Smoke(&pos, 2, 20); // flag 1 = randomize pos
6870 					}
6871 				}
6872 			}
6873 			break;
6874 			//-----------------------------------------------------------------------------------------
6875 			//*****************************************************************************************
6876 			// LEVEL 9 SPELLS -------------------------------------------------------------------------
6877 			//-----------------------------------------------------------------------------------------
6878 			case SPELL_SUMMON_CREATURE:
6879 			{
6880 
6881 
6882 				if (!arxtime.is_paused())
6883 				{
6884 
6885 					if (float(arxtime)-(float)spells[i].timcreation<=4000)
6886 					{
6887 						if (rnd()>0.7f)
6888 						{
6889 							CSummonCreature * pSummon = (CSummonCreature *)spells[i].pSpellFx;
6890 							if(pSummon) {
6891 								Vec3f pos = pSummon->eSrc;
6892 								MakeCoolFx(&pos);
6893 							}
6894 						}
6895 
6896 						CSpellFx *pCSpellFX = spells[i].pSpellFx;
6897 
6898 						if (pCSpellFX)
6899 						{
6900 							pCSpellFX->Update(FrameDiff);
6901 							pCSpellFX->Render();
6902 						}
6903 
6904 						spells[i].longinfo=1;
6905 						spells[i].longinfo2=-1;
6906 					}
6907 					else if (spells[i].longinfo)
6908 					{
6909 						if (spells[i].pSpellFx->lLightId > -1)
6910 						{
6911 							long id = spells[i].pSpellFx->lLightId;
6912 							DynLight[id].exist = 0;
6913 							spells[i].pSpellFx->lLightId=-1;
6914 						}
6915 
6916 						spells[i].longinfo=0;
6917 						ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &spells[i].caster_pos);
6918 						CSummonCreature *pSummon;
6919 						pSummon= (CSummonCreature *)spells[i].pSpellFx;
6920 
6921 						if (pSummon)
6922 						{
6923 							EERIE_CYLINDER phys;
6924 							phys.height=-200;
6925 							phys.radius=50;
6926 							phys.origin=spells[i].target_pos;
6927 									float anything = CheckAnythingInCylinder(&phys, NULL, CFLAG_JUST_TEST);
6928 
6929 							if (EEfabs(anything)<30)
6930 							{
6931 
6932 							long tokeep;
6933 							res::path cls;
6934 							if(spells[i].fdata == 1.f) {
6935 								if(rnd() > 0.5) {
6936 									tokeep = -1;
6937 									cls = "graph/obj3d/interactive/npc/wrat_base/wrat_base";
6938 								} else {
6939 									tokeep = 0;
6940 									cls = "graph/obj3d/interactive/npc/y_mx/y_mx";
6941 								}
6942 							} else if(rnd() > 0.997f || (sp_max && rnd() > 0.8f)
6943 							   || (cur_mr >= 3 && rnd() > 0.3f)) {
6944 								tokeep = 0;
6945 								cls = "graph/obj3d/interactive/npc/y_mx/y_mx";
6946 							} else if(rnd() > 0.997f || (cur_rf >= 3 && rnd() > 0.8f)
6947 							   || (cur_mr >= 3 && rnd() > 0.3f)) {
6948 								tokeep = -1;
6949 								cls = "graph/obj3d/interactive/npc/wrat_base/wrat_base";
6950 							} else if(spells[i].caster_level >= 9) {
6951 								tokeep = 1;
6952 								cls = "graph/obj3d/interactive/npc/demon/demon";
6953 							} else if(rnd() > 0.98f) {
6954 								tokeep = -1;
6955 								cls = "graph/obj3d/interactive/npc/wrat_base/wrat_base";
6956 							} else {
6957 								tokeep = 0;
6958 								cls = "graph/obj3d/interactive/npc/chicken_base/chicken_base";
6959 							}
6960 
6961 							Entity * io = AddNPC(cls, -1, IO_IMMEDIATELOAD);
6962 							if(!io) {
6963 								cls = "graph/obj3d/interactive/npc/chicken_base/chicken_base";
6964 								tokeep = 0;
6965 								io = AddNPC(cls, -1, IO_IMMEDIATELOAD);
6966 							}
6967 
6968 							if(io) {
6969 
6970 								RestoreInitialIOStatusOfIO(io);
6971 
6972 								long lSpellsCaster = spells[i].caster ;
6973 								io->summoner = checked_range_cast<short>(lSpellsCaster);
6974 
6975 
6976 								io->scriptload = 1;
6977 
6978 								if(tokeep == 1) {
6979 									io->ioflags |= IO_NOSAVE;
6980 								}
6981 
6982 								io->pos = phys.origin;
6983 								SendInitScriptEvent(io);
6984 
6985 								if (tokeep<0)
6986 								{
6987 									io->scale=1.65f;
6988 									io->physics.cyl.radius=25;
6989 									io->physics.cyl.height=-43;
6990 									io->speed_modif=1.f;
6991 								}
6992 
6993 								if(ValidIONum(spells[i].caster)) {
6994 									EVENT_SENDER = entities[spells[i].caster];
6995 								} else {
6996 									EVENT_SENDER = NULL;
6997 								}
6998 
6999 								SendIOScriptEvent(io,SM_SUMMONED);
7000 
7001 											Vec3f pos;
7002 
7003 								for (long j=0;j<3;j++)
7004 								{
7005 									pos.x=pSummon->eSrc.x+rnd()*100.f-50.f;
7006 									pos.y=pSummon->eSrc.y+100+rnd()*100.f-50.f;
7007 									pos.z=pSummon->eSrc.z+rnd()*100.f-50.f;
7008 									MakeCoolFx(&pos);
7009 								}
7010 
7011 								if (tokeep==1)	spells[i].longinfo2 = io->index();
7012 								else spells[i].longinfo2=-1;
7013 							}
7014 							}
7015 					}
7016 					}
7017 					else if (spells[i].longinfo2<=0)
7018 					{
7019 						spells[i].tolive=0;
7020 					}
7021 				}
7022 			}
7023 			break;
7024 			case SPELL_FAKE_SUMMON:
7025 			{
7026 
7027 
7028 					if (!arxtime.is_paused())
7029 						if(rnd() > 0.7f) {
7030 							CSummonCreature * pSummon = (CSummonCreature *)spells[i].pSpellFx;
7031 							if(pSummon) {
7032 								Vec3f pos = pSummon->eSrc;
7033 								MakeCoolFx(&pos);
7034 							}
7035 						}
7036 
7037 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
7038 
7039 				if (pCSpellFX)
7040 				{
7041 					pCSpellFX->Update(FrameDiff);
7042 					pCSpellFX->Render();
7043 				}
7044 			}
7045 			break;
7046 			//-----------------------------------------------------------------------------------------
7047 
7048 			case SPELL_INCINERATE:
7049 			{
7050 				if (ValidIONum(spells[i].caster))
7051 				{
7052 					ARX_SOUND_RefreshPosition(spells[i].snd_loop, &entities[spells[i].caster]->pos);
7053 				}
7054 			}
7055 			break;
7056 			case SPELL_NEGATE_MAGIC:
7057 			{
7058 				if (ValidIONum(spells[i].target))
7059 					LaunchAntiMagicField(i);
7060 
7061 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
7062 
7063 				if (pCSpellFX)
7064 				{
7065 					pCSpellFX->Update(FrameDiff);
7066 					pCSpellFX->Render();
7067 				}
7068 			}
7069 			break;
7070 			case SPELL_MASS_PARALYSE:
7071 			break;
7072 			//*******************************************************************************************
7073 			// LEVEL 10 SPELLS -----------------------------------------------------------------------------
7074 			//-----------------------------------------------------------------------------------------
7075 			case SPELL_FREEZE_TIME:
7076 			{
7077 			}
7078 			break;
7079 			//-----------------------------------------------------------------------------------------
7080 			case SPELL_CONTROL_TARGET:
7081 			{
7082 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
7083 
7084 				if (pCSpellFX)
7085 				{
7086 					pCSpellFX->Update(FrameDiff);
7087 					pCSpellFX->Render();
7088 				}
7089 			}
7090 			break;
7091 			case SPELL_MASS_INCINERATE:
7092 			{
7093 				if (ValidIONum(spells[i].caster))
7094 				{
7095 					ARX_SOUND_RefreshPosition(spells[i].snd_loop, &entities[spells[i].caster]->pos);
7096 				}
7097 			}
7098 			break;
7099 			//-----------------------------------------------------------------------------------------
7100 			case SPELL_MASS_LIGHTNING_STRIKE:
7101 			{
7102 				CSpellFx *pCSpellFX = spells[i].pSpellFx;
7103 
7104 				if (pCSpellFX)
7105 				{
7106 					pCSpellFX->Update(FrameDiff);
7107 					pCSpellFX->Render();
7108 				}
7109 
7110 						Vec3f _source = spells[i].vsource;
7111 						float _fx;
7112 						_fx = 0.5f;
7113 						unsigned long _gct;
7114 						_gct = 0;
7115 
7116 				Vec3f position;
7117 
7118 				spells[i].lastupdate=tim;
7119 
7120 				position = _source + randomVec(-250.f, 250.f);
7121 				ARX_SOUND_RefreshPosition(spells[i].snd_loop, &position);
7122 				ARX_SOUND_RefreshVolume(spells[i].snd_loop, _fx + 0.5F);
7123 				ARX_SOUND_RefreshPitch(spells[i].snd_loop, 0.8F + 0.4F * rnd());
7124 
7125 				if(rnd() > 0.62f) {
7126 					position = _source  + randomVec(-250.f, 250.f);
7127 					ARX_SOUND_PlaySFX(SND_SPELL_SPARK, &position, 0.8F + 0.4F * rnd());
7128 				}
7129 
7130 				if(rnd() > 0.82f) {
7131 					position = _source + randomVec(-250.f, 250.f);
7132 					ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, &position, 0.8F + 0.4F * rnd());
7133 				}
7134 
7135 				if ((_gct>spells[i].tolive-1800) && (spells[i].siz==0))
7136 				{
7137 					spells[i].siz=1;
7138 					ARX_SOUND_PlaySFX(SND_SPELL_ELECTRIC, NULL, 0.8F + 0.4F * rnd());
7139 				}
7140 
7141 				if (spells[i].longinfo!=-1)
7142 				{
7143 					float fxx;
7144 
7145 					if (_fx>0.2f)  fxx=1.f;
7146 					else fxx=_fx*5.f;
7147 
7148 					DynLight[spells[i].longinfo].intensity=1.3f+rnd()*1.f;
7149 					DynLight[spells[i].longinfo].fallend=850.f;
7150 					DynLight[spells[i].longinfo].fallstart=500.f;
7151 					DynLight[spells[i].longinfo].rgb = Color3f::red * fxx;
7152 				}
7153 			}
7154 		break;
7155 		//-----------------------------------------------------------------------------------------
7156 		case SPELL_TELEPORT:
7157 				{
7158 					float TELEPORT = (float)(((float)tim-(float)spells[i].timcreation)/(float)spells[i].tolive);
7159 
7160 					if(LASTTELEPORT < 0.5f && TELEPORT >= 0.5f) {
7161 						Vec3f pos = lastteleport;
7162 						lastteleport = player.pos;
7163 						player.pos = pos;
7164 						LASTTELEPORT = 32.f;
7165 						ARX_SOUND_PlaySFX(SND_SPELL_TELEPORTED, &player.pos);
7166 					} else {
7167 						LASTTELEPORT = TELEPORT;
7168 					}
7169 				}
7170 				break;
7171 				//-----------------------------------------------------------------------------------------
7172 				case SPELL_MAGIC_SIGHT:
7173 
7174 					if (spells[i].caster == 0)
7175 					{
7176 						ARX_SOUND_RefreshPosition(spells[i].snd_loop);
7177 
7178 						if (subj.focal>IMPROVED_FOCAL) subj.focal-=DEC_FOCAL;
7179 					}
7180 
7181 				break;
7182 				//-----------------------------------------------------------------------------------------
7183 				case SPELL_TELEKINESIS:
7184 				break;
7185 				//-----------------------------------------------------------------------------------------
7186 				case SPELL_INVISIBILITY:
7187 
7188 					if (spells[i].target!=0)
7189 					{
7190 						if (!(entities[spells[i].target]->gameFlags & GFLAG_INVISIBILITY))
7191 						{
7192 							ARX_SPELLS_RemoveSpellOn(spells[i].target,i);
7193 							ARX_SPELLS_Fizzle(i);
7194 						}
7195 					}
7196 				break;
7197 				//-----------------------------------------------------------------------------------------
7198 				case SPELL_MANA_DRAIN:
7199 					{
7200 
7201 					if ( (cabal!=NULL) )
7202 					{
7203 						float refpos;
7204 						float scaley;
7205 
7206 						if (spells[i].caster==0) scaley=90.f;
7207 						else scaley=EEfabs(entities[spells[i].caster]->physics.cyl.height*( 1.0f / 2 ))+30.f;
7208 
7209 						float mov1=EEsin((float)arxtime.get_last_frame_time()*( 1.0f / 800 ))*scaley;
7210 						float mov=EEsin((float)arxtime.get_frame_time()*( 1.0f / 800 ))*scaley;
7211 
7212 						if ((mov1<scaley-10.f) && (mov>scaley-10.f)) ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_SHIELD, &spells[i].caster_pos, 0.4F);
7213 
7214 						if ((mov1>-scaley+10.f) && (mov<-scaley+10.f)) ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_SHIELD, &spells[i].caster_pos, 0.4F);
7215 
7216 						if (spells[i].caster==0)
7217 						{
7218 								cabalpos.x = player.pos.x;
7219 							cabalpos.y=player.pos.y+60.f-mov;
7220 								cabalpos.z = player.pos.z;
7221 							refpos=player.pos.y+60.f;
7222 						}
7223 						else
7224 						{
7225 								cabalpos.x = entities[spells[i].caster]->pos.x;
7226 							cabalpos.y=entities[spells[i].caster]->pos.y-scaley-mov;
7227 								cabalpos.z = entities[spells[i].caster]->pos.z;
7228 							refpos=entities[spells[i].caster]->pos.y-scaley;
7229 						}
7230 
7231 						float Es=EEsin((float)arxtime.get_frame_time()*( 1.0f / 800 ) + radians(scaley));
7232 
7233 						if (spells[i].longinfo2!=-1)
7234 						{
7235 							DynLight[spells[i].longinfo2].pos.x = cabalpos.x;
7236 							DynLight[spells[i].longinfo2].pos.y = refpos;
7237 							DynLight[spells[i].longinfo2].pos.z = cabalpos.z;
7238 							DynLight[spells[i].longinfo2].rgb.b=rnd()*0.2f+0.8f;
7239 							DynLight[spells[i].longinfo2].fallstart=Es*1.5f;
7240 						}
7241 
7242 						GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
7243 						GRenderer->SetRenderState(Renderer::AlphaBlending, true);
7244 						GRenderer->SetRenderState(Renderer::DepthWrite, false);
7245 						cabalangle.b=spells[i].fdata+(float)FrameDiff*0.1f;
7246 						spells[i].fdata=cabalangle.b;
7247 
7248 							cabalangle.g = 0.f;
7249 
7250 							cabalcolor.r = cabalcolor.g = 0.4f;
7251 							cabalcolor.b = 0.8f;
7252 
7253 						cabalscale = Vec3f::repeat(Es);
7254 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7255 						mov=EEsin((float)(arxtime.get_frame_time()-30.f)*( 1.0f / 800 ))*scaley;
7256 						cabalpos.y = refpos - mov;
7257 							cabalcolor.r = cabalcolor.g = 0.2f;
7258 							cabalcolor.b = 0.5f;
7259 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7260 						mov=EEsin((float)(arxtime.get_frame_time()-60.f)*( 1.0f / 800 ))*scaley;
7261 						cabalpos.y=refpos-mov;
7262 							cabalcolor.r = cabalcolor.g = 0.1f;
7263 							cabalcolor.b = 0.25f;
7264 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7265 						mov=EEsin((float)(arxtime.get_frame_time()-120.f)*( 1.0f / 800 ))*scaley;
7266 						cabalpos.y=refpos-mov;
7267 							cabalcolor.r = cabalcolor.g = 0.f;
7268 							cabalcolor.b = 0.15f;
7269 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7270 						cabalangle.b=-cabalangle.b;
7271 						cabalpos.y=refpos-mov;
7272 						cabalscale = Vec3f::repeat(Es);
7273 							cabalcolor.r = cabalcolor.g = 0.f;
7274 							cabalcolor.b = 0.15f;
7275 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7276 						mov=EEsin((float)(arxtime.get_frame_time()+30.f)*( 1.0f / 800 ))*scaley;
7277 						cabalpos.y=refpos+mov;
7278 							cabalcolor.r = cabalcolor.g = 0.1f;
7279 							cabalcolor.b = 0.25f;
7280 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7281 						mov=EEsin((float)(arxtime.get_frame_time()+60.f)*( 1.0f / 800 ))*scaley;
7282 						cabalpos.y=refpos+mov;
7283 							cabalcolor.r = cabalcolor.g = 0.2f;
7284 							cabalcolor.b = 0.5f;
7285 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7286 						mov=EEsin((float)(arxtime.get_frame_time()+120.f)*( 1.0f / 800 ))*scaley;
7287 						cabalpos.y=refpos+mov;
7288 							cabalcolor.r = cabalcolor.g = 0.4f;
7289 							cabalcolor.b = 0.8f;
7290 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7291 						cabalangle.b=-cabalangle.b;
7292 						GRenderer->SetRenderState(Renderer::AlphaBlending, false);
7293 						GRenderer->SetRenderState(Renderer::DepthWrite, true);
7294 
7295 						ARX_SOUND_RefreshPosition(spells[i].snd_loop, &cabalpos);
7296 					}
7297 					}
7298 				break;
7299 				//-----------------------------------------------------------------------------------------
7300 				case SPELL_LIFE_DRAIN:
7301 
7302 					{
7303 					if ( (cabal!=NULL) )
7304 					{
7305 						float refpos;
7306 						float scaley;
7307 
7308 						if (spells[i].caster==0) scaley=90.f;
7309 						else scaley=EEfabs(entities[spells[i].caster]->physics.cyl.height*( 1.0f / 2 ))+30.f;
7310 
7311 
7312 						float mov=EEsin((float)arxtime.get_frame_time()*( 1.0f / 800 ))*scaley;
7313 
7314 						if (spells[i].caster==0)
7315 						{
7316 								cabalpos.x = player.pos.x;
7317 							cabalpos.y=player.pos.y+60.f-mov;
7318 								cabalpos.z = player.pos.z;
7319 							refpos=player.pos.y+60.f;
7320 						}
7321 						else
7322 						{
7323 								cabalpos.x = entities[spells[i].caster]->pos.x;
7324 							cabalpos.y=entities[spells[i].caster]->pos.y-scaley-mov;
7325 								cabalpos.z = entities[spells[i].caster]->pos.z;
7326 							refpos=entities[spells[i].caster]->pos.y-scaley;
7327 						}
7328 
7329 						float Es=EEsin((float)arxtime.get_frame_time()*( 1.0f / 800 ) + radians(scaley));
7330 
7331 						if (spells[i].longinfo2!=-1)
7332 						{
7333 							DynLight[spells[i].longinfo2].pos.x = cabalpos.x;
7334 							DynLight[spells[i].longinfo2].pos.y = refpos;
7335 							DynLight[spells[i].longinfo2].pos.z = cabalpos.z;
7336 							DynLight[spells[i].longinfo2].rgb.r=rnd()*0.2f+0.8f;
7337 							DynLight[spells[i].longinfo2].fallstart=Es*1.5f;
7338 						}
7339 
7340 						GRenderer->SetCulling(Renderer::CullNone);
7341 						GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
7342 						GRenderer->SetRenderState(Renderer::AlphaBlending, true);
7343 						GRenderer->SetRenderState(Renderer::DepthWrite, false);
7344 						cabalangle.b=spells[i].fdata+(float)FrameDiff*0.1f;
7345 						spells[i].fdata=cabalangle.b;
7346 							cabalangle.g = 0.f;
7347 							cabalcolor.r = 0.8f;
7348 							cabalcolor.g = 0.f;
7349 							cabalcolor.b = 0.f;
7350 						cabalscale = Vec3f::repeat(Es);
7351 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7352 						mov=EEsin((float)(arxtime.get_frame_time()-30.f)*( 1.0f / 800 ))*scaley;
7353 						cabalpos.y=refpos-mov;
7354 							cabalcolor.b = cabalcolor.g = 0.f;
7355 							cabalcolor.r = 0.5f;
7356 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7357 						mov=EEsin((float)(arxtime.get_frame_time()-60.f)*( 1.0f / 800 ))*scaley;
7358 						cabalpos.y=refpos-mov;
7359 							cabalcolor.b = cabalcolor.g = 0.f;
7360 							cabalcolor.r = 0.25f;
7361 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7362 						mov=EEsin((float)(arxtime.get_frame_time()-120.f)*( 1.0f / 800 ))*scaley;
7363 						cabalpos.y=refpos-mov;
7364 							cabalcolor.b = cabalcolor.g = 0.f;
7365 							cabalcolor.r = 0.15f;
7366 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7367 						cabalangle.b=-cabalangle.b;
7368 						cabalpos.y=refpos-mov;
7369 						cabalscale = Vec3f::repeat(Es);
7370 							cabalcolor.b = cabalcolor.g = 0.f;
7371 							cabalcolor.r = 0.15f;
7372 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7373 						mov=EEsin((float)(arxtime.get_frame_time()+30.f)*( 1.0f / 800 ))*scaley;
7374 						cabalpos.y=refpos+mov;
7375 							cabalcolor.b = cabalcolor.g = 0.f;
7376 							cabalcolor.r = 0.25f;
7377 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7378 						mov=EEsin((float)(arxtime.get_frame_time()+60.f)*( 1.0f / 800 ))*scaley;
7379 						cabalpos.y=refpos+mov;
7380 							cabalcolor.b = cabalcolor.g = 0.f;
7381 							cabalcolor.r = 0.5f;
7382 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7383 						mov=EEsin((float)(arxtime.get_frame_time()+120.f)*( 1.0f / 800 ))*scaley;
7384 						cabalpos.y=refpos+mov;
7385 							cabalcolor.b = cabalcolor.g = 0.f;
7386 							cabalcolor.r = 0.8f;
7387 						DrawEERIEObjEx(cabal,&cabalangle,&cabalpos,&cabalscale,&cabalcolor);
7388 						cabalangle.b=-cabalangle.b;
7389 						GRenderer->SetRenderState(Renderer::AlphaBlending, false);
7390 						GRenderer->SetRenderState(Renderer::DepthWrite, true);
7391 
7392 						ARX_SOUND_RefreshPosition(spells[i].snd_loop, &cabalpos);
7393 						}
7394 					}
7395 				break;
7396 
7397 				case SPELL_FLYING_EYE: {
7398 
7399 						eyeball.floating = EEsin(spells[i].lastupdate-spells[i].timcreation * 0.001f);
7400 						eyeball.floating *= 10.f;
7401 
7402 						if(spells[i].lastupdate-spells[i].timcreation <= 3000) {
7403 							eyeball.exist = spells[i].lastupdate - spells[i].timcreation * (1.0f / 30);
7404 							eyeball.size = Vec3f::repeat(1.f - float(eyeball.exist) * 0.01f);
7405 							eyeball.angle.b += framediff3 * 0.6f;
7406 						} else {
7407 							eyeball.exist = 2;
7408 						}
7409 
7410 						spells[i].lastupdate=tim;
7411 					break;
7412 				}
7413 
7414 			}
7415 		}
7416 	}
7417 }
7418 
7419 ////////////////////////////////////////////////////////////////////////////
7420 ////////////////////////////////////////////////////////////////////////////
7421 ////////////////////////////////////////////////////////////////////////////
7422 ////////////////////////////////////////////////////////////////////////////
7423 ////////////////////////////////////////////////////////////////////////////
7424 ////////////////////////////////////////////////////////////////////////////
TryToCastSpell(Entity * io,Spell spellid,long level,long target,SpellcastFlags flags,long duration)7425 void TryToCastSpell(Entity * io, Spell spellid, long level, long target, SpellcastFlags flags, long duration)
7426 {
7427 	if (!io || io->spellcast_data.castingspell != SPELL_NONE) return;
7428 
7429 	if (!(flags & SPELLCAST_FLAG_NOMANA)
7430 			&& (io->ioflags & IO_NPC) && (io->_npcdata->mana<=0.f))
7431 		return;
7432 
7433 	unsigned long i(0);
7434 
7435 	for (; i < SPELL_COUNT; i++)
7436 		if (spellicons[i].spellid == spellid) break;
7437 
7438 	if ( i >= SPELL_COUNT) return; // not an existing spell...
7439 
7440 	for (unsigned long j(0); j < 4; j++) io->spellcast_data.symb[j] = RUNE_NONE;
7441 
7442 	// checks for symbol drawing...
7443 	if (!(flags & SPELLCAST_FLAG_NOANIM) && io->ioflags & IO_NPC)
7444 	{
7445 		ANIM_USE *ause1 = &io->animlayer[1];
7446 
7447 		AcquireLastAnim(io);
7448 		FinishAnim(io, ause1->cur_anim);
7449 		ANIM_Set(ause1, io->anims[ANIM_CAST_START]);
7450 
7451 		for (unsigned long j(0); j < 4; j++)
7452 			io->spellcast_data.symb[j] = spellicons[i].symbols[j];
7453 	}
7454 
7455 	io->spellcast_data.castingspell = spellid;
7456 
7457 
7458 	io->spellcast_data.spell_flags = flags;
7459 	io->spellcast_data.spell_level = checked_range_cast<short>(level);
7460 
7461 
7462 	io->spellcast_data.duration = duration;
7463 	io->spellcast_data.target = target;
7464 
7465 	io->gameFlags &=~GFLAG_INVISIBILITY;
7466 
7467 	if (	((io->spellcast_data.spell_flags & SPELLCAST_FLAG_NOANIM)
7468 		&&	(io->spellcast_data.spell_flags & SPELLCAST_FLAG_NODRAW) )
7469 		||	(io->spellcast_data.spell_flags & SPELLCAST_FLAG_PRECAST))
7470 	{
7471 
7472 		ARX_SPELLS_Launch(io->spellcast_data.castingspell, io->index(), io->spellcast_data.spell_flags,io->spellcast_data.spell_level,io->spellcast_data.target,io->spellcast_data.duration);
7473 		io->spellcast_data.castingspell = SPELL_NONE;
7474 	}
7475 
7476 	io->spellcast_data.spell_flags &=~SPELLCAST_FLAG_NODRAW; // temporary, removes colored flares
7477 }
7478 
ApplySPWep()7479 static void ApplySPWep() {
7480 
7481 	if(!sp_wep) {
7482 
7483 		ARX_SPSound();
7484 
7485 		res::path cls = "graph/obj3d/interactive/items/weapons/sword_mx/sword_mx";
7486 		Entity * ioo = AddItem(cls);
7487 		if(ioo) {
7488 
7489 			sp_wep = 1;
7490 			MakeCoolFx(&player.pos);
7491 			MakeCoolFx(&player.pos);
7492 			ioo->scriptload = 1;
7493 			SendInitScriptEvent(ioo);
7494 
7495 			giveToPlayer(ioo);
7496 
7497 			MakeSpCol();
7498 			strcpy(sp_max_ch,"!!!_Grosbillite_!!!");
7499 			sp_max_nb=strlen(sp_max_ch);
7500 			sp_max_start=arxtime.get_updated();
7501 		}
7502 	}
7503 }
7504 
MakeSpCol()7505 void MakeSpCol() {
7506 
7507 	ARX_SPSound();
7508 
7509 	for(long i = 0; i < 64; i++) {
7510 		sp_max_y[i] = 0;
7511 	}
7512 
7513 	sp_max_col[0] = Color::fromRGBA(0x00FF0000);
7514 	sp_max_col[1] = Color::fromRGBA(0x0000FF00);
7515 	sp_max_col[2] = Color::fromRGBA(0x000000FF);
7516 
7517 	sp_max_col[3] = Color::fromRGBA(0x00FFFF00);
7518 	sp_max_col[4] = Color::fromRGBA(0x00FF00FF);
7519 	sp_max_col[5] = Color::fromRGBA(0x0000FFFF);
7520 
7521 	for(size_t i = 6; i < 24; i++) {
7522 		sp_max_col[i] = sp_max_col[i - 6];
7523 	}
7524 
7525 	for(size_t i = 24; i < 27; i++) {
7526 		sp_max_col[i] = sp_max_col[i - 3];
7527 	}
7528 
7529 	for(size_t i = 27; i < 33; i++) {
7530 		sp_max_col[i] = sp_max_col[i - 9];
7531 	}
7532 
7533 }
7534 
ApplyCurSOS()7535 static void ApplyCurSOS() {
7536 	MakeSpCol();
7537 	g_miniMap.reveal();
7538 	strcpy(sp_max_ch,"!!!_Temple of Elemental Lavis_!!!");
7539 	sp_max_nb=strlen(sp_max_ch);
7540 	sp_max_start=arxtime.get_updated();
7541 }
7542 
ApplySPBow()7543 static void ApplySPBow() {
7544 
7545 	ARX_SPSound();
7546 
7547 	const char * cls = "graph/obj3d/interactive/items/weapons/bow_mx/bow_mx";
7548 	Entity * ioo = AddItem(cls);
7549 	if(ioo) {
7550 
7551 		MakeCoolFx(&player.pos);
7552 		MakeCoolFx(&player.pos);
7553 
7554 		ioo->scriptload = 1;
7555 		SendInitScriptEvent(ioo);
7556 
7557 		giveToPlayer(ioo);
7558 
7559 		MakeSpCol();
7560 		strcpy(sp_max_ch,"!!!_Bow to Samy & Anne_!!!");
7561 		sp_max_nb=strlen(sp_max_ch);
7562 		sp_max_start=arxtime.get_updated();
7563 	}
7564 }
7565 
ApplySPArm()7566 static void ApplySPArm() {
7567 	ARX_SPSound();
7568 
7569 	res::path cls;
7570 	switch (sp_arm) {
7571 		case 0:
7572 			cls = "graph/obj3d/interactive/items/armor/helmet_plate_cm/helmet_plate_cm";
7573 		break;
7574 		case 1:
7575 			cls = "graph/obj3d/interactive/items/armor/legging_plate_cm/legging_plate_cm";
7576 		break;
7577 		case 2:
7578 			cls = "graph/obj3d/interactive/items/armor/chest_plate_cm/chest_plate_cm";
7579 		break;
7580 		default:
7581 			return;
7582 		break;
7583 	}
7584 
7585 	Entity * ioo = AddItem(cls);
7586 	if(ioo) {
7587 
7588 		sp_wep = 1;
7589 		MakeCoolFx(&player.pos);
7590 		MakeCoolFx(&player.pos);
7591 		ioo->scriptload = 1;
7592 		SendInitScriptEvent(ioo);
7593 
7594 		giveToPlayer(ioo);
7595 
7596 		MakeSpCol();
7597 		strcpy(sp_max_ch,"!! Toi aussi cherches les Cheats !!");
7598 
7599 		switch (sp_arm)
7600 		{
7601 		case 0:
7602 			strcpy(sp_max_ch,"------ZoliChapo------");
7603 		break;
7604 		case 1:
7605 			strcpy(sp_max_ch,"-----TiteBottine-----");
7606 		break;
7607 		case 2:
7608 			strcpy(sp_max_ch,"-----Roooo-La-La-----");
7609 		break;
7610 		default:
7611 			return;
7612 		break;
7613 		}
7614 
7615 		sp_max_nb=strlen(sp_max_ch);
7616 		sp_max_start=arxtime.get_updated();
7617 	}
7618 
7619 	sp_arm++;
7620 }
7621 
7622 long SPECIAL_PNUX;
ApplyCurPNux()7623 static void ApplyCurPNux() {
7624 
7625 	MakeSpCol();
7626 	strcpy(sp_max_ch,"! PhilNux & Gluonne !");
7627 	sp_max_nb=strlen(sp_max_ch);
7628 
7629 	SPECIAL_PNUX = (SPECIAL_PNUX + 1) % 3;
7630 
7631 	// TODO-RENDERING: Create a post-processing effect for that cheat... see original source...
7632 
7633 	cur_pnux=0;
7634 	sp_max_start=arxtime.get_updated();
7635 }
7636 
ApplyPasswall()7637 static void ApplyPasswall() {
7638 	MakeSpCol();
7639 	strcpy(sp_max_ch,"!!! PassWall !!!");
7640 	sp_max_nb=strlen(sp_max_ch);
7641 	sp_max_start=arxtime.get_updated();
7642 
7643 	if (USE_PLAYERCOLLISIONS)
7644 		USE_PLAYERCOLLISIONS=0;
7645 	else
7646 		USE_PLAYERCOLLISIONS=1;
7647 }
7648 
ApplySPRf()7649 static void ApplySPRf() {
7650 	if(cur_rf == 3) {
7651 		MakeSpCol();
7652 		strcpy(sp_max_ch,"!!! RaFMode !!!");
7653 		sp_max_nb=strlen(sp_max_ch);
7654 		sp_max_start=arxtime.get_updated();
7655 	}
7656 }
7657 
ApplyCurMr()7658 static void ApplyCurMr() {
7659 	if(cur_mr == 3) {
7660 		MakeSpCol();
7661 		strcpy(sp_max_ch,"!!! Marianna !!!");
7662 		sp_max_nb=strlen(sp_max_ch);
7663 		sp_max_start=arxtime.get_updated();
7664 	}
7665 }
7666 
ApplySPuw()7667 static void ApplySPuw() {
7668 	uw_mode_pos=0;
7669 	uw_mode=~uw_mode;
7670 	ARX_SOUND_PlayCinematic("menestrel_uw2", true);
7671 	MakeCoolFx(&player.pos);
7672 	if(uw_mode) {
7673 		MakeSpCol();
7674 		strcpy(sp_max_ch,"~-__-~~-__.U.W.__-~~-__-~");
7675 		sp_max_nb=strlen(sp_max_ch);
7676 		sp_max_start=arxtime.get_updated();
7677 	}
7678 }
7679 
ApplySPMax()7680 static void ApplySPMax() {
7681 
7682 	MakeCoolFx(&player.pos);
7683 	sp_max=~sp_max;
7684 
7685 	if (sp_max)
7686 	{
7687 		MakeSpCol();
7688 		strcpy(sp_max_ch,"!!!_FaNt0mAc1e_!!!");
7689 		sp_max_nb=strlen(sp_max_ch);
7690 		sp_max_start=arxtime.get_updated();
7691 
7692 			player.skin=4;
7693 
7694 			ARX_EQUIPMENT_RecreatePlayerMesh();
7695 
7696 		ARX_PLAYER_Rune_Add_All();
7697 		std::string text = "!!!!!!! FanTomAciE !!!!!!!";
7698 		ARX_SPEECH_Add(text);
7699 		player.Attribute_Redistribute+=10;
7700 		player.Skill_Redistribute+=50;
7701 		player.level=std::max((int)player.level,10);
7702 		player.xp=GetXPforLevel(10);
7703 	}
7704 	else
7705 	{
7706 		TextureContainer * tcm;
7707 		tcm = TextureContainer::Load("graph/obj3d/textures/npc_human_cm_hero_head");
7708 		if(tcm) {
7709 			delete tcm;
7710 			player.heads[0]
7711 				= TextureContainer::Load("graph/obj3d/textures/npc_human_base_hero_head");
7712 			player.heads[1]
7713 				= TextureContainer::Load("graph/obj3d/textures/npc_human_base_hero2_head");
7714 			player.heads[2]
7715 				= TextureContainer::Load("graph/obj3d/textures/npc_human_base_hero3_head");
7716 			ARX_EQUIPMENT_RecreatePlayerMesh();
7717 		}
7718 	}
7719 }
7720