1 /*
2  * Copyright 2011-2012 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 "physics/Collisions.h"
48 
49 #include "core/GameTime.h"
50 #include "core/Core.h"
51 #include "game/Damage.h"
52 #include "game/EntityManager.h"
53 #include "game/NPC.h"
54 #include "game/Player.h"
55 #include "graphics/Math.h"
56 #include "physics/Anchors.h"
57 #include "scene/Interactive.h"
58 
59 using std::min;
60 using std::max;
61 using std::vector;
62 
63 //-----------------------------------------------------------------------------
64 extern float FrameDiff;
65 long ON_PLATFORM=0;
66 //-----------------------------------------------------------------------------
67 size_t MAX_IN_SPHERE_Pos = 0;
68 short EVERYTHING_IN_SPHERE[MAX_IN_SPHERE + 1];
69 size_t EXCEPTIONS_LIST_Pos = 0;
70 short EXCEPTIONS_LIST[MAX_IN_SPHERE + 1];
71 
72 static long POLYIN = 0;
73 long COLLIDED_CLIMB_POLY=0;
74 long MOVING_CYLINDER=0;
75 
76 Vec3f vector2D;
77 bool DIRECT_PATH=true;
78 
79 //-----------------------------------------------------------------------------
80 // Added immediate return (return anything;)
IsPolyInCylinder(EERIEPOLY * ep,EERIE_CYLINDER * cyl,long flag)81 inline float IsPolyInCylinder(EERIEPOLY *ep, EERIE_CYLINDER * cyl,long flag)
82 {
83 	long flags=flag;
84 	POLYIN=0;
85 	float minf = cyl->origin.y + cyl->height;
86 	float maxf = cyl->origin.y;
87 
88 	if (minf>ep->max.y) return 999999.f;
89 
90 	if (maxf<ep->min.y) return 999999.f;
91 
92 	long to;
93 
94 	if (ep->type & POLY_QUAD) to=4;
95 	else to=3;
96 
97 	float nearest = 99999999.f;
98 
99 	for (long num=0;num<to;num++)
100 	{
101 		float dd = fdist(Vec2f(ep->v[num].p.x, ep->v[num].p.z), Vec2f(cyl->origin.x, cyl->origin.z));
102 
103 		if (dd<nearest)
104 		{
105 			nearest=dd;
106 		}
107 	}
108 
109 	if (nearest > max(82.f,cyl->radius)) return 999999.f;
110 
111 	if (	(cyl->radius<30.f)
112 		||	(cyl->height>-80.f)
113 		||	(ep->area>5000.f)	)
114 		flags|=CFLAG_EXTRA_PRECISION;
115 
116 	if (!(flags & CFLAG_EXTRA_PRECISION))
117 	{
118 		if (ep->area<100.f) return 999999.f;
119 	}
120 
121 	float anything=999999.f;
122 
123 	if (PointInCylinder(cyl, &ep->center))
124 	{
125 		POLYIN = 1;
126 
127 		if (ep->norm.y<0.5f)
128 			anything=min(anything,ep->min.y);
129 		else
130 			anything=min(anything,ep->center.y);
131 
132 		if (!(flags & CFLAG_EXTRA_PRECISION)) return anything;
133 	}
134 
135 
136 	long r=to-1;
137 
138 	Vec3f center;
139 	long n;
140 
141 	for (n=0;n<to;n++)
142 	{
143 		if (flags & CFLAG_EXTRA_PRECISION)
144 		{
145 			for (long o=0;o<5;o++)
146 			{
147 				float p=(float)o*( 1.0f / 5 );
148 				center = ep->v[n].p * p + ep->center * (1.f-p);
149 				if(PointInCylinder(cyl, &center)) {
150 					anything=min(anything,center.y);
151 					POLYIN=1;
152 					if (!(flags & CFLAG_EXTRA_PRECISION)) return anything;
153 				}
154 			}
155 		}
156 
157 		if ((ep->area>2000.f)
158 		        || (flags & CFLAG_EXTRA_PRECISION)  )
159 		{
160 			center = (ep->v[n].p + ep->v[r].p) * 0.5f;
161 			if(PointInCylinder(cyl, &center)) {
162 				anything=min(anything,center.y);
163 				POLYIN=1;
164 				if (!(flags & CFLAG_EXTRA_PRECISION)) return anything;
165 			}
166 
167 			if ((ep->area>4000.f) || (flags & CFLAG_EXTRA_PRECISION)) {
168 				center = (ep->v[n].p + ep->center) * 0.5f;
169 				if (PointInCylinder(cyl, &center))
170 				{
171 					anything=min(anything,center.y);
172 					POLYIN=1;
173 
174 					if (!(flags & CFLAG_EXTRA_PRECISION)) return anything;
175 				}
176 			}
177 
178 			if ((ep->area>6000.f) || (flags & CFLAG_EXTRA_PRECISION)) {
179 				center = (center + ep->v[n].p) * 0.5f;
180 				if(PointInCylinder(cyl, &center))
181 				{
182 					anything=min(anything,center.y);
183 					POLYIN=1;
184 
185 					if (!(flags & CFLAG_EXTRA_PRECISION)) return anything;
186 				}
187 			}
188 		}
189 
190 		if(PointInCylinder(cyl, &ep->v[n].p)) {
191 
192 			anything=min(anything,ep->v[n].p.y);
193 			POLYIN = 1;
194 
195 			if (!(flags & CFLAG_EXTRA_PRECISION)) return anything;
196 		}
197 
198 		r++;
199 
200 		if (r>=to) r=0;
201 
202 	}
203 
204 
205 //	To Add "more" precision
206 
207 /*if (flags & CFLAG_EXTRA_PRECISION)
208 {
209 
210 	for (long j=0;j<360;j+=90)
211 	{
212 		float xx=-EEsin(radians((float)j))*cyl->radius;
213 		float yy=EEcos(radians((float)j))*cyl->radius;
214 		EERIE_3D pos;
215 		pos.x=cyl->origin.x+xx;
216 
217 		pos.z=cyl->origin.z+yy;
218 		//EERIEPOLY * epp;
219 
220 		if (PointIn2DPolyXZ(ep, pos.x, pos.z))
221 		{
222 			if (GetTruePolyY(ep,&pos,&xx))
223 			{
224 				anything=min(anything,xx);
225 				return anything;
226 			}
227 		}
228 	}
229 //}*/
230 	if ((anything!=999999.f) && (ep->norm.y<0.1f) && (ep->norm.y>-0.1f))
231 		anything=min(anything,ep->min.y);
232 
233 	return anything;
234 }
235 
236 //-----------------------------------------------------------------------------
IsPolyInSphere(EERIEPOLY * ep,EERIE_SPHERE * sph)237 inline bool IsPolyInSphere(EERIEPOLY *ep, EERIE_SPHERE * sph)
238 {
239 	if ((!ep) || (!sph)) return false;
240 
241 	if (ep->area<100.f) return false;
242 
243 	long to;
244 
245 	if (ep->type & POLY_QUAD) to=4;
246 	else to=3;
247 
248 	long r=to-1;
249 	Vec3f center;
250 
251 	for (long n=0;n<to;n++)
252 	{
253 
254 		if(ep->area > 2000.f) {
255 			center = (ep->v[n].p + ep->v[r].p) * 0.5f;
256 			if(sph->contains(center)) {
257 				return true;
258 			}
259 			if(ep->area > 4000.f) {
260 				center = (ep->v[n].p + ep->center) * 0.5f;
261 				if(sph->contains(center)) {
262 					return true;
263 				}
264 			}
265 			if(ep->area > 6000.f) {
266 				center = (center + ep->v[n].p) * 0.5f;
267 				if(sph->contains(center)) {
268 					return true;
269 				}
270 			}
271 		}
272 
273 		Vec3f v(ep->v[n].p.x, ep->v[n].p.y, ep->v[n].p.z);
274 
275 		if(sph->contains(v)) {
276 			return true;
277 		}
278 
279 		r++;
280 
281 		if (r>=to) r=0;
282 	}
283 
284 	return false;
285 }
286 
287 //-----------------------------------------------------------------------------
IsCollidingIO(Entity * io,Entity * ioo)288 bool IsCollidingIO(Entity * io,Entity * ioo)
289 {
290 	if (   (ioo!=NULL)
291 		&& (io!=ioo)
292 		&& !(ioo->ioflags & IO_NO_COLLISIONS)
293 		&& (ioo->show==SHOW_FLAG_IN_SCENE)
294 		&& (ioo->obj)
295 		)
296 	{
297 		if (ioo->ioflags & IO_NPC)
298 		{
299 			float old=ioo->physics.cyl.radius;
300 			ioo->physics.cyl.radius+=25.f;
301 
302 			for (size_t j=0;j<io->obj->vertexlist3.size();j++)
303 			{
304 				if (PointInCylinder(&ioo->physics.cyl,&io->obj->vertexlist3[j].v))
305 				{
306 					ioo->physics.cyl.radius=old;
307 					return true;
308 				}
309 			}
310 
311 			ioo->physics.cyl.radius=old;
312 		}
313 	}
314 
315 	return false;
316 }
317 
318 // TODO include header?
319 extern void GetIOCyl(Entity * io,EERIE_CYLINDER * cyl);
PushIO_ON_Top(Entity * ioo,float ydec)320 void PushIO_ON_Top(Entity * ioo, float ydec) {
321 
322 	if(ydec != 0.f)
323 	for(size_t i = 0; i < entities.size(); i++) {
324 		Entity * io = entities[i];
325 
326 		if (   (io)
327 			&& (io!=ioo)
328 			&& !(io->ioflags & IO_NO_COLLISIONS)
329 			&& (io->show==SHOW_FLAG_IN_SCENE)
330 			&& (io->obj)
331 			&& (!(io->ioflags & (IO_FIX | IO_CAMERA | IO_MARKER)))
332 			)
333 		{
334 			if(closerThan(Vec2f(io->pos.x, io->pos.z), Vec2f(ioo->pos.x, ioo->pos.z), 450.f)) {
335 				EERIEPOLY ep;
336 				ep.type=0;
337 				float miny=9999999.f;
338 				float maxy=-9999999.f;
339 
340 				for (size_t ii=0;ii<ioo->obj->vertexlist3.size();ii++)
341 				{
342 					miny=min(miny,ioo->obj->vertexlist3[ii].v.y);
343 					maxy=max(maxy,ioo->obj->vertexlist3[ii].v.y);
344 				}
345 
346 				float posy = (io == entities.player()) ? player.basePosition().y : io->pos.y;
347 				float modd = (ydec > 0) ? -20.f : 0;
348 
349 				if ((posy<=maxy) && (posy>=miny+modd))
350 				{
351 					for (size_t ii=0;ii<ioo->obj->facelist.size();ii++)
352 					{
353 						float cx=0;
354 						float cz=0;
355 
356 						for (long kk=0;kk<3;kk++)
357 						{
358 							cx+=ep.v[kk].p.x=ioo->obj->vertexlist3[ioo->obj->facelist[ii].vid[kk]].v.x;
359 							ep.v[kk].p.y=ioo->obj->vertexlist3[ioo->obj->facelist[ii].vid[kk]].v.y;
360 							cz+=ep.v[kk].p.z=ioo->obj->vertexlist3[ioo->obj->facelist[ii].vid[kk]].v.z;
361 						}
362 
363 						cx*=( 1.0f / 3 );
364 						cz*=( 1.0f / 3 );
365 
366 						float tval=1.1f;
367 
368 						for (int kk=0;kk<3;kk++)
369 						{
370 								ep.v[kk].p.x = (ep.v[kk].p.x - cx) * tval + cx;
371 								ep.v[kk].p.z = (ep.v[kk].p.z - cz) * tval + cz;
372 						}
373 
374 						if(PointIn2DPolyXZ(&ep, io->pos.x, io->pos.z)) {
375 
376 							if(io == entities.player()) {
377 
378 								if(ydec <= 0) {
379 									player.pos.y += ydec;
380 									moveto.y += ydec;
381 									EERIE_CYLINDER cyl = player.baseCylinder();
382 									cyl.origin.y += ydec;
383 									float vv = CheckAnythingInCylinder(&cyl, entities.player(), 0);
384 									if(vv < 0) {
385 										player.pos.y += ydec + vv;
386 									}
387 								} else {
388 									EERIE_CYLINDER cyl = player.baseCylinder();
389 									cyl.origin.y += ydec;
390 									if(CheckAnythingInCylinder(&cyl, entities.player(), 0) >= 0) {
391 										player.pos.y += ydec;
392 										moveto.y += ydec;
393 									}
394 								}
395 
396 							} else {
397 
398 								if(ydec <= 0) {
399 									io->pos.y += ydec;
400 								} else {
401 									EERIE_CYLINDER cyl;
402 									GetIOCyl(io, &cyl);
403 									cyl.origin.y += ydec;
404 									if(CheckAnythingInCylinder(&cyl, io ,0) >= 0) {
405 										io->pos.y += ydec;
406 									}
407 								}
408 
409 							}
410 							break;
411 						}
412 					}
413 				}
414 			}
415 		}
416 	}
417 }
418 
IsAnyNPCInPlatform(Entity * pfrm)419 bool IsAnyNPCInPlatform(Entity * pfrm) {
420 
421 	for(size_t i = 0; i < entities.size(); i++) {
422 		Entity * io = entities[i];
423 
424 		if (	(io)
425 			&&	(io!=pfrm)
426 			&&	(io->ioflags & IO_NPC)
427 			&&	!(io->ioflags & IO_NO_COLLISIONS)
428 			&&	(io->show==SHOW_FLAG_IN_SCENE)
429 			)
430 		{
431 			EERIE_CYLINDER cyl;
432 			GetIOCyl(io,&cyl);
433 
434 			if (CylinderPlatformCollide(&cyl,pfrm)!=0.f) return true;
435 		}
436 	}
437 
438 	return false;
439 }
440 
CylinderPlatformCollide(EERIE_CYLINDER * cyl,Entity * io)441 float CylinderPlatformCollide(EERIE_CYLINDER * cyl, Entity * io) {
442 
443 	float miny = io->bbox3D.min.y;
444 	float maxy = io->bbox3D.max.y;
445 
446 	if(maxy <= cyl->origin.y + cyl->height || miny >= cyl->origin.y) {
447 		return 0.f;
448 	}
449 
450 	if(In3DBBoxTolerance(&cyl->origin,&io->bbox3D,cyl->radius)) {
451 		return 1.f;
452 	}
453 
454 	return 0.f;
455 }
456 
457 static long NPC_IN_CYLINDER = 0;
458 
459 extern void GetIOCyl(Entity * io,EERIE_CYLINDER * cyl);
460 
EE_RotateY(TexturedVertex * in,TexturedVertex * out,float c,float s)461 inline void EE_RotateY(TexturedVertex *in,TexturedVertex *out,float c, float s)
462 {
463 	out->p.x = (in->p.x*c) + (in->p.z*s);
464 	out->p.y = in->p.y;
465 	out->p.z = (in->p.z*c) - (in->p.x*s);
466 }
467 
CollidedFromBack(Entity * io,Entity * ioo)468 bool CollidedFromBack(Entity * io,Entity * ioo)
469 {
470 	// io was collided from back ?
471 	EERIEPOLY ep;
472 	ep.type=0;
473 
474 	if (	(io )
475 		&&	(ioo)
476 		&&	(io->ioflags & IO_NPC)
477 		&&	(ioo->ioflags & IO_NPC)	)
478 	{
479 
480 
481 	ep.v[0].p.x=io->pos.x;
482 	ep.v[0].p.z=io->pos.z;
483 	float ft=radians(135.f+90.f);
484 		ep.v[1].p.x = EEsin(ft) * 180.f;
485 		ep.v[1].p.z = -EEcos(ft) * 180.f;
486 	ft=radians(225.f+90.f);
487 		ep.v[2].p.x = EEsin(ft) * 180.f;
488 		ep.v[2].p.z = -EEcos(ft) * 180.f;
489 	ft=radians(270.f-io->angle.b);
490 	float ec=EEcos(ft);
491 	float es=EEsin(ft);
492 	EE_RotateY( &ep.v[1]  , &ep.tv[1]   , ec , es );
493 	EE_RotateY( &ep.v[2]  , &ep.tv[2]   , ec , es );
494 	ep.v[1].p.x=ep.tv[1].p.x+ep.v[0].p.x;
495 	ep.v[1].p.z=ep.tv[1].p.z+ep.v[0].p.z;
496 	ep.v[2].p.x=ep.tv[2].p.x+ep.v[0].p.x;
497 	ep.v[2].p.z=ep.tv[2].p.z+ep.v[0].p.z;
498 
499 	// To keep if we need some visual debug
500 	if (PointIn2DPolyXZ(&ep,ioo->pos.x,ioo->pos.z))
501 		return true;
502 	}
503 
504 	return false;
505 }
506 
507 // Returns 0 if nothing in cyl
508 // Else returns Y Offset to put cylinder in a proper place
CheckAnythingInCylinder(EERIE_CYLINDER * cyl,Entity * ioo,long flags)509 float CheckAnythingInCylinder(EERIE_CYLINDER * cyl,Entity * ioo,long flags) {
510 
511 	NPC_IN_CYLINDER = 0;
512 
513 	long rad = (cyl->radius + 100) * ACTIVEBKG->Xmul;
514 	long px,pz;
515 	px = cyl->origin.x*ACTIVEBKG->Xmul;
516 
517 	if (px>ACTIVEBKG->Xsize-2-rad)
518 		return 0.f;
519 
520 	if (px< 1+rad)
521 		return 0.f;
522 
523 	pz = cyl->origin.z*ACTIVEBKG->Zmul;
524 
525 	if (pz>ACTIVEBKG->Zsize-2-rad)
526 		return 0.f;
527 
528 	if (pz< 1+rad)
529 		return 0.f;
530 
531 	float anything = 999999.f;
532 
533 	EERIEPOLY * ep;
534 	FAST_BKG_DATA * feg;
535 
536 	for (long j=pz-rad;j<=pz+rad;j++)
537 	for (long i=px-rad;i<=px+rad;i++)
538 	{
539 		float nearx,nearz;
540 		float nearest=99999999.f;
541 
542 		for (long num=0;num<4;num++)
543 		{
544 
545 				nearx = static_cast<float>(i * 100);
546 				nearz = static_cast<float>(j * 100);
547 
548 			if ((num==1) || (num==2))
549 					nearx += 100;
550 
551 			if ((num==2) || (num==3))
552 					nearz += 100;
553 
554 			float dd = fdist(Vec2f(nearx, nearz), Vec2f(cyl->origin.x, cyl->origin.z));
555 
556 			if (dd<nearest)
557 			{
558 				nearest=dd;
559 			}
560 		}
561 
562 		if (nearest>max(82.f,cyl->radius)) continue;
563 
564 
565 		feg=&ACTIVEBKG->fastdata[i][j];
566 		for (long k=0;k<feg->nbpoly;k++)
567 		{
568 			ep=&feg->polydata[k];
569 
570 			if (ep->type & (POLY_WATER | POLY_TRANS | POLY_NOCOL) ) continue;
571 
572 			if (ep->min.y<anything)
573 			{
574 
575 				anything= min(anything,IsPolyInCylinder(ep,cyl,flags));
576 
577 				if (POLYIN)
578 				{
579 					if (ep->type & POLY_CLIMB)
580 						COLLIDED_CLIMB_POLY=1;
581 				}
582 			}
583 		}
584 	}
585 
586 	float tempo;
587 
588 	ep=CheckInPolyPrecis(cyl->origin.x,cyl->origin.y+cyl->height,cyl->origin.z,&tempo);
589 
590 	if (ep)
591 		{
592 			anything=min(anything,tempo);
593 	}
594 
595 	if (!(flags & CFLAG_NO_INTERCOL))
596 	{
597 		Entity * io;
598 		long FULL_TEST=0;
599 		long AMOUNT=TREATZONE_CUR;
600 
601 		if (	ioo
602 			&&	(ioo->ioflags & IO_NPC)
603 			&&	(ioo->_npcdata->pathfind.flags & PATHFIND_ALWAYS))
604 		{
605 			FULL_TEST=1;
606 			AMOUNT=entities.size();
607 		}
608 
609 		for (long i=0;i<AMOUNT;i++)
610 		{
611 			if(FULL_TEST) {
612 				io=entities[i];
613 			} else {
614 				io=treatio[i].io;
615 			}
616 
617 			if (	!io
618 				||	(io==ioo)
619 				||	(!io->obj)
620 				||	(	(io->show!=SHOW_FLAG_IN_SCENE)
621 					||	((io->ioflags & IO_NO_COLLISIONS)  && !(flags & CFLAG_COLLIDE_NOCOL))
622 					)
623 				||	distSqr(io->pos, cyl->origin) > square(1000.f)) continue;
624 
625 			{
626 				EERIE_CYLINDER * io_cyl=&io->physics.cyl;
627 				GetIOCyl(io,io_cyl);
628 				float dealt = 0;
629 
630 				if (	(io->gameFlags & GFLAG_PLATFORM)
631 					||	((flags & CFLAG_COLLIDE_NOCOL) && (io->ioflags & IO_NPC) &&  (io->ioflags & IO_NO_COLLISIONS))
632 					)
633 				{
634 					if(closerThan(Vec2f(io->pos.x, io->pos.z), Vec2f(cyl->origin.x, cyl->origin.z), 440.f + cyl->radius))
635 					if (In3DBBoxTolerance(&cyl->origin,&io->bbox3D, cyl->radius+80))
636 					{
637 						if (io->ioflags & IO_FIELD)
638 						{
639 							if (In3DBBoxTolerance(&cyl->origin,&io->bbox3D, cyl->radius+10))
640 								anything=-99999.f;
641 						}
642 							else
643 						{
644 						for (size_t ii=0;ii<io->obj->vertexlist3.size();ii++)
645 						{
646 							long res=PointInUnderCylinder(cyl,&io->obj->vertexlist3[ii].v);
647 
648 							if (res>0)
649 							{
650 								if (res==2) ON_PLATFORM=1;
651 
652 										anything = min(anything, io->obj->vertexlist3[ii].v.y - 10.f);
653 							}
654 						}
655 
656 						for (size_t ii=0;ii<io->obj->facelist.size();ii++)
657 						{
658 							Vec3f c = Vec3f::ZERO;
659 							float height=io->obj->vertexlist3[io->obj->facelist[ii].vid[0]].v.y;
660 
661 							for (long kk=0;kk<3;kk++)
662 							{
663 								c.x+=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.x;
664 								c.y+=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.y;
665 								height=min(height,io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.y);
666 								c.z+=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.z;
667 							}
668 
669 							c.x*=( 1.0f / 3 );
670 							c.z*=( 1.0f / 3 );
671 									c.y = io->bbox3D.min.y;
672 							long res=PointInUnderCylinder(cyl,&c);
673 
674 							if (res>0)
675 							{
676 								if (res==2) ON_PLATFORM=1;
677 										anything = min(anything, height);
678 									}
679 									}
680 								}
681 							}
682 				}
683 				else if (	(io->ioflags & IO_NPC)
684 						&&	(!(flags & CFLAG_NO_NPC_COLLIDE)) // MUST be checked here only (not before...)
685 						&&	(!(ioo && (ioo->ioflags & IO_NO_COLLISIONS)))
686 						&&	(io->_npcdata->life>0.f)
687 						)
688 				{
689 
690 					if(CylinderInCylinder(cyl, io_cyl)) {
691  						NPC_IN_CYLINDER = 1;
692 						anything = min(anything, io_cyl->origin.y + io_cyl->height);
693 
694 						if (!(flags & CFLAG_JUST_TEST) && ioo)
695 						{
696 							if (float(arxtime) > io->collide_door_time + 500)
697 							{
698 								EVENT_SENDER=ioo;
699 								io->collide_door_time = (unsigned long)(arxtime);
700 
701 								if (CollidedFromBack(io,ioo))
702 									SendIOScriptEvent(io,SM_COLLIDE_NPC,"back");
703 								else
704 									SendIOScriptEvent(io,SM_COLLIDE_NPC);
705 
706 								EVENT_SENDER=io;
707 								io->collide_door_time = (unsigned long)(arxtime);
708 
709 								if (CollidedFromBack(ioo,io))
710 									SendIOScriptEvent(ioo,SM_COLLIDE_NPC,"back");
711 								else
712 									SendIOScriptEvent(ioo,SM_COLLIDE_NPC);
713 							}
714 
715 							if ((!dealt) && ((ioo->damager_damages>0) || (io->damager_damages>0)))
716 							{
717 
718 								if (ioo->damager_damages>0)
719 									ARX_DAMAGES_DealDamages(i,ioo->damager_damages,ioo->index(),ioo->damager_type,&io->pos);
720 
721 								if (io->damager_damages>0)
722 									ARX_DAMAGES_DealDamages(ioo->index(),io->damager_damages,io->index(),io->damager_type,&ioo->pos);
723 							}
724 
725 							if(io->targetinfo == i) {
726 								if(io->_npcdata->pathfind.listnb > 0) {
727 									io->_npcdata->pathfind.listpos = 0;
728 									io->_npcdata->pathfind.listnb = -1;
729 									free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
730 									SendIOScriptEvent(io, SM_NULL, "", "pathfinder_end");
731 								}
732 								if(!io->_npcdata->reachedtarget) {
733 									EVENT_SENDER = ioo;
734 									SendIOScriptEvent(io, SM_REACHEDTARGET);
735 									io->_npcdata->reachedtarget = 1;
736 								}
737 							}
738 						}
739 					}
740 				}
741 				else if (io->ioflags & IO_FIX)
742 				{
743 					EERIE_SPHERE sp;
744 
745 					float miny = io->bbox3D.min.y;
746 					float maxy = io->bbox3D.max.y;
747 
748 					if (maxy<= cyl->origin.y+cyl->height) goto suivant;
749 
750 					if (miny>= cyl->origin.y) goto suivant;
751 
752 					if (In3DBBoxTolerance(&cyl->origin,&io->bbox3D,cyl->radius+30.f))
753 					{
754 						vector<EERIE_VERTEX> & vlist = io->obj->vertexlist3;
755 						size_t nbv = io->obj->vertexlist.size();
756 
757 						if (io->obj->nbgroups>10)
758 						{
759 							for (long ii=0;ii<io->obj->nbgroups;ii++)
760 							{
761 								long idx = io->obj->grouplist[ii].origin;
762 								sp.origin = vlist[idx].v;
763 
764 								if (ioo==entities.player())
765 								{
766 									sp.radius = 22.f;
767 								}
768 								else if (ioo->ioflags & IO_NPC)
769 									sp.radius = 26.f;
770 								else
771 									sp.radius = 22.f;
772 
773 								if (SphereInCylinder(cyl,&sp))
774 								{
775 									if (!(flags & CFLAG_JUST_TEST) && ioo)
776 									{
777 										if (io->gameFlags&GFLAG_DOOR)
778 										{
779 
780 											if (float(arxtime) > io->collide_door_time+500)
781 											{
782 												EVENT_SENDER=ioo;
783 												io->collide_door_time = (unsigned long)(arxtime);
784 												SendIOScriptEvent(io,SM_COLLIDE_DOOR);
785 												EVENT_SENDER=io;
786 												io->collide_door_time = (unsigned long)(arxtime);
787 												SendIOScriptEvent(ioo,SM_COLLIDE_DOOR);
788 											}
789 										}
790 
791 										if (io->ioflags & IO_FIELD)
792 										{
793 											EVENT_SENDER=NULL;
794 											io->collide_door_time = (unsigned long)(arxtime);
795 											SendIOScriptEvent(ioo,SM_COLLIDE_FIELD);
796 										}
797 
798 										if ((!dealt) && ((ioo->damager_damages>0) || (io->damager_damages>0)))
799 										{
800 											dealt=1;
801 
802 											if (ioo->damager_damages>0)
803 												ARX_DAMAGES_DealDamages(i, ioo->damager_damages, ioo->index(), ioo->damager_type, &io->pos);
804 
805 											if (io->damager_damages>0)
806 												ARX_DAMAGES_DealDamages(ioo->index(), io->damager_damages, io->index(), io->damager_type, &ioo->pos);
807 										}
808 									}
809 
810 									anything=min(anything,min(sp.origin.y-sp.radius , io->bbox3D.min.y));
811 								}
812 							}
813 						}
814 						else
815 						{
816 							long step;
817 
818 							if (ioo==entities.player())
819 								sp.radius = 23.f;
820 							else if (ioo && !(ioo->ioflags & IO_NPC))
821 								sp.radius = 32.f;
822 							else
823 								sp.radius = 25.f;
824 
825 							if (nbv<300)
826 							{
827 								step=1;
828 							}
829 							else if (nbv<600) step=2;
830 							else if (nbv<1200) step=4;
831 							else step=6;
832 
833 							for (size_t ii=1;ii<nbv;ii+=step)
834 							{
835 								if (ii != (size_t)io->obj->origin)
836 								{
837 									sp.origin = vlist[ii].v;
838 
839 									if (SphereInCylinder(cyl,&sp))
840 									{
841 										if (!(flags & CFLAG_JUST_TEST) && ioo)
842 										{
843 											if (io->gameFlags&GFLAG_DOOR)
844 											{
845 												if (float(arxtime) > io->collide_door_time+500)
846 												{
847 													EVENT_SENDER=ioo;
848 													io->collide_door_time = (unsigned long)(arxtime);
849 													SendIOScriptEvent(io,SM_COLLIDE_DOOR);
850 													EVENT_SENDER=io;
851 													io->collide_door_time = (unsigned long)(arxtime);
852 													SendIOScriptEvent(ioo,SM_COLLIDE_DOOR);
853 												}
854 											}
855 
856 										if (io->ioflags & IO_FIELD)
857 										{
858 											EVENT_SENDER=NULL;
859 												io->collide_door_time = (unsigned long)(arxtime);
860 											SendIOScriptEvent(ioo,SM_COLLIDE_FIELD);
861 										}
862 
863 											if ((!dealt) && ioo && ((ioo->damager_damages > 0) || (io->damager_damages > 0)))
864 							{
865 								dealt=1;
866 
867 											if (ioo->damager_damages>0)
868 												ARX_DAMAGES_DealDamages(i, ioo->damager_damages, ioo->index(), ioo->damager_type, &io->pos);
869 
870 												if (io->damager_damages>0)
871 													ARX_DAMAGES_DealDamages(ioo->index(), io->damager_damages, io->index(), io->damager_type, &ioo->pos);
872 											}
873 										}
874 										anything=min(anything,min(sp.origin.y-sp.radius,io->bbox3D.min.y));
875 									}
876 								}
877 							}
878 						}
879 					}
880 				}
881 			}
882 		suivant:
883 			;
884 		}
885 	}
886 
887 	if (anything == 999999.f) return 0.f;
888 
889 	anything=anything-cyl->origin.y;
890 
891 	return anything;
892 }
893 
InExceptionList(long val)894 static bool InExceptionList(long val) {
895 
896 	for(size_t i = 0; i < EXCEPTIONS_LIST_Pos; i++) {
897 		if(val == EXCEPTIONS_LIST[i]) {
898 			return true;
899 		}
900 	}
901 
902 	return false;
903 }
904 
905 //-----------------------------------------------------------------------------
CheckEverythingInSphere(EERIE_SPHERE * sphere,long source,long targ)906 bool CheckEverythingInSphere(EERIE_SPHERE * sphere,long source,long targ) //except source...
907 {
908 	bool vreturn = false;
909 	MAX_IN_SPHERE_Pos=0;
910 
911 	Entity * io;
912 	long ret_idx=-1;
913 
914 	float sr30=sphere->radius+20.f;
915 	float sr40=sphere->radius+30.f;
916 	float sr180=sphere->radius+500.f;
917 
918 	for (long i=0;i<TREATZONE_CUR;i++)
919 	{
920 		if (targ>-1)
921 		{
922 			i=TREATZONE_CUR;
923 			io=entities[targ];
924 
925 			if (   (!io)
926 				|| (InExceptionList(targ))
927 				|| (targ==source)
928 				|| (io->show!=SHOW_FLAG_IN_SCENE)
929 				|| !(io->gameFlags & GFLAG_ISINTREATZONE)
930 				|| !(io->obj)
931 			)
932 			return false;
933 
934 			ret_idx=targ;
935 		}
936 		else
937 		{
938 			if ( (treatio[i].show!=1) ||
939 			 (treatio[i].io==NULL) ||
940 			 (treatio[i].num==source) ||
941 			 (InExceptionList(treatio[i].num)) ) continue;
942 
943 			io=treatio[i].io;
944 			ret_idx=treatio[i].num;
945 		}
946 
947 		if (!(io->ioflags & IO_NPC) && (io->ioflags & IO_NO_COLLISIONS)) continue;
948 
949 		if (!io->obj) continue;
950 
951 
952 				if (io->gameFlags & GFLAG_PLATFORM)
953 				{
954 					float miny,maxy;
955 					miny=io->bbox3D.min.y;
956 					maxy=io->bbox3D.max.y;
957 
958 					if (	(maxy<= sphere->origin.y+sphere->radius)
959 						||	(miny>= sphere->origin.y) )
960 					if (In3DBBoxTolerance(&sphere->origin,&io->bbox3D,sphere->radius))
961 					{
962 						if(closerThan(Vec2f(io->pos.x, io->pos.z), Vec2f(sphere->origin.x, sphere->origin.z), 440.f + sphere->radius)) {
963 
964 							EERIEPOLY ep;
965 							ep.type=0;
966 
967 							for (size_t ii=0;ii<io->obj->facelist.size();ii++)
968 							{
969 								float cx=0;
970 								float cz=0;
971 
972 								for (long kk=0;kk<3;kk++)
973 								{
974 									cx+=ep.v[kk].p.x=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.x;
975 										ep.v[kk].p.y=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.y;
976 									cz+=ep.v[kk].p.z=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.z;
977 								}
978 
979 								cx*=( 1.0f / 3 );
980 								cz*=( 1.0f / 3 );
981 
982 								for (int kk=0;kk<3;kk++)
983 								{
984 									ep.v[kk].p.x=(ep.v[kk].p.x-cx)*3.5f+cx;
985 									ep.v[kk].p.z=(ep.v[kk].p.z-cz)*3.5f+cz;
986 								}
987 
988 								if (PointIn2DPolyXZ(&ep, sphere->origin.x, sphere->origin.z))
989 								{
990 									EVERYTHING_IN_SPHERE[MAX_IN_SPHERE_Pos]=(short)ret_idx;
991 									MAX_IN_SPHERE_Pos++;
992 
993 									if (MAX_IN_SPHERE_Pos>=MAX_IN_SPHERE) MAX_IN_SPHERE_Pos--;
994 
995 									vreturn = true;
996 									goto suivant;
997 								}
998 							}
999 						}
1000 					}
1001 				}
1002 
1003 			if(distSqr(io->pos, sphere->origin) < square(sr180)) {
1004 
1005 				long amount=1;
1006 				vector<EERIE_VERTEX> & vlist = io->obj->vertexlist3;
1007 
1008 				if (io->obj->nbgroups>4)
1009 				{
1010 					for (long ii=0;ii<io->obj->nbgroups;ii++)
1011 					{
1012 						if(distSqr(vlist[io->obj->grouplist[ii].origin].v, sphere->origin) < square(sr40)) {
1013 							EVERYTHING_IN_SPHERE[MAX_IN_SPHERE_Pos]=(short)ret_idx;
1014 							MAX_IN_SPHERE_Pos++;
1015 
1016 							if (MAX_IN_SPHERE_Pos>=MAX_IN_SPHERE) MAX_IN_SPHERE_Pos--;
1017 
1018 							vreturn = true;
1019 							goto suivant;
1020 						}
1021 					}
1022 
1023 					amount=2;
1024 				}
1025 
1026 					for (size_t ii=0;ii<io->obj->facelist.size();ii+=amount)
1027 					{
1028 						EERIE_FACE * ef=&io->obj->facelist[ii];
1029 
1030 						if (ef->facetype & POLY_HIDE) continue;
1031 
1032 						Vec3f fcenter = (vlist[ef->vid[0]].v + vlist[ef->vid[1]].v
1033 						                 + vlist[ef->vid[2]].v) * (1.0f / 3);
1034 						if (distSqr(fcenter, sphere->origin) < square(sr30) ||	distSqr(vlist[ef->vid[0]].v, sphere->origin) < square(sr30) || distSqr(vlist[ef->vid[1]].v, sphere->origin) < square(sr30) || distSqr(vlist[ef->vid[2]].v, sphere->origin) < square(sr30)) {
1035 							EVERYTHING_IN_SPHERE[MAX_IN_SPHERE_Pos]=(short)ret_idx;
1036 							MAX_IN_SPHERE_Pos++;
1037 
1038 							if (MAX_IN_SPHERE_Pos>=MAX_IN_SPHERE) MAX_IN_SPHERE_Pos--;
1039 
1040 							vreturn = true;
1041 							goto suivant;
1042 						}
1043 					}
1044 				}
1045 
1046 	suivant:
1047 	  ;
1048 
1049 	}
1050 
1051 	return vreturn;
1052 }
1053 
1054 //-----------------------------------------------------------------------------
CheckBackgroundInSphere(EERIE_SPHERE * sphere)1055 EERIEPOLY * CheckBackgroundInSphere(EERIE_SPHERE * sphere) //except source...
1056 {
1057 	long rad = sphere->radius*ACTIVEBKG->Xmul;
1058 	rad+=2;
1059 	long px,pz;
1060 	px = sphere->origin.x * ACTIVEBKG->Xmul;
1061 	pz = sphere->origin.z * ACTIVEBKG->Zmul;
1062 
1063 	EERIEPOLY * ep;
1064 	FAST_BKG_DATA * feg;
1065 	long spx=max(px-rad,0L);
1066 	long epx=min(px+rad,ACTIVEBKG->Xsize-1L);
1067 	long spz=max(pz-rad,0L);
1068 	long epz=min(pz+rad,ACTIVEBKG->Zsize-1L);
1069 
1070 	for (long j=spz;j<=epz;j++)
1071 	for (long i=spx;i<=epx;i++)
1072 	{
1073 		feg=&ACTIVEBKG->fastdata[i][j];
1074 
1075 		for (long k=0;k<feg->nbpoly;k++)
1076 		{
1077 			ep=&feg->polydata[k];
1078 
1079 			if (ep->type & (POLY_WATER | POLY_TRANS | POLY_NOCOL)) continue;
1080 
1081 			if (IsPolyInSphere(ep,sphere))
1082 			{
1083 				return ep;
1084 			}
1085 		}
1086 	}
1087 
1088 	return NULL;
1089 }
1090 
1091 //-----------------------------------------------------------------------------
1092 
CheckAnythingInSphere(EERIE_SPHERE * sphere,long source,CASFlags flags,long * num)1093 bool CheckAnythingInSphere(EERIE_SPHERE * sphere,long source,CASFlags flags,long * num) //except source...
1094 {
1095 	if (num) *num=-1;
1096 
1097 	long rad = sphere->radius*ACTIVEBKG->Xmul;
1098 	rad+=2;
1099 	long px,pz;
1100 	px = sphere->origin.x*ACTIVEBKG->Xmul;
1101 	pz = sphere->origin.z*ACTIVEBKG->Zmul;
1102 
1103 	EERIEPOLY * ep;
1104 	FAST_BKG_DATA * feg;
1105 
1106 	if (!(flags & CAS_NO_BACKGROUND_COL))
1107 	{
1108 		long spz=max(pz-rad,0L);
1109 		long epz=min(pz+rad,ACTIVEBKG->Zsize-1L);
1110 		long spx=max(px-rad,0L);
1111 		long epx=min(px+rad,ACTIVEBKG->Xsize-1L);
1112 
1113 		for (long j=spz;j<=epz;j++)
1114 		for (long i=spx;i<=epx;i++)
1115 		{
1116 			feg=&ACTIVEBKG->fastdata[i][j];
1117 
1118 			for (long k=0;k<feg->nbpoly;k++)
1119 			{
1120 				ep=&feg->polydata[k];
1121 
1122 				if (ep->type & (POLY_WATER | POLY_TRANS | POLY_NOCOL)) continue;
1123 
1124 				if (IsPolyInSphere(ep,sphere))
1125 					return true;
1126 			}
1127 		}
1128 	}
1129 
1130 	if (flags & CAS_NO_NPC_COL) return false;
1131 
1132 	long validsource=0;
1133 
1134 	if (flags & CAS_NO_SAME_GROUP) validsource=ValidIONum(source);
1135 
1136 	Entity * io;
1137 	float sr30=sphere->radius+20.f;
1138 	float sr40=sphere->radius+30.f;
1139 	float sr180=sphere->radius+500.f;
1140 
1141 	for (long i=0;i<TREATZONE_CUR;i++)
1142 	{
1143 
1144 		if ( (treatio[i].show!=1) ||
1145 			 (treatio[i].io==NULL) ||
1146 			 (treatio[i].num==source)
1147 			  ) continue;
1148 
1149 		io=treatio[i].io;
1150 
1151 		if (!io->obj) continue;
1152 
1153 		if (!(io->ioflags & IO_NPC) && (io->ioflags & IO_NO_COLLISIONS)) continue;
1154 
1155 		if ((flags & CAS_NO_DEAD_COL) && (io->ioflags & IO_NPC) && (IsDeadNPC(io))) continue;
1156 
1157 		if ((io->ioflags & IO_FIX) && (flags & CAS_NO_FIX_COL)) continue;
1158 
1159 		if ((io->ioflags & IO_ITEM) && (flags & CAS_NO_ITEM_COL)) continue;
1160 
1161 		if ((treatio[i].num!=0) && (source!=0)
1162 				&& validsource && (HaveCommonGroup(io,entities[source])))
1163 				continue;
1164 
1165 			if (io->gameFlags & GFLAG_PLATFORM)
1166 				{
1167 					float miny,maxy;
1168 					miny=io->bbox3D.min.y;
1169 					maxy=io->bbox3D.max.y;
1170 
1171 					if (	(maxy> sphere->origin.y-sphere->radius)
1172 						||	(miny< sphere->origin.y+sphere->radius) )
1173 					if (In3DBBoxTolerance(&sphere->origin,&io->bbox3D,sphere->radius))
1174 					{
1175 						if(closerThan(Vec2f(io->pos.x, io->pos.z), Vec2f(sphere->origin.x, sphere->origin.z), 440.f + sphere->radius)) {
1176 
1177 							EERIEPOLY ep;
1178 							ep.type=0;
1179 
1180 							for (size_t ii=0;ii<io->obj->facelist.size();ii++)
1181 							{
1182 								float cx=0;
1183 								float cz=0;
1184 
1185 								for (long kk=0;kk<3;kk++)
1186 								{
1187 									cx+=ep.v[kk].p.x=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.x;
1188 										ep.v[kk].p.y=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.y;
1189 									cz+=ep.v[kk].p.z=io->obj->vertexlist3[io->obj->facelist[ii].vid[kk]].v.z;
1190 								}
1191 
1192 								cx*=( 1.0f / 3 );
1193 								cz*=( 1.0f / 3 );
1194 
1195 								for (int kk=0;kk<3;kk++)
1196 								{
1197 									ep.v[kk].p.x=(ep.v[kk].p.x-cx)*3.5f+cx;
1198 									ep.v[kk].p.z=(ep.v[kk].p.z-cz)*3.5f+cz;
1199 								}
1200 
1201 								if (PointIn2DPolyXZ(&ep, sphere->origin.x, sphere->origin.z))
1202 								{
1203 									if (num) *num=treatio[i].num;
1204 
1205 									return true;
1206 								}
1207 							}
1208 						}
1209 					}
1210 				}
1211 
1212 			if(distSqr(io->pos, sphere->origin) < square(sr180)) {
1213 				long amount=1;
1214 				vector<EERIE_VERTEX> & vlist=io->obj->vertexlist3;
1215 
1216 				if (io->obj->nbgroups>4)
1217 				{
1218 					for (long ii=0;ii<io->obj->nbgroups;ii++)
1219 					{
1220 						if (distSqr(vlist[io->obj->grouplist[ii].origin].v, sphere->origin) < square(sr40)) {
1221 							if (num) *num=treatio[i].num;
1222 
1223 							return true;
1224 						}
1225 					}
1226 
1227 					amount=2;
1228 				}
1229 
1230 				for (size_t ii=0;ii<io->obj->facelist.size();ii+=amount)
1231 				{
1232 
1233 					if (io->obj->facelist[ii].facetype & POLY_HIDE) continue;
1234 
1235 					if(distSqr(vlist[io->obj->facelist[ii].vid[0]].v, sphere->origin) < square(sr30)
1236 					   || distSqr(vlist[io->obj->facelist[ii].vid[1]].v, sphere->origin) < square(sr30)) {
1237 						if (num) *num=treatio[i].num;
1238 						return true;
1239 					}
1240 				}
1241 			}
1242 
1243 		}
1244 
1245 	return false;
1246 }
1247 
1248 
CheckIOInSphere(EERIE_SPHERE * sphere,long target,bool ignoreNoCollisionFlag)1249 bool CheckIOInSphere(EERIE_SPHERE * sphere, long target, bool ignoreNoCollisionFlag) {
1250 
1251 	if (!ValidIONum(target)) return false;
1252 
1253 	Entity * io=entities[target];
1254 	float sr30 = sphere->radius + 22.f;
1255 	float sr40 = sphere->radius + 27.f;
1256 	float sr180=sphere->radius+500.f;
1257 
1258 	if ((ignoreNoCollisionFlag || !(io->ioflags & IO_NO_COLLISIONS))
1259 			&& (io->show==SHOW_FLAG_IN_SCENE)
1260 	    && (io->gameFlags & GFLAG_ISINTREATZONE)
1261 			&& (io->obj)
1262 			)
1263 		{
1264 			if(distSqr(io->pos, sphere->origin) < square(sr180)) {
1265 				vector<EERIE_VERTEX> & vlist = io->obj->vertexlist3;
1266 
1267 				if (io->obj->nbgroups>10)
1268 				{
1269 					long count=0;
1270 					long ii=io->obj->nbgroups-1;
1271 
1272 					while (ii)
1273 					{
1274 						if(distSqr(vlist[io->obj->grouplist[ii].origin].v, sphere->origin) < square(sr40)) {
1275 							count++;
1276 							if (count>3) return true;
1277 						}
1278 
1279 						ii--;
1280 					}
1281 				}
1282 
1283 					long count=0;
1284 					long step;
1285 
1286 					if (io->obj->vertexlist.size()<150) step=1;
1287 					else if (io->obj->vertexlist.size()<300) step=2;
1288 					else if (io->obj->vertexlist.size()<600) step=4;
1289 					else if (io->obj->vertexlist.size()<1200) step=6;
1290 					else step=7;
1291 
1292 					for (size_t ii=0;ii<vlist.size();ii+=step)
1293 					{
1294 						if(closerThan(vlist[ii].v, sphere->origin, sr30)) {
1295 							count++;
1296 
1297 							if (count>6) return true;
1298 						}
1299 
1300 						if (io->obj->vertexlist.size()<120)
1301 						{
1302 							for (size_t kk=0;kk<vlist.size();kk+=1)
1303 							{
1304 								if (kk!=ii)
1305 								{
1306 									for (float nn=0.2f;nn<1.f;nn+=0.2f)
1307 									{
1308 									Vec3f posi = vlist[ii].v * nn + vlist[kk].v * (1.f - nn);
1309 									if(distSqr(sphere->origin, posi) <= square(sr30 + 20)) {
1310 										count++;
1311 
1312 										if (count>3)
1313 									{
1314 										if (io->ioflags & IO_FIX)
1315 												return true;
1316 
1317 											if (count>6)
1318 												return true;
1319 										}
1320 									}
1321 									}
1322 								}
1323 							}
1324 						}
1325 					}
1326 
1327 					if ((count>3) && (io->ioflags & IO_FIX))
1328 						return true;
1329 
1330 				}
1331 			}
1332 
1333 	return false;
1334 }
1335 
1336 
1337 float MAX_ALLOWED_PER_SECOND=12.f;
1338 //-----------------------------------------------------------------------------
1339 // Checks if a position is valid, Modify it for height if necessary
1340 // Returns true or false
1341 
AttemptValidCylinderPos(EERIE_CYLINDER * cyl,Entity * io,CollisionFlags flags)1342 bool AttemptValidCylinderPos(EERIE_CYLINDER * cyl, Entity * io, CollisionFlags flags) {
1343 
1344 	float anything = CheckAnythingInCylinder(cyl, io, flags);
1345 
1346 	if ((flags & CFLAG_LEVITATE) && (anything==0.f)) return true;
1347 
1348 	if (anything>=0.f) // Falling Cylinder but valid pos !
1349 	{
1350 		if (flags & CFLAG_RETURN_HEIGHT)
1351 			cyl->origin.y+=anything;
1352 
1353 		return true;
1354 	}
1355 
1356 	EERIE_CYLINDER tmp;
1357 
1358 	if (!(flags & CFLAG_ANCHOR_GENERATION))
1359 	{
1360 
1361 		memcpy(&tmp,cyl,sizeof(EERIE_CYLINDER));
1362 
1363 		while (anything<0.f)
1364 		{
1365 			tmp.origin.y+=anything;
1366 			anything=CheckAnythingInCylinder(&tmp,io,flags);
1367 		}
1368 
1369 		anything=tmp.origin.y-cyl->origin.y;
1370 	}
1371 
1372 	if (MOVING_CYLINDER)
1373 	{
1374 		if (flags & CFLAG_NPC)
1375 		{
1376 			float tolerate;
1377 
1378 			if((flags & CFLAG_PLAYER) && player.jumpphase != NotJumping) {
1379 				tolerate=0;
1380 			}
1381 			else if ((io) && (io->ioflags & IO_NPC) && (io->_npcdata->pathfind.listnb > 0) && (io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb))
1382 			{
1383 				tolerate=-65-io->_npcdata->moveproblem;
1384 			}
1385 			else
1386 			{
1387 				if ((io) &&
1388 				        (io->_npcdata))
1389 				{
1390 					tolerate = -55 - io->_npcdata->moveproblem;
1391 				}
1392 				else
1393 				{
1394 					tolerate=0.f;
1395 				}
1396 			}
1397 
1398 			if(NPC_IN_CYLINDER) {
1399 				tolerate = cyl->height * 0.5f;
1400 			}
1401 
1402 			if(anything < tolerate) {
1403 				return false;
1404 			}
1405 		}
1406 
1407 		if (io && (flags & CFLAG_PLAYER) && (anything<0.f) && (flags & CFLAG_JUST_TEST))
1408 		{
1409 			EERIE_CYLINDER tmpp;
1410 			memcpy(&tmpp,cyl,sizeof(EERIE_CYLINDER));
1411 			tmpp.radius*=0.7f;
1412 			float tmp=CheckAnythingInCylinder(&tmpp,io,flags | CFLAG_JUST_TEST);
1413 
1414 			if ((tmp > 50.f))
1415 			{
1416 				tmpp.radius=cyl->radius*1.4f;
1417 					tmpp.origin.y-=30.f;
1418 					float tmp=CheckAnythingInCylinder(&tmpp,io,flags | CFLAG_JUST_TEST);
1419 
1420 					if (tmp<0)
1421 				return false;
1422 			}
1423 		}
1424 
1425 		if (io && (!(flags & CFLAG_JUST_TEST)))
1426 		{
1427 			if ((flags & CFLAG_PLAYER) && (anything<0.f))
1428 			{
1429 
1430 				if(player.jumpphase != NotJumping) {
1431 					io->_npcdata->climb_count = MAX_ALLOWED_PER_SECOND;
1432 					return false;
1433 				}
1434 
1435 				float dist = max(vector2D.length(),1.f);
1436 				float pente = EEfabs(anything) / dist * ( 1.0f / 2 );
1437 				io->_npcdata->climb_count+=pente;
1438 
1439 				if (io->_npcdata->climb_count>MAX_ALLOWED_PER_SECOND)
1440 				{
1441 					io->_npcdata->climb_count=MAX_ALLOWED_PER_SECOND;
1442 				}
1443 
1444 				if (anything < -55)
1445 				{
1446 					io->_npcdata->climb_count=MAX_ALLOWED_PER_SECOND;
1447 					return false;
1448 				}
1449 
1450 				EERIE_CYLINDER tmpp;
1451 				memcpy(&tmpp,cyl,sizeof(EERIE_CYLINDER));
1452 				tmpp.radius *= 0.65f;
1453 				float tmp=CheckAnythingInCylinder(&tmpp,io,flags | CFLAG_JUST_TEST);
1454 
1455 				if (tmp > 50.f)
1456 				{
1457 					tmpp.radius=cyl->radius*1.45f;
1458 					tmpp.origin.y-=30.f;
1459 					float tmp=CheckAnythingInCylinder(&tmpp,io,flags | CFLAG_JUST_TEST);
1460 
1461 					if (tmp<0)
1462 						return false;
1463 				}
1464 			}
1465 		}
1466 	}
1467 	else if (anything<-45) return false;
1468 
1469 	if ((flags & CFLAG_SPECIAL) && (anything<-40))
1470 	{
1471 		if (flags & CFLAG_RETURN_HEIGHT)
1472 			cyl->origin.y+=anything;
1473 
1474 		return false;
1475 	}
1476 
1477 	memcpy(&tmp,cyl,sizeof(EERIE_CYLINDER));
1478 	tmp.origin.y+=anything;
1479 	anything = CheckAnythingInCylinder(&tmp, io, flags);
1480 
1481 	if (anything<0.f)
1482 	{
1483 		if (flags & CFLAG_RETURN_HEIGHT)
1484 		{
1485 			while (anything<0.f)
1486 			{
1487 				tmp.origin.y+=anything;
1488 				anything=CheckAnythingInCylinder(&tmp,io,flags);
1489 			}
1490 
1491 			cyl->origin.y = tmp.origin.y;
1492 		}
1493 
1494 		return false;
1495 	}
1496 
1497 	cyl->origin.y=tmp.origin.y;
1498 	return true;
1499 }
1500 
1501 //----------------------------------------------------------------------------------------------
1502 //flags & 1 levitate
1503 //flags & 2 no inter col
1504 //flags & 4 special
1505 //flags & 8	easier sliding.
1506 //flags & 16 climbing !!!
1507 //flags & 32 Just Test !!!
1508 //flags & 64 NPC mode
1509 //----------------------------------------------------------------------------------------------
ARX_COLLISION_Move_Cylinder(IO_PHYSICS * ip,Entity * io,float MOVE_CYLINDER_STEP,CollisionFlags flags)1510 bool ARX_COLLISION_Move_Cylinder(IO_PHYSICS * ip,Entity * io,float MOVE_CYLINDER_STEP, CollisionFlags flags)
1511 {
1512 //	HERMESPerf script(HPERF_PHYSICS);
1513 //	+5 on 15
1514 //
1515 //	memcpy(&ip->cyl.origin,&ip->targetpos,sizeof(EERIE_3D));
1516 //	return true;
1517 
1518 	ON_PLATFORM=0;
1519 	MOVING_CYLINDER=1;
1520 	COLLIDED_CLIMB_POLY=0;
1521 	DIRECT_PATH=true;
1522 	IO_PHYSICS test;
1523 
1524 	if (ip==NULL)
1525 	{
1526 		MOVING_CYLINDER=0;
1527 		return false;
1528 	}
1529 
1530 	float distance = dist(ip->startpos, ip->targetpos);
1531 
1532 	if (distance < 0.1f)
1533 	{
1534 		MOVING_CYLINDER=0;
1535 
1536 		if (distance==0.f)
1537 			return true;
1538 
1539 		return false;
1540 	}
1541 
1542 	Vec3f mvector = (ip->targetpos - ip->startpos) / distance;
1543 	long count=100;
1544 
1545 	while ((distance>0.f) && (count--))
1546 	{
1547 		// First We compute current increment
1548 		float curmovedist=min(distance,MOVE_CYLINDER_STEP);
1549 		distance-=curmovedist;
1550 
1551 		// Store our cylinder desc into a test struct
1552 		memcpy(&test,ip,sizeof(IO_PHYSICS));
1553 
1554 		// uses test struct to simulate movement.
1555 		vector2D.x=mvector.x*curmovedist;
1556 		vector2D.y=0.f;
1557 		vector2D.z=mvector.z*curmovedist;
1558 
1559 		test.cyl.origin.x += vector2D.x;
1560 		test.cyl.origin.y+=mvector.y*curmovedist;
1561 		test.cyl.origin.z += vector2D.z;
1562 
1563 		if ((flags & CFLAG_CHECK_VALID_POS)
1564 			&& (CylinderAboveInvalidZone(&test.cyl)))
1565 				return false;
1566 
1567 		if (AttemptValidCylinderPos(&test.cyl,io,flags))
1568 		{
1569 			// Found without complication
1570 			 memcpy(ip,&test,sizeof(IO_PHYSICS));
1571 		}
1572 		else
1573 		{
1574 			//return false;
1575 			if ((mvector.x==0.f) && (mvector.z==0.f))
1576 				return true;
1577 
1578 			if (flags & CFLAG_CLIMBING)
1579 			{
1580 				memcpy(&test.cyl, &ip->cyl, sizeof(EERIE_CYLINDER));
1581 				test.cyl.origin.y+=mvector.y*curmovedist;
1582 
1583 				if (AttemptValidCylinderPos(&test.cyl,io,flags))
1584 				{
1585 					memcpy(ip,&test,sizeof(IO_PHYSICS));
1586 					goto oki;
1587 				}
1588 			}
1589 
1590 			DIRECT_PATH=false;
1591 			// Must Attempt To Slide along collisions
1592 			Vec3f vecatt;
1593 			Vec3f rpos;
1594 			Vec3f lpos;
1595 			long RFOUND=0;
1596 			long LFOUND=0;
1597 			long maxRANGLE=90;
1598 			float ANGLESTEPP;
1599 
1600 			if (flags & CFLAG_EASY_SLIDING)  // player sliding in fact...
1601 			{
1602 				ANGLESTEPP=10.f;
1603 				maxRANGLE=70;
1604 			}
1605 			else ANGLESTEPP=30.f;
1606 
1607 			float rangle = ANGLESTEPP;
1608 			float langle = 360.f - ANGLESTEPP;
1609 
1610 
1611 			while (rangle<=maxRANGLE) //tries on the Right and Left sides
1612 			{
1613 				memcpy(&test.cyl, &ip->cyl, sizeof(EERIE_CYLINDER));
1614 				float t=radians(MAKEANGLE(rangle));
1615 				YRotatePoint(&mvector,&vecatt,EEcos(t),EEsin(t));
1616 				test.cyl.origin += vecatt * curmovedist;
1617 				float cc=io->_npcdata->climb_count;
1618 
1619 				if (AttemptValidCylinderPos(&test.cyl, io, flags))
1620 				{
1621 					rpos = test.cyl.origin;
1622 					RFOUND=1;
1623 				}
1624 				else io->_npcdata->climb_count=cc;
1625 
1626 				rangle+=ANGLESTEPP;
1627 
1628 				memcpy(&test.cyl, &ip->cyl, sizeof(EERIE_CYLINDER));
1629 				t=radians(MAKEANGLE(langle));
1630 				YRotatePoint(&mvector,&vecatt,EEcos(t),EEsin(t));
1631 				test.cyl.origin += vecatt * curmovedist;
1632 				cc=io->_npcdata->climb_count;
1633 
1634 				if (AttemptValidCylinderPos(&test.cyl, io, flags))
1635 				{
1636 					lpos = test.cyl.origin;
1637 					LFOUND=1;
1638 				}
1639 				else io->_npcdata->climb_count=cc;
1640 
1641 				langle-=ANGLESTEPP;
1642 
1643 				if ((RFOUND) || (LFOUND)) break;
1644 			}
1645 
1646 			if ((LFOUND) && (RFOUND))
1647 			{
1648 				langle=360.f-langle;
1649 
1650 				if (langle<rangle)
1651 				{
1652 					ip->cyl.origin = lpos;
1653 					distance -= curmovedist;
1654 				}
1655 				else
1656 				{
1657 					ip->cyl.origin = rpos;
1658 					distance -= curmovedist;
1659 				}
1660 			}
1661 			else if (LFOUND)
1662 			{
1663 				ip->cyl.origin = lpos;
1664 				distance -= curmovedist;
1665 			}
1666 			else if (RFOUND)
1667 			{
1668 				ip->cyl.origin = rpos;
1669 				distance -= curmovedist;
1670 			}
1671 			else  //stopped
1672 			{
1673 				ip->velocity = Vec3f::ZERO;
1674 				MOVING_CYLINDER=0;
1675 				return false;
1676 			}
1677 		}
1678 
1679 		if (flags & CFLAG_NO_HEIGHT_MOD)
1680 		{
1681 			if (EEfabs(ip->startpos.y - ip->cyl.origin.y)>30.f)
1682 				return false;
1683 		}
1684 
1685 	oki:
1686 		;
1687 	}
1688 
1689 	MOVING_CYLINDER=0;
1690 	return true;
1691 }
1692 
1693 //-----------------------------------------------------------------------------
IO_Visible(Vec3f * orgn,Vec3f * dest,EERIEPOLY * epp,Vec3f * hit)1694 bool IO_Visible(Vec3f * orgn, Vec3f * dest,EERIEPOLY * epp,Vec3f * hit)
1695 {
1696 
1697 
1698 	float x,y,z; //current ray pos
1699 	float dx,dy,dz; // ray incs
1700 	float adx,ady,adz; // absolute ray incs
1701 	float ix,iy,iz;
1702 	long px,pz;
1703 	EERIEPOLY * ep;
1704 
1705 	FAST_BKG_DATA * feg;
1706 	float pas=35.f;
1707 
1708 
1709 	Vec3f found_hit = Vec3f::ZERO;
1710 	EERIEPOLY * found_ep=NULL;
1711 	float iter,t;
1712 
1713 	x=orgn->x;
1714 	y=orgn->y;
1715 	z=orgn->z;
1716 	float distance;
1717 	float nearest = distance = fdist(*orgn, *dest);
1718 
1719 	if (distance<pas) pas=distance*( 1.0f / 2 );
1720 
1721 	dx=(dest->x-orgn->x);
1722 	adx=EEfabs(dx);
1723 	dy=(dest->y-orgn->y);
1724 	ady=EEfabs(dy);
1725 	dz=(dest->z-orgn->z);
1726 	adz=EEfabs(dz);
1727 
1728 	if ( (adx>=ady) && (adx>=adz))
1729 	{
1730 		if (adx != dx)
1731 		{
1732 			ix = -pas;
1733 		}
1734 		else
1735 		{
1736 			ix = pas;
1737 		}
1738 
1739 		iter=adx/pas;
1740 		t=1.f/(iter);
1741 		iy=dy*t;
1742 		iz=dz*t;
1743 	}
1744 	else if ( (ady>=adx) && (ady>=adz))
1745 	{
1746 		if (ady != dy)
1747 		{
1748 			iy = -pas;
1749 		}
1750 		else
1751 		{
1752 			iy = pas;
1753 		}
1754 
1755 		iter=ady/pas;
1756 		t=1.f/(iter);
1757 		ix=dx*t;
1758 		iz=dz*t;
1759 	}
1760 	else
1761 	{
1762 		if (adz != dz)
1763 		{
1764 			iz = -pas;
1765 		}
1766 		else
1767 		{
1768 			iz = pas;
1769 		}
1770 
1771 		iter=adz/pas;
1772 		t=1.f/(iter);
1773 		ix=dx*t;
1774 		iy=dy*t;
1775 	}
1776 
1777 	float dd;
1778 	x-=ix;
1779 	y-=iy;
1780 	z-=iz;
1781 
1782 	while (iter>0.f)
1783 	{
1784 		iter-=1.f;
1785 		x+=ix;
1786 		y+=iy;
1787 		z+=iz;
1788 
1789 		EERIE_SPHERE sphere;
1790 		sphere.origin.x=x;
1791 		sphere.origin.y=y;
1792 		sphere.origin.z=z;
1793 		sphere.radius=65.f;
1794 
1795 		for(size_t num = 0; num < entities.size(); num++) {
1796 			Entity * io = entities[num];
1797 
1798 			if ((io) && (io->gameFlags & GFLAG_VIEW_BLOCKER))
1799 			{
1800 				if ( CheckIOInSphere(&sphere,num) )
1801 				{
1802 					dd = fdist(*orgn, sphere.origin);
1803 
1804 					if (dd<nearest)
1805 					{
1806 						hit->x=x;
1807 						hit->y=y;
1808 						hit->z=z;
1809 						return false;
1810 					}
1811 				}
1812 			}
1813 		}
1814 
1815 		px=(long)(x* ACTIVEBKG->Xmul);
1816 		pz=(long)(z* ACTIVEBKG->Zmul);
1817 
1818 		if (px>=ACTIVEBKG->Xsize)		goto fini;
1819 		else if (px< 0)					goto fini;
1820 
1821 		if (pz>= ACTIVEBKG->Zsize)		goto fini;
1822 		else if (pz< 0)					goto fini;
1823 
1824 			feg=&ACTIVEBKG->fastdata[px][pz];
1825 
1826 			for (long k=0;k<feg->nbpolyin;k++)
1827 			{
1828 				ep=feg->polyin[k];
1829 
1830 				if (!(ep->type & (POLY_WATER | POLY_TRANS | POLY_NOCOL) ) )
1831 				if ((ep->min.y-pas<y) && (ep->max.y+pas>y))
1832 				if ((ep->min.x-pas<x) && (ep->max.x+pas>x))
1833 				if ((ep->min.z-pas<z) && (ep->max.z+pas>z))
1834 				{
1835 					if (RayCollidingPoly(orgn,dest,ep,hit))
1836 					{
1837 						dd = fdist(*orgn, *hit);
1838 						if(dd < nearest) {
1839 							nearest = dd;
1840 							found_ep = ep;
1841 							found_hit = *hit;
1842 						}
1843 					}
1844 				}
1845 			}
1846 
1847 		}
1848 
1849 fini:
1850 	;
1851 
1852 	if ( found_ep == NULL ) return true;
1853 
1854 	if ( found_ep == epp ) return true;
1855 
1856 	*hit = found_hit;
1857 	return false;
1858 }
ANCHOR_BLOCK_Clear()1859 void ANCHOR_BLOCK_Clear()
1860 {
1861 	EERIE_BACKGROUND * eb=ACTIVEBKG;
1862 
1863 	if (eb)
1864 	{
1865 		for (long k=0;k<eb->nbanchors;k++)
1866 		{
1867 			ANCHOR_DATA * ad=&eb->anchors[k];
1868 			ad->flags&=~ANCHOR_FLAG_BLOCKED;
1869 		}
1870 	}
1871 }
1872 
ANCHOR_BLOCK_By_IO(Entity * io,long status)1873 void ANCHOR_BLOCK_By_IO(Entity * io,long status)
1874 {
1875 	EERIE_BACKGROUND * eb=ACTIVEBKG;
1876 
1877 	for (long k=0;k<eb->nbanchors;k++)
1878 	{
1879 		ANCHOR_DATA * ad=&eb->anchors[k];
1880 
1881 		if (distSqr(ad->pos, io->pos) > square(600.f)) continue;
1882 
1883 		if(closerThan(Vec2f(io->pos.x, io->pos.z), Vec2f(ad->pos.x, ad->pos.z), 440.f)) {
1884 
1885 			EERIEPOLY ep;
1886 			ep.type=0;
1887 
1888 			for (size_t ii=0;ii<io->obj->facelist.size();ii++)
1889 			{
1890 				float cx=0;
1891 				float cz=0;
1892 
1893 				for (long kk=0;kk<3;kk++)
1894 				{
1895 					cx+=ep.v[kk].p.x=io->obj->vertexlist[io->obj->facelist[ii].vid[kk]].v.x+io->pos.x;
1896 						ep.v[kk].p.y=io->obj->vertexlist[io->obj->facelist[ii].vid[kk]].v.y+io->pos.y;
1897 					cz+=ep.v[kk].p.z=io->obj->vertexlist[io->obj->facelist[ii].vid[kk]].v.z+io->pos.z;
1898 				}
1899 
1900 				cx*=( 1.0f / 3 );
1901 				cz*=( 1.0f / 3 );
1902 
1903 				for (int kk=0;kk<3;kk++)
1904 				{
1905 					ep.v[kk].p.x=(ep.v[kk].p.x-cx)*3.5f+cx;
1906 					ep.v[kk].p.z=(ep.v[kk].p.z-cz)*3.5f+cz;
1907 				}
1908 
1909 				if (PointIn2DPolyXZ(&ep, ad->pos.x, ad->pos.z))
1910 				{
1911 					if (status)
1912 						ad->flags|=ANCHOR_FLAG_BLOCKED;
1913 					else
1914 						ad->flags&=~ANCHOR_FLAG_BLOCKED;
1915 				}
1916 			}
1917 		}
1918 	}
1919 }
1920