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 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
46 
47 #include "game/NPC.h"
48 
49 #include <stddef.h>
50 #include <cmath>
51 #include <cstdio>
52 #include <cstdlib>
53 #include <cstring>
54 #include <algorithm>
55 #include <limits>
56 #include <vector>
57 
58 #include <boost/algorithm/string/predicate.hpp>
59 
60 #include "ai/Paths.h"
61 #include "ai/PathFinderManager.h"
62 
63 #include "core/GameTime.h"
64 #include "core/Core.h"
65 #include "core/Config.h"
66 
67 #include "game/Camera.h"
68 #include "game/Damage.h"
69 #include "game/EntityManager.h"
70 #include "game/Equipment.h"
71 #include "game/Inventory.h"
72 #include "game/Item.h"
73 #include "game/Player.h"
74 #include "game/Spells.h"
75 
76 #include "gui/Interface.h"
77 #include "gui/Speech.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/Vertex.h"
85 #include "graphics/data/TextureContainer.h"
86 #include "graphics/data/MeshManipulation.h"
87 #include "graphics/particle/ParticleEffects.h"
88 
89 #include "io/resource/ResourcePath.h"
90 
91 #include "math/Angle.h"
92 #include "math/Random.h"
93 #include "math/Vector3.h"
94 
95 #include "physics/Anchors.h"
96 #include "physics/Box.h"
97 #include "physics/CollisionShapes.h"
98 #include "physics/Collisions.h"
99 
100 #include "platform/Flags.h"
101 #include "platform/Platform.h"
102 
103 #include "scene/Object.h"
104 #include "scene/Interactive.h"
105 #include "scene/Scene.h"
106 #include "scene/GameSound.h"
107 #include "scene/Light.h"
108 
109 #include "script/Script.h"
110 
111 using std::sprintf;
112 using std::min;
113 using std::max;
114 using std::string;
115 
116 void CheckNPCEx(Entity * io);
117 
118 static const float ARX_NPC_ON_HEAR_MAX_DISTANCE_STEP(600.0F);
119 static const float ARX_NPC_ON_HEAR_MAX_DISTANCE_ITEM(800.0F);
120 
121 #ifdef BUILD_EDITOR
122 extern long LastSelectedIONum;
123 #endif
124 
125 void StareAtTarget(Entity * io);
126 #define RUN_WALK_RADIUS 450
127 
CheckHit(Entity * io,float ratioaim)128 static void CheckHit(Entity * io, float ratioaim) {
129 
130 	if (io == NULL) return;
131 
132 
133 
134 
135 	{
136 		Vec3f ppos, pos, to;
137 		Vec3f from(0.f, 0.f, -90.f);
138 		Vector_RotateY(&to, &from, MAKEANGLE(180.f - io->angle.b));
139 		ppos.x = io->pos.x;
140 		pos.x = ppos.x + to.x;
141 		ppos.y = io->pos.y - (80.f);
142 		pos.y = ppos.y + to.y;
143 		ppos.z = io->pos.z;
144 		pos.z = ppos.z + to.z;
145 
146 #ifdef BUILD_EDITOR
147 		if(DEBUGNPCMOVE) {
148 			EERIEDrawTrue3DLine(ppos, pos, Color::red);
149 		}
150 #endif
151 
152 		float dmg;
153 
154 		if (io->ioflags & IO_NPC)
155 		{
156 			dmg = io->_npcdata->damages;
157 		}
158 		else dmg = 40.f;
159 
160 
161 
162 		long i = io->targetinfo;
163 
164 		if (!ValidIONum(i)) return;
165 
166 		{
167 			Entity * ioo = entities[i];
168 
169 			if (! ioo) return;
170 
171 			if (ioo->ioflags & IO_MARKER) return;
172 
173 			if (ioo->ioflags & IO_CAMERA) return;
174 
175 
176 			if (ioo->gameFlags & GFLAG_ISINTREATZONE)
177 				if (ioo->show == SHOW_FLAG_IN_SCENE)
178 					if (ioo->obj)
179 						if (ioo->pos.y >	(io->pos.y + io->physics.cyl.height))
180 							if (io->pos.y >	(ioo->pos.y + ioo->physics.cyl.height))
181 							{
182 								float dist_limit = io->_npcdata->reach + io->physics.cyl.radius;
183 								long count = 0;
184 								float mindist = std::numeric_limits<float>::max();
185 
186 								for (size_t k = 0; k < ioo->obj->vertexlist.size(); k += 2)
187 								{
188 									float dist = fdist(pos, entities[i]->obj->vertexlist3[k].v);
189 
190 									if ((dist <= dist_limit)
191 											&&	(EEfabs(pos.y - entities[i]->obj->vertexlist3[k].v.y) < 60.f))
192 									{
193 										count++;
194 
195 										if(dist < mindist) mindist = dist;
196 									}
197 								}
198 
199 								float ratio = ((float)count / ((float)ioo->obj->vertexlist.size() * ( 1.0f / 2 )));
200 
201 								if (ioo->ioflags & IO_NPC)
202 								{
203 
204 									if (mindist <= dist_limit)
205 									{
206 										ARX_EQUIPMENT_ComputeDamages(io, ioo, ratioaim);
207 									}
208 
209 								} else {
210 									if(mindist <= 120.f) {
211 										ARX_DAMAGES_DamageFIX(ioo, dmg * ratio, io->index(), 0);
212 									}
213 								}
214 							}
215 		}
216 
217 
218 	}
219 }
220 
ARX_NPC_Kill_Spell_Launch(Entity * io)221 void ARX_NPC_Kill_Spell_Launch(Entity * io)
222 {
223 	if (io)
224 	{
225 		if (io->flarecount)
226 		{
227 			for (long i = 0; i < MAX_FLARES; i++)
228 			{
229 				if ((flare[i].exist)  && (flare[i].io == io))
230 					flare[i].io = NULL;
231 			}
232 		}
233 
234 		io->spellcast_data.castingspell = SPELL_NONE;
235 	}
236 }
237 
238 // Releases Pathfinder info from an NPC
ARX_NPC_ReleasePathFindInfo(Entity * io)239 void ARX_NPC_ReleasePathFindInfo(Entity * io) {
240 
241 	// Checks for valid IO/NPC
242 	if(!io || !(io->ioflags & IO_NPC)) {
243 		return;
244 	}
245 
246 	// Releases data & resets vars
247 	free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
248 	io->_npcdata->pathfind.listnb = -1;
249 	io->_npcdata->pathfind.listpos = 0;
250 	io->_npcdata->pathfind.pathwait = 0;
251 }
252 
253 // Creates an extra rotations structure for a NPC
ARX_NPC_CreateExRotateData(Entity * io)254 void ARX_NPC_CreateExRotateData(Entity * io) {
255 
256 	if(!io || !(io->ioflags & IO_NPC) || io->_npcdata->ex_rotate) {
257 		return;
258 	}
259 
260 	io->_npcdata->ex_rotate = (EERIE_EXTRA_ROTATE *)malloc(sizeof(EERIE_EXTRA_ROTATE));
261 	io->head_rot = 0;
262 
263 	if(io->_npcdata->ex_rotate) {
264 
265 		io->_npcdata->ex_rotate->group_number[0] = (short)EERIE_OBJECT_GetGroup(io->obj, "head");
266 		io->_npcdata->ex_rotate->group_number[1] = (short)EERIE_OBJECT_GetGroup(io->obj, "neck");
267 		io->_npcdata->ex_rotate->group_number[2] = (short)EERIE_OBJECT_GetGroup(io->obj, "chest");
268 		io->_npcdata->ex_rotate->group_number[3] = (short)EERIE_OBJECT_GetGroup(io->obj, "belt");
269 
270 		for(long n = 0; n < MAX_EXTRA_ROTATE; n++) {
271 			io->_npcdata->ex_rotate->group_rotate[n] = Anglef::ZERO;
272 		}
273 
274 		io->_npcdata->ex_rotate->flags = 0;
275 	}
276 
277 	io->_npcdata->look_around_inc = 0.f;
278 }
279 
280 //***********************************************************************************************
281 // Resurects an NPC
282 //-----------------------------------------------------------------------------------------------
283 // VERIFIED (Cyril 2001/10/15)
284 //***********************************************************************************************
ARX_NPC_Revive(Entity * io,long flags)285 void ARX_NPC_Revive(Entity * io, long flags)
286 {
287 	if ((TSecondaryInventory) && (TSecondaryInventory->io == io))
288 	{
289 		TSecondaryInventory = NULL;
290 	}
291 
292 	ARX_SCRIPT_SetMainEvent(io, "main");
293 
294 	if (io->ioflags & IO_NPC)
295 	{
296 		io->ioflags &= ~IO_NO_COLLISIONS;
297 		io->_npcdata->life = io->_npcdata->maxlife;
298 		ARX_SCRIPT_ResetObject(io, 1);
299 		io->_npcdata->life = io->_npcdata->maxlife;
300 	}
301 
302 	if(flags & 1) {
303 		io->room_flags |= 1;
304 		io->pos = io->initpos;
305 	}
306 
307 	long goretex = -1;
308 
309 	for (size_t i = 0; i < io->obj->texturecontainer.size(); i++)
310 	{
311 		if (!io->obj->texturecontainer.empty()
312 		        &&	io->obj->texturecontainer[i]
313 		        &&	(boost::contains(io->obj->texturecontainer[i]->m_texName.string(), "gore")))
314 		{
315 			goretex = i;
316 			break;
317 		}
318 	}
319 
320 	for (size_t ll = 0; ll < io->obj->facelist.size(); ll++)
321 	{
322 		if (io->obj->facelist[ll].texid != goretex)
323 		{
324 			io->obj->facelist[ll].facetype &= ~POLY_HIDE;
325 		}
326 		else
327 		{
328 			io->obj->facelist[ll].facetype |= POLY_HIDE;
329 		}
330 	}
331 
332 	if (io->ioflags & IO_NPC)
333 		io->_npcdata->cuts = 0;
334 }
335 //***********************************************************************************************
336 // Sets a new behaviour for NPC
337 //-----------------------------------------------------------------------------------------------
338 // VERIFIED (Cyril 2001/10/15)
339 //***********************************************************************************************
ARX_NPC_Behaviour_Change(Entity * io,Behaviour behavior,long behavior_param)340 void ARX_NPC_Behaviour_Change(Entity * io, Behaviour behavior, long behavior_param)
341 {
342 	if ((!io)
343 	        ||	(!(io->ioflags & IO_NPC)))
344 		return;
345 
346 	if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT) && !(behavior & BEHAVIOUR_FIGHT))
347 	{
348 		ANIM_USE * ause1 = &io->animlayer[1];
349 		AcquireLastAnim(io);
350 		FinishAnim(io, ause1->cur_anim);
351 		ause1->cur_anim = NULL;
352 	}
353 
354 	if ((behavior & BEHAVIOUR_NONE) || (behavior == 0))
355 	{
356 		ANIM_USE * ause0 = &io->animlayer[0];
357 		AcquireLastAnim(io);
358 		FinishAnim(io, ause0->cur_anim);
359 		ause0->cur_anim = NULL;
360 		ANIM_Set(ause0, io->anims[ANIM_DEFAULT]);
361 		ause0->flags &= ~EA_LOOP;
362 
363 		ANIM_USE * ause1 = &io->animlayer[1];
364 		AcquireLastAnim(io);
365 		FinishAnim(io, ause1->cur_anim);
366 		ause1->cur_anim = NULL;
367 		ause1->flags &= ~EA_LOOP;
368 
369 		ANIM_USE * ause2 = &io->animlayer[2];
370 		AcquireLastAnim(io);
371 		FinishAnim(io, ause2->cur_anim);
372 		ause2->cur_anim = NULL;
373 		ause2->flags &= ~EA_LOOP;
374 
375 
376 	}
377 
378 	if (behavior & BEHAVIOUR_FRIENDLY)
379 	{
380 		ANIM_USE * ause0 = &io->animlayer[0];
381 		AcquireLastAnim(io);
382 		FinishAnim(io, ause0->cur_anim);
383 		ANIM_Set(ause0, io->anims[ANIM_DEFAULT]);
384 		ause0->altidx_cur = 0;
385 	}
386 
387 	io->_npcdata->behavior = behavior;
388 	io->_npcdata->behavior_param = (float)behavior_param;
389 }
390 //***********************************************************************************************
391 // Resets all behaviour data from a NPC
392 //-----------------------------------------------------------------------------------------------
393 // VERIFIED (Cyril 2001/10/15)
394 //***********************************************************************************************
ARX_NPC_Behaviour_Reset(Entity * io)395 void ARX_NPC_Behaviour_Reset(Entity * io)
396 {
397 	if ((!io)
398 	        ||	(!(io->ioflags & IO_NPC)))
399 		return;
400 
401 	io->_npcdata->behavior = BEHAVIOUR_NONE;
402 
403 	for (long i = 0; i < MAX_STACKED_BEHAVIOR; i++)
404 		io->_npcdata->stacked[i].exist = 0;
405 }
406 
407 // Reset all Behaviours from all NPCs
ARX_NPC_Behaviour_ResetAll()408 void ARX_NPC_Behaviour_ResetAll() {
409 	for(size_t i = 0; i < entities.size(); i++) {
410 		if(entities[i]) {
411 			ARX_NPC_Behaviour_Reset(entities[i]);
412 		}
413 	}
414 }
415 
416 // Stacks an NPC behaviour
ARX_NPC_Behaviour_Stack(Entity * io)417 void ARX_NPC_Behaviour_Stack(Entity * io) {
418 
419 	if ((!io)
420 	        ||	(!(io->ioflags & IO_NPC)))
421 		return;
422 
423 	for (long i = 0; i < MAX_STACKED_BEHAVIOR; i++)
424 	{
425 		IO_BEHAVIOR_DATA * bd = &io->_npcdata->stacked[i];
426 
427 		if (bd->exist == 0)
428 		{
429 			bd->behavior = io->_npcdata->behavior;
430 			bd->behavior_param = io->_npcdata->behavior_param;
431 			bd->tactics = io->_npcdata->tactics;
432 
433 			if (io->_npcdata->pathfind.listnb > 0)
434 				bd->target = io->_npcdata->pathfind.truetarget;
435 			else
436 				bd->target = io->targetinfo;
437 
438 			bd->movemode = io->_npcdata->movemode;
439 			bd->exist = 1;
440 			return;
441 		}
442 	}
443 }
444 
445 //***********************************************************************************************
446 // Unstacks One stacked behaviour from an NPC
447 //-----------------------------------------------------------------------------------------------
448 // VERIFIED (Cyril 2001/10/15)
449 //***********************************************************************************************
ARX_NPC_Behaviour_UnStack(Entity * io)450 void ARX_NPC_Behaviour_UnStack(Entity * io)
451 {
452 	if ((!io)
453 	        ||	(!(io->ioflags & IO_NPC)))
454 		return;
455 
456 	for (long i = MAX_STACKED_BEHAVIOR - 1; i >= 0; i--)
457 	{
458 		IO_BEHAVIOR_DATA * bd = &io->_npcdata->stacked[i];
459 
460 		if (bd->exist)
461 		{
462 			AcquireLastAnim(io);
463 			io->_npcdata->behavior = bd->behavior;
464 			io->_npcdata->behavior_param = bd->behavior_param;
465 			io->_npcdata->tactics = bd->tactics;
466 			io->targetinfo = bd->target;
467 			io->_npcdata->movemode = bd->movemode;
468 			bd->exist = 0;
469 			ARX_NPC_LaunchPathfind(io, bd->target);
470 
471 			if (io->_npcdata->behavior & BEHAVIOUR_NONE)
472 			{
473 				memcpy(io->animlayer, bd->animlayer, sizeof(ANIM_USE)*MAX_ANIM_LAYERS);
474 			}
475 
476 			return;
477 		}
478 	}
479 }
480 extern void GetIOCyl(Entity * io, EERIE_CYLINDER * cyl);
481 //***********************************************************************************************
482 // long ARX_NPC_GetNextAttainableNodeIncrement(Entity * io)
483 // Description:
484 //			Checks for any direct shortcut between NPC and future anchors...
485 //-----------------------------------------------------------------------------------------------
486 // VERIFIED (Cyril 2001/10/15)
487 //***********************************************************************************************
ARX_NPC_GetNextAttainableNodeIncrement(Entity * io)488 long ARX_NPC_GetNextAttainableNodeIncrement(Entity * io)
489 {
490 	if ((!io)
491 	        ||	(!(io->ioflags & IO_NPC))
492 	        ||	(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND))
493 		return 0;
494 
495 	float dists = distSqr(io->pos, ACTIVECAM->pos);
496 
497 	if (dists > square(ACTIVECAM->cdepth) * square(1.0f / 2))
498 		return 0;
499 
500 	long MAX_TEST;
501 
502 	if (dists < square(ACTIVECAM->cdepth) * square(1.0f / 4))
503 		MAX_TEST = 6; //4;
504 	else
505 		MAX_TEST = 4; //3;
506 
507 	for (long l_try = MAX_TEST; l_try > 1; l_try--)
508 	{
509 		if (io->_npcdata->pathfind.listpos + l_try > io->_npcdata->pathfind.listnb - 1) continue;
510 
511 		float tot = 0.f;
512 
513 		for (long aa = l_try; aa > 1; aa--)
514 		{
515 			long v = io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos+aa];
516 			tot += ACTIVEBKG->anchors[v].nblinked;
517 
518 			if (aa == l_try)
519 				tot += ACTIVEBKG->anchors[v].nblinked;
520 		}
521 
522 		tot /= (float)(l_try + 1);
523 
524 		if (tot <= 3.5f) continue;
525 
526 		io->physics.startpos = io->pos;
527 		long pos = io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos+l_try];
528 		io->physics.targetpos = ACTIVEBKG->anchors[pos].pos;
529 
530 		if (EEfabs(io->physics.startpos.y - io->physics.targetpos.y) > 60.f) continue;
531 
532 		io->physics.targetpos.y += 60.f; // FAKE Gravity !
533 		IO_PHYSICS phys;
534 		memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
535 		GetIOCyl(io, &phys.cyl);
536 
537 		// Now we try the physical move for real
538 		if(io->physics.startpos == io->physics.targetpos
539 		        || ((ARX_COLLISION_Move_Cylinder(&phys, io, 40, CFLAG_JUST_TEST | CFLAG_NPC))))
540 		{
541 			if(distSqr(phys.cyl.origin, ACTIVEBKG->anchors[pos].pos) < square(30.f)) {
542 				return l_try;
543 			}
544 		}
545 	}
546 
547 	return 0;
548 }
549 //*****************************************************************************
550 // Checks for nearest VALID anchor for a cylinder from a position
551 //*****************************************************************************
AnchorData_GetNearest(Vec3f * pos,EERIE_CYLINDER * cyl)552 static long AnchorData_GetNearest(Vec3f * pos, EERIE_CYLINDER * cyl) {
553 	long returnvalue = -1;
554 	float distmax = std::numeric_limits<float>::max();
555 	EERIE_BACKGROUND * eb = ACTIVEBKG;
556 
557 	for (long i = 0; i < eb->nbanchors; i++)
558 	{
559 		if (eb->anchors[i].nblinked)
560 		{
561 			float d = distSqr(eb->anchors[i].pos, *pos);
562 
563 			if ((d < distmax) && (eb->anchors[i].height <= cyl->height)
564 			        && (eb->anchors[i].radius >= cyl->radius)
565 			        && (!(eb->anchors[i].flags & ANCHOR_FLAG_BLOCKED)))
566 			{
567 				returnvalue = i;
568 				distmax = d;
569 			}
570 		}
571 	}
572 
573 	return returnvalue;
574 }
575 
AnchorData_GetNearest_2(float beta,Vec3f * pos,EERIE_CYLINDER * cyl)576 static long AnchorData_GetNearest_2(float beta, Vec3f * pos, EERIE_CYLINDER * cyl) {
577 
578 	float d = radians(beta);
579 	Vec3f vect(-EEsin(d), 0, EEcos(d));
580 	vect.normalize();
581 
582 	Vec3f posi;
583 	posi.x = pos->x + vect.x * 50.f;
584 	posi.y = pos->y;
585 	posi.z = pos->z + vect.x * 50.f;
586 	return AnchorData_GetNearest(&posi, cyl);
587 }
588 
AnchorData_GetNearest_Except(Vec3f * pos,EERIE_CYLINDER * cyl,long except)589 static long AnchorData_GetNearest_Except(Vec3f * pos, EERIE_CYLINDER * cyl, long except) {
590 
591 	long returnvalue = -1;
592 	float distmax = std::numeric_limits<float>::max();
593 	EERIE_BACKGROUND * eb = ACTIVEBKG;
594 
595 	for (long i = 0; i < eb->nbanchors; i++)
596 	{
597 		if (i == except) continue;
598 
599 		if (eb->anchors[i].nblinked)
600 		{
601 			float d = distSqr(eb->anchors[i].pos, *pos);
602 
603 			if ((d < distmax) && (eb->anchors[i].height <= cyl->height)
604 			        && (eb->anchors[i].radius >= cyl->radius)
605 			        && (!(eb->anchors[i].flags & ANCHOR_FLAG_BLOCKED)))
606 			{
607 				returnvalue = i;
608 				distmax = d;
609 			}
610 		}
611 	}
612 
613 	return returnvalue;
614 }
615 
ARX_NPC_LaunchPathfind(Entity * io,long target)616 bool ARX_NPC_LaunchPathfind(Entity * io, long target)
617 {
618 	// Check Validity
619 	if ((!io) || (!(io->ioflags & IO_NPC)))
620 		return false;
621 
622 	long MUST_SELECT_Start_Anchor = -1;
623 	io->physics.cyl.origin = io->pos;
624 	long old_target = io->targetinfo;
625 
626 	if	((!(io->ioflags & IO_PHYSICAL_OFF)))
627 	{
628 		if (!ForceNPC_Above_Ground(io))
629 		{
630 			io->_npcdata->pathfind.pathwait = 0;
631 			return false;
632 		}
633 	}
634 
635 	if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)
636 	{
637 		io->targetinfo = target;
638 		io->_npcdata->pathfind.pathwait = 0;
639 		return false;
640 	}
641 
642 	if(io->_npcdata->pathfind.listnb > 0) {
643 		io->_npcdata->pathfind.listnb = -1;
644 		io->_npcdata->pathfind.listpos = 0;
645 		io->_npcdata->pathfind.pathwait = 0;
646 		io->_npcdata->pathfind.truetarget = TARGET_NONE;
647 		free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
648 	}
649 
650 	Vec3f pos1, pos2;
651 	if(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND) {
652 		pos1 = io->pos;
653 		pos2 = io->pos + Vec3f(1000.f, 0.f, 1000.f);
654 		goto wander;
655 	}
656 
657 	if ((target < 0) || (io->_npcdata->behavior & BEHAVIOUR_GO_HOME))
658 	{
659 		target = io->index();
660 
661 		if (target == -1)
662 			goto failure;
663 
664 		io->_npcdata->pathfind.truetarget = target;
665 		pos1 = io->pos;
666 
667 		if(io->_npcdata->behavior & BEHAVIOUR_GO_HOME) {
668 			pos2 = io->initpos;
669 		} else {
670 			pos2 = pos1;
671 		}
672 
673 		goto suite;
674 	}
675 
676 	if ((ValidIONum(target))
677 	        &&	(entities[target] == io))
678 	{
679 		io->_npcdata->pathfind.pathwait = 0;
680 		return false; // cannot pathfind self...
681 	}
682 
683 	if (old_target != target)
684 		io->_npcdata->reachedtarget = 0;
685 
686 	EVENT_SENDER = NULL;
687 
688 	pos1 = io->pos;
689 
690 	if(io->_npcdata->behavior & BEHAVIOUR_GO_HOME) {
691 		pos2 = io->initpos;
692 	} else if(ValidIONum(target)) {
693 		pos2 = entities[target]->pos;
694 	} else {
695 		pos2 = io->pos;
696 	}
697 
698 	io->_npcdata->pathfind.truetarget = target;
699 
700 	if ((distSqr(pos1, ACTIVECAM->pos) < square(ACTIVECAM->cdepth) * square(1.0f / 2))
701 	        &&	(EEfabs(pos1.y - pos2.y) < 50.f)
702 	        && (distSqr(pos1, pos2) < square(520)) && (io->_npcdata->behavior & BEHAVIOUR_MOVE_TO)
703 	        && (!(io->_npcdata->behavior & BEHAVIOUR_SNEAK))
704 	        && (!(io->_npcdata->behavior & BEHAVIOUR_FLEE))
705 	   )
706 	{
707 		// COLLISION Management START *********************************************************************
708 		io->physics.startpos = pos1;
709 		io->physics.targetpos = pos2;
710 		IO_PHYSICS phys;
711 		memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
712 		GetIOCyl(io, &phys.cyl);
713 
714 		// Now we try the physical move for real
715 		if(io->physics.startpos == io->physics.targetpos
716 		        || ((ARX_COLLISION_Move_Cylinder(&phys, io, 40, CFLAG_JUST_TEST | CFLAG_NPC | CFLAG_NO_HEIGHT_MOD)) ))
717 		{
718 			if(distSqr(phys.cyl.origin, pos2) < square(100.f)) {
719 				io->_npcdata->pathfind.pathwait = 0;
720 				return false;
721 			}
722 		}
723 	}
724 
725 suite:
726 	;
727 wander:
728 	;
729 	io->targetinfo = target;
730 	io->_npcdata->pathfind.truetarget = target;
731 
732 	long from;
733 
734 	if (MUST_SELECT_Start_Anchor == -1)
735 	{
736 		if ((io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
737 		        ||	(io->_npcdata->behavior & BEHAVIOUR_FLEE))
738 			from = AnchorData_GetNearest(&pos1, &io->physics.cyl);
739 		else
740 			from = AnchorData_GetNearest_2(io->angle.b, &pos1, &io->physics.cyl);
741 	}
742 	else from = MUST_SELECT_Start_Anchor;
743 
744 	long to;
745 
746 	if (io->_npcdata->behavior & BEHAVIOUR_FLEE)
747 		to = AnchorData_GetNearest_Except(&pos2, &io->physics.cyl, from);
748 	else if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
749 		to = from;
750 	else to = AnchorData_GetNearest(&pos2, &io->physics.cyl);
751 
752 	if ((from != -1) && (to != -1))
753 	{
754 		if ((from == to) && !(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND))
755 			return true;
756 
757 		if ((io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND) ||
758 		        closerThan(pos1, ACTIVEBKG->anchors[from].pos, 200.f))
759 		{
760 			if (!(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
761 			        && !(io->_npcdata->behavior & BEHAVIOUR_FLEE))
762 			{
763 				if(closerThan(ACTIVEBKG->anchors[from].pos, ACTIVEBKG->anchors[to].pos, 200.f)) {
764 					return false;
765 				}
766 
767 				if(fartherThan(pos2, ACTIVEBKG->anchors[to].pos, 200.f))
768 					goto failure;
769 			}
770 
771 			if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
772 			{
773 				io->_npcdata->pathfind.truetarget = TARGET_NONE;
774 			}
775 
776 			io->_npcdata->pathfind.listnb = -1;
777 			io->_npcdata->pathfind.listpos = 0;
778 			io->_npcdata->pathfind.pathwait = 1;
779 			free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
780 
781 			PATHFINDER_REQUEST tpr;
782 			tpr.from = from;
783 			tpr.to = to;
784 			tpr.returnlist = &io->_npcdata->pathfind.list;
785 			tpr.returnnumber = &io->_npcdata->pathfind.listnb;
786 			tpr.ioid = io;
787 			tpr.isvalid = true;
788 
789 			if (EERIE_PATHFINDER_Add_To_Queue(&tpr))
790 				return true;
791 		}
792 	}
793 
794 failure:
795 	;
796 	io->_npcdata->pathfind.pathwait = 0;
797 
798 	if (io->_npcdata->pathfind.list) ARX_NPC_ReleasePathFindInfo(io);
799 
800 	io->_npcdata->pathfind.listnb = -2;
801 
802 	if (io->_npcdata->pathfind.flags & PATHFIND_ALWAYS) return false; // TODO was BEHAVIOUR_NONE
803 
804 	SendIOScriptEvent(io, SM_PATHFINDER_FAILURE);
805 	return false;
806 }
807 
ARX_NPC_SetStat(Entity & io,const string & statname,float value)808 bool ARX_NPC_SetStat(Entity& io, const string & statname, float value) {
809 
810 	arx_assert(io.ioflags & IO_NPC);
811 
812 	if(statname == "armor_class") {
813 		io._npcdata->armor_class = value < 0 ? 0 : value;
814 	} else if(statname == "backstab_skill" || statname == "backstabskill") {
815 		io._npcdata->backstab_skill = value < 0 ? 0 : value;
816 	} else if(statname == "backstab") {
817 		if (value == 0) io._npcdata->npcflags &= ~NPCFLAG_BACKSTAB;
818 		else io._npcdata->npcflags |= NPCFLAG_BACKSTAB;
819 	} else if(statname == "reach") {
820 		io._npcdata->reach = value < 0 ? 0 : value;
821 	} else if(statname == "critical") {
822 		io._npcdata->critical = value < 0 ? 0 : value;
823 	} else if(statname == "absorb") {
824 		io._npcdata->absorb = value < 0 ? 0 : value;
825 	} else if(statname == "damages") {
826 		io._npcdata->damages = value < 0 ? 0 : value;
827 	} else if(statname == "tohit") {
828 		io._npcdata->tohit = value < 0 ? 0 : value;
829 	} else if(statname == "aimtime") {
830 		io._npcdata->aimtime = value < 0 ? 0 : value;
831 	} else if(statname == "life") {
832 		io._npcdata->maxlife = io._npcdata->life = value < 0 ? 0.0000001f : value;
833 	} else if(statname == "mana") {
834 		io._npcdata->maxmana = io._npcdata->mana = value < 0 ? 0 : value;
835 	} else if(statname == "resistfire") {
836 		io._npcdata->resist_fire = (unsigned char)clamp(value, 0.f, 100.f);
837 	} else if(statname == "resistpoison") {
838 		io._npcdata->resist_poison = (unsigned char)clamp(value, 0.f, 100.f);
839 	} else if(statname == "resistmagic") {
840 		io._npcdata->resist_magic = (unsigned char)clamp(value, 0.f, 100.f);
841 	} else {
842 		return false;
843 	}
844 
845 	return true;
846 }
847 
848 extern long CUR_COLLISION_MATERIAL;
849 static Entity * PHYSICS_CURIO = NULL;
850 
ARX_TEMPORARY_TrySound(float volume)851 void ARX_TEMPORARY_TrySound(float volume) {
852 
853 	if (PHYSICS_CURIO)
854 	{
855 		if (PHYSICS_CURIO->ioflags & IO_BODY_CHUNK)
856 			return;
857 
858 		unsigned long at = (unsigned long)(arxtime);
859 
860 		if (at > PHYSICS_CURIO->soundtime)
861 		{
862 
863 			PHYSICS_CURIO->soundcount++;
864 
865 			if (PHYSICS_CURIO->soundcount < 5)
866 			{
867 				long material;
868 				if ( EEIsUnderWater( &PHYSICS_CURIO->pos ) ) material = MATERIAL_WATER;
869 				else if (PHYSICS_CURIO->material) material = PHYSICS_CURIO->material;
870 				else material = MATERIAL_STONE;
871 
872 				if (volume > 1.f)
873 					volume = 1.f;
874 
875 				PHYSICS_CURIO->soundtime = at + (ARX_SOUND_PlayCollision(material, CUR_COLLISION_MATERIAL, volume, 1.f, &PHYSICS_CURIO->pos, PHYSICS_CURIO) >> 4) + 50;
876 			}
877 		}
878 	}
879 }
880 //***********************************************************************************************
881 // Sets a New MoveMode for a NPC
882 //-----------------------------------------------------------------------------------------------
883 // VERIFIED (Cyril 2001/10/15)
884 //***********************************************************************************************
ARX_NPC_ChangeMoveMode(Entity * io,MoveMode MOVEMODE)885 void ARX_NPC_ChangeMoveMode(Entity * io, MoveMode MOVEMODE)
886 {
887 	if ((!io)
888 	        ||	(!(io->ioflags & IO_NPC)))
889 		return;
890 
891 	ANIM_USE * ause0 = &io->animlayer[0];
892 	ANIM_HANDLE ** alist = io->anims;
893 
894 	switch (MOVEMODE)
895 	{
896 		case RUNMODE:
897 
898 			if (((ause0->cur_anim == alist[ANIM_WALK]) && (alist[ANIM_WALK]))
899 			        || ((ause0->cur_anim == alist[ANIM_WALK_SNEAK]) && (alist[ANIM_WALK_SNEAK])))
900 			{
901 				AcquireLastAnim(io);
902 				FinishAnim(io, ause0->cur_anim);
903 				ANIM_Set(ause0, alist[ANIM_RUN]);
904 				ause0->altidx_cur = 0;
905 			}
906 
907 			break;
908 		case WALKMODE:
909 
910 			if (((ause0->cur_anim == alist[ANIM_RUN]) && (alist[ANIM_RUN]))
911 			        || ((ause0->cur_anim == alist[ANIM_WALK_SNEAK]) && (alist[ANIM_WALK_SNEAK])))
912 			{
913 				AcquireLastAnim(io);
914 				FinishAnim(io, ause0->cur_anim);
915 				ANIM_Set(ause0, alist[ANIM_WALK]);
916 			}
917 
918 			break;
919 		case NOMOVEMODE:
920 
921 			if (((ause0->cur_anim == alist[ANIM_WALK]) && (alist[ANIM_WALK]))
922 			        || ((ause0->cur_anim == alist[ANIM_RUN]) && (alist[ANIM_RUN]))
923 			        || ((ause0->cur_anim == alist[ANIM_WALK_SNEAK]) && (alist[ANIM_WALK_SNEAK])))
924 			{
925 				AcquireLastAnim(io);
926 				FinishAnim(io, ause0->cur_anim);
927 				ANIM_Set(ause0, alist[ANIM_WAIT]);
928 				ause0->altidx_cur = 0;
929 			}
930 
931 			break;
932 		case SNEAKMODE:
933 
934 			if (((ause0->cur_anim == alist[ANIM_WALK]) && (alist[ANIM_WALK]))
935 			        || ((ause0->cur_anim == alist[ANIM_RUN]) && (alist[ANIM_RUN])))
936 			{
937 				AcquireLastAnim(io);
938 				FinishAnim(io, ause0->cur_anim);
939 				ANIM_Set(ause0, alist[ANIM_WALK_SNEAK]);
940 			}
941 
942 			break;
943 	}
944 
945 	io->_npcdata->movemode = MOVEMODE;
946 }
947 //***********************************************************************************************
948 // Diminishes life of a Poisoned NPC
949 //-----------------------------------------------------------------------------------------------
950 // VERIFIED (Cyril 2001/10/15)
951 //***********************************************************************************************
ARX_NPC_ManagePoison(Entity * io)952 void ARX_NPC_ManagePoison(Entity * io)
953 {
954 	float cp = io->_npcdata->poisonned;
955 	cp *= ( 1.0f / 2 ) * framedelay * ( 1.0f / 1000 ) * ( 1.0f / 2 );
956 	float faster = 10.f - io->_npcdata->poisonned;
957 
958 	if (faster < 0.f) faster = 0.f;
959 
960 	if (rnd() * 100.f > io->_npcdata->resist_poison + faster)
961 	{
962 		float dmg = cp * ( 1.0f / 3 );
963 
964 		if ((io->_npcdata->life > 0) && (io->_npcdata->life - dmg <= 0.f))
965 		{
966 			long xp = io->_npcdata->xpvalue;
967 			ARX_DAMAGES_DamageNPC(io, dmg, -1, 0, NULL);
968 			ARX_PLAYER_Modify_XP(xp);
969 		}
970 		else io->_npcdata->life -= dmg;
971 
972 		io->_npcdata->poisonned -= cp * ( 1.0f / 10 );
973 	}
974 	else io->_npcdata->poisonned -= cp;
975 
976 	if (io->_npcdata->poisonned < 0.1f) io->_npcdata->poisonned = 0.f;
977 }
978 
979 // FUNCTION:
980 //   Checks if the bottom of an IO is underwater.
981 // RESULT:
982 //   Plays Water sounds
983 //   Decrease/stops Ignition of this IO if necessary
984 // WARNINGS:
985 // io must be valid (no check !)
CheckUnderWaterIO(Entity * io)986 static void CheckUnderWaterIO(Entity * io) {
987 
988 	Vec3f ppos = io->pos;
989 	EERIEPOLY * ep = EEIsUnderWater(&ppos);
990 
991 	if (io->ioflags & IO_UNDERWATER)
992 	{
993 		if (!ep)
994 		{
995 			io->ioflags &= ~IO_UNDERWATER;
996 			ARX_SOUND_PlaySFX(SND_PLOUF, &ppos);
997 			ARX_PARTICLES_SpawnWaterSplash(&ppos);
998 		}
999 	}
1000 	else if (ep)
1001 	{
1002 		io->ioflags |= IO_UNDERWATER;
1003 		ARX_SOUND_PlaySFX(SND_PLOUF, &ppos);
1004 		ARX_PARTICLES_SpawnWaterSplash(&ppos);
1005 
1006 		if (io->ignition > 0.f)
1007 		{
1008 			ARX_SOUND_PlaySFX(SND_TORCH_END, &ppos);
1009 
1010 			if (ValidDynLight(io->ignit_light))
1011 				DynLight[io->ignit_light].exist = 0;
1012 
1013 			io->ignit_light = -1;
1014 
1015 			if (io->ignit_sound != audio::INVALID_ID)
1016 			{
1017 				ARX_SOUND_Stop(io->ignit_sound);
1018 				io->ignit_sound = audio::INVALID_ID;
1019 			}
1020 
1021 			io->ignition = 0;
1022 		}
1023 	}
1024 }
1025 
1026 static void ManageNPCMovement(Entity * io);
1027 
1028 extern float MAX_ALLOWED_PER_SECOND;
1029 
ARX_PHYSICS_Apply()1030 void ARX_PHYSICS_Apply()
1031 {
1032 
1033 	static long CURRENT_DETECT = 0;
1034 
1035 	CURRENT_DETECT++;
1036 
1037 	if (CURRENT_DETECT > TREATZONE_CUR) CURRENT_DETECT = 1;
1038 
1039 	for (long i = 1; i < TREATZONE_CUR; i++) // We don't manage Player(0) this way
1040 	{
1041 		if (treatio[i].show != 1) continue;
1042 
1043 		if (treatio[i].ioflags & (IO_FIX | IO_JUST_COLLIDE))
1044 			continue;
1045 
1046 		Entity * io = treatio[i].io;
1047 
1048 		if (!io) continue;
1049 
1050 		if ((io->ioflags & IO_NPC) && (io->_npcdata->poisonned > 0.f)) ARX_NPC_ManagePoison(io);
1051 
1052 		if ((io->ioflags & IO_ITEM)
1053 		        &&	(io->show != SHOW_FLAG_DESTROYED)
1054 		        &&	((io->gameFlags & GFLAG_GOREEXPLODE)
1055 		             &&	(float(arxtime) - io->lastanimtime > 300))
1056 		        &&	((io->obj)
1057 		             &&	!io->obj->vertexlist.empty())
1058 		   )
1059 		{
1060 			long cnt = (io->obj->vertexlist.size() << 12) + 1;
1061 
1062 			if (cnt < 2) cnt = 2;
1063 
1064 			if (cnt > 10) cnt = 10;
1065 
1066 			for (long nn = 0; nn < cnt; nn++)
1067 			{
1068 				std::vector<EERIE_VERTEX>::iterator it = Random::getIterator(io->obj->vertexlist);
1069 
1070 				ARX_PARTICLES_Spawn_Splat(it->v, 20.f, Color::red);
1071 				ARX_PARTICLES_Spawn_Blood(&it->v, 20.f, io->index());
1072 			}
1073 
1074 			ARX_INTERACTIVE_DestroyIO(io);
1075 			continue;
1076 		}
1077 
1078 		EERIEPOLY * ep = EECheckInPoly(&io->pos);
1079 
1080 		if ((ep)
1081 		        &&	(ep->type & POLY_LAVA)
1082 		        &&	(EEfabs(ep->center.y - io->pos.y) < 40))
1083 		{
1084 			ARX_PARTICLES_Spawn_Lava_Burn(&io->pos, io);
1085 
1086 			if (io->ioflags & IO_NPC)
1087 			{
1088 				const float LAVA_DAMAGE = 10.f;
1089 				ARX_DAMAGES_DamageNPC(io, LAVA_DAMAGE * FrameDiff * ( 1.0f / 100 ), -1, 0, NULL);
1090 			}
1091 		}
1092 
1093 		CheckUnderWaterIO(io);
1094 
1095 		if(io->obj && io->obj->pbox) {
1096 
1097 			io->gameFlags &= ~GFLAG_NOCOMPUTATION;
1098 
1099 			if(io->obj->pbox->active == 1) {
1100 				PHYSICS_CURIO = io;
1101 
1102 				if (ARX_PHYSICS_BOX_ApplyModel(io->obj, (float)FrameDiff, io->rubber, treatio[i].num))
1103 				{
1104 					if (io->damagedata >= 0)
1105 					{
1106 						damages[io->damagedata].active = 1;
1107 						ARX_DAMAGES_UpdateDamage(io->damagedata, float(arxtime));
1108 						damages[io->damagedata].exist = 0;
1109 						io->damagedata = -1;
1110 					}
1111 				}
1112 
1113 				if(io->soundcount > 12) {
1114 					io->soundtime = 0;
1115 					io->soundcount = 0;
1116 					for(long k = 0; k < io->obj->pbox->nb_physvert; k++) {
1117 						io->obj->pbox->vert[k].velocity = Vec3f::ZERO;
1118 					}
1119 					io->obj->pbox->active = 2;
1120 					io->obj->pbox->stopcount = 0;
1121 				}
1122 
1123 				io->room_flags |= 1;
1124 				io->pos = io->obj->pbox->vert[0].pos;
1125 
1126 				continue;
1127 			}
1128 		}
1129 
1130 		if (IsDeadNPC(io)) continue;
1131 
1132 		if (io->ioflags & IO_PHYSICAL_OFF)
1133 		{
1134 			if (io->ioflags & IO_NPC)
1135 			{
1136 				ANIM_USE * ause0 = &io->animlayer[0];
1137 
1138 				if ((ause0->cur_anim == 0) || (ause0->flags & EA_ANIMEND))
1139 				{
1140 					ANIM_Set(ause0, io->anims[ANIM_WAIT]);
1141 					ause0->altidx_cur = 0;
1142 				}
1143 
1144 				GetTargetPos(io);
1145 
1146 				if ((!arxtime.is_paused()) && (!(ause0->flags & EA_FORCEPLAY)))
1147 				{
1148 					if (io->_npcdata->behavior & BEHAVIOUR_STARE_AT)
1149 						StareAtTarget(io);
1150 					else	FaceTarget2(io);
1151 				}
1152 			}
1153 
1154 			continue;
1155 		}
1156 
1157 
1158 		if ((io->ioflags & IO_NPC)
1159 		        &&	(!EDITMODE))
1160 		{
1161 			if ((io->_npcdata->climb_count != 0.f) && (FrameDiff > 0))
1162 			{
1163 				io->_npcdata->climb_count -= MAX_ALLOWED_PER_SECOND * (float)FrameDiff * ( 1.0f / 1000 );
1164 
1165 				if (io->_npcdata->climb_count < 0)
1166 					io->_npcdata->climb_count = 0.f;
1167 			}
1168 
1169 			if (io->_npcdata->pathfind.pathwait) // Waiting For Pathfinder Answer
1170 			{
1171 #ifdef BUILD_EDITOR
1172 				if ((ValidIONum(LastSelectedIONum)) &&
1173 				        (io == entities[LastSelectedIONum])) ShowIOPath(io);
1174 #endif
1175 				if (io->_npcdata->pathfind.listnb == 0) // Not Found
1176 				{
1177 					SendIOScriptEvent(io, SM_PATHFINDER_FAILURE);
1178 					io->_npcdata->pathfind.pathwait = 0;
1179 
1180 					if (io->_npcdata->pathfind.list)
1181 						ARX_NPC_ReleasePathFindInfo(io);
1182 
1183 					io->_npcdata->pathfind.listnb = -2;
1184 				}
1185 				else if (io->_npcdata->pathfind.listnb > 0) // Found
1186 				{
1187 					SendIOScriptEvent(io, SM_PATHFINDER_SUCCESS);
1188 					io->_npcdata->pathfind.pathwait = 0;
1189 					io->_npcdata->pathfind.listpos += (unsigned short)ARX_NPC_GetNextAttainableNodeIncrement(io);
1190 
1191 					if (io->_npcdata->pathfind.listpos >= io->_npcdata->pathfind.listnb)
1192 						io->_npcdata->pathfind.listpos = 0;
1193 				}
1194 			}
1195 
1196 			ManageNPCMovement(io);
1197 			CheckNPC(io);
1198 
1199 			if (CURRENT_DETECT == i) CheckNPCEx(io);
1200 		}
1201 	}
1202 }
1203 //*************************************************************************************
1204 //*************************************************************************************
FaceTarget2(Entity * io)1205 void FaceTarget2(Entity * io)
1206 {
1207 	Vec3f tv;
1208 
1209 	if (!io->show) return;
1210 
1211 	if (io->ioflags & IO_NPC)
1212 	{
1213 		if (io->_npcdata->life <= 0.f) return;
1214 
1215 		if (io->_npcdata->behavior & BEHAVIOUR_NONE) return;
1216 
1217 		if ((io->_npcdata->pathfind.listnb <= 0)
1218 		        && (io->_npcdata->behavior & BEHAVIOUR_FLEE)) return;
1219 
1220 	}
1221 
1222 	GetTargetPos(io);
1223 	tv = io->pos;
1224 
1225 	if(!fartherThan(Vec2f(tv.x, tv.z), Vec2f(io->target.x, io->target.z), 5.f)) {
1226 		return;
1227 	}
1228 
1229 	float cangle, tangle;
1230 	tangle = MAKEANGLE(180.f + degrees(getAngle(io->target.x, io->target.z, tv.x, tv.z)));
1231 	cangle = io->angle.b;
1232 
1233 	float tt = (cangle - tangle);
1234 
1235 	if (tt == 0) return;
1236 
1237 	float rot = 0.33f * framedelay;
1238 
1239 	if (EEfabs(tt) < rot) rot = (float)EEfabs(tt);
1240 
1241 	rot = -rot;
1242 
1243 	if ((tt > 0.f) && (tt < 180.f)) rot = -rot;
1244 	else if ((tt < -180.f)) rot = -rot;
1245 
1246 	if(rot != 0) {
1247 		Vec3f temp = io->move;
1248 		Vector_RotateY(&io->move, &temp, rot);
1249 		temp = io->lastmove;
1250 		Vector_RotateY(&io->lastmove, &temp, rot);
1251 	}
1252 
1253 	// Needed angle to turn toward target
1254 	io->angle.b = MAKEANGLE(io->angle.b - rot); // -tt
1255 }
1256 
1257 //***********************************************************************************************
1258 //***********************************************************************************************
StareAtTarget(Entity * io)1259 void StareAtTarget(Entity * io)
1260 {
1261 	if (io->_npcdata->ex_rotate == NULL)
1262 	{
1263 		ARX_NPC_CreateExRotateData(io);
1264 	}
1265 
1266 	if (!io->show) return;
1267 
1268 	if (io->ioflags & IO_NPC)
1269 	{
1270 		if (io->_npcdata->life <= 0.f) return;
1271 
1272 		if ((io->_npcdata->pathfind.listnb <= 0) && (io->_npcdata->behavior & BEHAVIOUR_FLEE)) return;
1273 	}
1274 
1275 	if (io->_npcdata->behavior & BEHAVIOUR_NONE) return;
1276 
1277 	GetTargetPos(io);
1278 	Vec3f tv = io->pos;
1279 
1280 	if (dist(tv, io->target) <= 20.f) return; // To fix "stupid" rotation near target
1281 
1282 	if (((io->target.x - tv.x) == 0) && ((io->target.z - tv.z) == 0)) return;
1283 
1284 	float rot = 0.27f * framedelay;
1285 	float alpha = MAKEANGLE(io->angle.b);
1286 	float beta = -io->head_rot;
1287 	float pouet = MAKEANGLE(180.f + degrees(getAngle(io->target.x, io->target.z, tv.x, tv.z)));
1288 	float A = MAKEANGLE((MAKEANGLE(alpha + beta) - pouet));
1289 	float B = MAKEANGLE(alpha - pouet);
1290 
1291 	if (A == 0.f) rot = 0.f;
1292 
1293 	if ((B < 180) && (B > 90))
1294 	{
1295 		if (rot > A) rot = A;
1296 	}
1297 	else if ((B > 180) && (B < 270))
1298 	{
1299 		if (rot > 360 - A) rot = -(360 - A);
1300 		else rot = -rot;
1301 	}
1302 	else if (A < 180)
1303 	{
1304 		if (rot > A) rot = A;
1305 	}
1306 	else
1307 	{
1308 		if (rot > 360 - A) rot = -(360 - A);
1309 		else rot = -rot;
1310 	}
1311 
1312 	// Needed angle to turn toward target
1313 	rot *= ( 1.0f / 2 );
1314 	float HEAD_ANGLE_THRESHOLD;
1315 
1316 	if ((io) && (io->ioflags & IO_NPC))
1317 		HEAD_ANGLE_THRESHOLD = 45.f * io->_npcdata->stare_factor;
1318 	else HEAD_ANGLE_THRESHOLD = 45.f;
1319 
1320 	io->head_rot += rot;
1321 
1322 	if (io->head_rot > 120.f) io->head_rot = 120.f;
1323 
1324 	if (io->head_rot < -120.f) io->head_rot = -120.f;
1325 
1326 	io->_npcdata->ex_rotate->group_rotate[0].b = io->head_rot * 1.5f;
1327 
1328 	if (io->_npcdata->ex_rotate->group_rotate[0].b > HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[0].b = HEAD_ANGLE_THRESHOLD;
1329 
1330 	if (io->_npcdata->ex_rotate->group_rotate[0].b < -HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[0].b = -HEAD_ANGLE_THRESHOLD;
1331 
1332 	io->_npcdata->ex_rotate->group_rotate[1].b = io->head_rot * ( 1.0f / 2 );
1333 
1334 	if (io->_npcdata->ex_rotate->group_rotate[1].b > HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[1].b = HEAD_ANGLE_THRESHOLD;
1335 
1336 	if (io->_npcdata->ex_rotate->group_rotate[1].b < -HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[1].b = -HEAD_ANGLE_THRESHOLD;
1337 
1338 	//MAKEANGLE(io->angle.b-rot); // -tt
1339 	return;
1340 }
1341 
GetTRUETargetDist(Entity * io)1342 float GetTRUETargetDist(Entity * io) {
1343 
1344 	long t;
1345 	if(io->ioflags & IO_NPC) {
1346 		t = io->_npcdata->pathfind.truetarget;
1347 	} else {
1348 		t = io->targetinfo;
1349 	}
1350 
1351 	if(ValidIONum(t)) {
1352 		if(io->_npcdata->behavior & BEHAVIOUR_GO_HOME) {
1353 			return dist(io->pos, io->initpos);
1354 		}
1355 		return dist(io->pos, entities[t]->pos);
1356 	}
1357 
1358 	return 99999999.f;
1359 }
1360 
1361 extern Entity * EVENT_SENDER;
1362 
1363 // Checks If a NPC is dead
IsDeadNPC(Entity * io)1364 bool IsDeadNPC(Entity * io) {
1365 
1366 	if(!io || !(io->ioflags & IO_NPC)) {
1367 		return false;
1368 	}
1369 
1370 	return (io->_npcdata->life <= 0 || io->mainevent == "dead");
1371 }
1372 //***********************************************************************************************
1373 //***********************************************************************************************
IsInGroup(EERIE_3DOBJ * obj,long vert,long tw)1374 long IsInGroup(EERIE_3DOBJ * obj, long vert, long tw)
1375 {
1376 	if (obj == NULL) return -1;
1377 
1378 	if (tw < 0) return -1;
1379 
1380 	if (tw > obj->nbgroups) return -1;
1381 
1382 	if (vert < 0) return -1;
1383 
1384 	for (size_t i = 0; i < obj->grouplist[tw].indexes.size(); i++)
1385 	{
1386 		if (obj->grouplist[tw].indexes[i] == vert)
1387 			return i;
1388 	}
1389 
1390 	return -1;
1391 }
1392 
1393 //***********************************************************************************************
1394 //***********************************************************************************************
IsNearSelection(EERIE_3DOBJ * obj,long vert,long tw)1395 long IsNearSelection(EERIE_3DOBJ * obj, long vert, long tw)
1396 {
1397 	if (obj == NULL) return -1;
1398 
1399 	if (tw < 0) return -1;
1400 
1401 	if (vert < 0) return -1;
1402 
1403 	for (size_t i = 0; i < obj->selections[tw].selected.size(); i++)
1404 	{
1405 		float d = dist(obj->vertexlist[obj->selections[tw].selected[i]].v,
1406 		               obj->vertexlist[vert].v);
1407 
1408 		if (d < 8.f)
1409 			return i;
1410 	}
1411 
1412 	return -1;
1413 }
1414 
1415 // Spawns a body part from NPC
ARX_NPC_SpawnMember(Entity * ioo,long num)1416 void ARX_NPC_SpawnMember(Entity * ioo, long num) {
1417 
1418 	if (!ioo) return;
1419 
1420 	EERIE_3DOBJ * from = ioo->obj;
1421 
1422 	if ((!from) ||	(num < 0) || ((size_t)num >= from->selections.size()))
1423 		return;
1424 
1425 	EERIE_3DOBJ * nouvo = new EERIE_3DOBJ();
1426 
1427 	if (!nouvo)
1428 		return;
1429 
1430 	size_t nvertex = from->selections[num].selected.size();
1431 
1432 	long gore = -1;
1433 
1434 	for (size_t k = 0; k < from->texturecontainer.size(); k++)
1435 	{
1436 		if (from->texturecontainer[k]
1437 		        && (boost::contains(from->texturecontainer[k]->m_texName.string(), "gore")))
1438 		{
1439 			gore = k;
1440 			break;
1441 		}
1442 	}
1443 
1444 	for (size_t k = 0; k < from->facelist.size(); k++)
1445 	{
1446 		if (from->facelist[k].texid == gore)
1447 		{
1448 			if	((IsNearSelection(from, from->facelist[k].vid[0], num) >= 0)
1449 			        ||	(IsNearSelection(from, from->facelist[k].vid[1], num) >= 0)
1450 			        ||	(IsNearSelection(from, from->facelist[k].vid[2], num) >= 0))
1451 				nvertex += 3;
1452 		}
1453 	}
1454 
1455 	nouvo->vertexlist.resize(nvertex);
1456 	nouvo->vertexlist3.resize(nvertex);
1457 
1458 	long	inpos	= 0;
1459 	long *	equival = (long *)malloc(sizeof(long) * from->vertexlist.size());
1460 	if(!equival) {
1461 		delete nouvo;
1462 		return;
1463 	}
1464 
1465 	for(size_t k = 0; k < from->vertexlist.size(); k++) {
1466 		equival[k] = -1;
1467 	}
1468 
1469 	arx_assert(0 < from->selections[num].selected.size());
1470 
1471 	for(size_t k = 0; k < from->selections[num].selected.size(); k++) {
1472 		inpos = from->selections[num].selected[k];
1473 		equival[from->selections[num].selected[k]] = k;
1474 
1475 
1476 
1477 		nouvo->vertexlist[k] = from->vertexlist[from->selections[num].selected[k]];
1478 		nouvo->vertexlist[k].v = from->vertexlist3[from->selections[num].selected[k]].v;
1479 		nouvo->vertexlist[k].v -= ioo->pos;
1480 		nouvo->vertexlist[k].vert.p = nouvo->vertexlist[k].v;
1481 
1482 		nouvo->vertexlist[k].vert.color = from->vertexlist[k].vert.color;
1483 		nouvo->vertexlist[k].vert.uv = from->vertexlist[k].vert.uv;
1484 
1485 		nouvo->vertexlist3[k] = nouvo->vertexlist[k];
1486 	}
1487 
1488 	size_t count = from->selections[num].selected.size();
1489 
1490 	for(size_t k = 0; k < from->facelist.size(); k++)
1491 	{
1492 		if (from->facelist[k].texid == gore)
1493 		{
1494 			if ((IsNearSelection(from, from->facelist[k].vid[0], num) >= 0)
1495 			        ||	(IsNearSelection(from, from->facelist[k].vid[1], num) >= 0)
1496 			        ||	(IsNearSelection(from, from->facelist[k].vid[2], num) >= 0))
1497 			{
1498 				for (long j = 0; j < 3; j++)
1499 				{
1500 					equival[from->facelist[k].vid[j]] = count;
1501 
1502 					if (count < nouvo->vertexlist.size())
1503 					{
1504 						nouvo->vertexlist[count] = from->vertexlist[from->facelist[k].vid[j]];
1505 						nouvo->vertexlist[count].v = from->vertexlist3[from->facelist[k].vid[j]].v;
1506 						nouvo->vertexlist[count].v -= ioo->pos;
1507 						nouvo->vertexlist[count].vert.p = nouvo->vertexlist[count].v;
1508 						memcpy(&nouvo->vertexlist3[count], &nouvo->vertexlist[count], sizeof(EERIE_VERTEX));
1509 					}
1510 					else
1511 						equival[from->facelist[k].vid[j]] = -1;
1512 
1513 					count++;
1514 				}
1515 			}
1516 		}
1517 	}
1518 
1519 	float min = nouvo->vertexlist[0].vert.p.y;
1520 	long nummm = 0;
1521 
1522 	for(size_t k = 1; k < nouvo->vertexlist.size(); k++) {
1523 		if (nouvo->vertexlist[k].vert.p.y > min)
1524 		{
1525 			min = nouvo->vertexlist[k].vert.p.y;
1526 			nummm = k;
1527 		}
1528 	}
1529 
1530 	nouvo->origin = nummm;
1531 	nouvo->point0 = nouvo->vertexlist[nouvo->origin].v;
1532 
1533 	for(size_t k = 0; k < nouvo->vertexlist.size(); k++) {
1534 		nouvo->vertexlist[k].vert.p = nouvo->vertexlist[k].v -= nouvo->point0;
1535 		nouvo->vertexlist[k].vert.color = 0xFFFFFFFF;
1536 	}
1537 
1538 	nouvo->point0 = Vec3f::ZERO;
1539 
1540 	nouvo->pbox = NULL;
1541 	nouvo->pdata = NULL;
1542 	nouvo->cdata = NULL;
1543 	nouvo->sdata = NULL;
1544 	nouvo->ndata = NULL;
1545 
1546 	size_t nfaces = 0;
1547 	for (size_t k = 0; k < from->facelist.size(); k++)
1548 	{
1549 		if ((equival[from->facelist[k].vid[0]] != -1)
1550 		        &&	(equival[from->facelist[k].vid[1]] != -1)
1551 		        &&	(equival[from->facelist[k].vid[2]] != -1))
1552 			nfaces++;
1553 	}
1554 
1555 	if (nfaces)
1556 	{
1557 		nouvo->facelist.reserve(nfaces);
1558 
1559 		for (size_t k = 0; k < from->facelist.size(); k++)
1560 		{
1561 			if ((equival[from->facelist[k].vid[0]] != -1)
1562 			        &&	(equival[from->facelist[k].vid[1]] != -1)
1563 			        &&	(equival[from->facelist[k].vid[2]] != -1))
1564 			{
1565 				EERIE_FACE newface = from->facelist[k];
1566 				newface.vid[0] = (unsigned short)equival[from->facelist[k].vid[0]];
1567 				newface.vid[1] = (unsigned short)equival[from->facelist[k].vid[1]];
1568 				newface.vid[2] = (unsigned short)equival[from->facelist[k].vid[2]];
1569 				nouvo->facelist.push_back(newface);
1570 			}
1571 		}
1572 
1573 		long gore = -1;
1574 
1575 		for(size_t k = 0; k < from->texturecontainer.size(); k++) {
1576 			if (from->texturecontainer[k]
1577 			        && (boost::contains(from->texturecontainer[k]->m_texName.string(), "gore")))
1578 			{
1579 				gore = k;
1580 				break;
1581 			}
1582 		}
1583 
1584 		for(size_t k = 0; k < nouvo->facelist.size(); k++) {
1585 			nouvo->facelist[k].facetype &= ~POLY_HIDE;
1586 
1587 			if (nouvo->facelist[k].texid == gore)
1588 				nouvo->facelist[k].facetype |= POLY_DOUBLESIDED;
1589 		}
1590 
1591 	}
1592 
1593 	free(equival);
1594 	nouvo->texturecontainer = from->texturecontainer;
1595 
1596 	nouvo->linked = NULL;
1597 	nouvo->nblinked = 0;
1598 	nouvo->originaltextures = NULL;
1599 
1600 	Entity * io = new Entity("noname");
1601 
1602 	io->_itemdata = (IO_ITEMDATA *)malloc(sizeof(IO_ITEMDATA));
1603 
1604 	memset(io->_itemdata, 0, sizeof(IO_ITEMDATA));
1605 
1606 	io->ioflags = IO_ITEM;
1607 	io->script.size = 0;
1608 	io->script.data = NULL;
1609 	io->gameFlags |= GFLAG_NO_PHYS_IO_COL;
1610 
1611 	EERIE_COLLISION_Cylinder_Create(io);
1612 	EERIE_PHYSICS_BOX_Create(nouvo);
1613 	if(!nouvo->pbox){
1614 		delete nouvo;
1615 		return;
1616 	}
1617 
1618 	io->infracolor = Color3f::blue * 0.8f;
1619 	io->collision = COLLIDE_WITH_PLAYER;
1620 	io->inv = NULL;
1621 	io->scriptload = 1;
1622 	io->obj = nouvo;
1623 	io->lastpos = io->initpos = io->pos = ioo->obj->vertexlist3[inpos].v;
1624 	io->angle = ioo->angle;
1625 
1626 	io->gameFlags = ioo->gameFlags;
1627 	memcpy(&io->halo, &ioo->halo, sizeof(IO_HALO));
1628 	ioo->halo.dynlight = -1;
1629 	io->ioflags |= IO_MOVABLE;
1630 
1631 	io->angle.a = rnd() * 40.f + 340.f;
1632 	io->angle.b = rnd() * 360.f;
1633 	io->angle.g = 0;
1634 	io->obj->pbox->active = 1;
1635 	io->obj->pbox->stopcount = 0;
1636 
1637 	io->velocity = Vec3f::ZERO;
1638 	io->stopped = 1;
1639 
1640 	Vec3f vector;
1641 	vector.x = -(float)EEsin(radians(io->angle.b));
1642 	vector.y = EEsin(radians(io->angle.a)) * 2.f;
1643 	vector.z = (float)EEcos(radians(io->angle.b));
1644 	fnormalize(vector);
1645 	Vec3f pos = io->pos;
1646 	io->rubber = 0.6f;
1647 
1648 	io->no_collide = checked_range_cast<short>(long(ioo->index()));
1649 
1650 	io->gameFlags |= GFLAG_GOREEXPLODE;
1651 	io->lastanimtime = (unsigned long)(arxtime);
1652 	io->soundtime = 0;
1653 	io->soundcount = 0;
1654 	EERIE_PHYSICS_BOX_Launch(io->obj, &pos, &vector, 3, &io->angle);
1655 }
1656 
1657 #define	FLAG_CUT_HEAD  (1<<0)
1658 #define	FLAG_CUT_TORSO (1<<1)
1659 #define	FLAG_CUT_LARM  (1<<2)
1660 #define	FLAG_CUT_RARM  (1<<3)
1661 #define	FLAG_CUT_LLEG  (1<<4)
1662 #define	FLAG_CUT_RLEG  (1<<5)
1663 
GetCutFlag(const string & str)1664 static short GetCutFlag(const string & str) {
1665 
1666 	if(str == "cut_head") {
1667 		return FLAG_CUT_HEAD;
1668 	} else if(str == "cut_torso") {
1669 		return FLAG_CUT_TORSO;
1670 	} else if(str == "cut_larm") {
1671 		return FLAG_CUT_LARM;
1672 	} else if(str == "cut_rarm") {
1673 		return FLAG_CUT_HEAD;
1674 	} else if(str == "cut_lleg") {
1675 		return FLAG_CUT_LLEG;
1676 	} else if(str == "cut_rleg") {
1677 		return FLAG_CUT_RLEG;
1678 	}
1679 
1680 	return 0;
1681 }
1682 
GetCutSelection(Entity * io,short flag)1683 long GetCutSelection(Entity * io, short flag)
1684 {
1685 	if ((!io) || (!(io->ioflags & IO_NPC)) || flag == 0)
1686 		return -1;
1687 
1688 	std::string tx;
1689 
1690 	if (flag == FLAG_CUT_HEAD)
1691 		tx =  "cut_head";
1692 	else if (flag == FLAG_CUT_TORSO)
1693 		tx = "cut_torso";
1694 	else if (flag == FLAG_CUT_LARM)
1695 		tx = "cut_larm";
1696 
1697 	if (flag == FLAG_CUT_RARM)
1698 		tx = "cut_rarm";
1699 
1700 	if (flag == FLAG_CUT_LLEG)
1701 		tx = "cut_lleg";
1702 
1703 	if (flag == FLAG_CUT_RLEG)
1704 		tx = "cut_rleg";
1705 
1706 	if ( !tx.empty() )
1707 	{
1708 		typedef std::vector<EERIE_SELECTIONS>::iterator iterator; // Convenience
1709 		for(iterator iter = io->obj->selections.begin(); iter != io->obj->selections.end(); ++iter) {
1710 			if(iter->selected.size() > 0 && iter->name == tx) {
1711 				return iter - io->obj->selections.begin();
1712 			}
1713 		}
1714 	}
1715 
1716 	return -1;
1717 }
1718 
ReComputeCutFlags(Entity * io)1719 void ReComputeCutFlags(Entity * io)
1720 {
1721 	if ((!io) || (!(io->ioflags & IO_NPC)))
1722 		return;
1723 
1724 	if (io->_npcdata->cuts & FLAG_CUT_TORSO)
1725 	{
1726 		io->_npcdata->cuts &= ~FLAG_CUT_HEAD;
1727 		io->_npcdata->cuts &= ~FLAG_CUT_LARM;
1728 		io->_npcdata->cuts &= ~FLAG_CUT_RARM;
1729 	}
1730 }
IsAlreadyCut(Entity * io,short fl)1731 bool IsAlreadyCut(Entity * io, short fl)
1732 {
1733 	if (io->_npcdata->cuts & fl)
1734 		return true;
1735 
1736 	if (io->_npcdata->cuts & FLAG_CUT_TORSO)
1737 	{
1738 		if (fl == FLAG_CUT_HEAD)
1739 			return true;
1740 
1741 		if (fl == FLAG_CUT_LARM)
1742 			return true;
1743 
1744 		if (fl == FLAG_CUT_RARM)
1745 			return true;
1746 	}
1747 
1748 	return false;
1749 }
ARX_NPC_ApplyCuts(Entity * io)1750 long ARX_NPC_ApplyCuts(Entity * io)
1751 {
1752 	if ((!io) || (!(io->ioflags & IO_NPC)))
1753 		return 0 ;
1754 
1755 	if (io->_npcdata->cuts == 0)
1756 		return 0;	// No cuts
1757 
1758 	ReComputeCutFlags(io);
1759 	long goretex = -1;
1760 
1761 	for (size_t i = 0; i < io->obj->texturecontainer.size(); i++)
1762 	{
1763 		if (io->obj->texturecontainer[i]
1764 		        &&	(boost::contains(io->obj->texturecontainer[i]->m_texName.string(), "gore")))
1765 		{
1766 			goretex = i;
1767 			break;
1768 		}
1769 	}
1770 
1771 	long hid = 0;
1772 
1773 	for (size_t nn = 0; nn < io->obj->facelist.size(); nn++)
1774 	{
1775 		io->obj->facelist[nn].facetype &= ~POLY_HIDE;
1776 	}
1777 
1778 	for (long jj = 0; jj < 6; jj++)
1779 	{
1780 		short flg = 1 << jj;
1781 		long numsel = GetCutSelection(io, flg);
1782 
1783 		if ((io->_npcdata->cuts & flg) && (numsel >= 0))
1784 		{
1785 			for (size_t ll = 0; ll < io->obj->facelist.size(); ll++)
1786 			{
1787 				if	((IsInSelection(io->obj, io->obj->facelist[ll].vid[0], numsel) != -1)
1788 				        ||	(IsInSelection(io->obj, io->obj->facelist[ll].vid[1], numsel) != -1)
1789 				        ||	(IsInSelection(io->obj, io->obj->facelist[ll].vid[2], numsel) != -1)
1790 				   )
1791 				{
1792 					if (!(io->obj->facelist[ll].facetype & POLY_HIDE))
1793 					{
1794 						if (io->obj->facelist[ll].texid != goretex)
1795 							hid = 1;
1796 					}
1797 
1798 					io->obj->facelist[ll].facetype |= POLY_HIDE;
1799 				}
1800 			}
1801 
1802 			io->_npcdata->cut = 1;
1803 		}
1804 	}
1805 
1806 	return hid;
1807 }
1808 //***********************************************************************************************
1809 // Attempt to cut something on NPC
1810 //***********************************************************************************************
ARX_NPC_TryToCutSomething(Entity * target,Vec3f * pos)1811 void ARX_NPC_TryToCutSomething(Entity * target, Vec3f * pos)
1812 {
1813 	//return;
1814 	if (!target) return;
1815 
1816 	if (!(target->ioflags & IO_NPC)) return;
1817 
1818 	if	(target->gameFlags & GFLAG_NOGORE)
1819 		return;
1820 
1821 	float mindistSqr = std::numeric_limits<float>::max();
1822 	long numsel = -1;
1823 	long goretex = -1;
1824 
1825 	for (size_t i = 0; i < target->obj->texturecontainer.size(); i++)
1826 	{
1827 		if (target->obj->texturecontainer[i]
1828 		        &&	(boost::contains(target->obj->texturecontainer[i]->m_texName.string(), "gore")))
1829 		{
1830 			goretex = i;
1831 			break;
1832 		}
1833 	}
1834 
1835 	for (size_t i = 0; i < target->obj->selections.size(); i++)
1836 	{ // TODO iterator
1837 		if ((target->obj->selections[i].selected.size() > 0)
1838 		        &&	(boost::contains(target->obj->selections[i].name, "cut_")))
1839 		{
1840 			short fll = GetCutFlag( target->obj->selections[i].name );
1841 
1842 			if (IsAlreadyCut(target, fll))
1843 				continue;
1844 
1845 			long out = 0;
1846 
1847 			for (size_t ll = 0; ll < target->obj->facelist.size(); ll++)
1848 			{
1849 				if (target->obj->facelist[ll].texid != goretex)
1850 				{
1851 					if	((IsInSelection(target->obj, target->obj->facelist[ll].vid[0], i) != -1)
1852 					        ||	(IsInSelection(target->obj, target->obj->facelist[ll].vid[1], i) != -1)
1853 					        ||	(IsInSelection(target->obj, target->obj->facelist[ll].vid[2], i) != -1)
1854 					   )
1855 					{
1856 						if (target->obj->facelist[ll].facetype & POLY_HIDE)
1857 						{
1858 							out++;
1859 						}
1860 					}
1861 				}
1862 			}
1863 
1864 			if (out < 3)
1865 			{
1866 				float dist = distSqr(*pos, target->obj->vertexlist3[target->obj->selections[i].selected[0]].v);
1867 
1868 				if (dist < mindistSqr)
1869 				{
1870 					mindistSqr = dist;
1871 					numsel = i;
1872 				}
1873 			}
1874 		}
1875 	}
1876 
1877 	if (numsel == -1) return; // Nothing to cut...
1878 
1879 	long hid = 0;
1880 
1881 	if (mindistSqr < square(60)) // can only cut a close part...
1882 	{
1883 		short fl = GetCutFlag( target->obj->selections[numsel].name );
1884 
1885 		if ((fl)
1886 		        &&	(!(target->_npcdata->cuts & fl)))
1887 		{
1888 			target->_npcdata->cuts |= fl;
1889 			hid = ARX_NPC_ApplyCuts(target);
1890 		}
1891 	}
1892 
1893 	if(hid) {
1894 		ARX_SOUND_PlayCinematic("flesh_critical", false); // TODO why play cinmeatic sound?
1895 		ARX_NPC_SpawnMember(target, numsel);
1896 	}
1897 }
1898 
1899 
1900 extern float STRIKE_AIMTIME;
1901 //***********************************************************************************************
1902 // IsPlayerStriking()
1903 // Checks if Player is currently striking.
1904 //***********************************************************************************************
IsPlayerStriking()1905 bool IsPlayerStriking()
1906 {
1907 	Entity * io = entities.player();
1908 
1909 	if (!io) return false;
1910 
1911 	ANIM_USE * useanim = &io->animlayer[1];
1912 	long weapontype = ARX_EQUIPMENT_GetPlayerWeaponType();
1913 	long j;
1914 
1915 	switch (weapontype)
1916 	{
1917 		case WEAPON_BARE:
1918 
1919 			for (j = 0; j < 4; j++)
1920 			{
1921 				if ((STRIKE_AIMTIME > 300)
1922 				        && (useanim->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+j*3])) return true;
1923 
1924 				if (useanim->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT+j*3]) return true;
1925 			}
1926 
1927 			break;
1928 		case WEAPON_DAGGER:
1929 
1930 			for (j = 0; j < 4; j++)
1931 			{
1932 				if ((STRIKE_AIMTIME > 300)
1933 				        && (useanim->cur_anim == io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+j*3])) return true;
1934 
1935 				if (useanim->cur_anim == io->anims[ANIM_DAGGER_STRIKE_LEFT+j*3]) return true;
1936 			}
1937 
1938 			break;
1939 		case WEAPON_1H:
1940 
1941 			for (j = 0; j < 4; j++)
1942 			{
1943 				if ((STRIKE_AIMTIME > 300)
1944 				        && (useanim->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+j*3])) return true;
1945 
1946 				if (useanim->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT+j*3]) return true;
1947 			}
1948 
1949 			break;
1950 		case WEAPON_2H:
1951 
1952 			for (j = 0; j < 4; j++)
1953 			{
1954 				if ((STRIKE_AIMTIME > 300)
1955 				        && (useanim->cur_anim == io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+j*3])) return true;
1956 
1957 				if (useanim->cur_anim == io->anims[ANIM_2H_STRIKE_LEFT+j*3]) return true;
1958 			}
1959 
1960 			break;
1961 	}
1962 
1963 	return false;
1964 }
1965 //***********************************************************************************************
1966 //***********************************************************************************************
ARX_NPC_Manage_NON_Fight(Entity * io)1967 void ARX_NPC_Manage_NON_Fight(Entity * io)
1968 {
1969 	ANIM_USE * ause1 = &io->animlayer[1];
1970 
1971 	if ((ause1->flags & EA_ANIMEND) && (ause1->cur_anim != NULL))
1972 	{
1973 		if (!(ause1->flags & EA_FORCEPLAY))
1974 		{
1975 			AcquireLastAnim(io);
1976 			FinishAnim(io, ause1->cur_anim);
1977 			ause1->cur_anim = NULL;
1978 		}
1979 	}
1980 }
1981 //***********************************************************************************************
1982 //***********************************************************************************************
Strike_StartTickCount(Entity * io)1983 void Strike_StartTickCount(Entity * io)
1984 {
1985 	io->_npcdata->strike_time = 0;
1986 }
1987 //***********************************************************************************************
1988 //***********************************************************************************************
1989 // NPC IS in fight mode and close to target...
ARX_NPC_Manage_Fight(Entity * io)1990 void ARX_NPC_Manage_Fight(Entity * io)
1991 {
1992 	if (!(io->ioflags & IO_NPC)) return;
1993 
1994 	Entity * ioo = io->_npcdata->weapon;
1995 
1996 	if (ioo)
1997 		io->_npcdata->weapontype = ioo->type_flags;
1998 	else io->_npcdata->weapontype = 0;
1999 
2000 	if ((io->_npcdata->weapontype != 0) && (io->_npcdata->weaponinhand != 1))
2001 		return;
2002 
2003 	ANIM_USE * ause = &io->animlayer[1];
2004 	{
2005 		// BARE HANDS fight !!! *******************************
2006 		if (io->_npcdata->weapontype == 0)
2007 		{
2008 			if (((ause->cur_anim != io->anims[ANIM_BARE_WAIT])
2009 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_READY])
2010 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START ])
2011 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START+3 ])
2012 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START+6 ])
2013 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START+9 ])
2014 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE])
2015 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+3])
2016 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+6])
2017 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+9])
2018 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT ])
2019 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT+3 ])
2020 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT+6 ])
2021 			        &&	(ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT+9 ])
2022 			        && (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
2023 			        && (ause->cur_anim != io->anims[ANIM_CAST])
2024 			        && (ause->cur_anim != io->anims[ANIM_CAST_END])
2025 			        && (ause->cur_anim != io->anims[ANIM_CAST_START]))
2026 			        || (ause->cur_anim == NULL))
2027 			{
2028 				AcquireLastAnim(io);
2029 				FinishAnim(io, ause->cur_anim);
2030 				ANIM_Set(ause, io->anims[ANIM_BARE_WAIT]);
2031 				Strike_StartTickCount(io);
2032 				ause->flags |= EA_LOOP;
2033 			}
2034 		}
2035 		// DAGGER fight !!! ***********************************
2036 		else if (io->_npcdata->weapontype & OBJECT_TYPE_DAGGER)
2037 		{
2038 			if (((ause->cur_anim != io->anims[ANIM_DAGGER_WAIT])
2039 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_READY_PART_1])
2040 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_READY_PART_2])
2041 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START ])
2042 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START+3 ])
2043 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START+6 ])
2044 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START+9 ])
2045 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE])
2046 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+3])
2047 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+6])
2048 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+9])
2049 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT ])
2050 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT+3 ])
2051 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT+6 ])
2052 			        &&	(ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT+9 ])
2053 			        && (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
2054 			        && (ause->cur_anim != io->anims[ANIM_CAST])
2055 			        && (ause->cur_anim != io->anims[ANIM_CAST_END])
2056 			        && (ause->cur_anim != io->anims[ANIM_CAST_START]))
2057 			        || (ause->cur_anim == NULL))
2058 			{
2059 				AcquireLastAnim(io);
2060 				FinishAnim(io, ause->cur_anim);
2061 				ANIM_Set(ause, io->anims[ANIM_DAGGER_WAIT]);
2062 				Strike_StartTickCount(io);
2063 				ause->flags |= EA_LOOP;
2064 			}
2065 		}
2066 		// 1H fight !!! ***************************************
2067 		else if (io->_npcdata->weapontype & OBJECT_TYPE_1H)
2068 		{
2069 			if (((ause->cur_anim != io->anims[ANIM_1H_WAIT])
2070 			        &&	(ause->cur_anim != io->anims[ANIM_1H_READY_PART_1])
2071 			        &&	(ause->cur_anim != io->anims[ANIM_1H_READY_PART_2])
2072 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START ])
2073 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START+3 ])
2074 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START+6 ])
2075 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START+9 ])
2076 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE])
2077 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+3])
2078 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+6])
2079 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+9])
2080 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT ])
2081 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT+3 ])
2082 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT+6 ])
2083 			        &&	(ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT+9 ])
2084 			        && (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
2085 			        && (ause->cur_anim != io->anims[ANIM_CAST])
2086 			        && (ause->cur_anim != io->anims[ANIM_CAST_END])
2087 			        && (ause->cur_anim != io->anims[ANIM_CAST_START]))
2088 			        || (ause->cur_anim == NULL))
2089 			{
2090 				AcquireLastAnim(io);
2091 				FinishAnim(io, ause->cur_anim);
2092 				ANIM_Set(ause, io->anims[ANIM_1H_WAIT]);
2093 				Strike_StartTickCount(io);
2094 				ause->flags |= EA_LOOP;
2095 			}
2096 		}
2097 		// 2H fight !!! ***************************************
2098 		else if (io->_npcdata->weapontype & OBJECT_TYPE_2H)
2099 		{
2100 			if (((ause->cur_anim != io->anims[ANIM_2H_WAIT])
2101 			        &&	(ause->cur_anim != io->anims[ANIM_2H_READY_PART_1])
2102 			        &&	(ause->cur_anim != io->anims[ANIM_2H_READY_PART_2])
2103 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START ])
2104 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START+3 ])
2105 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START+6 ])
2106 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START+9 ])
2107 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE])
2108 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+3])
2109 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+6])
2110 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+9])
2111 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT ])
2112 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT+3 ])
2113 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT+6 ])
2114 			        &&	(ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT+9 ])
2115 			        && (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
2116 			        && (ause->cur_anim != io->anims[ANIM_CAST])
2117 			        && (ause->cur_anim != io->anims[ANIM_CAST_END])
2118 			        && (ause->cur_anim != io->anims[ANIM_CAST_START]))
2119 			        || (ause->cur_anim == NULL))
2120 			{
2121 				AcquireLastAnim(io);
2122 				FinishAnim(io, ause->cur_anim);
2123 				ANIM_Set(ause, io->anims[ANIM_2H_WAIT]);
2124 				ause->flags |= EA_LOOP;
2125 			}
2126 		}
2127 		// BOW fight !!! **************************************
2128 		else if (io->_npcdata->weapontype & OBJECT_TYPE_BOW)
2129 		{
2130 			////////////// later...
2131 		}
2132 	}
2133 }
2134 //***********************************************************************************************
2135 //***********************************************************************************************
ARX_NPC_Manage_Anims_End(Entity * io)2136 void ARX_NPC_Manage_Anims_End(Entity * io)
2137 {
2138 	ANIM_USE * ause = &io->animlayer[0];
2139 
2140 	if ((ause->flags & EA_ANIMEND) && (ause->cur_anim != NULL))
2141 	{
2142 		if (ause->flags & EA_FORCEPLAY)
2143 		{
2144 			AcquireLastAnim(io);
2145 			FinishAnim(io, ause->cur_anim);
2146 			ANIM_Set(ause, io->anims[ANIM_DEFAULT]);
2147 
2148 			if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause->altidx_cur = 0;
2149 		}
2150 
2151 		// some specific code for combat animation end management
2152 		if (ause->cur_anim == io->anims[ANIM_FIGHT_STRAFE_LEFT])
2153 		{
2154 			AcquireLastAnim(io);
2155 			FinishAnim(io, ause->cur_anim);
2156 			ANIM_Set(ause, io->anims[ANIM_FIGHT_WAIT]);
2157 			ause->flags |= EA_LOOP;
2158 		}
2159 
2160 		if (ause->cur_anim == io->anims[ANIM_FIGHT_STRAFE_RIGHT])
2161 		{
2162 			AcquireLastAnim(io);
2163 			FinishAnim(io, ause->cur_anim);
2164 			ANIM_Set(ause, io->anims[ANIM_FIGHT_WAIT]);
2165 			ause->flags |= EA_LOOP;
2166 		}
2167 
2168 		if (ause->cur_anim == io->anims[ANIM_FIGHT_WALK_BACKWARD])
2169 		{
2170 			AcquireLastAnim(io);
2171 			FinishAnim(io, ause->cur_anim);
2172 			ANIM_Set(ause, io->anims[ANIM_FIGHT_WAIT]);
2173 			ause->flags |= EA_LOOP;
2174 		}
2175 	}
2176 }
TryIOAnimMove(Entity * io,long animnum)2177 bool TryIOAnimMove(Entity * io, long animnum)
2178 {
2179 	if ((!io) || (!io->anims[animnum])) return false;
2180 
2181 	Vec3f trans, trans2;
2182 	GetAnimTotalTranslate(io->anims[animnum], 0, &trans);
2183 	float temp = radians(MAKEANGLE(180.f - io->angle.b));
2184 	YRotatePoint(&trans, &trans2, (float)EEcos(temp), (float)EEsin(temp));
2185 	IO_PHYSICS phys;
2186 	memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
2187 	GetIOCyl(io, &phys.cyl);
2188 
2189 	phys.startpos = io->pos;
2190 	phys.targetpos = io->pos + trans2;
2191 	bool res = ARX_COLLISION_Move_Cylinder(&phys, io, 30, CFLAG_JUST_TEST | CFLAG_NPC);
2192 
2193 	if (res && (EEfabs(phys.cyl.origin.y - io->pos.y) < 20.f))
2194 		return true;
2195 
2196 	return false;
2197 }
TryAndCheckAnim(Entity * io,long animnum,long layer)2198 void TryAndCheckAnim(Entity * io, long animnum, long layer)
2199 {
2200 	if (!io) return;
2201 
2202 	ANIM_USE * ause = &io->animlayer[layer];
2203 
2204 	if ((ause->cur_anim != io->anims[animnum])
2205 	        && (ause->cur_anim != NULL))
2206 	{
2207 		if (TryIOAnimMove(io, animnum))
2208 		{
2209 			AcquireLastAnim(io);
2210 			FinishAnim(io, ause->cur_anim);
2211 			ANIM_Set(ause, io->anims[animnum]);
2212 		}
2213 	}
2214 }
2215 //Define Time of Strike Damage
2216 #define STRIKE_MUL 0.25f
2217 #define STRIKE_MUL2 0.8f
2218 #define STRIKE_DISTANCE 220
2219 // Main animations management
2220 //***********************************************************************************************
2221 //***********************************************************************************************
ARX_NPC_Manage_Anims(Entity * io,float TOLERANCE)2222 void ARX_NPC_Manage_Anims(Entity * io, float TOLERANCE)
2223 {
2224 	io->_npcdata->strike_time += (short)FrameDiff;
2225 	ANIM_USE * ause = &io->animlayer[0];
2226 	ANIM_USE * ause1 = &io->animlayer[1];
2227 	float tdist = std::numeric_limits<float>::max();
2228 
2229 	if ((io->_npcdata->pathfind.listnb) && (ValidIONum(io->_npcdata->pathfind.truetarget))) {
2230 		tdist = distSqr(io->pos, entities[io->_npcdata->pathfind.truetarget]->pos);
2231 	} else if(ValidIONum(io->targetinfo)) {
2232 		tdist = distSqr(io->pos, entities[io->targetinfo]->pos);
2233 	}
2234 
2235 
2236 
2237 
2238 
2239 	Entity * ioo = io->_npcdata->weapon;
2240 
2241 	if (ValidIOAddress(ioo))
2242 		io->_npcdata->weapontype = ioo->type_flags;
2243 	else
2244 		io->_npcdata->weapontype = 0;
2245 
2246 	if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
2247 	        &&	(tdist <= square(TOLERANCE + 10))
2248 	        &&	((tdist <= square(TOLERANCE - 20)) || (rnd() > 0.97f)))
2249 	{
2250 		{
2251 			if ((ause->cur_anim == io->anims[ANIM_FIGHT_WAIT])
2252 			        && (ause->cur_anim != NULL))
2253 			{
2254 				float r = rnd();
2255 
2256 				if (tdist < square(TOLERANCE - 20)) r = 0;
2257 
2258 				if (r < 0.1f)
2259 					TryAndCheckAnim(io, ANIM_FIGHT_WALK_BACKWARD, 0);
2260 				else if (r < 0.55f)
2261 					TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_LEFT, 0);
2262 				else
2263 					TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_RIGHT, 0);
2264 
2265 			}
2266 		}
2267 	}
2268 	//Decides fight moves
2269 	else if ((io->_npcdata->behavior & (BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT))
2270 	         ||	(io->spellcast_data.castingspell != SPELL_NONE))
2271 	{
2272 		if (rnd() > 0.85f)
2273 		{
2274 			if ((ause->cur_anim == io->anims[ANIM_FIGHT_WAIT])
2275 			        && (ause->cur_anim != NULL))
2276 			{
2277 				AcquireLastAnim(io);
2278 				FinishAnim(io, ause->cur_anim);
2279 				float r = rnd();
2280 
2281 				if (tdist < square(340)) r = 0;
2282 
2283 				if (r < 0.33f)
2284 					TryAndCheckAnim(io, ANIM_FIGHT_WALK_BACKWARD, 0);
2285 				else if (r < 0.66f)
2286 					TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_LEFT, 0);
2287 				else
2288 					TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_RIGHT, 0);
2289 			}
2290 		}
2291 	}
2292 
2293 
2294 	if (IsPlayerStriking())
2295 	{
2296 		if ((ause->cur_anim == io->anims[ANIM_FIGHT_WAIT])
2297 		        && (ause->cur_anim != NULL))
2298 		{
2299 			AcquireLastAnim(io);
2300 			FinishAnim(io, ause->cur_anim);
2301 			float r = rnd();
2302 
2303 			if (r < 0.2f)
2304 				TryAndCheckAnim(io, ANIM_FIGHT_WALK_BACKWARD, 0);
2305 			else if (r < 0.6f)
2306 				TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_LEFT, 0);
2307 			else
2308 				TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_RIGHT, 0);
2309 		}
2310 	}
2311 	long j;
2312 
2313 
2314 
2315 
2316 
2317 
2318 	// MAGICAL FIGHT
2319 	if ((ause1->cur_anim == io->anims[ANIM_CAST])
2320 	        &&	(io->anims[ANIM_CAST])
2321 	        &&	(ause1->flags & EA_ANIMEND))
2322 	{
2323 		AcquireLastAnim(io);
2324 		FinishAnim(io, ause1->cur_anim);
2325 		ANIM_Set(ause1, io->anims[ANIM_CAST_END]);
2326 	}
2327 	else if ((ause1->cur_anim == io->anims[ANIM_CAST_END])
2328 	         &&	(io->anims[ANIM_CAST_END])
2329 	         &&	(ause1->flags & EA_ANIMEND))
2330 	{
2331 		AcquireLastAnim(io);
2332 		FinishAnim(io, ause1->cur_anim);
2333 		ause1->cur_anim = NULL;
2334 	}
2335 
2336 	if ((io->spellcast_data.castingspell == SPELL_NONE)
2337 	        &&	(ause1->cur_anim == io->anims[ANIM_CAST_START])
2338 	        &&	(io->anims[ANIM_CAST_START]))
2339 	{
2340 		AcquireLastAnim(io);
2341 		FinishAnim(io, ause1->cur_anim);
2342 		ANIM_Set(ause1, io->anims[ANIM_CAST]);
2343 	}
2344 
2345 	if ((ause1->cur_anim == io->anims[ANIM_CAST_START])
2346 	        &&	(io->anims[ANIM_CAST_START]))
2347 	{
2348 		return;
2349 	}
2350 
2351 	if ((ause1->cur_anim == io->anims[ANIM_CAST_CYCLE])
2352 	        &&	(io->anims[ANIM_CAST_CYCLE]))
2353 	{
2354 		return;
2355 	}
2356 
2357 	if (io->spellcast_data.castingspell != SPELL_NONE) return;
2358 
2359 	if (ause1->cur_anim)
2360 	{
2361 		if ((ause1->cur_anim == io->anims[ANIM_CAST_CYCLE])
2362 		        ||	(ause1->cur_anim == io->anims[ANIM_CAST])
2363 		        ||	(ause1->cur_anim == io->anims[ANIM_CAST_END])
2364 		        ||	(ause1->cur_anim == io->anims[ANIM_CAST_START]))
2365 			return;
2366 	}
2367 
2368 	// BARE HANDS fight !!! *******************************
2369 	if (io->_npcdata->weapontype == 0)
2370 	{
2371 		if (io->_npcdata->weaponinhand == -1)
2372 		{
2373 			io->_npcdata->weaponinhand = 1;
2374 		}
2375 
2376 		if ((ause1->cur_anim == io->anims[ANIM_BARE_WAIT])
2377 		        &&	(ause1->cur_anim))
2378 		{
2379 			if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
2380 			        &&	(tdist < square(STRIKE_DISTANCE))
2381 			        &&	(io->_npcdata->strike_time > 0))
2382 			{
2383 				AcquireLastAnim(io);
2384 				j = Random::get(0, 3);
2385 				FinishAnim(io, ause1->cur_anim);
2386 				ANIM_Set(ause1, io->anims[ANIM_BARE_STRIKE_LEFT_START+j*3]);
2387 			}
2388 		}
2389 
2390 		for (j = 0; j < 4; j++)
2391 		{
2392 			if ((ause1->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT_START+j*3])
2393 			        &&	(ause1->cur_anim)
2394 			        &&	(ause1->flags & EA_ANIMEND))
2395 			{
2396 				io->ioflags &= ~IO_HIT;
2397 				AcquireLastAnim(io);
2398 				FinishAnim(io, ause1->cur_anim);
2399 				ANIM_Set(ause1, io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+j*3]);
2400 				io->_npcdata->aiming_start	=	(long)arxtime;
2401 				ause1->flags |= EA_LOOP;
2402 			}
2403 			else if ((ause1->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+j*3])
2404 			         &&	(ause1->cur_anim))
2405 			{
2406 				if (((float(arxtime) > io->_npcdata->aiming_start + io->_npcdata->aimtime) || ((float(arxtime) > io->_npcdata->aiming_start + io->_npcdata->aimtime * ( 1.0f / 2 )) && (rnd() > 0.9f)))
2407 				        &&	(tdist < square(STRIKE_DISTANCE)))
2408 				{
2409 					AcquireLastAnim(io);
2410 					FinishAnim(io, ause1->cur_anim);
2411 					ANIM_Set(ause1, io->anims[ANIM_BARE_STRIKE_LEFT+j*3]);
2412 					SendIOScriptEvent(io, SM_STRIKE, "bare");
2413 				}
2414 			}
2415 			else if ((ause1->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT+j*3])
2416 			         &&	(ause1->cur_anim))
2417 			{
2418 
2419 				if (ause1->flags & EA_ANIMEND)
2420 				{
2421 					AcquireLastAnim(io);
2422 					FinishAnim(io, ause1->cur_anim);
2423 					ANIM_Set(ause1, io->anims[ANIM_BARE_WAIT]);
2424 					Strike_StartTickCount(io);
2425 					io->ioflags &= ~IO_HIT;
2426 
2427 					if ((io->ioflags & IO_NPC) && (!io->_npcdata->reachedtarget))
2428 						ause1->cur_anim = NULL;
2429 				}
2430 				else if (!(io->ioflags & IO_HIT))
2431 				{
2432 					if ((ause1->ctime > ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL)
2433 					        &&	(ause1->ctime <= ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL2))
2434 					{
2435 						CheckHit(io, 1.f);
2436 						io->ioflags |= IO_HIT;
2437 					}
2438 				}
2439 			}
2440 		}
2441 
2442 	}
2443 
2444 	// 1H fight !!! ***************************************
2445 	else if (io->_npcdata->weapontype & (OBJECT_TYPE_1H | OBJECT_TYPE_2H | OBJECT_TYPE_DAGGER))
2446 	{
2447 		long ANIMBase = 0;
2448 
2449 		if (io->_npcdata->weapontype & OBJECT_TYPE_1H)
2450 			ANIMBase = 0;
2451 		else if (io->_npcdata->weapontype & OBJECT_TYPE_2H)
2452 			ANIMBase = ANIM_2H_READY_PART_1 - ANIM_1H_READY_PART_1;
2453 		else if (io->_npcdata->weapontype & OBJECT_TYPE_DAGGER)
2454 			ANIMBase = ANIM_DAGGER_READY_PART_1 - ANIM_1H_READY_PART_1;
2455 
2456 		// desire to remove weapon
2457 		if (io->_npcdata->weaponinhand == 2)
2458 		{
2459 			if ((ause1->cur_anim == io->anims[ANIM_1H_UNREADY_PART_1+ANIMBase])
2460 			        &&	(ause1->cur_anim)
2461 			        &&	(ause1->flags & EA_ANIMEND))
2462 			{
2463 				SetWeapon_Back(io);
2464 
2465 				AcquireLastAnim(io);
2466 				FinishAnim(io, ause1->cur_anim);
2467 				ANIM_Set(ause1, io->anims[ANIM_1H_UNREADY_PART_2+ANIMBase]);
2468 			}
2469 			else if ((ause1->cur_anim == io->anims[ANIM_1H_UNREADY_PART_2+ANIMBase])
2470 			         &&	(ause1->cur_anim)
2471 			         &&	(ause1->flags & EA_ANIMEND))
2472 			{
2473 				AcquireLastAnim(io);
2474 				FinishAnim(io, ause1->cur_anim);
2475 				ause1->cur_anim = NULL;
2476 				io->_npcdata->weaponinhand = 0;
2477 			}
2478 			else if ((ause1->cur_anim != io->anims[ANIM_1H_UNREADY_PART_1+ANIMBase])
2479 			         &&	(ause1->cur_anim != io->anims[ANIM_1H_UNREADY_PART_2+ANIMBase]))
2480 			{
2481 				AcquireLastAnim(io);
2482 				FinishAnim(io, ause1->cur_anim);
2483 				ANIM_Set(ause1, io->anims[ANIM_1H_UNREADY_PART_1+ANIMBase]);
2484 			}
2485 		}
2486 		// Desire to have weapon in hand...
2487 		else if (io->_npcdata->weaponinhand == -1)
2488 		{
2489 			if ((ause1->cur_anim == io->anims[ANIM_1H_READY_PART_1+ANIMBase])
2490 			        &&	(ause1->cur_anim)
2491 			        &&	(ause1->flags & EA_ANIMEND))
2492 			{
2493 				SetWeapon_On(io);
2494 
2495 				AcquireLastAnim(io);
2496 				FinishAnim(io, ause1->cur_anim);
2497 				ANIM_Set(ause1, io->anims[ANIM_1H_READY_PART_2+ANIMBase]);
2498 
2499 			}
2500 			else if ((ause1->cur_anim == io->anims[ANIM_1H_READY_PART_2+ANIMBase])
2501 			         &&	(ause1->cur_anim)
2502 			         &&	(ause1->flags & EA_ANIMEND))
2503 			{
2504 				AcquireLastAnim(io);
2505 				FinishAnim(io, ause1->cur_anim);
2506 
2507 				if (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
2508 				{
2509 					ANIM_Set(ause1, io->anims[ANIM_1H_WAIT+ANIMBase]);
2510 					Strike_StartTickCount(io);
2511 					ause1->flags |= EA_LOOP;
2512 				}
2513 				else ause1->cur_anim = NULL;
2514 
2515 				io->_npcdata->weaponinhand = 1;
2516 			}
2517 			else if ((!ause1->cur_anim)
2518 			         ||	((ause1->cur_anim) && (ause1->flags & EA_ANIMEND)))
2519 			{
2520 				AcquireLastAnim(io);
2521 				FinishAnim(io, ause1->cur_anim);
2522 				ANIM_Set(ause1, io->anims[ANIM_1H_READY_PART_1+ANIMBase]);
2523 			}
2524 		}
2525 		// Weapon in hand... ready to strike
2526 		else if (io->_npcdata->weaponinhand > 0)
2527 		{
2528 			if ((ause1->cur_anim == io->anims[ANIM_1H_WAIT+ANIMBase])
2529 			        &&	(ause1->cur_anim))
2530 			{
2531 				if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
2532 				        &&	(tdist < square(STRIKE_DISTANCE))
2533 				        &&	(io->_npcdata->strike_time > 0))
2534 				{
2535 					AcquireLastAnim(io);
2536 					FinishAnim(io, ause1->cur_anim);
2537 					j = Random::get(0, 3);
2538 					ANIM_Set(ause1, io->anims[ANIM_1H_STRIKE_LEFT_START+j*3+ANIMBase]);
2539 				}
2540 			}
2541 
2542 			for (j = 0; j < 4; j++)
2543 			{
2544 				if ((ause1->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT_START+j*3+ANIMBase])
2545 				        &&	(ause1->cur_anim)
2546 				        &&	(ause1->flags & EA_ANIMEND))
2547 				{
2548 					AcquireLastAnim(io);
2549 					FinishAnim(io, ause1->cur_anim);
2550 					ANIM_Set(ause1, io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+j*3+ANIMBase]);
2551 					io->_npcdata->aiming_start	=	(long)arxtime;
2552 					ause1->flags |= EA_LOOP;
2553 				}
2554 				else if ((ause1->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+j*3+ANIMBase])
2555 				         &&	(ause1->cur_anim))
2556 				{
2557 					if (((float(arxtime) > io->_npcdata->aiming_start + io->_npcdata->aimtime) || ((float(arxtime) > io->_npcdata->aiming_start + io->_npcdata->aimtime * ( 1.0f / 2 )) && (rnd() > 0.9f)))
2558 					        &&	(tdist < square(STRIKE_DISTANCE)))
2559 					{
2560 						AcquireLastAnim(io);
2561 						FinishAnim(io, ause1->cur_anim);
2562 						ANIM_Set(ause1, io->anims[ANIM_1H_STRIKE_LEFT+j*3+ANIMBase]);
2563 
2564 						if (io->_npcdata->weapontype & OBJECT_TYPE_1H)
2565 							SendIOScriptEvent(io, SM_STRIKE, "1h");
2566 
2567 						if (io->_npcdata->weapontype & OBJECT_TYPE_2H)
2568 							SendIOScriptEvent(io, SM_STRIKE, "2h");
2569 
2570 						if (io->_npcdata->weapontype & OBJECT_TYPE_DAGGER)
2571 							SendIOScriptEvent(io, SM_STRIKE, "dagger");
2572 					}
2573 				}
2574 				else if ((ause1->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT+j*3+ANIMBase])
2575 				         &&	(ause1->cur_anim))
2576 				{
2577 					if (ause1->flags & EA_ANIMEND)
2578 					{
2579 						AcquireLastAnim(io);
2580 						FinishAnim(io, ause1->cur_anim);
2581 						ANIM_Set(ause1, io->anims[ANIM_1H_WAIT+ANIMBase]);
2582 						Strike_StartTickCount(io);
2583 						io->ioflags &= ~IO_HIT;
2584 					}
2585 					else
2586 					{
2587 						if ((ause1->ctime > ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL)
2588 						        &&	(ause1->ctime <= ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL2))
2589 						{
2590 							if (!(io->ioflags & IO_HIT))
2591 							{
2592 								if (ARX_EQUIPMENT_Strike_Check(io, io->_npcdata->weapon, 1, 0, io->targetinfo))
2593 									io->ioflags |= IO_HIT;
2594 							}
2595 							else
2596 								ARX_EQUIPMENT_Strike_Check(io, io->_npcdata->weapon, 1, 1, io->targetinfo);
2597 
2598 						}
2599 					}
2600 				}
2601 			}
2602 		}
2603 	}
2604 	// BOW fight !!! **************************************
2605 	else if (io->_npcdata->weapontype & OBJECT_TYPE_BOW)
2606 	{
2607 		////////////// later...
2608 	}
2609 
2610 
2611 
2612 }
GetIOHeight(Entity * io)2613 float GetIOHeight(Entity * io)
2614 {
2615 	if (io == entities.player())
2616 	{
2617 		return io->physics.cyl.height;
2618 	}
2619 
2620 	float v = (io->original_height * io->scale);
2621 
2622 	if (v < -165.f) return -165.f;
2623 
2624 	return min(v, -45.f);
2625 }
2626 
GetIORadius(Entity * io)2627 float GetIORadius(Entity * io) {
2628 
2629 	if(io == entities.player()) {
2630 		return player.baseRadius();
2631 	}
2632 
2633 	return clamp(io->original_radius * io->scale, 25.f, 60.f);
2634 }
2635 
GetIOCyl(Entity * io,EERIE_CYLINDER * cyl)2636 void GetIOCyl(Entity * io, EERIE_CYLINDER * cyl) {
2637 	cyl->height = GetIOHeight(io);
2638 	cyl->radius = GetIORadius(io);
2639 	cyl->origin = io->pos;
2640 }
2641 
2642 //***********************************************************************************************
2643 // Computes distance tolerance between NPC and its target
2644 //-----------------------------------------------------------------------------------------------
2645 // VERIFIED (Cyril 2001/10/15)
2646 //***********************************************************************************************
ComputeTolerance(Entity * io,long targ,float * dst)2647 void ComputeTolerance(Entity * io, long targ, float * dst) {
2648 
2649 	float TOLERANCE = 30.f;
2650 
2651 	if(ValidIONum(targ)) {
2652 
2653 		float self_dist, targ_dist;
2654 
2655 		// Compute min target close-dist
2656 		if (entities[targ]->ioflags & IO_NO_COLLISIONS)
2657 			targ_dist = 0.f;
2658 		else targ_dist = max(entities[targ]->physics.cyl.radius, GetIORadius(entities[targ])); //entities[targ]->physics.cyl.radius;
2659 
2660 		// Compute min self close-dist
2661 		if (io->ioflags & IO_NO_COLLISIONS)
2662 			self_dist = 0.f;
2663 		else self_dist = max(io->physics.cyl.radius, GetIORadius(io)); //io->physics.cyl.radius;
2664 
2665 		// Base tolerance = radius added
2666 		TOLERANCE = targ_dist + self_dist + 5.f;
2667 
2668 		if (TOLERANCE < 0.f)
2669 		{
2670 			TOLERANCE = 0.f;
2671 		}
2672 
2673 		if (entities[targ]->ioflags & IO_FIX)
2674 			TOLERANCE += 100.f;
2675 
2676 		// If target is a NPC add another tolerance
2677 		if (entities[targ]->_npcdata)
2678 		{
2679 			TOLERANCE += 20.f;
2680 		}
2681 
2682 		// If target is the player improve again tolerance
2683 		if (io->targetinfo == 0) // PLAYER TARGET
2684 		{
2685 			TOLERANCE += 10.f;
2686 		}
2687 
2688 		if (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
2689 		{
2690 			TOLERANCE += io->_npcdata->reach * 0.7f;
2691 		}
2692 
2693 		// If distant of magic behavior Maximize tolerance
2694 		if ((io->_npcdata->behavior & (BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT)) || (io->spellcast_data.castingspell != SPELL_NONE))
2695 			TOLERANCE += 300.f;
2696 
2697 		// if target is a marker set to a minimal tolerance
2698 		if (entities[targ]->ioflags & IO_MARKER)
2699 			TOLERANCE = 21.f + (float)io->_npcdata->moveproblem * ( 1.0f / 10 );
2700 	}
2701 
2702 	// Tolerance is modified by current moveproblem status
2703 	TOLERANCE += (float)io->_npcdata->moveproblem * ( 1.0f / 10 );
2704 	// Now fill our return value with TOLERANCE
2705 	*dst = TOLERANCE;
2706 }
2707 extern long FRAME_COUNT;
2708 //now APOS is computed in Anim but used here and mustn't be used elsewhere...
2709 //***********************************************************************************************
2710 //***********************************************************************************************
2711 
ManageNPCMovement(Entity * io)2712 static void ManageNPCMovement(Entity * io)
2713 {
2714 	float dis = std::numeric_limits<float>::max();
2715 	IO_PHYSICS phys;
2716 	float TOLERANCE = 0.f;
2717 	float TOLERANCE2 = 0.f;
2718 
2719 	// Ignores invalid or dead IO
2720 	if	((!io)
2721 	        ||	(!io->show)
2722 	        ||	(!(io->ioflags & IO_NPC)))
2723 		return;
2724 
2725 	//	AnchorData_GetNearest_2(io->angle.b,&io->pos,&io->physics.cyl);
2726 	// Specific USEPATH management
2727 	ARX_USE_PATH * aup = io->usepath;
2728 
2729 	if ((aup)
2730 	        &&	(aup->aupflags & ARX_USEPATH_WORM_SPECIFIC))
2731 	{
2732 		io->room_flags |= 1;
2733 		Vec3f tv;
2734 
2735 		if (aup->_curtime - aup->_starttime > 500)
2736 		{
2737 			aup->_curtime -= 500;
2738 			ARX_PATHS_Interpolate(aup, &tv);
2739 			aup->_curtime += 500;
2740 			io->angle.b = MAKEANGLE(degrees(getAngle(tv.x, tv.z, io->pos.x, io->pos.z)));
2741 		}
2742 		else
2743 		{
2744 			aup->_curtime += 500;
2745 			ARX_PATHS_Interpolate(aup, &tv);
2746 			aup->_curtime -= 500;
2747 			io->angle.b = MAKEANGLE(180.f + degrees(getAngle(tv.x, tv.z, io->pos.x, io->pos.z)));
2748 		}
2749 
2750 		return;
2751 	}
2752 
2753 	// Frozen ?
2754 	if (io->ioflags & IO_FREEZESCRIPT)
2755 		return;
2756 
2757 	// Dead ?
2758 	if (IsDeadNPC(io))
2759 	{
2760 		io->ioflags |= IO_NO_COLLISIONS;
2761 		io->animlayer[0].next_anim = NULL;
2762 		io->animlayer[1].next_anim = NULL;
2763 		io->animlayer[2].next_anim = NULL;
2764 		io->animlayer[3].next_anim = NULL;
2765 		return;
2766 	}
2767 
2768 	ANIM_USE * ause0 = &io->animlayer[0];
2769 	ANIM_HANDLE ** alist = io->anims;
2770 
2771 	// Using USER animation ?
2772 	if ((ause0->cur_anim)
2773 	        &&	(ause0->flags & EA_FORCEPLAY)
2774 	        &&	(ause0->cur_anim != alist[ANIM_DIE])
2775 	        &&	(ause0->cur_anim != alist[ANIM_HIT1])
2776 	        &&	(ause0->cur_anim != alist[ANIM_HIT_SHORT])
2777 	        &&	!(ause0->flags & EA_ANIMEND))
2778 	{
2779 		io->room_flags |= 1;
2780 		io->lastpos = (io->pos += io->move);
2781 
2782 		return;
2783 	}
2784 
2785 	if ((io->_npcdata->pathfind.listnb > 0)
2786 	        &&	(!io->_npcdata->pathfind.list))
2787 		io->_npcdata->pathfind.listnb = 0;
2788 
2789 
2790 	// waiting for pathfinder ? or pathfinder failure ? ---> Wait Anim
2791 	if ((io->_npcdata->pathfind.pathwait)		// waiting for pathfinder
2792 	        ||	(io->_npcdata->pathfind.listnb == -2))	// pathfinder failure
2793 	{
2794 		if (io->_npcdata->pathfind.listnb == -2)
2795 		{
2796 			if (!io->_npcdata->pathfind.pathwait)
2797 			{
2798 				if (io->_npcdata->pathfind.flags & PATHFIND_NO_UPDATE)
2799 				{
2800 					io->_npcdata->reachedtarget = 1;
2801 					io->_npcdata->reachedtime = (unsigned long)(arxtime);//treat warning C4244 conversion from 'float' to 'unsigned long'
2802 
2803 					if (io->targetinfo != long(io->index()))
2804 						SendIOScriptEvent(io, SM_REACHEDTARGET);
2805 				}
2806 				else if ((ause0->cur_anim == alist[ANIM_WAIT]) && (ause0->flags & EA_ANIMEND))
2807 				{
2808 					io->_npcdata->pathfind.listnb = -1;
2809 					io->_npcdata->pathfind.pathwait = 0;
2810 					ARX_NPC_LaunchPathfind(io, io->targetinfo);
2811 					goto afterthat;
2812 				}
2813 			}
2814 		}
2815 
2816 		if (!(io->_npcdata->behavior & BEHAVIOUR_FIGHT))
2817 		{
2818 			if ((ause0->cur_anim == alist[ANIM_WALK])
2819 			        ||	(ause0->cur_anim == alist[ANIM_RUN])
2820 			        || (ause0->cur_anim == alist[ANIM_WALK_SNEAK]))
2821 			{
2822 				AcquireLastAnim(io);
2823 				FinishAnim(io, ause0->cur_anim);
2824 				ANIM_Set(ause0, alist[ANIM_WAIT]);
2825 				ause0->altidx_cur = 0;
2826 				return;
2827 			}
2828 			else if (ause0->cur_anim == alist[ANIM_WAIT])
2829 			{
2830 				if (ause0->flags & EA_ANIMEND)
2831 				{
2832 					FinishAnim(io, ause0->cur_anim);
2833 					ANIM_Set(ause0, alist[ANIM_WAIT]);
2834 					ause0->altidx_cur = 0;
2835 				}
2836 
2837 				return;
2838 			}
2839 		}
2840 
2841 	afterthat:
2842 		;
2843 	}
2844 
2845 	if ((io->_npcdata->behavior  & BEHAVIOUR_NONE))
2846 	{
2847 		ARX_NPC_Manage_Anims(io, 0);
2848 
2849 		if ((ause0->cur_anim == NULL)
2850 		        ||	((ause0->cur_anim) && (ause0->flags & EA_ANIMEND)))
2851 		{
2852 			AcquireLastAnim(io);
2853 			FinishAnim(io, ause0->cur_anim);
2854 			ANIM_Set(ause0, alist[ANIM_WAIT]);
2855 			ause0->flags &= ~EA_LOOP;
2856 		}
2857 
2858 		return;
2859 	}
2860 
2861 	// First retrieves current position of target...
2862 	GetTargetPos(io);
2863 
2864 	ARX_NPC_Manage_Anims_End(io);
2865 
2866 	if ((io->_npcdata->behavior & BEHAVIOUR_LOOK_AROUND)
2867 	        && (io->_npcdata->pathfind.listnb <= 0)
2868 	        &&  !(io->_npcdata->pathfind.pathwait))
2869 	{
2870 		ARX_NPC_LaunchPathfind(io, io->targetinfo);
2871 	}
2872 
2873 	if ((io->_npcdata->behavior & (BEHAVIOUR_FRIENDLY | BEHAVIOUR_NONE))
2874 	        && ((ause0->cur_anim == alist[ANIM_WALK])
2875 	            || (ause0->cur_anim == alist[ANIM_WALK_SNEAK])
2876 	            ||	(ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
2877 	            ||	(ause0->cur_anim == alist[ANIM_RUN])
2878 	            ||	(ause0->cur_anim == alist[ANIM_RUN])
2879 	            ||	(ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_LEFT])
2880 	            ||	(ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_RIGHT])
2881 	            ||	(ause0->cur_anim == alist[ANIM_FIGHT_WALK_BACKWARD])
2882 	           ))
2883 	{
2884 		AcquireLastAnim(io);
2885 		FinishAnim(io, ause0->cur_anim);
2886 		ANIM_Set(ause0, alist[ANIM_WAIT]);
2887 		ause0->altidx_cur = 0;
2888 	}
2889 
2890 
2891 	// look around if finished fleeing or being looking around !
2892 	if ((io->_npcdata->behavior & BEHAVIOUR_LOOK_AROUND)
2893 	        &&	distSqr(io->pos, io->target) > square(150.f))
2894 	{
2895 		if (!io->_npcdata->ex_rotate)
2896 		{
2897 			ARX_NPC_CreateExRotateData(io);
2898 		}
2899 		else // already created
2900 		{
2901 			if	(((ause0->cur_anim == alist[ANIM_WAIT])
2902 			        ||	(ause0->cur_anim == alist[ANIM_WALK])
2903 			        || (ause0->cur_anim == alist[ANIM_WALK_SNEAK])
2904 			        ||	(ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
2905 			        ||	(ause0->cur_anim == alist[ANIM_RUN]))
2906 			        ||	(ause0->cur_anim != NULL))
2907 			{
2908 				io->_npcdata->look_around_inc = 0.f;
2909 
2910 				for (long n = 0; n < 4; n++)
2911 				{
2912 					io->_npcdata->ex_rotate->group_rotate[n].b -= io->_npcdata->ex_rotate->group_rotate[n].b * ( 1.0f / 3 );
2913 
2914 					if (fabs(io->_npcdata->ex_rotate->group_rotate[n].b) < 0.01f)
2915 						io->_npcdata->ex_rotate->group_rotate[n].b = 0.f;
2916 				}
2917 			}
2918 			else
2919 			{
2920 				if (io->_npcdata->look_around_inc == 0.f)
2921 				{
2922 					io->_npcdata->look_around_inc = (rnd() - 0.5f) * 0.08f;
2923 
2924 				}
2925 
2926 				for (long n = 0; n < 4; n++)
2927 				{
2928 					float t = 1.5f - (float)n * ( 1.0f / 5 );
2929 					io->_npcdata->ex_rotate->group_rotate[n].b += io->_npcdata->look_around_inc * framedelay * t;
2930 				}
2931 
2932 				if (io->_npcdata->ex_rotate->group_rotate[0].b > 30)
2933 					io->_npcdata->look_around_inc = -io->_npcdata->look_around_inc;
2934 
2935 				if (io->_npcdata->ex_rotate->group_rotate[0].b < -30)
2936 					io->_npcdata->look_around_inc = -io->_npcdata->look_around_inc;
2937 			}
2938 		}
2939 
2940 	}
2941 	else
2942 	{
2943 		if ((!(io->_npcdata->behavior & BEHAVIOUR_STARE_AT))
2944 		        &&	(io->_npcdata->ex_rotate != NULL))
2945 		{
2946 			io->_npcdata->look_around_inc = 0.f;
2947 
2948 			for (long n = 0; n < 4; n++)
2949 			{
2950 				io->_npcdata->ex_rotate->group_rotate[n].b -= io->_npcdata->ex_rotate->group_rotate[n].b * ( 1.0f / 3 );
2951 
2952 				if (fabs(io->_npcdata->ex_rotate->group_rotate[n].b) < 0.01f)
2953 					io->_npcdata->ex_rotate->group_rotate[n].b = 0.f;
2954 			}
2955 		}
2956 	}
2957 
2958 	ause0->flags &= ~EA_STATICANIM;
2959 
2960 	if ((ause0->cur_anim)
2961 	        &&	((ause0->cur_anim == alist[ANIM_HIT1]) || (ause0->cur_anim == alist[ANIM_HIT_SHORT])))
2962 	{
2963 		if (ause0->flags & EA_ANIMEND)
2964 		{
2965 			AcquireLastAnim(io);
2966 			FinishAnim(io, ause0->cur_anim);
2967 
2968 			if (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
2969 				ANIM_Set(ause0, alist[ANIM_FIGHT_WAIT]);
2970 			else
2971 				ANIM_Set(ause0, alist[ANIM_WAIT]);
2972 
2973 			if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)
2974 				ause0->altidx_cur = 0;
2975 		}
2976 		else
2977 			return;
2978 	}
2979 
2980 	float _dist = std::numeric_limits<float>::max();
2981 	long CHANGE = 0;
2982 
2983 	Vec3f ForcedMove;
2984 
2985 	// GetTargetPos MUST be called before FaceTarget2
2986 	if ((io->_npcdata->pathfind.listnb > 0)
2987 	        &&	(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
2988 	        &&	(io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb - 2)
2989 	        &&	(io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos] == io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos+1]))
2990 	{
2991 		if (ause0->cur_anim != io->anims[ANIM_DEFAULT])
2992 		{
2993 			AcquireLastAnim(io);
2994 			FinishAnim(io, ause0->cur_anim);
2995 			ANIM_Set(ause0, alist[ANIM_DEFAULT]);
2996 
2997 			if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
2998 		}
2999 		else if (ause0->flags & EA_ANIMEND)
3000 		{
3001 			ause0->flags &= ~EA_FORCEPLAY;
3002 			goto argh;
3003 		}
3004 
3005 		return;
3006 	}
3007 
3008 	{
3009 
3010 	// XS : Moved to top of func
3011 	_dist = dist(Vec2f(io->pos.x, io->pos.z), Vec2f(io->target.x, io->target.z));
3012 	dis = _dist;
3013 
3014 	if (io->_npcdata->pathfind.listnb > 0)
3015 		dis = GetTRUETargetDist(io);
3016 
3017 	if (io->_npcdata->behavior & BEHAVIOUR_FLEE)
3018 		dis = 9999999;
3019 
3020 	// Force to flee/wander again
3021 	if ((!io->_npcdata->pathfind.pathwait)
3022 	        &&	(io->_npcdata->pathfind.listnb <= 0))
3023 	{
3024 		if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
3025 		{
3026 			ARX_NPC_LaunchPathfind(io, io->targetinfo);
3027 		}
3028 		else if ((dis > STRIKE_DISTANCE)
3029 		         &&	(io->_npcdata->behavior & BEHAVIOUR_MOVE_TO)
3030 		         &&	(!(io->_npcdata->behavior & (BEHAVIOUR_FIGHT | BEHAVIOUR_MAGIC | BEHAVIOUR_SNEAK))))
3031 		{
3032 			ARX_NPC_LaunchPathfind(io, io->targetinfo);
3033 		}
3034 	}
3035 
3036 	if ((io->_npcdata->pathfind.listnb <= 0)
3037 	        &&	(io->_npcdata->behavior & BEHAVIOUR_FLEE))
3038 	{
3039 		if (ause0->cur_anim != alist[ANIM_DEFAULT])
3040 		{
3041 			AcquireLastAnim(io);
3042 			FinishAnim(io, ause0->cur_anim);
3043 			ANIM_Set(ause0, alist[ANIM_DEFAULT]);
3044 
3045 			if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3046 		}
3047 	}
3048 	else 	// Force object to walk if using pathfinder !!!
3049 	{
3050 		if (io->_npcdata->behavior & (BEHAVIOUR_FRIENDLY | BEHAVIOUR_NONE))
3051 		{
3052 		}
3053 		else if (!io->_npcdata->reachedtarget)
3054 		{
3055 			if (
3056 			    ((io->targetinfo != -2)
3057 			     || (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
3058 			     || (io->_npcdata->behavior & BEHAVIOUR_FLEE)
3059 			     || (io->_npcdata->behavior & BEHAVIOUR_GO_HOME))
3060 			    && (io->_npcdata->pathfind.listnb > 0)
3061 			    && (ause0->cur_anim != alist[ANIM_WALK])
3062 			    && (ause0->cur_anim != alist[ANIM_FIGHT_WALK_FORWARD])
3063 			    && (ause0->cur_anim != alist[ANIM_RUN])
3064 			    && (ause0->cur_anim != alist[ANIM_WALK_SNEAK])
3065 			    && (!(ause0->flags & EA_FORCEPLAY))
3066 			)
3067 			{
3068 				AcquireLastAnim(io);
3069 				FinishAnim(io, ause0->cur_anim);
3070 
3071 				if ((dis <= RUN_WALK_RADIUS) && (io->_npcdata->behavior & BEHAVIOUR_FIGHT))
3072 				{
3073 					ANIM_Set(ause0, alist[ANIM_FIGHT_WALK_FORWARD]);
3074 				}
3075 				else
3076 					switch (io->_npcdata->movemode)
3077 					{
3078 						case SNEAKMODE:
3079 							ANIM_Set(ause0, alist[ANIM_WALK_SNEAK]);
3080 							break;
3081 						case WALKMODE:
3082 							ANIM_Set(ause0, alist[ANIM_WALK]);
3083 							break;
3084 						case RUNMODE:
3085 
3086 							if (dis <= RUN_WALK_RADIUS)
3087 							{
3088 								ANIM_Set(ause0, alist[ANIM_WALK]);
3089 							}
3090 							else
3091 							{
3092 								if (alist[ANIM_RUN])
3093 									ANIM_Set(ause0, alist[ANIM_RUN]);
3094 								else ANIM_Set(ause0, alist[ANIM_WALK]);
3095 							}
3096 
3097 							break;
3098 						case NOMOVEMODE:
3099 							ANIM_Set(ause0, alist[ANIM_WAIT]);
3100 
3101 							if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3102 
3103 							break;
3104 					}
3105 
3106 				ause0->flags |= EA_LOOP;
3107 				io->_npcdata->walk_start_time = 0;
3108 
3109 			}
3110 		}
3111 		else // our object has reached its target...
3112 		{
3113 			// Put it in Fighting stance if using Fight Behavior
3114 			if ((io->_npcdata->behavior & (BEHAVIOUR_FIGHT | BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT))
3115 			        && io->anims[ANIM_FIGHT_WAIT])
3116 			{
3117 				if ((ause0->cur_anim != alist[ANIM_FIGHT_WAIT])
3118 				        &&	(ause0->cur_anim != alist[ANIM_FIGHT_STRAFE_LEFT])
3119 				        &&	(ause0->cur_anim != alist[ANIM_FIGHT_STRAFE_RIGHT])
3120 				        &&	(ause0->cur_anim != alist[ANIM_FIGHT_WALK_BACKWARD])
3121 				        &&	(ause0->cur_anim != alist[ANIM_FIGHT_WALK_FORWARD])
3122 				   )
3123 
3124 				AcquireLastAnim(io);
3125 				FinishAnim(io, ause0->cur_anim);
3126 				ANIM_Set(ause0, alist[ANIM_FIGHT_WAIT]);
3127 				ause0->flags |= EA_LOOP;
3128 
3129 			}
3130 			// Stop it and put it in Wait anim after finishing his walk anim...
3131 			else
3132 			{
3133 				if	(ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
3134 				{
3135 					ause0->flags &= ~EA_LOOP;
3136 					AcquireLastAnim(io);
3137 					FinishAnim(io, ause0->cur_anim);
3138 					ANIM_Set(ause0, alist[ANIM_DEFAULT]);
3139 
3140 					if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3141 
3142 					ause0->flags |= EA_LOOP;
3143 				}
3144 				else if ((ause0->cur_anim == alist[ANIM_WALK])
3145 				         || (ause0->cur_anim == alist[ANIM_RUN])
3146 				         || (ause0->cur_anim == alist[ANIM_WALK_SNEAK]))
3147 				{
3148 					ause0->flags &= ~EA_LOOP;
3149 
3150 					if (io->_npcdata->reachedtime + 500 < float(arxtime))
3151 					{
3152 						AcquireLastAnim(io);
3153 						FinishAnim(io, ause0->cur_anim);
3154 						ANIM_Set(ause0, alist[ANIM_DEFAULT]);
3155 
3156 						if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3157 
3158 						ause0->flags |= EA_LOOP;
3159 					}
3160 				}
3161 
3162 				if (!(ause0->flags & EA_FORCEPLAY)
3163 				        &&	(ause0->cur_anim != alist[ANIM_DEFAULT])
3164 				        &&	(ause0->cur_anim != alist[ANIM_FIGHT_WAIT])
3165 				        && (ause0->flags & EA_ANIMEND)
3166 				        &&	!(ause0->flags & EA_LOOP))
3167 				{
3168 					AcquireLastAnim(io);
3169 					FinishAnim(io, ause0->cur_anim);
3170 					ANIM_Set(ause0, alist[ANIM_DEFAULT]);
3171 
3172 					if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3173 				}
3174 			}
3175 		}
3176 	}
3177 
3178 	// Force Run when far from target and using RUNMODE
3179 	if ((dis > RUN_WALK_RADIUS) && (io->_npcdata->movemode == RUNMODE)
3180 	        && (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
3181 	        && alist[ANIM_RUN])
3182 	{
3183 		AcquireLastAnim(io);
3184 		FinishAnim(io, ause0->cur_anim);
3185 		ANIM_Set(ause0, alist[ANIM_RUN]);
3186 	}
3187 
3188 	// Reset WAIT Animation if reached end !
3189 	if ((ause0->cur_anim == alist[ANIM_DEFAULT])
3190 	        && (ause0->cur_anim != NULL)
3191 	        && (ause0->flags & EA_ANIMEND))
3192 	{
3193 		AcquireLastAnim(io);
3194 		FinishAnim(io, ause0->cur_anim);
3195 		ANIM_Set(ause0, alist[ANIM_DEFAULT]);
3196 
3197 		if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3198 	}
3199 
3200 	// Can only change direction during some specific animations
3201 	if (((ause0->cur_anim == alist[ANIM_DEFAULT]) && (ause0->altidx_cur == 0))
3202 	        || (ause0->cur_anim == alist[ANIM_FIGHT_WAIT])
3203 	        || (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
3204 	        || (ause0->cur_anim == alist[ANIM_WALK])
3205 	        || (ause0->cur_anim == alist[ANIM_WALK_SNEAK])
3206 	        || (ause0->cur_anim == alist[ANIM_RUN])
3207 	        || (ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_LEFT])
3208 	        || (ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_RIGHT])
3209 	        || (ause0->cur_anim == alist[ANIM_FIGHT_WALK_BACKWARD])
3210 	        || (ause0->cur_anim == NULL)
3211 	   )	CHANGE = 1;
3212 
3213 	// Tries to face/stare at target
3214 	if ((!arxtime.is_paused()) && (CHANGE)
3215 	    && (!(ause0->flags & EA_FORCEPLAY)))
3216 	{
3217 		if (io->_npcdata->behavior & BEHAVIOUR_STARE_AT)
3218 			StareAtTarget(io);
3219 		else	FaceTarget2(io);
3220 	}
3221 
3222 	// Choose tolerance value depending on target...
3223 	if ((io->_npcdata->pathfind.listnb > 0)
3224 	        && (io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb))
3225 	{
3226 		ComputeTolerance(io, io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos], &TOLERANCE);
3227 		ComputeTolerance(io, io->_npcdata->pathfind.truetarget, &TOLERANCE2);
3228 	}
3229 	else
3230 	{
3231 		ComputeTolerance(io, io->targetinfo, &TOLERANCE);
3232 		TOLERANCE2 = TOLERANCE;
3233 	}
3234 
3235 	// COLLISION Management START *********************************************************************
3236 	//	EERIE_CYLINDER cyl;
3237 	// Try physics from last valid pos to current desired pos...
3238 	// For this frame we want to try a move from startpos (valid pos)
3239 	// to targetpos (potentially invalid pos)
3240 	io->physics.startpos = io->physics.cyl.origin = io->pos;
3241 
3242 	if(io->forcedmove == Vec3f::ZERO) {
3243 		ForcedMove = Vec3f::ZERO;
3244 	} else {
3245 		float dd = min(1.f, (float)FrameDiff * ( 1.0f / 6 ) / io->forcedmove.length());
3246 		ForcedMove = io->forcedmove * dd;
3247 	}
3248 
3249 	// Sets Target position to desired position...
3250 	io->physics.targetpos.x = io->pos.x + io->move.x + ForcedMove.x;
3251 	io->physics.targetpos.z = io->pos.z + io->move.z + ForcedMove.z;
3252 	// IO_PHYSICS phys;	// XS : Moved to func beginning
3253 	memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
3254 	GetIOCyl(io, &phys.cyl);
3255 
3256 	CollisionFlags levitate = 0;
3257 
3258 	if(ARX_SPELLS_GetSpellOn(io, SPELL_LEVITATE) >= 0) {
3259 		levitate = CFLAG_LEVITATE;
3260 		io->physics.targetpos.y = io->pos.y + io->move.y + ForcedMove.y;
3261 	}
3262 	else  // Gravity 'simulation'
3263 	{
3264 		phys.cyl.origin.y += 10.f;
3265 		float anything = CheckAnythingInCylinder(&phys.cyl, io, CFLAG_JUST_TEST | CFLAG_NPC);
3266 
3267 		if (anything >= 0)
3268 		{
3269 			io->physics.targetpos.y = io->pos.y + (float)FrameDiff * 1.5f + ForcedMove.y;
3270 		}
3271 		else io->physics.targetpos.y = io->pos.y + ForcedMove.y;
3272 
3273 		phys.cyl.origin.y -= 10.f;
3274 	}
3275 
3276 	memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
3277 	GetIOCyl(io, &phys.cyl);
3278 
3279 	io->forcedmove -= ForcedMove;
3280 
3281 #ifdef BUILD_EDITOR
3282 	// Some visual debug stuff
3283 	if(DEBUGNPCMOVE) {
3284 		EERIE_CYLINDER cyll;
3285 		cyll.height = GetIOHeight(io);
3286 		cyll.radius = GetIORadius(io);
3287 		cyll.origin = phys.startpos;
3288 		EERIEDraw3DCylinder(cyll, Color::green);
3289 
3290 		if (!(AttemptValidCylinderPos(&cyll, io, levitate | CFLAG_NPC)))
3291 		{
3292 			cyll.height = -40.f;
3293 			EERIEDraw3DCylinder(cyll, Color::blue);
3294 			cyll.height = GetIOHeight(io);
3295 		}
3296 
3297 		cyll.origin = io->physics.targetpos;
3298 		EERIEDraw3DCylinder(cyll, Color::red);
3299 
3300 		if (!(AttemptValidCylinderPos(&cyll, io, levitate | CFLAG_NPC)))
3301 		{
3302 			cyll.height = GetIOHeight(io);
3303 		}
3304 	}
3305 #endif
3306 
3307 	DIRECT_PATH = true;
3308 
3309 	// Now we try the physical move for real
3310 	if(io->physics.startpos == io->physics.targetpos
3311 	        || ARX_COLLISION_Move_Cylinder(&phys, io, 40, levitate | CFLAG_NPC))
3312 	{
3313 		// Successfull move now validate it
3314 		if (!DIRECT_PATH) io->_npcdata->moveproblem += 1;
3315 		else io->_npcdata->moveproblem = 0;
3316 
3317 		io->_npcdata->collid_state = 0;
3318 
3319 	}
3320 	else // Object was unable to move to target... Stop it
3321 	{
3322 		io->_npcdata->moveproblem += 3;
3323 	}
3324 
3325 	io->room_flags |= 1;
3326 	io->physics.cyl.origin = io->pos = phys.cyl.origin;
3327 	io->physics.cyl.radius = GetIORadius(io);
3328 	io->physics.cyl.height = GetIOHeight(io);
3329 
3330 	// Compute distance 2D to target.
3331 	_dist = dist(Vec2f(io->pos.x, io->pos.z), Vec2f(io->target.x, io->target.z));
3332 	dis = _dist;
3333 
3334 	if (io->_npcdata->pathfind.listnb > 0)
3335 		dis = GetTRUETargetDist(io);
3336 
3337 	if (io->_npcdata->behavior & BEHAVIOUR_FLEE)
3338 		dis = 9999999;
3339 
3340 	// Tries to solve Moveproblems... sort of...
3341 	if (io->_npcdata->moveproblem > 11)
3342 	{
3343 		if ((_dist > TOLERANCE) && (!io->_npcdata->pathfind.pathwait))
3344 		{
3345 			long targ;
3346 
3347 			if (io->_npcdata->pathfind.listnb > 0)
3348 				targ = io->_npcdata->pathfind.truetarget;
3349 			else
3350 				targ = io->targetinfo;
3351 
3352 			ARX_NPC_LaunchPathfind(io, targ);
3353 		}
3354 
3355 		io->_npcdata->moveproblem = 0;
3356 	}
3357 
3358 	// Checks if pathfind final target is still locked on true target
3359 	if ((FRAME_COUNT <= 0)
3360 	        &&	(!io->_npcdata->pathfind.pathwait)
3361 	        && !(io->_npcdata->pathfind.flags & PATHFIND_ONCE)
3362 	        && !(io->_npcdata->pathfind.flags & PATHFIND_NO_UPDATE)
3363 	        &&	(io->_npcdata->pathfind.listnb > 0)
3364 	        &&	(io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb)
3365 	        &&	(io->_npcdata->behavior & BEHAVIOUR_MOVE_TO)
3366 	        && !(io->_npcdata->behavior & BEHAVIOUR_FLEE)
3367 	   )
3368 	{
3369 		if (ValidIONum(io->_npcdata->pathfind.truetarget))
3370 		{
3371 			Vec3f * p = &entities[io->_npcdata->pathfind.truetarget]->pos;
3372 			long t = AnchorData_GetNearest(p, &io->physics.cyl);
3373 
3374 			if ((t != -1) && (t != io->_npcdata->pathfind.list[io->_npcdata->pathfind.listnb-1]))
3375 			{
3376 				float d = dist(ACTIVEBKG->anchors[t].pos, ACTIVEBKG->anchors[io->_npcdata->pathfind.list[io->_npcdata->pathfind.listnb-1]].pos);
3377 
3378 				if (d > 200.f)
3379 					ARX_NPC_LaunchPathfind(io, io->_npcdata->pathfind.truetarget);
3380 			}
3381 		}
3382 	}
3383 
3384 	}
3385 
3386 	// We are still too far from our target...
3387 	if (io->_npcdata->pathfind.pathwait == 0)
3388 	{
3389 		if ((_dist > TOLERANCE) && (dis > TOLERANCE2))
3390 		{
3391 			if ((io->_npcdata->reachedtarget))
3392 			{
3393 				if (ValidIONum(io->targetinfo))
3394 					EVENT_SENDER = entities[io->targetinfo];
3395 				else
3396 					EVENT_SENDER = NULL;
3397 
3398 				SendIOScriptEvent(io, SM_LOSTTARGET);
3399 				io->_npcdata->reachedtarget = 0;
3400 			}
3401 
3402 			// if not blocked & not Flee-Pathfinding
3403 			if ( !((io->_npcdata->pathfind.listnb <= 0) && (io->_npcdata->behavior & BEHAVIOUR_FLEE))
3404 			    || (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
3405 			    || (io->_npcdata->behavior & BEHAVIOUR_GO_HOME))
3406 			{
3407 				ANIM_HANDLE * desiredanim = NULL;
3408 
3409 				//long desiredloop=1;
3410 				if ((dis <= RUN_WALK_RADIUS) && (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
3411 				        && (ause0->cur_anim != alist[ANIM_RUN]))
3412 				{
3413 
3414 
3415 					float fCalc = io->_npcdata->walk_start_time + FrameDiff ;
3416 
3417 					io->_npcdata->walk_start_time = checked_range_cast<short>(fCalc);
3418 
3419 
3420 					if (io->_npcdata->walk_start_time > 600)
3421 					{
3422 						desiredanim = alist[ANIM_FIGHT_WALK_FORWARD];
3423 						io->_npcdata->walk_start_time = 0;
3424 					}
3425 				}
3426 				else switch (io->_npcdata->movemode)
3427 					{
3428 						case SNEAKMODE:
3429 							desiredanim = alist[ANIM_WALK_SNEAK];
3430 							break;
3431 						case WALKMODE:
3432 							desiredanim = alist[ANIM_WALK];
3433 							break;
3434 						case RUNMODE:
3435 
3436 							if (dis <= RUN_WALK_RADIUS)
3437 							{
3438 								desiredanim = alist[ANIM_WALK];
3439 							}
3440 							else
3441 								desiredanim = alist[ANIM_RUN];
3442 
3443 							break;
3444 						case NOMOVEMODE:
3445 							desiredanim = alist[ANIM_DEFAULT];
3446 							break;
3447 					}
3448 
3449 				if (io->targetinfo == -2) desiredanim = alist[ANIM_DEFAULT];
3450 
3451 				if ((desiredanim)
3452 				        && (desiredanim != ause0->cur_anim)
3453 				        && (!(ause0->flags & EA_FORCEPLAY))
3454 				        &&
3455 				        ((ause0->cur_anim == alist[ANIM_DEFAULT])
3456 				         || (ause0->cur_anim == alist[ANIM_FIGHT_WAIT]))
3457 				   )
3458 				{
3459 					AcquireLastAnim(io);
3460 					FinishAnim(io, ause0->cur_anim);
3461 					ANIM_Set(ause0, desiredanim);
3462 
3463 					if ((desiredanim == alist[ANIM_DEFAULT])
3464 					        && (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)) ause0->altidx_cur = 0;
3465 
3466 					ause0->flags |= EA_LOOP;
3467 				}
3468 			}
3469 		}
3470 		// Near target
3471 		else
3472 		{
3473 			if (dis <= TOLERANCE2)
3474 			{
3475 				io->_npcdata->pathfind.listpos = 0;
3476 				io->_npcdata->pathfind.listnb = -1;
3477 				io->_npcdata->pathfind.pathwait = 0;
3478 				free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
3479 
3480 				if	(ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
3481 				{
3482 					ause0->flags &= ~EA_LOOP;
3483 					AcquireLastAnim(io);
3484 					FinishAnim(io, ause0->cur_anim);
3485 					ANIM_Set(ause0, alist[ANIM_DEFAULT]);
3486 
3487 					if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3488 
3489 					ause0->flags |= EA_LOOP;
3490 				}
3491 			}
3492 
3493 			if (io->_npcdata->pathfind.listnb > 0)
3494 			{
3495 			argh:;
3496 
3497 
3498 				long lMax = max(ARX_NPC_GetNextAttainableNodeIncrement(io), 1L);
3499 
3500 				io->_npcdata->pathfind.listpos = checked_range_cast<unsigned short>(io->_npcdata->pathfind.listpos + lMax);
3501 
3502 
3503 				if ((io->_npcdata->pathfind.listpos >= io->_npcdata->pathfind.listnb)) // || (dis<=120.f))
3504 				{
3505 					io->_npcdata->pathfind.listpos = 0;
3506 					io->_npcdata->pathfind.listnb = -1;
3507 					io->_npcdata->pathfind.pathwait = 0;
3508 					free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
3509 
3510 					EVENT_SENDER = NULL;
3511 
3512 					if ((io->_npcdata->behavior & BEHAVIOUR_FLEE)
3513 					        && (!io->_npcdata->pathfind.pathwait))
3514 						SendIOScriptEvent(io, SM_NULL, "", "flee_end");
3515 
3516 					if ((io->_npcdata->pathfind.flags & PATHFIND_NO_UPDATE) &&
3517 					        (io->_npcdata->pathfind.pathwait == 0))
3518 					{
3519 						if (!io->_npcdata->reachedtarget)
3520 						{
3521 							long num = io->index();
3522 							io->_npcdata->reachedtarget = 1;
3523 							io->_npcdata->reachedtime = (unsigned long)(arxtime);
3524 
3525 							if (io->targetinfo != num)
3526 							{
3527 								SendIOScriptEvent(io, SM_REACHEDTARGET, "fake");
3528 								io->targetinfo = num;
3529 							}
3530 						}
3531 
3532 					}
3533 					else
3534 					{
3535 						io->targetinfo = io->_npcdata->pathfind.truetarget;
3536 						GetTargetPos(io);
3537 
3538 						if (fabs(io->pos.y - io->target.y) > 200.f)
3539 						{
3540 							io->_npcdata->pathfind.listnb = -2;
3541 						}
3542 					}
3543 				}
3544 			}
3545 			else if (!io->_npcdata->reachedtarget)
3546 			{
3547 				if (ValidIONum(io->targetinfo))
3548 					EVENT_SENDER = entities[io->targetinfo];
3549 				else
3550 					EVENT_SENDER = NULL;
3551 
3552 				io->_npcdata->reachedtarget = 1;
3553 				io->_npcdata->reachedtime = (unsigned long)(arxtime);//treat warning C4244 conversion from 'float' to 'unsigned long'
3554 
3555 				if (io->animlayer[1].flags & EA_ANIMEND)
3556 					io->animlayer[1].cur_anim = NULL;
3557 
3558 				if (io->targetinfo != long(io->index()))
3559 					SendIOScriptEvent(io, SM_REACHEDTARGET);
3560 			}
3561 		}
3562 	}
3563 
3564 	if (dis < 280.f)
3565 	{
3566 		if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
3567 		        && !(io->_npcdata->behavior & BEHAVIOUR_FLEE))
3568 		{
3569 			ARX_NPC_Manage_Fight(io);
3570 		}
3571 		else
3572 		{
3573 			ARX_NPC_Manage_NON_Fight(io);
3574 		}
3575 	}
3576 
3577 	ARX_NPC_Manage_Anims(io, TOLERANCE2);
3578 
3579 	// Puts at least WAIT anim on NPC if he has no main animation...
3580 	if (ause0->cur_anim == NULL)
3581 	{
3582 		if (io->_npcdata->behavior & (BEHAVIOUR_FIGHT | BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT))
3583 		{
3584 			FinishAnim(io, ause0->cur_anim);
3585 			ANIM_Set(ause0, alist[ANIM_FIGHT_WAIT]);
3586 			ause0->flags |= EA_LOOP;
3587 		}
3588 		else
3589 		{
3590 			FinishAnim(io, ause0->cur_anim);
3591 			ANIM_Set(ause0, alist[ANIM_WAIT]);
3592 
3593 			if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
3594 		}
3595 	}
3596 
3597 	// Now update lastpos values for next call use...
3598 	io->lastpos = io->pos;
3599 }
3600 
AngularDifference(float a1,float a2)3601 float AngularDifference(float a1, float a2)
3602 {
3603 	a1 = MAKEANGLE(a1);
3604 	a2 = MAKEANGLE(a2);
3605 
3606 	if (a1 == a2) return 0;
3607 
3608 	float ret;
3609 
3610 	if (a1 < a2)
3611 	{
3612 		ret = a2;
3613 		a2 = a1;
3614 		a1 = ret;
3615 	}
3616 
3617 	ret = a1 - a2;
3618 	ret = min(ret, (a2 + 360) - a1);
3619 	return ret;
3620 
3621 }
3622 extern float CURRENT_PLAYER_COLOR;
3623 //***********************************************************************************************
3624 // Entity * ARX_NPC_GetFirstNPCInSight(Entity * ioo)
3625 //-----------------------------------------------------------------------------------------------
3626 // FUNCTION:
3627 //   returns the "first" NPC in sight for another NPC (ioo)
3628 //***********************************************************************************************
ARX_NPC_GetFirstNPCInSight(Entity * ioo)3629 Entity * ARX_NPC_GetFirstNPCInSight(Entity * ioo)
3630 {
3631 	if (!ioo) return NULL;
3632 
3633 	// Basic Clipping to avoid performance loss
3634 	if(distSqr(ACTIVECAM->pos, ioo->pos) > square(2500)) {
3635 		return NULL;
3636 	}
3637 
3638 	Entity * found_io = NULL;
3639 	float found_dist = std::numeric_limits<float>::max();
3640 
3641 	for (size_t i = 0; i < entities.size(); i++)
3642 	{
3643 		Entity * io = entities[i];
3644 
3645 		if ((!io)
3646 		        ||	(IsDeadNPC(io))
3647 		        ||	(io == ioo)
3648 		        ||	(!(io->ioflags & IO_NPC))
3649 		        ||	(io->show != SHOW_FLAG_IN_SCENE))
3650 			continue;
3651 
3652 		float dist_io = distSqr(io->pos, ioo->pos);
3653 
3654 		if ((dist_io > found_dist)
3655 		        ||	(dist_io > square(1800)))
3656 			continue; // too far
3657 
3658 		if (dist_io < square(130))
3659 		{
3660 			if (found_dist > dist_io)
3661 			{
3662 				found_io = io;
3663 				found_dist = dist_io;
3664 			}
3665 
3666 			continue;
3667 		}
3668 
3669 		Vec3f orgn, dest;
3670 
3671 		float ab = MAKEANGLE(ioo->angle.b);
3672 
3673 		long grp = ioo->obj->fastaccess.head_group_origin;
3674 
3675 		if (grp < 0)
3676 		{
3677 			orgn.x = ioo->pos.x;
3678 			orgn.y = ioo->pos.y - 90.f;
3679 			orgn.z = ioo->pos.z;
3680 
3681 			if (ioo == entities.player())	orgn.y = player.pos.y + 90.f;
3682 		}
3683 		else
3684 			GetVertexPos(ioo, ioo->obj->fastaccess.head_group_origin, &orgn);
3685 
3686 		grp = io->obj->fastaccess.head_group_origin;
3687 
3688 		if (grp < 0)
3689 		{
3690 			dest.x = io->pos.x;
3691 			dest.y = io->pos.y - 90.f;
3692 			dest.z = io->pos.z;
3693 
3694 			if (io == entities.player())	dest.y = player.pos.y + 90.f;
3695 		}
3696 		else
3697 			GetVertexPos(io, io->obj->fastaccess.head_group_origin, &dest);
3698 
3699 
3700 		float aa = getAngle(orgn.x, orgn.z, dest.x, dest.z);
3701 		aa = MAKEANGLE(degrees(aa));
3702 
3703 		if (EEfabs(AngularDifference(aa, ab)) < 110.f)
3704 		{
3705 			if (dist_io < square(200))
3706 			{
3707 				if (found_dist > dist_io)
3708 				{
3709 					found_io = io;
3710 					found_dist = dist_io;
3711 				}
3712 
3713 				continue;
3714 			}
3715 
3716 
3717 			float grnd_color = CURRENT_PLAYER_COLOR - GetPlayerStealth();
3718 
3719 			if (grnd_color > 0)
3720 			{
3721 				Vec3f ppos;
3722 				EERIEPOLY * epp = NULL;
3723 
3724 				if (IO_Visible(&orgn, &dest, epp, &ppos))
3725 				{
3726 					if (found_dist > dist_io)
3727 					{
3728 						found_io = io;
3729 						found_dist = dist_io;
3730 					}
3731 
3732 					continue;
3733 				}
3734 				else if (distSqr(ppos, dest) < square(25.f))
3735 				{
3736 					if (found_dist > dist_io)
3737 					{
3738 						found_io = io;
3739 						found_dist = dist_io;
3740 					}
3741 
3742 					continue;
3743 				}
3744 			}
3745 		}
3746 	}
3747 
3748 	return found_io;
3749 }
3750 extern float CURRENT_PLAYER_COLOR;
3751 
3752 //***********************************************************************************************
3753 // void CheckNPC(Entity * io)
3754 //-----------------------------------------------------------------------------------------------
3755 // FUNCTION
3756 //   Checks if a NPC is dead to prevent further Layers Animation
3757 //***********************************************************************************************
CheckNPC(Entity * io)3758 void CheckNPC(Entity * io)
3759 {
3760 	if ((!io)
3761 	        ||	(io->show != SHOW_FLAG_IN_SCENE))
3762 		return;
3763 
3764 	if (IsDeadNPC(io))
3765 	{
3766 		io->animlayer[1].cur_anim = NULL;
3767 		io->animlayer[2].cur_anim = NULL;
3768 		io->animlayer[3].cur_anim = NULL;
3769 
3770 		io->animlayer[0].next_anim = NULL;
3771 		io->animlayer[1].next_anim = NULL;
3772 		io->animlayer[2].next_anim = NULL;
3773 		io->animlayer[3].next_anim = NULL;
3774 	}
3775 }
3776 extern long GLOBAL_Player_Room;
3777 
3778 // Checks an NPC Visibility Field (Player Detect)
3779 // NECESSARY:
3780 //   Uses Invisibility/Confuse/Torch infos.
3781 // RESULT:
3782 //   Sends appropriate Detectplayer/Undetectplayer events to the IO
3783 // WARNINGS:
3784 //   io and io->obj must be valid (no check !)
CheckNPCEx(Entity * io)3785 void CheckNPCEx(Entity * io) {
3786 
3787 	// Distance Between Player and IO
3788 	float ds = distSqr(io->pos, player.basePosition());
3789 
3790 	// Start as not visible
3791 	long Visible = 0;
3792 
3793 	// Check visibility only if player is visible, not too far and not dead
3794 	if(entities.player()->invisibility <= 0.f && ds < square(2000.f) && player.life > 0.f) {
3795 
3796 		// checks for near contact +/- 15 cm --> force visibility
3797 		if(io->room_flags & 1) {
3798 			UpdateIORoom(io);
3799 		}
3800 
3801 		if(GLOBAL_Player_Room == -1) {
3802 			GLOBAL_Player_Room = ARX_PORTALS_GetRoomNumForPosition(&player.pos, 1);
3803 		}
3804 
3805 		float fdist = SP_GetRoomDist(&io->pos, &player.pos, io->room, GLOBAL_Player_Room);
3806 
3807 		// Use Portal Room Distance for Extra Visibility Clipping.
3808 		if(GLOBAL_Player_Room > -1 && io->room > -1 && fdist > 2000.f) {
3809 			// nothing to do
3810 		} else if(ds < square(GetIORadius(io) + GetIORadius(entities.player()) + 15.f)
3811 		          && EEfabs(player.pos.y - io->pos.y) < 200.f) {
3812 			Visible = 1;
3813 		} else { // Make full visibility test
3814 
3815 			// Retreives Head group position for "eye" pos.
3816 			long grp = io->obj->fastaccess.head_group_origin;
3817 			Vec3f orgn = io->pos - Vec3f(0.f, (grp < 0) ? 90.f : 120.f, 0.f);
3818 			Vec3f dest = player.pos + Vec3f(0.f, 90.f, 0.f);
3819 
3820 			// Check for Field of vision angle
3821 			float aa = getAngle(orgn.x, orgn.z, dest.x, dest.z);
3822 			aa = MAKEANGLE(degrees(aa));
3823 			float ab = MAKEANGLE(io->angle.b);
3824 			if(EEfabs(AngularDifference(aa, ab)) < 110.f) {
3825 
3826 				// Check for Darkness/Stealth
3827 				if(CURRENT_PLAYER_COLOR > GetPlayerStealth() || SHOW_TORCH
3828 				   || ds < square(200.f)) {
3829 					Vec3f ppos;
3830 					// Check for Geometrical Visibility
3831 					if(IO_Visible(&orgn, &dest, NULL, &ppos)
3832 					   || distSqr(ppos, dest) < square(25.f)) {
3833 						Visible = 1;
3834 					}
3835 				}
3836 			}
3837 		}
3838 
3839 		if(Visible && !io->_npcdata->detect) {
3840 			// if visible but was NOT visible, sends an Detectplayer Event
3841 			EVENT_SENDER = NULL;
3842 			SendIOScriptEvent(io, SM_DETECTPLAYER);
3843 			io->_npcdata->detect = 1;
3844 		}
3845 	}
3846 
3847 	// if not visible but was visible, sends an Undetectplayer Event
3848 	if(!Visible && io->_npcdata->detect) {
3849 		EVENT_SENDER = NULL;
3850 		SendIOScriptEvent(io, SM_UNDETECTPLAYER);
3851 		io->_npcdata->detect = 0;
3852 	}
3853 }
3854 
ARX_NPC_NeedStepSound(Entity * io,Vec3f * pos,const float volume,const float power)3855 void ARX_NPC_NeedStepSound(Entity * io, Vec3f * pos, const float volume, const float power) {
3856 
3857 	string _step_material = "foot_bare";
3858 	const string * step_material = &_step_material;
3859 	string floor_material = "earth";
3860 
3861 	if (EEIsUnderWater(pos))
3862 		floor_material = "water";
3863 	else
3864 	{
3865 		EERIEPOLY * ep;
3866 		ep = CheckInPoly(pos->x, pos->y - 100.0F, pos->z);
3867 
3868 		if (ep &&  ep->tex && !ep->tex->m_texName.empty())
3869 			floor_material = GetMaterialString( ep->tex->m_texName );
3870 	}
3871 
3872 	if(io && !io->stepmaterial.empty()) {
3873 		step_material = &io->stepmaterial;
3874 	}
3875 
3876 	if(io == entities.player() && player.equiped[EQUIP_SLOT_LEGGINGS] > 0) {
3877 		if(ValidIONum(player.equiped[EQUIP_SLOT_LEGGINGS])) {
3878 			Entity * ioo = entities[player.equiped[EQUIP_SLOT_LEGGINGS]];
3879 			if(!ioo->stepmaterial.empty()) {
3880 				step_material = &ioo->stepmaterial;
3881 			}
3882 		}
3883 	}
3884 
3885 	ARX_SOUND_PlayCollision(*step_material, floor_material, volume, power, pos, io);
3886 }
3887 //-------------------------------------------------------------------------
3888 //***********************************************************************************************
3889 // ARX_NPC_SpawnAudibleSound
3890 // Sends ON HEAR events to NPCs for audible sounds
3891 // factor > 1.0F harder to hear, < 0.0F easier to hear
3892 //***********************************************************************************************
ARX_NPC_SpawnAudibleSound(Vec3f * pos,Entity * source,const float factor,const float presence)3893 void ARX_NPC_SpawnAudibleSound(Vec3f * pos, Entity * source, const float factor, const float presence)
3894 {
3895 	float max_distance;
3896 
3897 	if (source == entities.player())
3898 		max_distance = ARX_NPC_ON_HEAR_MAX_DISTANCE_STEP;
3899 	else if (source && source->ioflags & IO_ITEM)
3900 		max_distance = ARX_NPC_ON_HEAR_MAX_DISTANCE_ITEM;
3901 	else return;
3902 
3903 	max_distance *= presence;
3904 	max_distance /= factor;
3905 
3906 	EVENT_SENDER = source;
3907 
3908 
3909 	long Source_Room = ARX_PORTALS_GetRoomNumForPosition(pos, 1);
3910 
3911 
3912 	for (size_t i = 0; i < entities.size(); i++)
3913 		if ((entities[i])
3914 		        &&	(entities[i]->ioflags & IO_NPC)
3915 		        &&	(entities[i]->gameFlags & GFLAG_ISINTREATZONE)
3916 		        &&	(entities[i] != source)
3917 		        &&	((entities[i]->show == SHOW_FLAG_IN_SCENE)
3918 		             ||	(entities[i]->show == SHOW_FLAG_HIDDEN))
3919 		        &&	(entities[i]->_npcdata->life > 0.f)
3920 		   )
3921 		{
3922 			float distance = fdist(*pos, entities[i]->pos);
3923 
3924 			if (distance < max_distance)
3925 			{
3926 				if (entities[i]->room_flags & 1)
3927 					UpdateIORoom(entities[i]);
3928 
3929 				if ((Source_Room > -1) && (entities[i]->room > -1))
3930 				{
3931 					float fdist = SP_GetRoomDist(pos, &entities[i]->pos, Source_Room, entities[i]->room);
3932 
3933 					if (fdist < max_distance * 1.5f)
3934 					{
3935 						long ldistance = fdist;
3936 						char temp[64];
3937 
3938 						sprintf(temp, "%ld", ldistance);
3939 
3940 						SendIOScriptEvent(entities[i], SM_HEAR, temp);
3941 					}
3942 				} else {
3943 					long ldistance = distance;
3944 					char temp[64];
3945 
3946 					sprintf(temp, "%ld", ldistance);
3947 
3948 					SendIOScriptEvent(entities[i], SM_HEAR, temp);
3949 				}
3950 			}
3951 		}
3952 }
3953 extern Entity * CURRENT_TORCH;
3954 //-------------------------------------------------------------------------
ManageIgnition(Entity * io)3955 void ManageIgnition(Entity * io)
3956 {
3957 	if (!io) return;
3958 
3959 	if (CURRENT_TORCH == io)
3960 	{
3961 		if (ValidDynLight(io->ignit_light))
3962 			DynLight[io->ignit_light].exist = 0;
3963 
3964 		io->ignit_light = -1;
3965 
3966 		if (io->ignit_sound != audio::INVALID_ID)
3967 		{
3968 			ARX_SOUND_Stop(io->ignit_sound);
3969 			io->ignit_sound = audio::INVALID_ID;
3970 		}
3971 
3972 		return;
3973 	}
3974 
3975 	// Torch Management
3976 	Entity * plw = NULL;
3977 
3978 	if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
3979 	        &&	(ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])))
3980 		plw = entities[player.equiped[EQUIP_SLOT_WEAPON]];
3981 
3982 	if((io->ioflags & IO_FIERY) && (!(io->type_flags & OBJECT_TYPE_BOW))
3983 	   && (io->show == SHOW_FLAG_IN_SCENE || io == plw)) {
3984 
3985 		float p = io->ignition = 25.f;
3986 		while(p > 0.f) {
3987 			p -= 6.f;
3988 
3989 			if(!io || !io->obj || io->obj->facelist.empty()) {
3990 				break;
3991 			}
3992 
3993 			long notok = 10;
3994 			size_t num = 0;
3995 			while(notok-- > 0) {
3996 				num = Random::get(0, io->obj->facelist.size() - 1);
3997 				if(io->obj->facelist[num].facetype & POLY_HIDE) {
3998 					continue;
3999 				}
4000 				notok = -1;
4001 			}
4002 
4003 			if(notok >= 0) {
4004 				continue;
4005 			}
4006 
4007 			if(rnd() >= 0.4f) {
4008 				continue;
4009 			}
4010 
4011 			PARTICLE_DEF * pd = createParticle();
4012 			if(!pd) {
4013 				break;
4014 			}
4015 
4016 			pd->ov = io->obj->vertexlist3[io->obj->facelist[num].vid[0]].v;
4017 			pd->move = Vec3f(2.f - 4.f * rnd(), 2.f - 22.f * rnd(), 2.f - 4.f * rnd());
4018 			pd->siz = 7.f;
4019 			pd->tolive = Random::get(500, 1500);
4020 			pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
4021 			pd->tc = fire2;
4022 			pd->fparam = 0.1f - rnd() * 0.2f;
4023 			pd->scale = Vec3f::repeat(-8.f);
4024 			pd->rgb = Color3f(0.71f, 0.43f, 0.29f);
4025 		}
4026 
4027 	} else if(io->obj && io->obj->fastaccess.fire >= 0 && io->ignition > 0.f) {
4028 
4029 		io->ignition = 25.f;
4030 		io->durability -= FrameDiff * ( 1.0f / 10000 );
4031 
4032 		if (io->durability <= 0.F)
4033 		{
4034 			if (ValidDynLight(io->ignit_light))
4035 				DynLight[io->ignit_light].exist = 0;
4036 
4037 			io->ignit_light = -1;
4038 
4039 			if (io->ignit_sound != audio::INVALID_ID)
4040 			{
4041 				ARX_SOUND_Stop(io->ignit_sound);
4042 				io->ignit_sound = audio::INVALID_ID;
4043 			}
4044 
4045 			// Need To Kill timers
4046 			ARX_SCRIPT_Timer_Clear_By_IO(io);
4047 			io->show = SHOW_FLAG_KILLED;
4048 			io->gameFlags &= ~GFLAG_ISINTREATZONE;
4049 			RemoveFromAllInventories(io);
4050 			ARX_INTERACTIVE_DestroyDynamicInfo(io);
4051 			ARX_SOUND_PlaySFX(SND_TORCH_END, &io->pos);
4052 
4053 			if (io == DRAGINTER)
4054 				Set_DragInter(NULL);
4055 
4056 			ARX_INTERACTIVE_DestroyIO(io);
4057 			return;
4058 		}
4059 
4060 		Vec3f pos = io->obj->vertexlist3[io->obj->fastaccess.fire].v;
4061 
4062 		for(long nn = 0; nn < 2; nn++) {
4063 
4064 			if(rnd() >= 0.4f) {
4065 				continue;
4066 			}
4067 
4068 			PARTICLE_DEF * pd = createParticle();
4069 			if(!pd) {
4070 				break;
4071 			}
4072 
4073 			pd->ov = pos;
4074 			pd->move = Vec3f(2.f - 4.f * rnd(), 2.f - 22.f * rnd(), 2.f - 4.f * rnd());
4075 			pd->siz = 7.f;
4076 			pd->tolive = Random::get(500, 1500);
4077 			pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
4078 			pd->tc = fire2;
4079 			pd->fparam = 0.1f - rnd() * 0.2f;
4080 			pd->scale = Vec3f::repeat(-8.f);
4081 			pd->rgb = Color3f(0.71f, 0.43f, 0.29f);
4082 			pd->delay = nn * 2;
4083 		}
4084 
4085 	} else {
4086 
4087 		io->ignition -= framedelay * 0.01f;
4088 		if(!io->obj) {
4089 			return;
4090 		}
4091 
4092 		float p = io->ignition * framedelay * 0.001f * io->obj->facelist.size() * 0.001f;
4093 		p = std::min(p, 5.f);
4094 		while(p > 0.f) {
4095 			p -= 0.5f;
4096 
4097 			if(io->obj->facelist.empty()) {
4098 				break;
4099 			}
4100 
4101 			long notok = 10;
4102 			size_t num = 0;
4103 			while(notok-- > 0) {
4104 				num = Random::get(0, io->obj->facelist.size() - 1);
4105 				if(io->obj->facelist[num].facetype & POLY_HIDE) {
4106 					continue;
4107 				}
4108 				notok = -1;
4109 			}
4110 
4111 			if(notok >= 0) {
4112 				continue;
4113 			}
4114 
4115 			for(long nn = 0 ; nn < 6 ; nn++) {
4116 
4117 				if(rnd() >= 0.4f) {
4118 					continue;
4119 				}
4120 
4121 				PARTICLE_DEF * pd = createParticle();
4122 				if(!pd) {
4123 					break;
4124 				}
4125 
4126 				pd->ov = io->obj->vertexlist3[io->obj->facelist[num].vid[0]].v;
4127 				pd->move = Vec3f(2.f - 4.f * rnd(), 2.f - 22.f * rnd(), 2.f - 4.f * rnd());
4128 				pd->siz = 7.f;
4129 				pd->tolive = Random::get(500, 1500);
4130 				pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
4131 				pd->tc = fire2;
4132 				pd->fparam = 0.1f - rnd() * 0.2f;
4133 				pd->scale = Vec3f::repeat(-8.f);
4134 				pd->rgb = Color3f(0.71f, 0.43f, 0.29f);
4135 				pd->delay = nn * 180;
4136 			}
4137 
4138 		}
4139 
4140 	}
4141 
4142 	ManageIgnition_2(io);
4143 }
4144 
ManageIgnition_2(Entity * io)4145 void ManageIgnition_2(Entity * io) {
4146 
4147 	if (!io) return;
4148 
4149 	if (io->ignition > 0.f)
4150 	{
4151 		if (io->ignition > 100.f)
4152 			io->ignition = 100.f;
4153 
4154 		Vec3f position;
4155 
4156 		if (io->obj && (io->obj->fastaccess.fire >= 0))
4157 		{
4158 			if (io == DRAGINTER)
4159 				position = player.pos;
4160 			else
4161 			{
4162 				position = io->obj->vertexlist3[io->obj->fastaccess.fire].v;
4163 			}
4164 		}
4165 		else
4166 		{
4167 			position = io->pos;
4168 		}
4169 
4170 		if (io->ignit_light == -1)
4171 			io->ignit_light = GetFreeDynLight();
4172 
4173 		if (io->ignit_light != -1)
4174 		{
4175 			long id = io->ignit_light;
4176 			DynLight[id].exist = 1;
4177 
4178 			DynLight[id].intensity = max(io->ignition * ( 1.0f / 10 ), 1.f);
4179 			DynLight[id].fallstart = max(io->ignition * 10.f, 100.f);
4180 			DynLight[id].fallend   = max(io->ignition * 25.f, 240.f);
4181 			float v = max((io->ignition * ( 1.0f / 10 )), 0.5f);
4182 			v = min(v, 1.f);
4183 			DynLight[id].rgb.r = (1.f - rnd() * 0.2f) * v;
4184 			DynLight[id].rgb.g = (0.8f - rnd() * 0.2f) * v;
4185 			DynLight[id].rgb.b = (0.6f - rnd() * 0.2f) * v;
4186 			DynLight[id].pos.x = position.x;
4187 			DynLight[id].pos.y = position.y - 30.f;
4188 			DynLight[id].pos.z = position.z;
4189 			DynLight[id].ex_flaresize = 40.f; //16.f;
4190 			DynLight[id].extras |= EXTRAS_FLARE;
4191 		}
4192 
4193 		if (io->ignit_sound == audio::INVALID_ID)
4194 		{
4195 			io->ignit_sound = SND_FIREPLACE;
4196 			ARX_SOUND_PlaySFX(io->ignit_sound, &position, 0.95F + 0.1F * rnd(), ARX_SOUND_PLAY_LOOPED);
4197 		}
4198 		else ARX_SOUND_RefreshPosition(io->ignit_sound, &position);
4199 
4200 		if (rnd() > 0.9f) CheckForIgnition(&position, io->ignition, 1);
4201 	}
4202 	else
4203 	{
4204 		if (ValidDynLight(io->ignit_light))
4205 			DynLight[io->ignit_light].exist = 0;
4206 
4207 		io->ignit_light = -1;
4208 
4209 		if (io->ignit_sound != audio::INVALID_ID)
4210 		{
4211 			ARX_SOUND_Stop(io->ignit_sound);
4212 			io->ignit_sound = audio::INVALID_ID;
4213 		}
4214 	}
4215 }
4216 
4217 
4218 extern EERIE_BACKGROUND * ACTIVEBKG;
4219 
GetTargetPos(Entity * io,unsigned long smoothing)4220 void GetTargetPos(Entity * io, unsigned long smoothing) {
4221 
4222 	if(!io) {
4223 		return;
4224 	}
4225 
4226 	if(io->ioflags & IO_NPC) {
4227 
4228 		if(io->_npcdata->behavior & BEHAVIOUR_NONE) {
4229 			io->target = io->pos;
4230 			return;
4231 		}
4232 
4233 		if(io->_npcdata->behavior & BEHAVIOUR_GO_HOME) {
4234 			if(io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb) {
4235 				long pos = io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos];
4236 				io->target = ACTIVEBKG->anchors[pos].pos;
4237 			} else {
4238 				io->target = io->initpos;
4239 			}
4240 			return;
4241 		}
4242 
4243 		if(io->_npcdata && io->_npcdata->pathfind.listnb != -1 && io->_npcdata->pathfind.list
4244 		   && !(io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)) { // Targeting Anchors !
4245 			if(io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb) {
4246 				long pos = io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos];
4247 				io->target = ACTIVEBKG->anchors[pos].pos;
4248 			} else if(ValidIONum(io->_npcdata->pathfind.truetarget)) {
4249 				io->target = entities[io->_npcdata->pathfind.truetarget]->pos;
4250 			}
4251 			return;
4252 		}
4253 	}
4254 
4255 	if(io->targetinfo == TARGET_PATH) {
4256 
4257 		if(!io->usepath) {
4258 			io->target = io->pos;
4259 			return;
4260 		}
4261 
4262 		ARX_USE_PATH * aup = io->usepath;
4263 		aup->_curtime += smoothing + 100;
4264 		Vec3f tp;
4265 		long wp = ARX_PATHS_Interpolate(aup, &tp);
4266 		if(wp >= 0) {
4267 			io->target = tp;
4268 		} else if(io->ioflags & IO_CAMERA) {
4269 			io->_camdata->cam.lastinfovalid = false;
4270 		}
4271 
4272 		return;
4273 	}
4274 
4275 	if(io->targetinfo == TARGET_NONE) {
4276 		io->target = io->pos;
4277 		return;
4278 	}
4279 
4280 	if(io->targetinfo == TARGET_PLAYER || io->targetinfo == -1) {
4281 		io->target = player.pos + Vec3f(0.f, player.size.y, 0.f);
4282 		return;
4283 	} else if(ValidIONum(io->targetinfo)) {
4284 		Vec3f pos;
4285 		if(GetItemWorldPosition(entities[io->targetinfo], &pos)) {
4286 			io->target = pos;
4287 			return;
4288 		}
4289 		io->target = entities[io->targetinfo]->pos;
4290 		return;
4291 	}
4292 
4293 	io->target = io->pos;
4294 }
4295