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-2000 ARKANE Studios SA. All rights reserved
46 
47 #include "scene/Interactive.h"
48 
49 #include <cstdlib>
50 #include <iomanip>
51 #include <algorithm>
52 #include <string>
53 #include <vector>
54 #include <sstream>
55 #include <cstdio>
56 
57 #include <boost/algorithm/string/predicate.hpp>
58 
59 #include "ai/Paths.h"
60 
61 #include "animation/Animation.h"
62 
63 #include "core/Application.h"
64 #include "core/GameTime.h"
65 #include "core/Config.h"
66 #include "core/Core.h"
67 
68 #include "game/Camera.h"
69 #include "game/Damage.h"
70 #include "game/EntityManager.h"
71 #include "game/Equipment.h"
72 #include "game/Inventory.h"
73 #include "game/Item.h"
74 #include "game/Levels.h"
75 #include "game/NPC.h"
76 #include "game/Player.h"
77 
78 #include "gui/Speech.h"
79 #include "gui/Interface.h"
80 
81 #include "graphics/Draw.h"
82 #include "graphics/Math.h"
83 #include "graphics/data/TextureContainer.h"
84 #include "graphics/data/MeshManipulation.h"
85 #include "graphics/particle/ParticleEffects.h"
86 #include "graphics/texture/TextureStage.h"
87 
88 #include "io/fs/FilePath.h"
89 #include "io/fs/Filesystem.h"
90 #include "io/fs/SystemPaths.h"
91 #include "io/resource/ResourcePath.h"
92 #include "io/resource/PakReader.h"
93 #include "io/log/Logger.h"
94 
95 #include "math/Random.h"
96 
97 #include "physics/Anchors.h"
98 #include "physics/Collisions.h"
99 #include "physics/CollisionShapes.h"
100 #include "physics/Box.h"
101 #include "physics/Clothes.h"
102 
103 #include "platform/Thread.h"
104 
105 #include "scene/ChangeLevel.h"
106 #include "scene/GameSound.h"
107 #include "scene/Scene.h"
108 #include "scene/LinkedObject.h"
109 #include "scene/LoadLevel.h"
110 #include "scene/Light.h"
111 #include "scene/Object.h"
112 
113 #include "script/ScriptEvent.h"
114 
115 using std::min;
116 using std::string;
117 using std::vector;
118 
119 extern long FRAME_COUNT;
120 
121 extern Entity * CAMERACONTROLLER;
122 extern TextureContainer * Movable;
123 extern long EXTERNALVIEW;
124 extern long NEED_TEST_TEXT;
125 
126 ARX_NODES nodes;
127 static float TREATZONE_LIMIT = 1800.f;
128 
129 long HERO_SHOW_1ST = 1;
130 #ifdef BUILD_EDITOR
131 long NbIOSelected = 0;
132 #endif
133 long INTER_DRAW = 0;
134 
135 static bool IsCollidingInter(Entity * io, Vec3f * pos);
136 static Entity * AddCamera(const res::path & classPath,
137                           EntityInstance instance = -1);
138 static Entity * AddMarker(const res::path & classPath,
139                           EntityInstance instance = -1);
140 
141 float STARTED_ANGLE = 0;
Set_DragInter(Entity * io)142 void Set_DragInter(Entity * io)
143 {
144 	if (io != DRAGINTER)
145 		STARTED_ANGLE = player.angle.b;
146 
147 	DRAGINTER = io;
148 
149 	if (io)
150 	{
151 		if ((io->obj) && (io->obj->pbox))
152 		{
153 			io->obj->pbox->active = 0;
154 		}
155 	}
156 }
157 
158 // Checks if an IO index number is valid
ValidIONum(long num)159 long ValidIONum(long num) {
160 
161 	if(num < 0 || num >= long(entities.size()) || !entities[num]) {
162 		return 0;
163 	}
164 
165 	return 1;
166 }
167 
ValidIOAddress(const Entity * io)168 long ValidIOAddress(const Entity * io) {
169 
170 	if(!io) {
171 		return 0;
172 	}
173 
174 	for(size_t i = 0; i < entities.size(); i++) {
175 		if(entities[i] == io) {
176 			return 1;
177 		}
178 	}
179 
180 	return 0;
181 }
182 
ARX_INTERACTIVE_fGetPrice(Entity * io,Entity * shop)183 static float ARX_INTERACTIVE_fGetPrice(Entity * io, Entity * shop) {
184 
185 	if ((!io)
186 	        ||	(!(io->ioflags & IO_ITEM)))
187 		return 0;
188 
189 	float durability_ratio = io->durability / io->max_durability;
190 	float shop_multiply = 1.f;
191 
192 	if (shop)
193 		shop_multiply = shop->shop_multiply;
194 
195 	return io->_itemdata->price * shop_multiply * durability_ratio;
196 
197 }
ARX_INTERACTIVE_GetPrice(Entity * io,Entity * shop)198 long ARX_INTERACTIVE_GetPrice(Entity * io, Entity * shop) {
199 	return ARX_INTERACTIVE_fGetPrice(io, shop);
200 }
201 
ARX_INTERACTIVE_ForceIOLeaveZone(Entity * io,long flags)202 static void ARX_INTERACTIVE_ForceIOLeaveZone(Entity * io, long flags) {
203 
204 	ARX_PATH * op = io->inzone;
205 
206 	if (op)
207 	{
208 		std::string temp = op->name;
209 
210 		if (flags & 1) // no need when being destroyed !
211 			SendIOScriptEvent(io, SM_LEAVEZONE, temp);
212 
213 		if(!op->controled.empty())
214 		{
215 			long t = entities.getById(op->controled);
216 
217 			if (t >= 0)
218 			{
219 				std::string str = io->long_name() + ' ' + temp;
220 				SendIOScriptEvent( entities[t], SM_CONTROLLEDZONE_LEAVE, str );
221 			}
222 		}
223 	}
224 }
225 
ARX_INTERACTIVE_DestroyDynamicInfo(Entity * io)226 void ARX_INTERACTIVE_DestroyDynamicInfo(Entity * io)
227 {
228 	if (!io) return;
229 
230 	long n = io->index();
231 
232 	short sN = checked_range_cast<short>(n);
233 
234 	ARX_INTERACTIVE_ForceIOLeaveZone(io, 0);
235 
236 	for (long i = 0; i < MAX_EQUIPED; i++)
237 	{
238 		if ((player.equiped[i])
239 		        &&	(player.equiped[i] == n)
240 		        &&	ValidIONum(player.equiped[i]))
241 		{
242 			ARX_EQUIPMENT_UnEquip(entities.player(), entities[player.equiped[i]], 1);
243 			player.equiped[i] = 0;
244 		}
245 	}
246 
247 	ARX_SPEECH_ReleaseIOSpeech(io);
248 
249 	ARX_SCRIPT_EventStackClearForIo(io);
250 
251 	if (ValidIONum(n))
252 	{
253 		for (size_t i = 0; i < MAX_SPELLS; i++)
254 		{
255 			if ((spells[i].exist) && (spells[i].caster == n))
256 			{
257 				spells[i].tolive = 0;
258 			}
259 		}
260 	}
261 
262 	if (io->flarecount)
263 	{
264 		for (long i = 0; i < MAX_FLARES; i++)
265 		{
266 			if ((flare[i].exist)
267 			        &&	(flare[i].io == io))
268 				flare[i].io = NULL;
269 		}
270 	}
271 
272 	if(io->ioflags & IO_NPC) {
273 		// to check again later...
274 		long count = 50;
275 		while((io->_npcdata->pathfind.pathwait == 1) && count--) {
276 			Thread::sleep(1);
277 		}
278 		free(io->_npcdata->pathfind.list);
279 		memset(&io->_npcdata->pathfind, 0, sizeof(IO_PATHFIND));
280 	}
281 
282 	if (ValidDynLight(io->dynlight))
283 	{
284 		DynLight[io->dynlight].exist = 0;
285 	}
286 
287 	io->dynlight = -1;
288 
289 	if (io->obj)
290 	{
291 		EERIE_3DOBJ * eobj = io->obj;
292 
293 		for (long k = 0; k < eobj->nblinked; k++)
294 		{
295 			if ((eobj->linked[k].lgroup != -1) && eobj->linked[k].obj)
296 			{
297 				Entity * ioo = (Entity *)eobj->linked[k].io;
298 
299 				if ((ioo) && ValidIOAddress(ioo))
300 				{
301 					long ll = eobj->linked[k].lidx;
302 					Vec3f pos, vector;
303 					pos = io->obj->vertexlist3[ll].v;
304 					ioo->angle = Anglef(rnd() * 40.f + 340.f, rnd() * 360.f, 0.f);
305 					vector.x = -(float)EEsin(radians(ioo->angle.b)) * ( 1.0f / 2 );
306 					vector.y = EEsin(radians(ioo->angle.a));
307 					vector.z = (float)EEcos(radians(ioo->angle.b)) * ( 1.0f / 2 );
308 					ioo->soundtime = 0;
309 					ioo->soundcount = 0;
310 					EERIE_PHYSICS_BOX_Launch_NOCOL(ioo, ioo->obj, &pos, &vector, 2, &ioo->angle);
311 					ioo->show = SHOW_FLAG_IN_SCENE;
312 					ioo->no_collide = sN;
313 					EERIE_LINKEDOBJ_UnLinkObjectFromObject(io->obj, ioo->obj);
314 				}
315 			}
316 		}
317 	}
318 }
319 
320 
ARX_INTERACTIVE_Attach(long n_source,long n_target,const std::string & ap_source,const std::string & ap_target)321 bool ARX_INTERACTIVE_Attach(long n_source, long n_target, const std::string& ap_source, const std::string& ap_target)
322 {
323 	if (!ValidIONum(n_source) || !ValidIONum(n_target))
324 		return false;
325 
326 	entities[n_source]->show = SHOW_FLAG_LINKED;
327 	EERIE_LINKEDOBJ_UnLinkObjectFromObject(entities[n_target]->obj, entities[n_source]->obj);
328 	return EERIE_LINKEDOBJ_LinkObjectToObject(entities[n_target]->obj,
329 	        entities[n_source]->obj, ap_target, ap_source, entities[n_source]);
330 }
ARX_INTERACTIVE_Detach(long n_source,long n_target)331 void ARX_INTERACTIVE_Detach(long n_source, long n_target)
332 {
333 	if (!ValidIONum(n_source)
334 	        ||	!ValidIONum(n_target))
335 		return;
336 
337 	entities[n_source]->show = SHOW_FLAG_IN_SCENE;
338 	EERIE_LINKEDOBJ_UnLinkObjectFromObject(entities[n_target]->obj, entities[n_source]->obj);
339 }
340 
ARX_INTERACTIVE_Show_Hide_1st(Entity * io,long state)341 void ARX_INTERACTIVE_Show_Hide_1st(Entity * io, long state)
342 {
343 	if ((!io)
344 	        ||	(HERO_SHOW_1ST == state))
345 		return;
346 
347 	HERO_SHOW_1ST = state;
348 	long grp = EERIE_OBJECT_GetSelection(io->obj, "1st");
349 
350 	if (grp != -1)
351 	{
352 		for (size_t nn = 0; nn < io->obj->facelist.size(); nn++)
353 		{
354 			EERIE_FACE * ef = &io->obj->facelist[nn];
355 
356 			for (long jj = 0; jj < 3; jj++)
357 			{
358 				if (IsInSelection(io->obj, ef->vid[jj], grp) != -1)
359 				{
360 					if (state)
361 						ef->facetype |= POLY_HIDE;
362 					else
363 						ef->facetype &= ~POLY_HIDE;
364 
365 					break;
366 				}
367 			}
368 		}
369 	}
370 
371 	ARX_INTERACTIVE_HideGore(entities.player(), 1);
372 }
373 
374 
ARX_INTERACTIVE_RemoveGoreOnIO(Entity * io)375 void ARX_INTERACTIVE_RemoveGoreOnIO(Entity * io)
376 {
377 	if (!io || !io->obj || io->obj->texturecontainer.empty())
378 		return;
379 
380 	long gorenum = -1;
381 
382 	for (size_t nn = 0; nn < io->obj->texturecontainer.size(); nn++)
383 	{
384 		if (io->obj->texturecontainer[nn] && ( io->obj->texturecontainer[nn]->m_texName.string().find("gore") != std::string::npos ) )
385 		{
386 			gorenum = nn;
387 			break;
388 		}
389 	}
390 
391 	if (gorenum > -1)
392 	{
393 		for (size_t nn = 0; nn < io->obj->facelist.size(); nn++)
394 		{
395 			if (io->obj->facelist[nn].texid == gorenum)
396 			{
397 				io->obj->facelist[nn].facetype |= POLY_HIDE;
398 				io->obj->facelist[nn].texid = -1;
399 			}
400 		}
401 	}
402 }
403 
404 
405 // flag & 1 == no unhide non-gore
406 // TODO very simmilar to ARX_INTERACTIVE_RemoveGoreOnIO
ARX_INTERACTIVE_HideGore(Entity * io,long flag)407 void ARX_INTERACTIVE_HideGore(Entity * io, long flag)
408 {
409 	if (!io || !io->obj || io->obj->texturecontainer.empty())
410 		return;
411 
412 	if ((io == entities.player()) && (!flag & 1))
413 		return;
414 
415 	long gorenum = -1;
416 
417 	for (size_t nn = 0; nn < io->obj->texturecontainer.size(); nn++)
418 	{
419 		if (io->obj->texturecontainer[nn] && io->obj->texturecontainer[nn]->m_texName.string().find("gore") != string::npos)
420 		{
421 			gorenum = nn;
422 			break;
423 		}
424 	}
425 
426 	if (gorenum > -1)
427 	{
428 		for (size_t nn = 0; nn < io->obj->facelist.size(); nn++)
429 		{
430 			//Hide Gore Polys...
431 			if (io->obj->facelist[nn].texid == gorenum)
432 				io->obj->facelist[nn].facetype |= POLY_HIDE;
433 			else if (!flag & 1)
434 				io->obj->facelist[nn].facetype &= ~POLY_HIDE;
435 		}
436 	}
437 }
438 
439 
ForceNPC_Above_Ground(Entity * io)440 bool ForceNPC_Above_Ground(Entity * io) {
441 
442 	if(io && (io->ioflags & IO_NPC) && !(io->ioflags & IO_PHYSICAL_OFF)) {
443 		io->physics.cyl.origin = io->pos;
444 		AttemptValidCylinderPos(&io->physics.cyl, io, CFLAG_NO_INTERCOL);
445 		if(EEfabs(io->pos.y - io->physics.cyl.origin.y) < 45.f) {
446 			io->pos.y = io->physics.cyl.origin.y;
447 			return true;
448 		}
449 	}
450 	return false;
451 }
452 
453 // Unlinks all linked objects from all IOs
UnlinkAllLinkedObjects()454 void UnlinkAllLinkedObjects() {
455 	for(size_t i = 0; i < entities.size(); i++) {
456 		if(entities[i]) {
457 			EERIE_LINKEDOBJ_ReleaseData(entities[i]->obj);
458 		}
459 	}
460 }
461 
IO_UnlinkAllLinkedObjects(Entity * io)462 void IO_UnlinkAllLinkedObjects(Entity * io) {
463 	if(io && io->obj) {
464 		for(long k = 0; k < io->obj->nblinked; k++) {
465 			if(io->obj->linked[k].io) {
466 				Entity * ioo = io->obj->linked[k].io;
467 				if(ValidIOAddress(ioo)) {
468 					IO_Drop_Item(io, ioo);
469 				}
470 			}
471 		}
472 		EERIE_LINKEDOBJ_ReleaseData(io->obj);
473 	}
474 }
475 
476 // First is always the player
477 TREATZONE_IO * treatio = NULL;
478 long TREATZONE_CUR = 0;
479 static long TREATZONE_MAX = 0;
480 
TREATZONE_Clear()481 void TREATZONE_Clear() {
482 	TREATZONE_CUR = 0;
483 }
484 
TREATZONE_Release()485 void TREATZONE_Release() {
486 	free(treatio), treatio = NULL;
487 	TREATZONE_MAX = 0;
488 	TREATZONE_CUR = 0;
489 }
490 
TREATZONE_RemoveIO(Entity * io)491 void TREATZONE_RemoveIO(Entity * io)
492 {
493 	if (treatio)
494 	{
495 		for (long i = 0; i < TREATZONE_CUR; i++)
496 		{
497 			if (treatio[i].io == io)
498 			{
499 				treatio[i].io = NULL;
500 				treatio[i].ioflags = 0;
501 				treatio[i].show = 0;
502 			}
503 		}
504 	}
505 }
506 
507 // flag & 1 IO_JUST_COLLIDE
TREATZONE_AddIO(Entity * io,long flag)508 void TREATZONE_AddIO(Entity * io, long flag)
509 {
510 	if (TREATZONE_MAX == TREATZONE_CUR)
511 	{
512 		TREATZONE_MAX++;
513 		treatio = (TREATZONE_IO *)realloc(treatio, sizeof(TREATZONE_IO) * TREATZONE_MAX);
514 	}
515 
516 	for (long i = 0; i < TREATZONE_CUR; i++)
517 	{
518 		if (treatio[i].io == io)
519 			return;
520 	}
521 
522 	treatio[TREATZONE_CUR].io = io;
523 	treatio[TREATZONE_CUR].ioflags = io->ioflags;
524 
525 	if (flag & 1) treatio[TREATZONE_CUR].ioflags |= IO_JUST_COLLIDE;
526 
527 	treatio[TREATZONE_CUR].show = io->show;
528 	treatio[TREATZONE_CUR].num = io->index();
529 	TREATZONE_CUR++;
530 }
531 
CheckSetAnimOutOfTreatZone(Entity * io,long num)532 void CheckSetAnimOutOfTreatZone(Entity * io, long num)
533 {
534 	if ((io)
535 	        &&	(io->animlayer[num].cur_anim)
536 	        &&	!(io->gameFlags & GFLAG_ISINTREATZONE)
537 	        &&	distSqr(io->pos, ACTIVECAM->pos) > square(2500.f))
538 	{
539 
540 		io->animlayer[num].ctime =
541 		    checked_range_cast<long>(io->animlayer[num].cur_anim->anims[io->animlayer[num].altidx_cur]->anim_time - 1.f);
542 
543 	}
544 }
545 
546 long GLOBAL_Player_Room = -1;
PrepareIOTreatZone(long flag)547 void PrepareIOTreatZone(long flag)
548 {
549 	static long status = -1;
550 	static Vec3f lastpos;
551 
552 	if ((flag)
553 	        ||	(status == -1))
554 	{
555 		status = 0;
556 		lastpos = ACTIVECAM->pos;
557 	}
558 	else if (status == 3) status = 0;
559 
560 	if (distSqr(ACTIVECAM->pos, lastpos) > square(100.f))
561 	{
562 		status = 0;
563 		lastpos = ACTIVECAM->pos;
564 	}
565 
566 	if (status++) return;
567 
568 	TREATZONE_Clear();
569 	long Cam_Room = ARX_PORTALS_GetRoomNumForPosition(&ACTIVECAM->pos, 1);
570 	GLOBAL_Player_Room = ARX_PORTALS_GetRoomNumForPosition(&player.pos, 1);
571 	TREATZONE_AddIO(entities.player());
572 
573 	short sGlobalPlayerRoom = checked_range_cast<short>(GLOBAL_Player_Room);
574 
575 	for (long i = 0; i < MAX_EQUIPED; i++)
576 	{
577 		if ((player.equiped[i] != 0)
578 		        &&	ValidIONum(player.equiped[i]))
579 		{
580 			Entity * toequip = entities[player.equiped[i]];
581 
582 			if (toequip)
583 			{
584 				toequip->room = sGlobalPlayerRoom;
585 				toequip->room_flags = 0;
586 			}
587 		}
588 	}
589 
590 	if (DRAGINTER) TREATZONE_AddIO(DRAGINTER);
591 
592 	TREATZONE_LIMIT = 3200;
593 
594 	if (RoomDistance)
595 	{
596 		TREATZONE_LIMIT += 600;
597 
598 		if (CURRENTLEVEL == 4)
599 			TREATZONE_LIMIT += 1200;
600 
601 		if (ACTIVECAM->cdepth > 3000)
602 			TREATZONE_LIMIT += 500;
603 
604 		if (ACTIVECAM->cdepth > 4000)
605 			TREATZONE_LIMIT += 500;
606 
607 		if (ACTIVECAM->cdepth > 6000)
608 			TREATZONE_LIMIT += 500;
609 	}
610 
611 	char treat;
612 	for(size_t i = 1; i < entities.size(); i++) {
613 		Entity * io = entities[i];
614 
615 		if ((io)
616 		        &&	((io->show == SHOW_FLAG_IN_SCENE)
617 		             ||	(io->show == SHOW_FLAG_TELEPORTING)
618 		             ||	(io->show == SHOW_FLAG_ON_PLAYER)
619 		             ||	(io->show == SHOW_FLAG_HIDDEN)))
620 		{
621 			if ((io->ioflags & IO_CAMERA) && (!EDITMODE))
622 				treat = 0;
623 			else if ((io->ioflags & IO_MARKER) && (!EDITMODE))
624 				treat = 0;
625 			else if ((io->ioflags & IO_NPC)
626 			         && (io->_npcdata->pathfind.flags & PATHFIND_ALWAYS))
627 			{
628 				treat = 1;
629 			}
630 			else
631 			{
632 				float dists;
633 
634 				if (Cam_Room >= 0)
635 				{
636 					if (io->show == SHOW_FLAG_TELEPORTING)
637 					{
638 						Vec3f pos;
639 						GetItemWorldPosition(io, &pos);
640 						dists = distSqr(ACTIVECAM->pos, pos);
641 					}
642 					else
643 					{
644 						if (io->room_flags & 1)
645 							UpdateIORoom(io);
646 
647 						dists = square(SP_GetRoomDist(&io->pos, &ACTIVECAM->pos, io->room, Cam_Room));
648 					}
649 				}
650 				else
651 				{
652 					if (io->show == SHOW_FLAG_TELEPORTING)
653 					{
654 						Vec3f pos;
655 						GetItemWorldPosition(io, &pos);
656 						dists = distSqr(ACTIVECAM->pos, pos); //&io->pos,&pos);
657 					}
658 					else
659 						dists = distSqr(io->pos, ACTIVECAM->pos);
660 				}
661 
662 				if (dists < square(TREATZONE_LIMIT)) treat = 1;
663 				else treat = 0;
664 
665 			}
666 
667 			if (!treat)
668 			{
669 				if (io == CAMERACONTROLLER)
670 					treat = 1;
671 
672 				if (io == DRAGINTER)
673 					treat = 1;
674 			}
675 
676 			if(io->gameFlags & GFLAG_ISINTREATZONE) {
677 				io->gameFlags |= GFLAG_WASINTREATZONE;
678 			} else {
679 				io->gameFlags &= ~GFLAG_WASINTREATZONE;
680 			}
681 
682 			if(treat) {
683 				io->gameFlags |= GFLAG_ISINTREATZONE;
684 				TREATZONE_AddIO(io);
685 				if((io->ioflags & IO_NPC) && io->_npcdata->weapon) {
686 					Entity * iooo = io->_npcdata->weapon;
687 					iooo->room = io->room;
688 					iooo->room_flags = io->room_flags;
689 				}
690 			} else {
691 				io->gameFlags &= ~GFLAG_ISINTREATZONE;
692 			}
693 
694 			EVENT_SENDER = NULL;
695 
696 			if ((io->gameFlags & GFLAG_ISINTREATZONE)
697 			        && (!(io->gameFlags & GFLAG_WASINTREATZONE)))
698 			{
699 				//coming back; doesn't really matter right now
700 				//	SendIOScriptEvent(entities[i],SM_TREATIN);
701 
702 			}
703 			else if ((!(io->gameFlags & GFLAG_ISINTREATZONE))
704 			         &&	(io->gameFlags & GFLAG_WASINTREATZONE))
705 			{
706 				//going away;
707 				io->gameFlags |= GFLAG_ISINTREATZONE;
708 
709 				if (SendIOScriptEvent(io, SM_TREATOUT) != REFUSE)
710 				{
711 					if (io->ioflags & IO_NPC)
712 						io->_npcdata->pathfind.flags &= ~PATHFIND_ALWAYS;
713 
714 					io->gameFlags &= ~GFLAG_ISINTREATZONE;
715 				}
716 			}
717 		}
718 	}
719 
720 	long M_TREAT = TREATZONE_CUR;
721 
722 	for(size_t i = 1; i < entities.size(); i++) {
723 		Entity * io = entities[i];
724 
725 		if ((io != NULL)
726 		        &&	!(io->gameFlags & GFLAG_ISINTREATZONE)
727 		        && ((io->show == SHOW_FLAG_IN_SCENE)
728 		            ||	(io->show == SHOW_FLAG_TELEPORTING)
729 		            ||	(io->show == SHOW_FLAG_ON_PLAYER)
730 		            ||	(io->show == SHOW_FLAG_HIDDEN)))   // show 5 = ininventory; 15 = destroyed
731 		{
732 			if ((io->ioflags & IO_CAMERA)
733 			        ||	(io->ioflags & IO_ITEM)
734 			        ||	(io->ioflags & IO_MARKER))
735 				continue;
736 
737 			long toadd = 0;
738 
739 			for (long ii = 1; ii < M_TREAT; ii++)
740 			{
741 				Entity * ioo = treatio[ii].io;
742 
743 				if (ioo)
744 				{
745 					if (distSqr(io->pos, ioo->pos) < square(300.f))
746 					{
747 						toadd = 1;
748 						break;
749 					}
750 				}
751 			}
752 
753 			if(toadd) {
754 				TREATZONE_AddIO(io, 1);
755 			}
756 		}
757 	}
758 }
759 
760 //*************************************************************************************
761 //*************************************************************************************
GetNumNodeByName(char * name)762 long GetNumNodeByName(char * name)
763 {
764 	for (long i = 0; i < nodes.nbmax; i++)
765 	{
766 		if ((nodes.nodes[i].exist)
767 		        &&	(!strcmp(name, nodes.nodes[i].name)))
768 			return i;
769 	}
770 
771 	return -1;
772 }
773 
RestoreNodeNumbers()774 void RestoreNodeNumbers() {
775 	for(long i = 0; i < nodes.nbmax; i++) {
776 		for(size_t j = 0; j < MAX_LINKS; j++) {
777 			if(nodes.nodes[i].lnames[j][0] != 0) {
778 				nodes.nodes[i].link[j] = GetNumNodeByName(nodes.nodes[i].lnames[j]);
779 			}
780 		}
781 	}
782 }
783 
ClearNode(long i,long first=0)784 void ClearNode(long i, long first = 0) {
785 
786 	nodes.nodes[i].exist = 0;
787 	nodes.nodes[i].selected = 0;
788 
789 	for (size_t j = 0; j < MAX_LINKS; j++)
790 	{
791 		if ((nodes.nodes[i].link[j] != -1) && (!first))
792 		{
793 			long k = nodes.nodes[i].link[j];
794 
795 			for (size_t l = 0; l < MAX_LINKS; l++)
796 				if (nodes.nodes[k].link[l] == i)
797 					nodes.nodes[k].link[l] = -1;
798 		}
799 
800 		nodes.nodes[i].lnames[j][0] = 0;
801 		nodes.nodes[i].link[j] = -1;
802 	}
803 
804 	strcpy(nodes.nodes[i].name, "");
805 	nodes.nodes[i].pos = Vec3f::ZERO;
806 }
807 
ClearNodes()808 void ClearNodes()
809 {
810 	static long first = 1;
811 
812 	for (long i = 0; i < nodes.nbmax; i++)
813 	{
814 		ClearNode(i, first);
815 	}
816 
817 	first = 0;
818 }
819 //*************************************************************************************
820 //*************************************************************************************
SelectNode(long i)821 void SelectNode(long i)
822 {
823 	if ((i >= nodes.nbmax)
824 	        ||	(i < 0))
825 		return;
826 
827 	if (nodes.nodes[i].exist)  nodes.nodes[i].selected = 1;
828 }
829 
UnselectAllNodes()830 void UnselectAllNodes() {
831 	for(long i = 0; i < nodes.nbmax; i++) {
832 		if(nodes.nodes[i].exist) {
833 			nodes.nodes[i].selected = 0;
834 		}
835 	}
836 }
837 
TranslateSelectedNodes(Vec3f * trans)838 void TranslateSelectedNodes(Vec3f * trans) {
839 	for(long i = 0; i < nodes.nbmax; i++) {
840 		if(nodes.nodes[i].exist && nodes.nodes[i].selected) {
841 			nodes.nodes[i].pos += *trans;
842 		}
843 	}
844 }
845 
IsLinkedNode(long i,long j)846 bool IsLinkedNode(long i, long j)
847 {
848 	if ((!nodes.nodes[i].exist)
849 	        ||	(!nodes.nodes[j].exist))
850 		return false;
851 
852 	for (size_t k = 0; k < MAX_LINKS; k++)
853 	{
854 		if (nodes.nodes[i].link[k] == j) return true;
855 	}
856 
857 	return false;
858 }
859 
CountNodes()860 long CountNodes() {
861 	long count = 0;
862 	for(long i = 0; i < nodes.nbmax; i++) {
863 		if(nodes.nodes[i].exist) {
864 			count++;
865 		}
866 	}
867 	return count;
868 }
869 
AddLink(long i,long j)870 void AddLink(long i, long j)
871 {
872 	if ((!nodes.nodes[i].exist)
873 	        ||	(!nodes.nodes[j].exist))
874 		return;
875 
876 	for (size_t k = 0; k < MAX_LINKS; k++)
877 	{
878 		if (nodes.nodes[i].link[k] == -1)
879 		{
880 			nodes.nodes[i].link[k] = j;
881 			return;
882 		}
883 	}
884 }
885 //*************************************************************************************
886 //*************************************************************************************
RemoveLink(long i,long j)887 void RemoveLink(long i, long j)
888 {
889 	if ((!nodes.nodes[i].exist)
890 	        ||	(!nodes.nodes[j].exist))
891 		return;
892 
893 	for (size_t k = 0; k < MAX_LINKS; k++)
894 	{
895 		if (nodes.nodes[i].link[k] == j)
896 		{
897 			nodes.nodes[i].link[k] = -1;
898 			return;
899 		}
900 	}
901 }
902 //*************************************************************************************
903 //*************************************************************************************
LinkNodeToNode(long i,long j)904 void LinkNodeToNode(long i, long j)
905 {
906 	if ((!IsLinkedNode(i, j))
907 	        ||	(!IsLinkedNode(j, i)))
908 		return;
909 
910 	AddLink(i, j);
911 	AddLink(j, i);
912 }
913 //*************************************************************************************
914 //*************************************************************************************
UnLinkNodeFromNode(long i,long j)915 void UnLinkNodeFromNode(long i, long j)
916 {
917 	if ((!IsLinkedNode(i, j))
918 	        ||	(!IsLinkedNode(j, i)))
919 		return;
920 
921 	RemoveLink(i, j);
922 	RemoveLink(j, i);
923 }
924 //*************************************************************************************
925 //*************************************************************************************
ClearSelectedNodes()926 void ClearSelectedNodes()
927 {
928 	for (long i = 0; i < nodes.nbmax; i++)
929 	{
930 		if ((nodes.nodes[i].exist)
931 		        &&	(nodes.nodes[i].selected))
932 		{
933 			ClearNode(i, 0);
934 		}
935 	}
936 }
937 
938 //*************************************************************************************
939 //*************************************************************************************
ExistNodeName(char * name)940 bool ExistNodeName(char * name)
941 {
942 	for (long i = 0; i < nodes.nbmax; i++)
943 	{
944 		if ((nodes.nodes[i].exist)
945 		        &&	(!strcmp(name, nodes.nodes[i].name)))
946 			return true;
947 	}
948 
949 	return false;
950 }
951 //*************************************************************************************
952 //*************************************************************************************
MakeNodeName(long i)953 void MakeNodeName(long i)
954 {
955 	char name[64];
956 	long o;
957 	//float f;
958 	sprintf(name, "node_%08ld", i);
959 
960 	while (ExistNodeName(name))
961 	{
962 		//f=rnd()*99999999.f;
963 		//o=(long)f;
964 		o = rnd() * 99999999.f;
965 		sprintf(name, "node_%08ld", o);
966 	}
967 
968 	strcpy(nodes.nodes[i].name, name);
969 }
970 
971 //*************************************************************************************
972 //*************************************************************************************
InitNodes(long nb)973 void InitNodes(long nb)
974 {
975 	if (nb < 1) nb = 1;
976 
977 	nodes.init = 1;
978 	nodes.nbmax = nb;
979 	nodes.nodes = (ARX_NODE *)malloc(sizeof(ARX_NODE) * nodes.nbmax);
980 	memset(nodes.nodes, 0, sizeof(ARX_NODE)*nodes.nbmax);
981 	ClearNodes();
982 }
983 //*************************************************************************************
984 //*************************************************************************************
GetFreeNode()985 long GetFreeNode()
986 {
987 	for (long i = 0; i < nodes.nbmax; i++)
988 	{
989 		if (!nodes.nodes[i].exist) return i;
990 	}
991 
992 	return -1;
993 }
994 
ReleaseNode()995 void ReleaseNode() {
996 	free(nodes.nodes), nodes.nodes = NULL;
997 	nodes.nbmax = 0;
998 }
999 
1000 // Removes an IO loaded by a script command
CleanScriptLoadedIO()1001 void CleanScriptLoadedIO() {
1002 	for(size_t i = 1; i < entities.size(); i++) {
1003 		Entity * io = entities[i];
1004 		if(io) {
1005 			if(io->scriptload) {
1006 				delete io;
1007 			} else {
1008 				// TODO why not jus leave it as is?
1009 				io->show = SHOW_FLAG_IN_SCENE;
1010 			}
1011 		}
1012 	}
1013 }
1014 
1015 // Restores an IO to its initial status (Game start Status)
RestoreInitialIOStatus()1016 void RestoreInitialIOStatus() {
1017 	ARX_INTERACTIVE_HideGore(entities.player());
1018 	ARX_NPC_Behaviour_ResetAll();
1019 	if(entities.player()) {
1020 		entities.player()->spellcast_data.castingspell = SPELL_NONE;
1021 	}
1022 	for(size_t i = 1; i < entities.size(); i++) {
1023 		RestoreInitialIOStatusOfIO(entities[i]);
1024 	}
1025 }
1026 
ARX_INTERACTIVE_USEMESH(Entity * io,const res::path & temp)1027 bool ARX_INTERACTIVE_USEMESH(Entity * io, const res::path & temp) {
1028 
1029 	if(!io || temp.empty()) {
1030 		return false;
1031 	}
1032 
1033 	if(io->ioflags & IO_NPC) {
1034 		io->usemesh = "graph/obj3d/interactive/npc" / temp;
1035 	} else if(io->ioflags & IO_FIX) {
1036 		io->usemesh = "graph/obj3d/interactive/fix_inter" / temp;
1037 	} else if (io->ioflags & IO_ITEM) {
1038 		io->usemesh = "graph/obj3d/interactive/items" / temp;
1039 	} else {
1040 		io->usemesh.clear();
1041 	}
1042 
1043 	if(io->usemesh.empty() ) {
1044 		return false;
1045 	}
1046 
1047 	delete io->obj, io->obj = NULL;
1048 
1049 	bool pbox = (!(io->ioflags & IO_FIX) && !(io->ioflags & IO_NPC));
1050 	io->obj = loadObject(io->usemesh, pbox);
1051 
1052 	EERIE_COLLISION_Cylinder_Create(io);
1053 	return true;
1054 }
1055 
ARX_INTERACTIVE_MEMO_TWEAK(Entity * io,TweakType type,const res::path & param1,const res::path & param2)1056 void ARX_INTERACTIVE_MEMO_TWEAK(Entity * io, TweakType type, const res::path & param1, const res::path & param2) {
1057 
1058 	io->tweaks.resize(io->tweaks.size() + 1);
1059 
1060 	io->tweaks.back().type = type;
1061 	io->tweaks.back().param1 = param1;
1062 	io->tweaks.back().param2 = param2;
1063 }
1064 
ARX_INTERACTIVE_APPLY_TWEAK_INFO(Entity * io)1065 void ARX_INTERACTIVE_APPLY_TWEAK_INFO(Entity * io) {
1066 
1067 	for(std::vector<TWEAK_INFO>::const_iterator i = io->tweaks.begin(); i != io->tweaks.end(); ++i) {
1068 		switch(i->type) {
1069 			case TWEAK_REMOVE: EERIE_MESH_TWEAK_Do(io, TWEAK_REMOVE, res::path()); break;
1070 			case TWEAK_TYPE_SKIN: EERIE_MESH_TWEAK_Skin(io->obj, i->param1, i->param2); break;
1071 			case TWEAK_TYPE_ICON: ARX_INTERACTIVE_TWEAK_Icon(io, i->param1); break;
1072 			case TWEAK_TYPE_MESH: ARX_INTERACTIVE_USEMESH(io, i->param1); break;
1073 			default: EERIE_MESH_TWEAK_Do(io, i->type, i->param1);
1074 		}
1075 	}
1076 }
1077 
ARX_INTERACTIVE_ClearIODynData(Entity * io)1078 void ARX_INTERACTIVE_ClearIODynData(Entity * io) {
1079 
1080 	if(!io) {
1081 		return;
1082 	}
1083 
1084 	if(ValidDynLight(io->dynlight)) {
1085 		DynLight[io->dynlight].exist = 0;
1086 	}
1087 	io->dynlight = -1;
1088 
1089 	if(ValidDynLight(io->halo.dynlight)) {
1090 		DynLight[io->halo.dynlight].exist = 0;
1091 	}
1092 	io->halo.dynlight = -1;
1093 
1094 	free(io->symboldraw), io->symboldraw = NULL;
1095 
1096 	io->spellcast_data.castingspell = SPELL_NONE;
1097 }
1098 
ARX_INTERACTIVE_ClearIODynData_II(Entity * io)1099 void ARX_INTERACTIVE_ClearIODynData_II(Entity * io)
1100 {
1101 	if (io)
1102 	{
1103 		ARX_INTERACTIVE_ClearIODynData(io);
1104 
1105 		io->shop_category.clear();
1106 		io->inventory_skin.clear();
1107 
1108 		io->tweaks.clear();
1109 		io->groups.clear();
1110 		ARX_INTERACTIVE_HideGore(io);
1111 		MOLLESS_Clear(io->obj);
1112 		ARX_SCRIPT_Timer_Clear_For_IO(io);
1113 
1114 		io->stepmaterial.clear();
1115 		io->armormaterial.clear();
1116 		io->weaponmaterial.clear();
1117 		io->strikespeech.clear();
1118 
1119 		free(io->lastanimvertex), io->lastanimvertex = NULL, io->nb_lastanimvertex = 0;
1120 
1121 		for (long j = 0; j < MAX_ANIMS; j++)
1122 		{
1123 			EERIE_ANIMMANAGER_ReleaseHandle(io->anims[j]);
1124 			io->anims[j] = NULL;
1125 		}
1126 
1127 		ARX_SPELLS_RemoveAllSpellsOn(io);
1128 		ARX_EQUIPMENT_ReleaseAll(io);
1129 
1130 		if(io->ioflags & IO_NPC) {
1131 			free(io->_npcdata->pathfind.list), io->_npcdata->pathfind.list = NULL;
1132 			memset(&io->_npcdata->pathfind, 0, sizeof(IO_PATHFIND));
1133 			io->_npcdata->pathfind.truetarget = -1;
1134 			io->_npcdata->pathfind.listnb = -1;
1135 			ARX_NPC_Behaviour_Reset(io);
1136 		}
1137 
1138 		delete io->tweakerinfo, io->tweakerinfo = NULL;
1139 
1140 		if (io->inventory != NULL)
1141 		{
1142 			INVENTORY_DATA * id = io->inventory;
1143 
1144 			for (long nj = 0; nj < id->sizey; nj++) {
1145 				for (long ni = 0; ni < id->sizex; ni++) {
1146 					if (id->slot[ni][nj].io != NULL) {
1147 						id->slot[ni][nj].io->destroy();
1148 						id->slot[ni][nj].io = NULL;
1149 					}
1150 				}
1151 			}
1152 
1153 			if ((TSecondaryInventory) && (TSecondaryInventory->io == io))
1154 			{
1155 				TSecondaryInventory = NULL;
1156 			}
1157 
1158 			free(io->inventory);
1159 			io->inventory = NULL;
1160 		}
1161 
1162 		io->inventory = NULL;
1163 		io->gameFlags |= GFLAG_INTERACTIVITY;
1164 
1165 		if(io->tweaky) {
1166 			delete io->obj;
1167 			io->obj = io->tweaky;
1168 			io->tweaky = NULL;
1169 		}
1170 	}
1171 }
1172 
ARX_INTERACTIVE_ClearAllDynData()1173 void ARX_INTERACTIVE_ClearAllDynData() {
1174 	ARX_INTERACTIVE_HideGore(entities.player());
1175 	ARX_NPC_Behaviour_ResetAll();
1176 	for(size_t i = 1; i < entities.size(); i++) {
1177 		ARX_INTERACTIVE_ClearIODynData(entities[i]);
1178 	}
1179 }
1180 
RestoreIOInitPos(Entity * io)1181 static void RestoreIOInitPos(Entity * io) {
1182 	if(io) {
1183 		ARX_INTERACTIVE_Teleport(io, &io->initpos, 0);
1184 		io->pos = io->lastpos = io->initpos;
1185 		io->move = Vec3f::ZERO;
1186 		io->lastmove = Vec3f::ZERO;
1187 		io->angle = io->initangle;
1188 	}
1189 }
1190 
RestoreAllIOInitPos()1191 void RestoreAllIOInitPos() {
1192 	for(size_t i = 1; i < entities.size(); i++) {
1193 		RestoreIOInitPos(entities[i]);
1194 	}
1195 }
1196 
ARX_HALO_SetToNative(Entity * io)1197 void ARX_HALO_SetToNative(Entity * io) {
1198 	io->halo.color = io->halo_native.color;
1199 	io->halo.radius = io->halo_native.radius;
1200 	io->halo.flags = io->halo_native.flags;
1201 }
1202 
RestoreInitialIOStatusOfIO(Entity * io)1203 void RestoreInitialIOStatusOfIO(Entity * io)
1204 {
1205 	if (io)
1206 	{
1207 		ARX_INTERACTIVE_ClearIODynData_II(io);
1208 
1209 		io->shop_multiply = 1.f;
1210 
1211 		ARX_INTERACTIVE_HideGore(io, 1);
1212 
1213 		io->halo_native.color.r = 0.2f;
1214 		io->halo_native.color.g = 0.5f;
1215 		io->halo_native.color.b = 1.f;
1216 		io->halo_native.radius = 45.f;
1217 		io->halo_native.flags = 0;
1218 
1219 		ARX_HALO_SetToNative(io);
1220 		io->halo.dynlight = -1;
1221 
1222 		io->forcedmove = Vec3f::ZERO;
1223 		io->ioflags &= ~IO_NO_COLLISIONS;
1224 		io->ioflags &= ~IO_INVERTED;
1225 		io->lastspeechflag = 2;
1226 
1227 		//HALO_NEGATIVE;
1228 		io->no_collide = -1;
1229 
1230 		for (long i = 0; i < MAX_FLARES; i++)
1231 		{
1232 			if ((flare[i].exist)  && (flare[i].io == io))
1233 			{
1234 				flare[i].io = NULL;
1235 			}
1236 		}
1237 
1238 		io->flarecount = 0;
1239 		io->inzone = NULL;
1240 		io->speed_modif = 0.f;
1241 		io->basespeed = 1.f;
1242 		io->frameloss = 0.f;
1243 		io->sfx_flag = 0;
1244 		io->max_durability = io->durability = 100;
1245 		io->gameFlags &= ~GFLAG_INVISIBILITY;
1246 		io->gameFlags &= ~GFLAG_MEGAHIDE;
1247 		io->gameFlags &= ~GFLAG_NOGORE;
1248 		io->gameFlags &= ~GFLAG_ISINTREATZONE;
1249 		io->gameFlags &= ~GFLAG_PLATFORM;
1250 		io->gameFlags &= ~GFLAG_ELEVATOR;
1251 		io->gameFlags &= ~GFLAG_HIDEWEAPON;
1252 		io->gameFlags &= ~GFLAG_NOCOMPUTATION;
1253 		io->gameFlags &= ~GFLAG_INTERACTIVITYHIDE;
1254 		io->gameFlags &= ~GFLAG_DOOR;
1255 		io->gameFlags &= ~GFLAG_GOREEXPLODE;
1256 		io->invisibility = 0.f;
1257 		io->rubber = BASE_RUBBER;
1258 		io->scale = 1.f;
1259 		io->move = Vec3f::ZERO;
1260 		io->type_flags = 0;
1261 		io->sound = -1;
1262 		io->soundtime = 0;
1263 		io->soundcount = 0;
1264 		io->material = MATERIAL_STONE;
1265 		io->collide_door_time = 0;
1266 		io->ouch_time = 0;
1267 		io->dmg_sum = 0;
1268 		io->ignition = 0.f;
1269 		io->ignit_light = -1;
1270 		io->ignit_sound = audio::INVALID_ID;
1271 
1272 		if ((io->obj) && (io->obj->pbox)) io->obj->pbox->active = 0;
1273 
1274 		io->room = -1;
1275 		io->room_flags = 1;
1276 		RestoreIOInitPos(io);
1277 		ARX_INTERACTIVE_Teleport(io, &io->initpos, 0);
1278 		io->lastanimtime = 1;
1279 		io->secretvalue = -1;
1280 
1281 		if (io->damagedata >= 0) damages[io->damagedata].exist = 0;
1282 
1283 		io->damagedata = -1;
1284 		io->poisonous = 0;
1285 		io->poisonous_count = 0;
1286 
1287 		for (long count = 0; count < MAX_ANIM_LAYERS; count++)
1288 		{
1289 			memset(&io->animlayer[count], 0, sizeof(ANIM_USE));
1290 			io->animlayer[count].cur_anim = NULL;
1291 			io->animlayer[count].next_anim = NULL;
1292 		}
1293 
1294 		if ((io->obj) && (io->obj->pbox))
1295 		{
1296 			io->obj->pbox->storedtiming = 0;
1297 		}
1298 
1299 		io->physics.cyl.origin = io->pos;
1300 		io->physics.cyl.radius = io->original_radius;
1301 		io->physics.cyl.height = io->original_height;
1302 		io->fall = 0;
1303 		io->show = SHOW_FLAG_IN_SCENE;
1304 		io->targetinfo = TARGET_NONE;
1305 		io->spellcast_data.castingspell = SPELL_NONE;
1306 		io->summoner = -1;
1307 		io->spark_n_blood = 0;
1308 
1309 		if (io->ioflags & IO_NPC)
1310 		{
1311 			io->_npcdata->climb_count = 0;
1312 			io->_npcdata->vvpos = -99999.f;
1313 			io->_npcdata->SPLAT_DAMAGES = 0;
1314 			io->_npcdata->speakpitch = 1.f;
1315 			io->_npcdata->behavior = BEHAVIOUR_NONE;
1316 			io->_npcdata->cut = 0;
1317 			io->_npcdata->cuts = 0;
1318 			io->_npcdata->poisonned = 0.f;
1319 			io->_npcdata->blood_color = Color::red;
1320 			io->_npcdata->stare_factor = 1.f;
1321 
1322 			io->_npcdata->weapon = NULL;
1323 			io->_npcdata->weaponinhand = 0;
1324 			io->_npcdata->weapontype = 0;
1325 			io->_npcdata->weaponinhand = 0;
1326 			io->_npcdata->fightdecision = 0;
1327 			io->_npcdata->collid_state = 0;
1328 			io->_npcdata->collid_time = 0;
1329 			io->_npcdata->strike_time = 0;
1330 			io->_npcdata->walk_start_time = 0;
1331 
1332 			io->_npcdata->reachedtarget = 0;
1333 			io->_npcdata->maxlife = 20.f;
1334 			io->_npcdata->life = io->_npcdata->maxlife;
1335 			io->_npcdata->maxmana = 10.f;
1336 			io->_npcdata->mana = io->_npcdata->mana;
1337 			io->_npcdata->critical = 5.f;
1338 			io->infracolor.r = 1.f;
1339 			io->infracolor.g = 0.f;
1340 			io->infracolor.b = 0.2f;
1341 			io->_npcdata->detect = 0;
1342 			io->_npcdata->movemode = WALKMODE;
1343 			io->_npcdata->reach = 20.f;
1344 			io->_npcdata->armor_class = 0;
1345 			io->_npcdata->absorb = 0;
1346 			io->_npcdata->damages = 20;
1347 			io->_npcdata->tohit = 50;
1348 			io->_npcdata->aimtime = 0;
1349 			io->_npcdata->aiming_start = 0;
1350 			io->_npcdata->npcflags = 0;
1351 			io->_npcdata->backstab_skill = 0;
1352 			io->_npcdata->fDetect = -1;
1353 
1354 		}
1355 
1356 		if (io->ioflags & IO_ITEM)
1357 		{
1358 			io->collision = COLLIDE_WITH_PLAYER;
1359 			io->_itemdata->count = 1;
1360 			io->_itemdata->maxcount = 1;
1361 			io->_itemdata->food_value = 0;
1362 			io->_itemdata->playerstacksize = 1;
1363 			io->_itemdata->stealvalue = -1;
1364 			io->_itemdata->LightValue = -1;
1365 		}
1366 		else io->collision = 0;
1367 
1368 		if (io->ioflags & IO_FIX)
1369 		{
1370 			memset(io->_fixdata, 0, sizeof(IO_FIXDATA));
1371 			io->_fixdata->trapvalue = -1;
1372 		}
1373 	}
1374 }
1375 
ARX_INTERACTIVE_TWEAK_Icon(Entity * io,const res::path & s1)1376 void ARX_INTERACTIVE_TWEAK_Icon(Entity * io, const res::path & s1) {
1377 
1378 	if(!io || s1.empty()) {
1379 		return;
1380 	}
1381 
1382 	res::path icontochange = io->classPath().parent() / s1;
1383 
1384 	TextureContainer * tc = TextureContainer::LoadUI(icontochange,
1385 	                                                 TextureContainer::Level);
1386 	if(!tc) {
1387 		tc = TextureContainer::LoadUI("graph/interface/misc/default[icon]");
1388 	}
1389 
1390 	if(tc) {
1391 
1392 		unsigned long w = tc->m_dwWidth >> 5;
1393 		unsigned long h = tc->m_dwHeight >> 5;
1394 
1395 		if ((w << 5) != tc->m_dwWidth) io->sizex = (char)(w + 1);
1396 		else io->sizex = (char)(w);
1397 
1398 		if ((h << 5) != tc->m_dwHeight) io->sizey = (char)(h + 1);
1399 		else io->sizey = (char)(h);
1400 
1401 		if (io->sizex < 1) io->sizex = 1;
1402 		else if (io->sizex > 3) io->sizex = 3;
1403 
1404 		if (io->sizey < 1) io->sizey = 1;
1405 		else if (io->sizey > 3) io->sizey = 3;
1406 
1407 		io->inv = tc;
1408 	}
1409 }
1410 
1411 // Count IO number ignoring ScriptLoaded IOs
GetNumberInterWithOutScriptLoad()1412 long GetNumberInterWithOutScriptLoad() {
1413 	long count = 0;
1414 	for(size_t i = 1; i < entities.size(); i++) {
1415 		if(entities[i] != NULL && !entities[i]->scriptload) {
1416 			count++;
1417 		}
1418 	}
1419 	return count;
1420 }
1421 
1422 // Be careful with this func...
CloneIOItem(Entity * src)1423 Entity * CloneIOItem(Entity * src) {
1424 
1425 	Entity * dest = AddItem(src->classPath());
1426 	if(!dest) {
1427 		return NULL;
1428 	}
1429 
1430 	SendInitScriptEvent(dest);
1431 	dest->inv = src->inv;
1432 	dest->sizex = src->sizex;
1433 	dest->sizey = src->sizey;
1434 	delete dest->obj;
1435 	dest->obj = Eerie_Copy(src->obj);
1436 	CloneLocalVars(dest, src);
1437 	dest->_itemdata->price = src->_itemdata->price;
1438 	dest->_itemdata->maxcount = src->_itemdata->maxcount;
1439 	dest->_itemdata->count = src->_itemdata->count;
1440 	dest->_itemdata->food_value = src->_itemdata->food_value;
1441 	dest->_itemdata->stealvalue = src->_itemdata->stealvalue;
1442 	dest->_itemdata->playerstacksize = src->_itemdata->playerstacksize;
1443 	dest->_itemdata->LightValue = src->_itemdata->LightValue;
1444 
1445 	if(src->_itemdata->equipitem) {
1446 		dest->_itemdata->equipitem = (IO_EQUIPITEM *)malloc(sizeof(IO_EQUIPITEM));
1447 		memcpy(dest->_itemdata->equipitem, src->_itemdata->equipitem,
1448 		       sizeof(IO_EQUIPITEM));
1449 	}
1450 
1451 	dest->locname = src->locname;
1452 
1453 	if(dest->obj->pbox == NULL && src->obj->pbox != NULL) {
1454 		dest->obj->pbox = (PHYSICS_BOX_DATA *)malloc(sizeof(PHYSICS_BOX_DATA));
1455 		memcpy(dest->obj->pbox, src->obj->pbox, sizeof(PHYSICS_BOX_DATA));
1456 		dest->obj->pbox->vert = (PHYSVERT *)malloc(sizeof(PHYSVERT)
1457 		                                           * src->obj->pbox->nb_physvert);
1458 		memcpy(dest->obj->pbox->vert, src->obj->pbox->vert,
1459 		       sizeof(PHYSVERT) * src->obj->pbox->nb_physvert);
1460 	}
1461 
1462 	return dest;
1463 }
1464 
ARX_INTERACTIVE_ConvertToValidPosForIO(Entity * io,Vec3f * target)1465 bool ARX_INTERACTIVE_ConvertToValidPosForIO(Entity * io, Vec3f * target) {
1466 
1467 	EERIE_CYLINDER phys;
1468 	if (io && io != entities.player()) {
1469 		phys.height = io->original_height * io->scale;
1470 		phys.radius = io->original_radius * io->scale;
1471 	} else {
1472 		phys.height = -200;
1473 		phys.radius = 50;
1474 	}
1475 
1476 	phys.origin = *target;
1477 	long count = 0;
1478 	float modx, modz;
1479 
1480 	while(count < 600) {
1481 
1482 		modx = -EEsin(count) * (float)count * ( 1.0f / 3 );
1483 		modz = EEcos(count) * (float)count * ( 1.0f / 3 );
1484 		phys.origin.x = target->x + modx;
1485 		phys.origin.z = target->z + modz;
1486 		float anything = CheckAnythingInCylinder(&phys, io, CFLAG_JUST_TEST);
1487 
1488 		if (EEfabs(anything) < 150.f)
1489 		{
1490 			EERIEPOLY * ep = CheckInPoly(phys.origin.x, phys.origin.y + anything - 20.f, phys.origin.z);
1491 			EERIEPOLY * ep2 = CheckTopPoly(phys.origin.x, phys.origin.y + anything, phys.origin.z);
1492 
1493 			if (ep && ep2 && (EEfabs((phys.origin.y + anything) - ep->center.y) < 20.f))
1494 			{
1495 				target->x = phys.origin.x;
1496 				target->y = phys.origin.y + anything;
1497 				target->z = phys.origin.z;
1498 				return true;
1499 			}
1500 		}
1501 
1502 		count += 5;
1503 	}
1504 
1505 	return false;
1506 }
ARX_INTERACTIVE_TeleportBehindTarget(Entity * io)1507 void ARX_INTERACTIVE_TeleportBehindTarget(Entity * io)
1508 {
1509 	if (!io) return;
1510 
1511 	if (ARX_SCRIPT_GetSystemIOScript(io, "_r_a_t_") < 0)
1512 	{
1513 		long num = ARX_SCRIPT_Timer_GetFree();
1514 
1515 		if (num != -1)
1516 		{
1517 			long t = io->index();
1518 			ActiveTimers++;
1519 			scr_timer[num].es = NULL;
1520 			scr_timer[num].exist = 1;
1521 			scr_timer[num].io = io;
1522 			scr_timer[num].msecs = Random::get(3000, 6000);
1523 			scr_timer[num].name = "_r_a_t_";
1524 			scr_timer[num].pos = -1;
1525 			scr_timer[num].tim = (unsigned long)(arxtime);
1526 			scr_timer[num].times = 1;
1527 			entities[t]->show = SHOW_FLAG_TELEPORTING;
1528 			AddRandomSmoke(io, 10);
1529 			ARX_PARTICLES_Add_Smoke(&io->pos, 3, 20);
1530 			Vec3f pos;
1531 			pos.x = entities[t]->pos.x;
1532 			pos.y = entities[t]->pos.y + entities[t]->physics.cyl.height * ( 1.0f / 2 );
1533 			pos.z = entities[t]->pos.z;
1534 			io->room_flags |= 1;
1535 			io->room = -1;
1536 			ARX_PARTICLES_Add_Smoke(&pos, 3, 20);
1537 			MakeCoolFx(&io->pos);
1538 			io->gameFlags |= GFLAG_INVISIBILITY;
1539 			FRAME_COUNT = 0;
1540 		}
1541 	}
1542 }
ResetVVPos(Entity * io)1543 void ResetVVPos(Entity * io)
1544 {
1545 	if ((io) && (io->ioflags & IO_NPC))
1546 		io->_npcdata->vvpos = io->pos.y;
1547 }
ComputeVVPos(Entity * io)1548 void ComputeVVPos(Entity * io)
1549 {
1550 	if (io->ioflags & IO_NPC)
1551 	{
1552 		float vvp = io->_npcdata->vvpos;
1553 
1554 		if ((vvp == -99999.f) || (vvp == io->pos.y))
1555 		{
1556 			io->_npcdata->vvpos = io->pos.y;
1557 			return;
1558 		}
1559 
1560 		float diff = io->pos.y - vvp;
1561 		float fdiff = EEfabs(diff);
1562 		float eediff = fdiff;
1563 
1564 		if (fdiff > 120.f)
1565 		{
1566 			fdiff = 120.f;
1567 		}
1568 		else
1569 		{
1570 			float mul = ((fdiff * ( 1.0f / 120 )) * 0.9f + 0.6f);
1571 
1572 			if ((eediff < 15.f))
1573 			{
1574 				float val = (float)FrameDiff * ( 1.0f / 4 ) * mul;
1575 
1576 				if (eediff < 10.f)
1577 					val *= ( 1.0f / 10 );
1578 				else
1579 				{
1580 					float ratio = (eediff - 10.f) * ( 1.0f / 5 );
1581 					val = val * ratio + val * (1.f - ratio);
1582 				}
1583 
1584 				fdiff -= val;
1585 			}
1586 			else
1587 			{
1588 				fdiff -= (float)FrameDiff * ( 1.0f / 4 ) * mul;
1589 			}
1590 		}
1591 
1592 		if (fdiff > eediff)
1593 			fdiff = eediff;
1594 
1595 		if (fdiff < 0.f) fdiff = 0.f;
1596 
1597 		if (diff < 0.f) io->_npcdata->vvpos = io->pos.y + fdiff;
1598 		else io->_npcdata->vvpos = io->pos.y - fdiff;
1599 	}
1600 }
1601 
ARX_INTERACTIVE_Teleport(Entity * io,Vec3f * target,long flags)1602 void ARX_INTERACTIVE_Teleport(Entity * io, Vec3f * target, long flags) {
1603 
1604 	if(!io) {
1605 		return;
1606 	}
1607 
1608 	FRAME_COUNT = -1;
1609 	io->gameFlags &= ~GFLAG_NOCOMPUTATION;
1610 	io->room_flags |= 1;
1611 	io->room = -1;
1612 
1613 	if(io == entities.player()) {
1614 		moveto = player.pos = *target + player.baseOffset();
1615 	}
1616 
1617 	// In case it is being dragged... (except for drag teleport update)
1618 	if((!flags & 1) && io == DRAGINTER) { // TODO probably wrong
1619 		Set_DragInter(NULL);
1620 	}
1621 
1622 	if(io->ioflags & IO_NPC) {
1623 		io->_npcdata->vvpos = io->pos.y;
1624 	}
1625 
1626 	Vec3f translate = *target - io->pos;
1627 	io->lastpos = io->physics.cyl.origin = io->pos = *target;
1628 
1629 	if(io->obj) {
1630 		if(io->obj->pbox) {
1631 			if(io->obj->pbox->active) {
1632 				for(long i = 0; i < io->obj->pbox->nb_physvert; i++) {
1633 					io->obj->pbox->vert[i].pos += translate;
1634 				}
1635 				io->obj->pbox->active = 0;
1636 			}
1637 		}
1638 		for(size_t i = 0; i < io->obj->vertexlist.size(); i++) {
1639 			io->obj->vertexlist3[i].v += translate;
1640 		}
1641 	}
1642 
1643 	MOLLESS_Clear(io->obj, 1);
1644 	ResetVVPos(io);
1645 }
1646 
AddInteractive(const res::path & classPath,EntityInstance instance,AddInteractiveFlags flags)1647 Entity * AddInteractive(const res::path & classPath, EntityInstance instance,
1648                         AddInteractiveFlags flags) {
1649 
1650 	const string & ficc = classPath.string();
1651 
1652 	Entity * io = NULL;
1653 	if(boost::contains(ficc, "items")) {
1654 		io = AddItem(classPath, instance, flags);
1655 	} else if(boost::contains(ficc, "npc")) {
1656 		io = AddNPC(classPath, instance, flags);
1657 	} else if(boost::contains(ficc, "fix")) {
1658 		io = AddFix(classPath, instance, flags);
1659 	} else if(boost::contains(ficc, "camera")) {
1660 		io = AddCamera(classPath, instance);
1661 	} else if (boost::contains(ficc, "marker")) {
1662 		io = AddMarker(classPath, instance);
1663 	}
1664 
1665 	return io;
1666 }
1667 //***********************************************************************************
1668 // SetWeapon:
1669 // Links an object designed by path "temp" to the primary attach of interactive object
1670 // "io".
1671 //***********************************************************************************
1672 
SetWeapon_On(Entity * io)1673 void SetWeapon_On(Entity * io) {
1674 
1675 	if(!io || !(io->ioflags & IO_NPC)) {
1676 		return;
1677 	}
1678 
1679 	Entity * ioo = io->_npcdata->weapon;
1680 
1681 	if(ioo && ioo->obj) {
1682 		EERIE_LINKEDOBJ_UnLinkObjectFromObject(io->obj, ioo->obj);
1683 		EERIE_LINKEDOBJ_LinkObjectToObject(io->obj, ioo->obj, "primary_attach", "primary_attach", ioo);
1684 	}
1685 }
1686 
SetWeapon_Back(Entity * io)1687 void SetWeapon_Back(Entity * io) {
1688 
1689 	if(!io || !(io->ioflags & IO_NPC)) {
1690 		return;
1691 	}
1692 
1693 	Entity * ioo = io->_npcdata->weapon;
1694 
1695 	if(ioo && ioo->obj) {
1696 
1697 		EERIE_LINKEDOBJ_UnLinkObjectFromObject(io->obj, ioo->obj);
1698 
1699 		if (io->gameFlags & GFLAG_HIDEWEAPON) return;
1700 
1701 		long ni = io->obj->fastaccess.weapon_attach;
1702 
1703 		if (ni >= 0)
1704 			EERIE_LINKEDOBJ_LinkObjectToObject(io->obj, ioo->obj, "weapon_attach", "primary_attach", ioo);
1705 		else
1706 		{
1707 			ni = io->obj->fastaccess.secondary_attach;
1708 
1709 			if (ni >= 0)
1710 				EERIE_LINKEDOBJ_LinkObjectToObject(io->obj, ioo->obj, "secondary_attach", "primary_attach", ioo);
1711 		}
1712 	}
1713 }
1714 
Prepare_SetWeapon(Entity * io,const res::path & temp)1715 void Prepare_SetWeapon(Entity * io, const res::path & temp) {
1716 
1717 	arx_assert(io && (io->ioflags & IO_NPC));
1718 
1719 	if(io->_npcdata->weapon) {
1720 		Entity * ioo = io->_npcdata->weapon;
1721 		EERIE_LINKEDOBJ_UnLinkObjectFromObject(io->obj, ioo->obj);
1722 		io->_npcdata->weapon = NULL;
1723 		delete ioo;
1724 	}
1725 
1726 	res::path file = "graph/obj3d/interactive/items/weapons" / temp / temp;
1727 	Entity * ioo = io->_npcdata->weapon = AddItem(file);
1728 	if(ioo) {
1729 
1730 		SendIOScriptEvent(ioo, SM_INIT);
1731 		SendIOScriptEvent(ioo, SM_INITEND);
1732 		io->_npcdata->weapontype = ioo->type_flags;
1733 		ioo->show = SHOW_FLAG_LINKED;
1734 		ioo->scriptload = 2;
1735 
1736 		SetWeapon_Back(io);
1737 	}
1738 }
1739 
GetIOScript(Entity * io,const res::path & script)1740 static void GetIOScript(Entity * io, const res::path & script) {
1741 	loadScript(io->script, resources->getFile(script));
1742 }
1743 
1744 // Links an Interactive Object to another interactive object using an attach point
LinkObjToMe(Entity * io,Entity * io2,const std::string & attach)1745 void LinkObjToMe(Entity * io, Entity * io2, const std::string & attach) {
1746 
1747 	if(!io || !io2) {
1748 		return;
1749 	}
1750 
1751 	RemoveFromAllInventories(io2);
1752 	io2->show = SHOW_FLAG_LINKED;
1753 	EERIE_LINKEDOBJ_LinkObjectToObject(io->obj, io2->obj, attach, attach, io2);
1754 }
1755 
1756 // Creates a Temporary IO Ident
MakeTemporaryIOIdent(Entity * io)1757 static void MakeTemporaryIOIdent(Entity * io) {
1758 
1759 	if(!io) {
1760 		return;
1761 	}
1762 
1763 	// TODO keep the current game open all the time (or even in memory)
1764 	ARX_Changelevel_CurGame_Open();
1765 
1766 	std::string className = io->short_name();
1767 	res::path classDir = io->classPath().parent();
1768 
1769 	for(long t = 1; ; t++) {
1770 
1771 		// Check if the candidate instance number is used in the current scene
1772 		bool used = false;
1773 		// TODO replace this loop by an (className, instance) index
1774 		for(size_t i = 0; i < entities.size(); i++) {
1775 			if(entities[i] && entities[i]->ident == t && io != entities[i]) {
1776 				if(entities[i]->short_name() == className) {
1777 					used = true;
1778 					break;
1779 				}
1780 			}
1781 		}
1782 		if(used) {
1783 			continue;
1784 		}
1785 
1786 		std::stringstream ss;
1787 		ss << className << '_' << std::setw(4) << std::setfill('0') << t;
1788 
1789 		// Check if the candidate instance number is reserved for any scene
1790 		if(resources->getDirectory(classDir / ss.str())) {
1791 			continue;
1792 		}
1793 
1794 		// Check if the candidate instance number is used in any visited area
1795 		if(ARX_Changelevel_CurGame_Seek(ss.str())) {
1796 			continue;
1797 		}
1798 
1799 		io->ident = t;
1800 
1801 		ARX_Changelevel_CurGame_Close();
1802 
1803 		return;
1804 	}
1805 }
1806 
AddFix(const res::path & classPath,EntityInstance instance,AddInteractiveFlags flags)1807 Entity * AddFix(const res::path & classPath, EntityInstance instance,
1808                 AddInteractiveFlags flags) {
1809 
1810 	res::path object = classPath + ".teo";
1811 	res::path script = classPath + ".asl";
1812 
1813 	if(!resources->getFile(("game" / classPath) + ".ftl")
1814 	   && !resources->getFile(object) && !resources->getFile(script)) {
1815 		return NULL;
1816 	}
1817 
1818 	Entity * io = new Entity(classPath);
1819 
1820 	if(instance == -1) {
1821 		MakeTemporaryIOIdent(io);
1822 	} else {
1823 		arx_assert(instance > 0);
1824 		io->ident = instance;
1825 	}
1826 
1827 	io->_fixdata = (IO_FIXDATA *)malloc(sizeof(IO_FIXDATA));
1828 	memset(io->_fixdata, 0, sizeof(IO_FIXDATA));
1829 	io->ioflags = IO_FIX;
1830 	io->_fixdata->trapvalue = -1;
1831 
1832 	GetIOScript(io, script);
1833 
1834 	if(!(flags & NO_ON_LOAD)) {
1835 		SendIOScriptEvent(io, SM_LOAD);
1836 	}
1837 
1838 	io->spellcast_data.castingspell = SPELL_NONE;
1839 	io->pos = player.pos;
1840 	io->pos.x -= EEsin(radians(player.angle.b)) * 140.f;
1841 	io->pos.z += EEcos(radians(player.angle.b)) * 140.f;
1842 	io->lastpos = io->initpos = io->pos;
1843 	io->lastpos.x = io->initpos.x = EEfabs(io->initpos.x / 20) * 20.f;
1844 	io->lastpos.z = io->initpos.z = EEfabs(io->initpos.z / 20) * 20.f;
1845 
1846 	float tempo;
1847 	EERIEPOLY * ep = CheckInPoly(io->pos.x, io->pos.y + player.baseHeight(), io->pos.z);
1848 	if(ep && GetTruePolyY(ep, &io->pos, &tempo)) {
1849 		io->lastpos.y = io->initpos.y = io->pos.y = tempo;
1850 	}
1851 
1852 	ep = CheckInPoly(io->pos.x, player.pos.y, io->pos.z);
1853 	if(ep) {
1854 		io->pos.y = min(ep->v[0].p.y, ep->v[1].p.y);
1855 		io->lastpos.y = io->initpos.y = io->pos.y = min(io->pos.y, ep->v[2].p.y);
1856 	}
1857 
1858 	if(!io->obj && !(flags & NO_MESH)) {
1859 		io->obj = loadObject(object, false);
1860 	}
1861 
1862 	io->infracolor = Color3f(0.6f, 0.f, 1.f);
1863 
1864 	TextureContainer * tc = TextureContainer::LoadUI("graph/interface/misc/default[icon]");
1865 	if(tc) {
1866 		unsigned long w = tc->m_dwWidth >> 5;
1867 		unsigned long h = tc->m_dwHeight >> 5;
1868 		io->sizex = char(clamp(((w << 5) != tc->m_dwWidth) ? (w + 1) : w, 1ul, 3ul));
1869 		io->sizey = char(clamp(((h << 5) != tc->m_dwHeight) ? (h + 1) : h, 1ul, 3ul));
1870 		io->inv = tc;
1871 	}
1872 
1873 	io->collision = COLLIDE_WITH_PLAYER;
1874 
1875 	return io;
1876 }
1877 
AddCamera(const res::path & classPath,EntityInstance instance)1878 static Entity * AddCamera(const res::path & classPath,
1879                           EntityInstance instance) {
1880 
1881 	res::path object = classPath + ".teo";
1882 	res::path script = classPath + ".asl";
1883 
1884 	if(!resources->getFile(("game" / classPath) + ".ftl")
1885 	   && !resources->getFile(object) && !resources->getFile(script)) {
1886 		return NULL;
1887 	}
1888 
1889 	Entity * io = new Entity(classPath);
1890 
1891 	if(instance == -1) {
1892 		MakeTemporaryIOIdent(io);
1893 	} else {
1894 		arx_assert(instance > 0);
1895 		io->ident = instance;
1896 	}
1897 
1898 	GetIOScript(io, script);
1899 
1900 	io->pos = player.pos;
1901 	io->pos.x -= EEsin(radians(player.angle.b)) * 140.f;
1902 	io->pos.z += EEcos(radians(player.angle.b)) * 140.f;
1903 	io->lastpos = io->initpos = io->pos;
1904 	io->lastpos.x = io->initpos.x = EEfabs(io->initpos.x / 20) * 20.f;
1905 	io->lastpos.z = io->initpos.z = EEfabs(io->initpos.z / 20) * 20.f;
1906 
1907 	float tempo;
1908 	EERIEPOLY * ep;
1909 	ep = CheckInPoly(io->pos.x, io->pos.y + player.baseHeight(), io->pos.z, &tempo);
1910 	if(ep) {
1911 		io->lastpos.y = io->initpos.y = io->pos.y = tempo;
1912 	}
1913 
1914 	ep = CheckInPoly(io->pos.x, player.pos.y, io->pos.z);
1915 	if(ep) {
1916 		io->pos.y = min(ep->v[0].p.y, ep->v[1].p.y);
1917 		io->lastpos.y = io->initpos.y = io->pos.y = min(io->pos.y, ep->v[2].p.y);
1918 	}
1919 
1920 	io->lastpos.y = io->initpos.y = io->pos.y += player.baseHeight();
1921 
1922 	io->obj = cameraobj;
1923 
1924 	io->_camdata = (IO_CAMDATA *)malloc(sizeof(IO_CAMDATA));
1925 	memcpy(&io->_camdata->cam, &subj, sizeof(EERIE_CAMERA));
1926 	io->_camdata->cam.focal = 350.f;
1927 	io->ioflags = IO_CAMERA;
1928 	io->collision = 0;
1929 
1930 	return io;
1931 }
1932 
AddMarker(const res::path & classPath,EntityInstance instance)1933 static Entity * AddMarker(const res::path & classPath,
1934                           EntityInstance instance) {
1935 
1936 	res::path object = classPath + ".teo";
1937 	res::path script = classPath + ".asl";
1938 
1939 	if(!resources->getFile(("game" / classPath) + ".ftl")
1940 	   && !resources->getFile(object) && !resources->getFile(script)) {
1941 		return NULL;
1942 	}
1943 
1944 	Entity * io = new Entity(classPath);
1945 
1946 	if(instance == -1) {
1947 		MakeTemporaryIOIdent(io);
1948 	} else {
1949 		arx_assert(instance > 0);
1950 		io->ident = instance;
1951 	}
1952 
1953 	GetIOScript(io, script);
1954 
1955 	io->pos = player.pos;
1956 	io->pos.x -= EEsin(radians(player.angle.b)) * 140.f;
1957 	io->pos.z += EEcos(radians(player.angle.b)) * 140.f;
1958 	io->lastpos = io->initpos = io->pos;
1959 	io->lastpos.x = io->initpos.x = EEfabs(io->initpos.x / 20) * 20.f;
1960 	io->lastpos.z = io->initpos.z = EEfabs(io->initpos.z / 20) * 20.f;
1961 
1962 	float tempo;
1963 	EERIEPOLY * ep;
1964 	ep = CheckInPoly(io->pos.x, io->pos.y + player.baseHeight(), io->pos.z);
1965 	if(ep && GetTruePolyY(ep, &io->pos, &tempo)) {
1966 		io->lastpos.y = io->initpos.y = io->pos.y = tempo;
1967 	}
1968 
1969 	ep = CheckInPoly(io->pos.x, player.pos.y, io->pos.z);
1970 	if(ep) {
1971 		io->pos.y = min(ep->v[0].p.y, ep->v[1].p.y);
1972 		io->lastpos.y = io->initpos.y = io->pos.y = min(io->pos.y, ep->v[2].p.y);
1973 	}
1974 
1975 	io->lastpos.y = io->initpos.y = io->pos.y += player.baseHeight();
1976 
1977 	io->obj = markerobj;
1978 	io->ioflags = IO_MARKER;
1979 	io->collision = 0;
1980 
1981 	return io;
1982 }
1983 
ShowIOPath(Entity * io)1984 void ShowIOPath(Entity * io) {
1985 
1986 	for(long i = 0; i < ACTIVEBKG->nbanchors; i++) {
1987 		ANCHOR_DATA * ad = &ACTIVEBKG->anchors[i];
1988 		ad->flags &= ~ANCHOR_FLAG_GREEN_DRAW;
1989 	}
1990 
1991 	if(io && (io->ioflags & IO_NPC)) {
1992 		for(long j = 0; j < io->_npcdata->pathfind.listnb; j++) {
1993 			ANCHOR_DATA * ad = &ACTIVEBKG->anchors[io->_npcdata->pathfind.list[j]];
1994 			ad->flags |= ANCHOR_FLAG_GREEN_DRAW;
1995 		}
1996 	}
1997 }
1998 
1999 #ifdef BUILD_EDITOR
2000 
2001 //*************************************************************************************
2002 // Delete All Selected IOs
2003 //*************************************************************************************
ARX_INTERACTIVE_DeleteByIndex(long i,DeleteByIndexFlags flag)2004 void ARX_INTERACTIVE_DeleteByIndex(long i, DeleteByIndexFlags flag) {
2005 
2006 	if(!ValidIONum(i) || i == 0) {
2007 		return;
2008 	}
2009 
2010 	//Must KILL dir...
2011 	if(!(flag & FLAG_DONTKILLDIR) && entities[i]->scriptload == 0 && entities[i]->ident > 0) {
2012 
2013 		fs::path dir = fs::paths.user / entities[i]->full_name().string();
2014 
2015 		if(fs::is_directory(dir) && !fs::remove_all(dir)) {
2016 			LogError << "Could not remove directory " << dir;
2017 		}
2018 	}
2019 
2020 	delete entities[i];
2021 }
2022 
2023 // Snaps to ground all selected IOs
GroundSnapSelectedIO()2024 void GroundSnapSelectedIO() {
2025 	for(size_t i = 1; i < entities.size(); i++) {
2026 		if(entities[i] != NULL && false /* is selected */) {
2027 			Entity * io = entities[i];
2028 			float ay;
2029 			EERIEPOLY * ep = CheckInPoly(io->pos.x, io->pos.y + player.baseHeight(), io->pos.z);
2030 			if(ep && GetTruePolyY(ep, &io->pos, &ay)) {
2031 				io->initpos.y = io->pos.y = ay;
2032 			}
2033 		}
2034 	}
2035 }
2036 
2037 #endif // BUILD_EDITOR
2038 
IO_NPCDATA()2039 IO_NPCDATA::IO_NPCDATA() {
2040 
2041 	life = maxlife = 20.f;
2042 	mana = maxmana = 0.f;
2043 
2044 	reachedtime = 0ul;
2045 	reachedtarget = 0l;
2046 	weapon = NULL;
2047 	detect = 0;
2048 	movemode = WALKMODE;
2049 	armor_class = 0.f;
2050 	absorb = 0.f;
2051 	damages = 0.f;
2052 	tohit = 0.f;
2053 	aimtime = 0.f;
2054 	critical = 0.f;
2055 	reach = 0.f;
2056 	backstab_skill = 0.f;
2057 
2058 	behavior = 0;
2059 	behavior_param = 0.f;
2060 	tactics = 0l;
2061 	xpvalue = 0l;
2062 	cut = 0l;
2063 
2064 	moveproblem = 0.f;
2065 	weapontype = 0;
2066 	weaponinhand = 0l;
2067 	fightdecision = 0l;
2068 
2069 	look_around_inc = 0.f;
2070 	collid_time = 0ul;
2071 	collid_state = 0l;
2072 	speakpitch = 1.f;
2073 	lastmouth = 0.f;
2074 	ltemp = 0l;
2075 
2076 	memset(stacked, 0, sizeof(IO_BEHAVIOR_DATA) * MAX_STACKED_BEHAVIOR); // TODO use constructor
2077 
2078 	poisonned = 0.f;
2079 	resist_poison = 0;
2080 	resist_magic = 0;
2081 	resist_fire = 0;
2082 
2083 	strike_time = 0;
2084 	walk_start_time = 0;
2085 	aiming_start = 0l;
2086 	npcflags = 0l;
2087 	memset(&pathfind, 0, sizeof(IO_PATHFIND)); // TODO use constructor
2088 	ex_rotate = 0;
2089 	blood_color = Color::red;
2090 
2091 	SPLAT_DAMAGES = 0;
2092 	SPLAT_TOT_NB = 0;
2093 	last_splat_pos = Vec3f::ZERO;
2094 	vvpos = 0.f;
2095 
2096 	climb_count = 0.f;
2097 	stare_factor = 0.f;
2098 	fDetect = 0.f;
2099 	cuts = 0;
2100 	unused = 0;
2101 
2102 }
2103 
~IO_NPCDATA()2104 IO_NPCDATA::~IO_NPCDATA() {
2105 	free(ex_rotate);
2106 	free(pathfind.list);
2107 }
2108 
AddNPC(const res::path & classPath,EntityInstance instance,AddInteractiveFlags flags)2109 Entity * AddNPC(const res::path & classPath, EntityInstance instance,
2110                 AddInteractiveFlags flags) {
2111 
2112 	res::path object = classPath + ".teo";
2113 	res::path script = classPath + ".asl";
2114 
2115 	if(!resources->getFile(("game" / classPath) + ".ftl")
2116 	   && !resources->getFile(object) && !resources->getFile(script)) {
2117 		return NULL;
2118 	}
2119 
2120 	Entity * io = new Entity(classPath);
2121 
2122 	if(instance == -1) {
2123 		MakeTemporaryIOIdent(io);
2124 	} else {
2125 		arx_assert(instance > 0);
2126 		io->ident = instance;
2127 	}
2128 
2129 	io->forcedmove = Vec3f::ZERO;
2130 
2131 	io->_npcdata = new IO_NPCDATA;
2132 	io->ioflags = IO_NPC;
2133 
2134 	GetIOScript(io, script);
2135 
2136 	io->spellcast_data.castingspell = SPELL_NONE;
2137 	io->_npcdata->mana = io->_npcdata->maxmana = 10.f;
2138 	io->_npcdata->critical = 5.f;
2139 	io->_npcdata->reach = 20.f;
2140 	io->_npcdata->stare_factor = 1.f;
2141 
2142 	if(!(flags & NO_ON_LOAD)) {
2143 		SendIOScriptEvent(io, SM_LOAD);
2144 	}
2145 
2146 	io->pos = player.pos;
2147 	io->pos.x -= EEsin(radians(player.angle.b)) * 140.f;
2148 	io->pos.z += EEcos(radians(player.angle.b)) * 140.f;
2149 	io->lastpos = io->initpos = io->pos;
2150 	io->lastpos.x = io->initpos.x = EEfabs(io->initpos.x / 20) * 20.f;
2151 	io->lastpos.z = io->initpos.z = EEfabs(io->initpos.z / 20) * 20.f;
2152 
2153 	float tempo;
2154 	EERIEPOLY * ep = CheckInPoly(io->pos.x, io->pos.y + player.baseHeight(), io->pos.z);
2155 	if(ep && GetTruePolyY(ep, &io->pos, &tempo)) {
2156 		io->lastpos.y = io->initpos.y = io->pos.y = tempo;
2157 	}
2158 
2159 	ep = CheckInPoly(io->pos.x, player.pos.y, io->pos.z);
2160 	if(ep) {
2161 		io->pos.y = min(ep->v[0].p.y, ep->v[1].p.y);
2162 		io->lastpos.y = io->initpos.y = io->pos.y = min(io->pos.y, ep->v[2].p.y);
2163 	}
2164 
2165 	if(!io->obj && !(flags & NO_MESH)) {
2166 		io->obj = loadObject(object, false);
2167 	}
2168 
2169 	io->_npcdata->pathfind.listnb = -1;
2170 	io->_npcdata->behavior = BEHAVIOUR_NONE;
2171 	io->_npcdata->pathfind.truetarget = -1;
2172 
2173 	if(!(flags & NO_MESH) && (flags & IO_IMMEDIATELOAD)) {
2174 		EERIE_COLLISION_Cylinder_Create(io);
2175 	}
2176 
2177 	io->infracolor = Color3f(1.f, 0.f, 0.2f);
2178 	io->collision = COLLIDE_WITH_PLAYER;
2179 	io->inv = NULL;
2180 
2181 	ARX_INTERACTIVE_HideGore(io);
2182 	return io;
2183 }
2184 
2185 //*************************************************************************************
2186 // Reload Scripts (Class & Local) for an IO
2187 //*************************************************************************************
ReloadScript(Entity * io)2188 void ReloadScript(Entity * io) {
2189 
2190 	ARX_SCRIPT_Timer_Clear_For_IO(io);
2191 	ReleaseScript(&io->over_script);
2192 	ReleaseScript(&io->script);
2193 
2194 	loadScript(io->script, resources->getFile(io->classPath() + ".asl"));
2195 	loadScript(io->over_script, resources->getFile((io->full_name() / io->short_name()) + ".asl"));
2196 
2197 	long num = io->index();
2198 
2199 	if (ValidIONum(num))
2200 	{
2201 		if (entities[num] && entities[num]->script.data)
2202 			ScriptEvent::send(&entities[num]->script, SM_INIT, "", entities[num], "");
2203 
2204 		if (entities[num] && entities[num]->over_script.data)
2205 			ScriptEvent::send(&entities[num]->over_script, SM_INIT, "", entities[num], "");
2206 
2207 		if (entities[num] && entities[num]->script.data)
2208 			ScriptEvent::send(&entities[num]->script, SM_INITEND, "", entities[num], "");
2209 
2210 		if (entities[num] && entities[num]->over_script.data)
2211 			ScriptEvent::send(&entities[num]->over_script, SM_INITEND, "", entities[num], "");
2212 	}
2213 }
2214 
2215 // Reloads All Scripts for all IOs
ReloadAllScripts()2216 void ReloadAllScripts() {
2217 	ARX_SCRIPT_Timer_ClearAll();
2218 	for(size_t i = 0; i < entities.size(); i++) {
2219 		if(entities[i]) {
2220 			ReloadScript(entities[i]);
2221 		}
2222 	}
2223 }
2224 
2225 #ifdef BUILD_EDIT_LOADSAVE
2226 
2227 // Creates an unique identifier for an IO
MakeIOIdent(Entity * io)2228 void MakeIOIdent(Entity * io) {
2229 
2230 	if(!io) {
2231 		return;
2232 	}
2233 
2234 	long t = 1;
2235 
2236 	while(io->ident == 0) {
2237 
2238 		fs::path temp = fs::paths.user / io->full_name().string();
2239 
2240 		if(!fs::is_directory(temp)) {
2241 			io->ident = t;
2242 
2243 			if(fs::create_directories(temp)) {
2244 				LogDirCreation(temp);
2245 				WriteIOInfo(io, temp);
2246 			} else {
2247 				LogError << "Could not create a unique identifier " << temp;
2248 			}
2249 		}
2250 
2251 		t++;
2252 	}
2253 }
2254 
2255 #endif // BUILD_EDIT_LOADSAVE
2256 
2257 extern EERIE_3DOBJ	* arrowobj;
2258 
AddItem(const res::path & classPath_,EntityInstance instance,AddInteractiveFlags flags)2259 Entity * AddItem(const res::path & classPath_, EntityInstance instance,
2260                  AddInteractiveFlags flags) {
2261 
2262 	EntityFlags type = IO_ITEM;
2263 
2264 	res::path classPath = classPath_;
2265 
2266 	if(boost::starts_with(classPath.filename(), "gold_coin")) {
2267 		classPath.up() /= "gold_coin";
2268 		type = IO_ITEM | IO_GOLD;
2269 	}
2270 
2271 	if(boost::contains(classPath.string(), "movable")) {
2272 		type = IO_ITEM | IO_MOVABLE;
2273 	}
2274 
2275 	res::path object = classPath + ".teo";
2276 	res::path script = classPath + ".asl";
2277 	res::path icon = classPath + "[icon]";
2278 
2279 	if(!resources->getFile(("game" / classPath) + ".ftl")
2280 	   && !resources->getFile(object) && !resources->getFile(script)) {
2281 		return NULL;
2282 	}
2283 
2284 	if(!resources->getFile(res::path(icon).set_ext("bmp"))) {
2285 		return NULL;
2286 	}
2287 
2288 	Entity * io = new Entity(classPath);
2289 
2290 	if(instance == -1) {
2291 		MakeTemporaryIOIdent(io);
2292 	} else {
2293 		arx_assert(instance > 0);
2294 		io->ident = instance;
2295 	}
2296 
2297 	io->ioflags = type;
2298 	io->_itemdata = (IO_ITEMDATA *)malloc(sizeof(IO_ITEMDATA));
2299 	memset(io->_itemdata, 0, sizeof(IO_ITEMDATA));
2300 	io->_itemdata->count = 1;
2301 	io->_itemdata->maxcount = 1;
2302 	io->_itemdata->food_value = 0;
2303 	io->_itemdata->LightValue = -1;
2304 
2305 	if (io->ioflags & IO_GOLD)
2306 	{
2307 		io->_itemdata->price = 1;
2308 	}
2309 	else
2310 	{
2311 		io->_itemdata->price = 10;
2312 	}
2313 
2314 	io->_itemdata->playerstacksize = 1;
2315 
2316 	GetIOScript(io, script);
2317 
2318 	if(!(flags & NO_ON_LOAD)) {
2319 		SendIOScriptEvent(io, SM_LOAD);
2320 	}
2321 
2322 	io->spellcast_data.castingspell = SPELL_NONE;
2323 	io->lastpos.x = io->initpos.x = io->pos.x = player.pos.x - (float)EEsin(radians(player.angle.b)) * 140.f;
2324 	io->lastpos.y = io->initpos.y = io->pos.y = player.pos.y;
2325 	io->lastpos.z = io->initpos.z = io->pos.z = player.pos.z + (float)EEcos(radians(player.angle.b)) * 140.f;
2326 	io->lastpos.x = io->initpos.x = (float)((long)(io->initpos.x / 20)) * 20.f;
2327 	io->lastpos.z = io->initpos.z = (float)((long)(io->initpos.z / 20)) * 20.f;
2328 
2329 	EERIEPOLY * ep;
2330 	ep = CheckInPoly(io->pos.x, io->pos.y - 60.f, io->pos.z);
2331 
2332 	if (ep)
2333 	{
2334 		float tempo;
2335 
2336 		if (GetTruePolyY(ep, &io->pos, &tempo))
2337 			io->lastpos.y = io->initpos.y = io->pos.y = tempo;
2338 	}
2339 
2340 	ep = CheckInPoly(io->pos.x, player.pos.y, io->pos.z);
2341 
2342 	if (ep)
2343 	{
2344 		io->pos.y = min(ep->v[0].p.y, ep->v[1].p.y);
2345 		io->lastpos.y = io->initpos.y = io->pos.y = min(io->pos.y, ep->v[2].p.y);
2346 	}
2347 
2348 	if(io->ioflags & IO_GOLD) {
2349 		io->obj = GoldCoinsObj[0];
2350 	}
2351 
2352 	if(!io->obj && !(flags & NO_MESH)) {
2353 			io->obj = loadObject(object);
2354 	}
2355 
2356 	TextureContainer * tc;
2357 	if (io->ioflags & IO_MOVABLE) {
2358 		tc = Movable;
2359 	} else if (io->ioflags & IO_GOLD) {
2360 		tc = GoldCoinsTC[0];
2361 	} else {
2362 		tc = TextureContainer::LoadUI(icon, TextureContainer::Level);
2363 	}
2364 	if(!tc) {
2365 		tc = TextureContainer::LoadUI("graph/interface/misc/default[icon]");
2366 	}
2367 
2368 	if (tc)
2369 	{
2370 		unsigned long w = tc->m_dwWidth >> 5;
2371 		unsigned long h = tc->m_dwHeight >> 5;
2372 
2373 		if ((w << 5) != tc->m_dwWidth)
2374 			io->sizex = (char)(w + 1);
2375 		else
2376 			io->sizex = (char)(w);
2377 
2378 		if ((h << 5) != tc->m_dwHeight)
2379 			io->sizey = (char)(h + 1);
2380 		else
2381 			io->sizey = (char)(h);
2382 
2383 		if (io->sizex < 1)
2384 			io->sizex = 1;
2385 		else if (io->sizex > 3)
2386 			io->sizex = 3;
2387 
2388 		if (io->sizey < 1)
2389 			io->sizey = 1;
2390 		else if (io->sizey > 3)
2391 			io->sizey = 3;
2392 
2393 		io->inv = tc;
2394 	}
2395 
2396 	io->infracolor.r = 0.2f;
2397 	io->infracolor.g = 0.2f;
2398 	io->infracolor.b = 1.f;
2399 	io->collision = 0;
2400 
2401 	return io;
2402 }
2403 
2404 //*************************************************************************************
2405 // Returns nearest interactive object found at position x,y
2406 //*************************************************************************************
GetFirstInterAtPos(Vec2s * pos,long flag,Vec3f * _pRef,Entity ** _pTable,int * _pnNbInTable)2407 Entity * GetFirstInterAtPos(Vec2s * pos, long flag, Vec3f * _pRef, Entity ** _pTable, int * _pnNbInTable)
2408 {
2409 	float n;
2410 
2411 	float _fdist = 9999999999.f;
2412 	float fdistBB = 9999999999.f;
2413 	float fMaxDist = flag ? 9999999999.f : 350;
2414 	Entity * foundBB = NULL;
2415 	Entity * foundPixel = NULL;
2416 	bool bPlayerEquiped = false;
2417 
2418 	if (Project.telekinesis)
2419 	{
2420 		fMaxDist = 850;
2421 	}
2422 
2423 	int nStart = 1;
2424 	int nEnd = entities.size();
2425 
2426 	if ((flag == 3) && _pTable && _pnNbInTable)
2427 	{
2428 		nStart = 0;
2429 		nEnd = *_pnNbInTable;
2430 	}
2431 
2432 	for (long i = nStart; i < nEnd; i++)
2433 	{
2434 		bool bPass = true;
2435 
2436 		Entity * io;
2437 
2438 		if ((flag == 3) && _pTable && _pnNbInTable) {
2439 			io = _pTable[i];
2440 		} else {
2441 			io = entities[i];
2442 		}
2443 
2444 		// Is Object Valid ??
2445 		if (io == NULL) continue;
2446 
2447 		if (((io->ioflags & IO_CAMERA) || (io->ioflags & IO_MARKER))
2448 		        && (EDITMODE != 1)) continue;
2449 
2450 		if ((!(io->gameFlags & GFLAG_INTERACTIVITY)) && (!EDITMODE)) continue;
2451 
2452 		// Is Object in TreatZone ??
2453 		bPlayerEquiped = IsEquipedByPlayer(io);
2454 		if ((bPlayerEquiped  && (player.Interface & INTER_MAP))
2455 		    || (io->gameFlags & GFLAG_ISINTREATZONE))
2456 
2457 			// Is Object Displayed on screen ???
2458 			if ((io->show == SHOW_FLAG_IN_SCENE) || (bPlayerEquiped && flag) || (bPlayerEquiped  && (player.Interface & INTER_MAP) && (Book_Mode == BOOKMODE_STATS))) //((io->show==9) && (player.Interface & INTER_MAP)) )
2459 			{
2460 				if ((flag == 2) && _pTable && _pnNbInTable && ((*_pnNbInTable) < 256))
2461 				{
2462 					_pTable[ *_pnNbInTable ] = io;
2463 					(*_pnNbInTable)++;
2464 					continue;
2465 				}
2466 
2467 				if ((pos->x >= io->bbox1.x) && (pos->x <= io->bbox2.x)
2468 				        && (pos->y >= io->bbox1.y) && (pos->y <= io->bbox2.y))
2469 				{
2470 					if (flag && _pRef)
2471 					{
2472 						float flDistanceToRef = distSqr(ACTIVECAM->pos, *_pRef);
2473 						float flDistanceToIO = distSqr(ACTIVECAM->pos, io->pos);
2474 						bPass = bPlayerEquiped || (flDistanceToIO < flDistanceToRef);
2475 					}
2476 
2477 					float fp = fdist(io->pos, player.pos);
2478 
2479 					if ((!flag && (fp <= fMaxDist)) && ((foundBB == NULL) || (fp < fdistBB)))
2480 					{
2481 						fdistBB = fp;
2482 						foundBB = io;
2483 					}
2484 
2485 					if ((io->ioflags & (IO_CAMERA | IO_MARKER | IO_GOLD)) || (bPlayerEquiped && !flag))
2486 					{
2487 						if (bPlayerEquiped)
2488 							fp = 0.f;
2489 						else
2490 							fp = fdist(io->pos, player.pos);
2491 
2492 						if ((fp < fdistBB) || (foundBB == NULL))
2493 						{
2494 							fdistBB = fp;
2495 							foundBB = io;
2496 							foundPixel = io;
2497 						}
2498 
2499 						goto suite;
2500 					}
2501 
2502 					for(size_t j = 0; j < io->obj->facelist.size(); j++) {
2503 
2504 						if(io->animlayer[0].cur_anim != NULL) {
2505 							n = CEDRIC_PtIn2DPolyProjV2(io->obj, &io->obj->facelist[j] , pos->x, pos->y);
2506 						} else {
2507 							n = PtIn2DPolyProj(io->obj, &io->obj->facelist[j] , pos->x, pos->y);
2508 						}
2509 
2510 						if (n > 0.f)
2511 						{
2512 							if (bPlayerEquiped)
2513 								fp = 0.f;
2514 							else
2515 								fp = fdist(io->pos, player.pos);
2516 
2517 							if ((bPass && (fp <= fMaxDist)) && ((fp < _fdist) || (foundPixel == NULL)))
2518 							{
2519 								{
2520 									_fdist = fp;
2521 									foundPixel = io;
2522 									goto suite;
2523 								}
2524 							}
2525 						}
2526 					}
2527 
2528 				suite:
2529 					;
2530 				}
2531 			}
2532 	}
2533 
2534 	if (foundPixel) return foundPixel;
2535 
2536 	return foundBB;
2537 }
IsEquipedByPlayer(const Entity * io)2538 bool IsEquipedByPlayer(const Entity * io)
2539 {
2540 	if (!io)
2541 		return false;
2542 
2543 	if ((io->ioflags & IO_ICONIC) && (io->show == SHOW_FLAG_ON_PLAYER))
2544 		return true;
2545 
2546 	long num = io->index();
2547 
2548 	for (long i = 0; i < MAX_EQUIPED; i++)
2549 	{
2550 		if ((player.equiped[i] != 0) && (player.equiped[i] == num)) return true;
2551 	}
2552 
2553 	return false;
2554 }
2555 
2556 extern long LOOKING_FOR_SPELL_TARGET;
InterClick(Vec2s * pos)2557 Entity * InterClick(Vec2s * pos) {
2558 
2559 	if (IsFlyingOverInventory(pos))
2560 	{
2561 		return NULL;
2562 	}
2563 
2564 	float dist_Threshold;
2565 
2566 	if (LOOKING_FOR_SPELL_TARGET)
2567 		dist_Threshold = 550.f;
2568 	else
2569 		dist_Threshold = 360.f;
2570 
2571 	Entity * io = GetFirstInterAtPos(pos);
2572 
2573 	if(io != NULL)
2574 	{
2575 		if(io->ioflags & IO_NPC) {
2576 			if(closerThan(player.pos, io->pos, dist_Threshold)) {
2577 				return io;
2578 			}
2579 		}
2580 		else if ((Project.telekinesis) || (EDITMODE))
2581 		{
2582 			return io;
2583 		}
2584 		else if (IsEquipedByPlayer(io)
2585 		         || closerThan(player.pos, io->pos, dist_Threshold))
2586 		{
2587 			return io;
2588 		}
2589 	}
2590 
2591 	return NULL;
2592 }
2593 
2594 // Need To upgrade to a more precise collision.
IsCollidingAnyInter(float x,float y,float z,Vec3f * size)2595 long IsCollidingAnyInter(float x, float y, float z, Vec3f * size) {
2596 
2597 	Vec3f pos;
2598 	for(size_t i = 0; i < entities.size(); i++) {
2599 		Entity * io = entities[i];
2600 
2601 		if ((io)
2602 		        && (!(io->ioflags & IO_NO_COLLISIONS))
2603 		        && (io->collision)
2604 		        && (io->gameFlags & GFLAG_ISINTREATZONE)
2605 		        && ((io->ioflags & IO_NPC) || (io->ioflags & IO_FIX))
2606 		        && (io->show == SHOW_FLAG_IN_SCENE)
2607 		   )
2608 		{
2609 			if (io->ioflags & IO_NPC)
2610 			{
2611 				if (io->_npcdata->life <= 0.f)
2612 					goto suitet;
2613 			}
2614 
2615 			pos.x = x;
2616 			pos.y = y;
2617 			pos.z = z;
2618 
2619 			if (IsCollidingInter(io, &pos)) return i;
2620 
2621 			pos.y += size->y;
2622 
2623 			if (IsCollidingInter(io, &pos)) return i;
2624 
2625 		suitet:
2626 			;
2627 		}
2628 	}
2629 
2630 	return -1;
2631 }
2632 
2633 //*************************************************************************************
2634 // To upgrade to a more precise collision.
2635 //*************************************************************************************
IsCollidingInter(Entity * io,Vec3f * pos)2636 static bool IsCollidingInter(Entity * io, Vec3f * pos) {
2637 
2638 	if ((!io)
2639 	        ||	(!io->obj))
2640 		return false;
2641 
2642 	if(closerThan(*pos, io->pos, 190.f)) {
2643 
2644 		vector<EERIE_VERTEX> & vlist = io->obj->vertexlist3;
2645 
2646 		if (io->obj->nbgroups > 4)
2647 		{
2648 			for (long i = 0; i < io->obj->nbgroups; i++)
2649 			{
2650 				long idx = io->obj->grouplist[i].origin;
2651 
2652 				if(!fartherThan(*pos, vlist[idx].v, 50.f)) {
2653 					return true;
2654 				}
2655 			}
2656 		}
2657 		else
2658 		{
2659 			long nbv = io->obj->vertexlist3.size();
2660 			for (long i = 0; i < nbv; i++)
2661 			{
2662 				if (i != io->obj->origin)
2663 					if(!fartherThan(*pos, vlist[i].v, 30.f)) {
2664 						return true;
2665 					}
2666 			}
2667 		}
2668 	}
2669 
2670 	return false;
2671 }
2672 
SetYlsideDeath(Entity * io)2673 void SetYlsideDeath(Entity * io)
2674 {
2675 	io->sfx_flag = SFX_TYPE_YLSIDE_DEATH;
2676 	io->sfx_time = (unsigned long)(arxtime);
2677 }
ARX_INTERACTIVE_CheckCollision(EERIE_3DOBJ * obj,long kk,long source)2678 bool ARX_INTERACTIVE_CheckCollision(EERIE_3DOBJ * obj, long kk, long source)
2679 {
2680 	bool col = false;
2681 	long avoid = -1;
2682 	Entity * io_source = NULL;
2683 
2684 	if (ValidIONum(source))
2685 	{
2686 		io_source = entities[source];
2687 		avoid = io_source->no_collide;
2688 
2689 	}
2690 
2691 	for(size_t i = 1; i < entities.size(); i++) {
2692 		Entity * io = entities[i];
2693 
2694 		if (
2695 		    (io)
2696 				&& long(i) != avoid
2697 		    && (!(io->ioflags & (IO_CAMERA | IO_MARKER | IO_ITEM)))
2698 		    && (io->show == SHOW_FLAG_IN_SCENE)
2699 		    && !(io->ioflags & IO_NO_COLLISIONS)
2700 		    && (io->obj)
2701 		    && (io->obj != obj)
2702 		    && (!io->usepath)
2703 		)
2704 		{
2705 			if (distSqr(io->pos, obj->pbox->vert[0].pos) < square(450.f) && (In3DBBoxTolerance(&obj->pbox->vert[kk].pos, &io->bbox3D, obj->pbox->radius)))
2706 			{
2707 				if ((io->ioflags & IO_NPC) && (io->_npcdata->life > 0.f))
2708 				{
2709 					if (PointInCylinder(&io->physics.cyl, &obj->pbox->vert[kk].pos))
2710 					{
2711 						return true;
2712 					}
2713 				}
2714 				else if (io->ioflags & IO_FIX)
2715 				{
2716 					long step;
2717 					long nbv;
2718 					nbv = io->obj->vertexlist.size();
2719 
2720 					if (nbv < 300) step = 1;
2721 					else if (nbv < 600) step = 2;
2722 					else if (nbv < 1200) step = 4;
2723 					else step = 6;
2724 
2725 					vector<EERIE_VERTEX> & vlist = io->obj->vertexlist3;
2726 
2727 					EERIE_SPHERE sp;
2728 					sp.radius = 22.f;
2729 
2730 					for (long ii = 1; ii < nbv; ii += step)
2731 					{
2732 						if (ii != io->obj->origin)
2733 						{
2734 							sp.origin = vlist[ii].v;
2735 
2736 							if(sp.contains(obj->pbox->vert[kk].pos)) {
2737 								if ((io_source) && (io->gameFlags & GFLAG_DOOR))
2738 								{
2739 									if (float(arxtime) > io->collide_door_time + 500)
2740 									{
2741 										EVENT_SENDER = io_source;
2742 										io->collide_door_time = (unsigned long)(arxtime);
2743 										SendIOScriptEvent(io, SM_COLLIDE_DOOR);
2744 										EVENT_SENDER = io;
2745 										io->collide_door_time = (unsigned long)(arxtime);
2746 										SendIOScriptEvent(io_source, SM_COLLIDE_DOOR);
2747 									}
2748 								}
2749 
2750 								return true;
2751 							}
2752 						}
2753 					}
2754 				}
2755 			}
2756 		}
2757 	}
2758 
2759 	return col;
2760 }
ARX_INTERACTIVE_CheckFULLCollision(EERIE_3DOBJ * obj,long source)2761 bool ARX_INTERACTIVE_CheckFULLCollision(EERIE_3DOBJ * obj, long source)
2762 {
2763 	bool col = false;
2764 	long i;
2765 	long avoid = -1;
2766 	Entity * io_source = NULL;
2767 	Entity * io = NULL;
2768 
2769 	if (ValidIONum(source))
2770 	{
2771 		io_source = entities[source];
2772 		avoid = io_source->no_collide;
2773 
2774 	}
2775 
2776 	for (i = 0; i < TREATZONE_CUR; i++)
2777 	{
2778 		if	((treatio[i].show != SHOW_FLAG_IN_SCENE)
2779 		        ||	((treatio[i].ioflags & IO_NO_COLLISIONS))
2780 		        || (!treatio[i].io)) continue;
2781 
2782 		io = treatio[i].io;
2783 
2784 		if ((io == io_source) || (!io->obj) || (io == entities.player()))
2785 			continue;
2786 
2787 		if (treatio[i].num == avoid) continue;
2788 
2789 		if ((io->ioflags  & (IO_CAMERA | IO_MARKER | IO_ITEM))
2790 		        ||	(io->usepath))
2791 			continue;
2792 
2793 		if ((io->ioflags & IO_NPC)
2794 		        &&	(io_source)
2795 		        &&	(io_source->ioflags & IO_NO_NPC_COLLIDE))
2796 			continue;
2797 
2798 
2799 		if (distSqr(io->pos, obj->pbox->vert[0].pos) < square(600.f) && (In3DBBoxTolerance(&obj->pbox->vert[0].pos, &io->bbox3D, obj->pbox->radius)))
2800 		{
2801 			if ((io->ioflags & IO_NPC) && (io->_npcdata->life > 0.f))
2802 			{
2803 				for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
2804 					if (PointInCylinder(&io->physics.cyl, &obj->pbox->vert[kk].pos))
2805 					{
2806 						return true;
2807 					}
2808 			}
2809 			else if (io->ioflags & IO_FIX)
2810 			{
2811 				long step;
2812 				long nbv;
2813 				nbv = io->obj->vertexlist.size();
2814 				EERIE_SPHERE sp;
2815 				sp.radius = 28.f;
2816 
2817 				if (nbv < 500)
2818 				{
2819 					step = 1;
2820 					sp.radius = 36.f;
2821 				}
2822 				else if (nbv < 900) step = 2;
2823 				else if (nbv < 1500) step = 4;
2824 				else step = 6;
2825 
2826 				vector<EERIE_VERTEX> & vlist = io->obj->vertexlist3;
2827 
2828 
2829 				if (io->gameFlags & GFLAG_PLATFORM)
2830 				{
2831 					for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
2832 					{
2833 						EERIE_SPHERE sphere;
2834 						sphere.origin = obj->pbox->vert[kk].pos;
2835 						sphere.radius = 30.f;
2836 						float miny, maxy;
2837 						miny = io->bbox3D.min.y;
2838 						maxy = io->bbox3D.max.y;
2839 
2840 						if ((maxy <= sphere.origin.y + sphere.radius)
2841 						        ||	(miny >= sphere.origin.y))
2842 							if (In3DBBoxTolerance(&sphere.origin, &io->bbox3D, sphere.radius))
2843 							{
2844 								// TODO why ignore the z components?
2845 								if(closerThan(Vec2f(io->pos.x, io->pos.z), Vec2f(sphere.origin.x, sphere.origin.z), 440.f + sphere.radius)) {
2846 
2847 									EERIEPOLY ep;
2848 									ep.type = 0;
2849 
2850 									for (size_t ii = 0; ii < io->obj->facelist.size(); ii++)
2851 									{
2852 										float cx = 0;
2853 										float cz = 0;
2854 
2855 										for (long idx = 0 ; idx < 3 ; idx++)
2856 										{
2857 											cx			+=	ep.v[idx].p.x	=	io->obj->vertexlist3[ io->obj->facelist[ii].vid[idx] ].v.x;
2858 											ep.v[idx].p.y	=	io->obj->vertexlist3[ io->obj->facelist[ii].vid[idx] ].v.y;
2859 											cz			+=	ep.v[idx].p.z	=	io->obj->vertexlist3[ io->obj->facelist[ii].vid[idx] ].v.z;
2860 										}
2861 
2862 										cx *= ( 1.0f / 3 );
2863 										cz *= ( 1.0f / 3 );
2864 
2865 										for (kk = 0; kk < 3; kk++)
2866 										{
2867 											ep.v[kk].p.x = (ep.v[kk].p.x - cx) * 3.5f + cx;
2868 											ep.v[kk].p.z = (ep.v[kk].p.z - cz) * 3.5f + cz;
2869 										}
2870 
2871 										if (PointIn2DPolyXZ(&ep, sphere.origin.x, sphere.origin.z))
2872 										{
2873 											return true;
2874 										}
2875 									}
2876 								}
2877 							}
2878 					}
2879 				}
2880 
2881 
2882 				for (long ii = 1; ii < nbv; ii += step)
2883 				{
2884 					if (ii != io->obj->origin)
2885 					{
2886 
2887 						if (0)
2888 						{
2889 							for (long jii = 1; jii < nbv; jii += step)
2890 							{
2891 								sp.origin = (vlist[ii].v + vlist[jii].v) * ( 1.0f / 2 );
2892 
2893 								for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
2894 									if(sp.contains(obj->pbox->vert[kk].pos)) {
2895 										if ((io_source) && (io->gameFlags & GFLAG_DOOR))
2896 										{
2897 											if (float(arxtime) > io->collide_door_time + 500)
2898 											{
2899 												EVENT_SENDER = io_source;
2900 												io->collide_door_time = (unsigned long)(arxtime);
2901 												SendIOScriptEvent(io, SM_COLLIDE_DOOR);
2902 												EVENT_SENDER = io;
2903 												io->collide_door_time = (unsigned long)(arxtime);
2904 												SendIOScriptEvent(io_source, SM_COLLIDE_DOOR);
2905 											}
2906 										}
2907 
2908 										return true;
2909 									}
2910 							}
2911 						}
2912 
2913 						sp.origin = vlist[ii].v;
2914 
2915 						for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
2916 							if (sp.contains(obj->pbox->vert[kk].pos))
2917 							{
2918 								if ((io_source) && (io->gameFlags & GFLAG_DOOR))
2919 								{
2920 									if (float(arxtime) > io->collide_door_time + 500)
2921 									{
2922 										EVENT_SENDER = io_source;
2923 										io->collide_door_time = (unsigned long)(arxtime);
2924 										SendIOScriptEvent(io, SM_COLLIDE_DOOR);
2925 										EVENT_SENDER = io;
2926 										io->collide_door_time = (unsigned long)(arxtime);
2927 										SendIOScriptEvent(io_source, SM_COLLIDE_DOOR);
2928 									}
2929 								}
2930 								return true;
2931 							}
2932 					}
2933 				}
2934 			}
2935 		}
2936 
2937 	}
2938 
2939 	return col;
2940 }
2941 
UpdateCameras()2942 void UpdateCameras() {
2943 
2944 	arxtime.get_updated();
2945 
2946 	for(size_t i = 1; i < entities.size(); i++) {
2947 		Entity * io = entities[i];
2948 
2949 		if(io) {
2950 
2951 			// interpolate & send events
2952 			if(io->usepath) {
2953 
2954 				ARX_USE_PATH * aup = io->usepath;
2955 				float diff = float(arxtime) - aup->_curtime;
2956 
2957 				if (aup->aupflags & ARX_USEPATH_FORWARD)
2958 				{
2959 					if (aup->aupflags & ARX_USEPATH_FLAG_FINISHED)
2960 					{
2961 					}
2962 					else aup->_curtime += diff;
2963 				}
2964 
2965 				if (aup->aupflags & ARX_USEPATH_BACKWARD)
2966 				{
2967 					aup->_starttime += diff * 2;
2968 					aup->_curtime += diff;
2969 
2970 					if (aup->_starttime >= aup->_curtime)
2971 						aup->_curtime = aup->_starttime + 1;
2972 				}
2973 
2974 				if (aup->aupflags & ARX_USEPATH_PAUSE)
2975 				{
2976 					aup->_starttime += diff;
2977 					aup->_curtime += diff;
2978 				}
2979 
2980 				long last = ARX_PATHS_Interpolate(aup, &io->pos);
2981 
2982 				if (aup->lastWP != last)
2983 				{
2984 					if (last == -2)
2985 					{
2986 						char str[16];
2987 						sprintf(str, "%ld", aup->path->nb_pathways - 1);
2988 						EVENT_SENDER = NULL;
2989 						SendIOScriptEvent(io, SM_WAYPOINT, str);
2990 						sprintf(str, "waypoint%ld", aup->path->nb_pathways - 1);
2991 						SendIOScriptEvent(io, SM_NULL, "", str);
2992 						SendIOScriptEvent(io, SM_PATHEND);
2993 						aup->lastWP = last;
2994 					}
2995 					else
2996 					{
2997 						last--;
2998 						long _from = aup->lastWP;
2999 						long _to = last;
3000 
3001 						if (_from > _to) _from = -1;
3002 
3003 						if (_from < 0) _from = -1;
3004 
3005 						long ii = _from + 1;
3006 
3007 						char str[16];
3008 						sprintf(str, "%ld", ii);
3009 						EVENT_SENDER = NULL;
3010 						SendIOScriptEvent(io, SM_WAYPOINT, str);
3011 						sprintf(str, "waypoint%ld", ii);
3012 						SendIOScriptEvent(io, SM_NULL, "", str);
3013 
3014 						if (ii == aup->path->nb_pathways)
3015 						{
3016 							SendIOScriptEvent(io, SM_PATHEND);
3017 						}
3018 
3019 						aup->lastWP = last + 1;
3020 					}
3021 				}
3022 
3023 				if ((io->damager_damages > 0)
3024 				        &&	(io->show == SHOW_FLAG_IN_SCENE))
3025 				{
3026 					for(size_t ii = 0; ii < entities.size(); ii++) {
3027 						Entity * ioo = entities[ii];
3028 
3029 						if ((ioo)
3030 						        &&	(ii != i)
3031 						        &&	(ioo->show == SHOW_FLAG_IN_SCENE)
3032 						        &&	(ioo->ioflags & IO_NPC)
3033 						        &&	closerThan(io->pos, ioo->pos, 600.f))
3034 						{
3035 							bool Touched = false;
3036 
3037 							for (size_t ri = 0; ri < io->obj->vertexlist.size(); ri += 3)
3038 							{
3039 								for (size_t rii = 0; rii < ioo->obj->vertexlist.size(); rii += 3)
3040 								{
3041 									if(closerThan(io->obj->vertexlist3[ri].v, ioo->obj->vertexlist3[rii].v, 20.f)) {
3042 										Touched = true;
3043 										ri = io->obj->vertexlist.size();
3044 										break;
3045 									}
3046 								}
3047 							}
3048 
3049 							if (Touched)
3050 								ARX_DAMAGES_DealDamages(ii, io->damager_damages, i, io->damager_type, &ioo->pos);
3051 						}
3052 					}
3053 				}
3054 			}
3055 
3056 			if(io->ioflags & IO_CAMERA) {
3057 
3058 				entities[i]->_camdata->cam.pos = io->pos;
3059 
3060 				if (io->targetinfo != TARGET_NONE) // Follows target
3061 				{
3062 					GetTargetPos(io, (unsigned long)entities[i]->_camdata->cam.smoothing);
3063 					io->target += io->_camdata->cam.translatetarget;
3064 
3065 					if ((io->_camdata->cam.lastinfovalid) && (io->_camdata->cam.smoothing != 0.f))
3066 					{
3067 						Vec3f smoothtarget;
3068 
3069 						float vv = (float)io->_camdata->cam.smoothing;
3070 
3071 						if (vv > 8000) vv = 8000;
3072 
3073 						vv = (8000 - vv) * ( 1.0f / 4000 );
3074 
3075 						float f1 = framedelay * ( 1.0f / 1000 ) * vv;
3076 
3077 						if (f1 > 1.f) f1 = 1.f;
3078 
3079 						float f2 = 1.f - f1;
3080 						smoothtarget = io->target * f2 + io->_camdata->cam.lasttarget * f1;
3081 
3082 						SetTargetCamera(&io->_camdata->cam, smoothtarget.x, smoothtarget.y, smoothtarget.z);
3083 						io->_camdata->cam.lasttarget = smoothtarget;
3084 						io->_camdata->cam.lastinfovalid = true;
3085 						io->_camdata->cam.lastpos = io->_camdata->cam.pos;
3086 					}
3087 					else
3088 					{
3089 						if ((io->target.x == io->_camdata->cam.pos.x)
3090 						        &&	(io->target.y == io->_camdata->cam.pos.y)
3091 						        &&	(io->target.z == io->_camdata->cam.pos.z))
3092 						{
3093 						}
3094 						else SetTargetCamera(&io->_camdata->cam, io->target.x, io->target.y, io->target.z);
3095 
3096 						io->_camdata->cam.lasttarget = io->target;
3097 						io->_camdata->cam.lastinfovalid = true;
3098 						io->_camdata->cam.lastpos = io->_camdata->cam.pos;
3099 					}
3100 
3101 					io->_camdata->cam.angle.b -= 180.f;
3102 					io->_camdata->cam.angle.a = -io->_camdata->cam.angle.a;
3103 					io->angle.a = 0.f;
3104 					io->angle.b = io->_camdata->cam.angle.b + 90.f;
3105 					io->angle.g = 0.f;
3106 				}
3107 				else // no target...
3108 				{
3109 					float tr = radians(MAKEANGLE(io->angle.b + 90));
3110 					io->target.x = io->pos.x - (float)EEsin(tr) * 20.f;
3111 					io->target.y = io->pos.y;
3112 					io->target.z = io->pos.z + (float)EEcos(tr) * 20.f;
3113 					SetTargetCamera(&io->_camdata->cam, io->target.x, io->target.y, io->target.z);
3114 					io->_camdata->cam.lasttarget = io->target;
3115 					io->_camdata->cam.lastinfovalid = true;
3116 					io->_camdata->cam.lastpos = io->_camdata->cam.pos;
3117 				}
3118 			}
3119 		}
3120 	}
3121 }
3122 
ARX_INTERACTIVE_UnfreezeAll()3123 void ARX_INTERACTIVE_UnfreezeAll() {
3124 	for(size_t i = 0; i < entities.size(); i++) {
3125 		if(entities[i]) {
3126 			entities[i]->ioflags &= ~IO_FREEZESCRIPT;
3127 		}
3128 	}
3129 }
3130 
UpdateIOInvisibility(Entity * io)3131 void UpdateIOInvisibility(Entity * io)
3132 {
3133 	if (io && (io->invisibility <= 1.f))
3134 	{
3135 		if ((io->gameFlags & GFLAG_INVISIBILITY) && (io->invisibility < 1.f))
3136 		{
3137 			io->invisibility += framedelay * ( 1.0f / 1000 );
3138 
3139 			if (io->invisibility > 1.f) io->invisibility = 1.f;
3140 		}
3141 		else if ((!(io->gameFlags & GFLAG_INVISIBILITY)) && (io->invisibility != 0.f))
3142 		{
3143 			io->invisibility -= framedelay * ( 1.0f / 1000 );
3144 
3145 			if (io->invisibility < 0.f) io->invisibility = 0.f;
3146 		}
3147 	}
3148 }
3149 extern Entity * DESTROYED_DURING_RENDERING;
3150 
3151 //*************************************************************************************
3152 // Renders Interactive objects.
3153 // Will render objects between distance "from" (included)
3154 // to distance "to" (not included)
3155 // from camera position.
3156 //*************************************************************************************
RenderInter(float from,float to)3157 void RenderInter(float from, float to) {
3158 
3159 	GRenderer->GetTextureStage(0)->SetWrapMode(TextureStage::WrapClamp);
3160 	float val = -0.6f;
3161 	GRenderer->GetTextureStage(0)->SetMipMapLODBias(val);
3162 	Anglef temp;
3163 	EERIEMATRIX mat;
3164 	INTER_DRAW = 0;
3165 	float dist;
3166 	long diff;
3167 
3168 	if (entities.player() && (entities.player()->ignition > 0.f))
3169 	{
3170 		ManageIgnition(entities.player());
3171 	}
3172 
3173 	for(size_t i = 1; i < entities.size(); i++) { // Player isn't rendered here...
3174 
3175 		Entity * io = entities[i];
3176 
3177 		if ((io)
3178 		        &&	(io != DRAGINTER)
3179 		        &&	(io->gameFlags & GFLAG_ISINTREATZONE))
3180 		{
3181 			if ((i == 0) && ((player.Interface & INTER_MAP) && (!(player.Interface & INTER_COMBATMODE)))
3182 			        && (Book_Mode == BOOKMODE_STATS)) continue;
3183 
3184 			if(io->show != SHOW_FLAG_IN_SCENE) {
3185 				continue;
3186 			}
3187 
3188 			if(!EDITMODE && ((io->ioflags & IO_CAMERA) || (io->ioflags & IO_MARKER))) {
3189 				continue;
3190 			}
3191 
3192 			if ((io->obj) &&
3193 			        (io->obj->pbox) &&
3194 			        (io->obj->pbox->active))
3195 			{
3196 				dist = fdist(ACTIVECAM->pos, io->obj->pbox->vert[0].pos);
3197 			}
3198 			else dist = fdist(ACTIVECAM->pos, io->pos);
3199 
3200 			if ((io) && (io->ioflags & IO_NPC) && (io->_npcdata->pathfind.flags  & PATHFIND_ALWAYS))
3201 			{
3202 			}
3203 			else if ((dist < from) || (dist >= to)) continue;
3204 
3205 			UpdateIOInvisibility(io);
3206 
3207 			io->bbox1.x = 9999;
3208 			io->bbox2.x = -1;
3209 
3210 #ifdef BUILD_EDITOR
3211 			if((io->obj) && (io->obj->pbox) && DEBUGNPCMOVE)
3212 				EERIE_PHYSICS_BOX_Show(io->obj);
3213 #endif
3214 
3215 			if (
3216 			    (io->obj)
3217 			    &&	(io->obj->pbox)
3218 			    &&	(io->obj->pbox->active))
3219 			{
3220 				Vec3f tmp = io->obj->pbox->vert[14].pos - io->obj->pbox->vert[13].pos;
3221 				Vec3f up = io->obj->pbox->vert[2].pos - io->obj->pbox->vert[1].pos;
3222 				up += io->obj->pbox->vert[3].pos - io->obj->pbox->vert[4].pos;
3223 				up += io->obj->pbox->vert[10].pos - io->obj->pbox->vert[9].pos;
3224 				up += io->obj->pbox->vert[11].pos - io->obj->pbox->vert[12].pos;
3225 				up *= 0.25f;
3226 
3227 				MatrixSetByVectors(&mat, &up, &tmp);
3228 				mat._14 = mat._24 = mat._34 = 0.f;
3229 				mat._41 = mat._42 = mat._43 = mat._44 = 0.f;
3230 
3231 
3232 			}
3233 
3234 			if(io->animlayer[0].cur_anim) {
3235 
3236 				temp.a = io->angle.a;
3237 				if(io->ioflags & IO_NPC) {
3238 					temp.b = MAKEANGLE(180.f - io->angle.b);
3239 				} else {
3240 					temp.b = MAKEANGLE(270.f - io->angle.b);
3241 				}
3242 				temp.g = io->angle.g;
3243 
3244 				if (io->animlayer[0].flags & EA_PAUSED)
3245 					diff = 0;
3246 				else diff = static_cast<long>(FrameDiff);
3247 
3248 				if ((io == FlyingOverIO)
3249 				        &&	(!(io->ioflags & IO_NPC))
3250 				        &&	io->obj)
3251 					io->obj->drawflags |= DRAWFLAG_HIGHLIGHT;
3252 
3253 				Vec3f pos = io->pos;
3254 
3255 				if (io->ioflags & IO_NPC)
3256 				{
3257 					ComputeVVPos(io);
3258 					pos.y = io->_npcdata->vvpos;
3259 				}
3260 
3261 				bool render = (EDITMODE || !ARX_SCENE_PORTAL_Basic_ClipIO(io));
3262 
3263 				EERIEDrawAnimQuat(io->obj, &io->animlayer[0], &temp, &pos, diff, io, render);
3264 
3265 				if (DESTROYED_DURING_RENDERING)
3266 					continue;
3267 
3268 				if (io->obj)
3269 					io->obj->drawflags &= ~DRAWFLAG_HIGHLIGHT;
3270 			}
3271 			else
3272 			{
3273 				if ((!EDITMODE) && (ARX_SCENE_PORTAL_Basic_ClipIO(io))) continue;
3274 
3275 				temp.a = io->angle.a;
3276 				if(io->ioflags & IO_NPC) {
3277 					temp.b = MAKEANGLE(180.f - io->angle.b);
3278 				} else {
3279 					temp.b = MAKEANGLE(270.f - io->angle.b);
3280 				}
3281 				temp.g = io->angle.g;
3282 
3283 				if ((io->ioflags & IO_GOLD) && io->obj)
3284 				{
3285 					if (io->_itemdata->price <= 3)
3286 					{
3287 						io->obj = GoldCoinsObj[io->_itemdata->price-1];
3288 						io->inv = GoldCoinsTC[io->_itemdata->price-1];
3289 					}
3290 					else if (io->_itemdata->price <= 8)
3291 					{
3292 						io->obj = GoldCoinsObj[3];
3293 						io->inv = GoldCoinsTC[3];
3294 					}
3295 					else if (io->_itemdata->price <= 20)
3296 					{
3297 						io->obj = GoldCoinsObj[4];
3298 						io->inv = GoldCoinsTC[4];
3299 					}
3300 					else if (io->_itemdata->price <= 50)
3301 					{
3302 						io->obj = GoldCoinsObj[5];
3303 						io->inv = GoldCoinsTC[5];
3304 					}
3305 					else
3306 					{
3307 						io->obj = GoldCoinsObj[6];
3308 						io->inv = GoldCoinsTC[6];
3309 					}
3310 				}
3311 
3312 				if ((!(io->ioflags & IO_NPC))
3313 				        ||	(EDITMODE))
3314 				{
3315 					if (io->obj)
3316 					{
3317 						if ((io == FlyingOverIO)
3318 						        &&	(!(io->ioflags & IO_NPC)))
3319 						{
3320 							io->obj->drawflags |= DRAWFLAG_HIGHLIGHT;
3321 						}
3322 
3323 						if ((io->obj->pbox)
3324 						        &&	(io->obj->pbox->active))
3325 						{
3326 							DrawEERIEInterMatrix(io->obj, &mat, &io->pos, io);
3327 						}
3328 						else
3329 						{
3330 							DrawEERIEInter(io->obj, &temp, &io->pos, io);
3331 						}
3332 
3333 						if (DESTROYED_DURING_RENDERING)
3334 							continue;
3335 
3336 						io->obj->drawflags &= ~DRAWFLAG_HIGHLIGHT;
3337 
3338 					}
3339 				}
3340 			}
3341 
3342 			if ((io->ignition > 0.f) || (io->ioflags & IO_FIERY))
3343 				ManageIgnition(io);
3344 
3345 			if(NEED_TEST_TEXT || EDITMODE) {
3346 				Color color = Color::blue;
3347 				if(io->bbox1.x != io->bbox2.x && io->bbox1.x < DANAESIZX) {
3348 					EERIEDraw2DLine(io->bbox1.x, io->bbox1.y, io->bbox2.x, io->bbox1.y, 0.01f, color);
3349 					EERIEDraw2DLine(io->bbox2.x, io->bbox1.y, io->bbox2.x, io->bbox2.y, 0.01f, color);
3350 					EERIEDraw2DLine(io->bbox2.x, io->bbox2.y, io->bbox1.x, io->bbox2.y, 0.01f, color);
3351 					EERIEDraw2DLine(io->bbox1.x, io->bbox2.y, io->bbox1.x, io->bbox1.y, 0.01f, color);
3352 				}
3353 			}
3354 		}
3355 	}
3356 
3357 
3358 	GRenderer->GetTextureStage(0)->SetWrapMode(TextureStage::WrapRepeat);
3359 	val = -0.3f;
3360 	GRenderer->GetTextureStage(0)->SetMipMapLODBias(val);
3361 }
3362 
ARX_INTERACTIVE_DestroyIO(Entity * ioo)3363 void ARX_INTERACTIVE_DestroyIO(Entity * ioo)
3364 {
3365 	if (ioo)
3366 	{
3367 		if (ioo->show == SHOW_FLAG_DESTROYED)
3368 			return;
3369 
3370 		ARX_INTERACTIVE_ForceIOLeaveZone(ioo, 0);
3371 
3372 		if (DRAGINTER == ioo)
3373 			Set_DragInter(NULL);
3374 
3375 		if (FlyingOverIO == ioo)
3376 			FlyingOverIO = NULL;
3377 
3378 		if (COMBINE == ioo)
3379 			COMBINE = NULL;
3380 
3381 		if ((ioo->ioflags & IO_ITEM) && (ioo->_itemdata->count > 1))
3382 		{
3383 			ioo->_itemdata->count--;
3384 		}
3385 		else
3386 		{
3387 			// Kill all spells
3388 			long numm = ioo->index();
3389 
3390 			if (ValidIONum(numm))
3391 				ARX_SPELLS_FizzleAllSpellsFromCaster(numm);
3392 
3393 			// Need To Kill timers
3394 			ARX_SCRIPT_Timer_Clear_By_IO(ioo);
3395 			ioo->show = SHOW_FLAG_DESTROYED;
3396 			ioo->gameFlags &= ~GFLAG_ISINTREATZONE;
3397 
3398 			if (!FAST_RELEASE)
3399 				RemoveFromAllInventories(ioo);
3400 
3401 			if (ioo->obj)
3402 			{
3403 				EERIE_3DOBJ * eobj = ioo->obj;
3404 
3405 				while (eobj->nblinked)
3406 				{
3407 					long k = 0;
3408 
3409 					if ((eobj->linked[k].lgroup != -1) && eobj->linked[k].obj)
3410 					{
3411 						Entity * iooo = (Entity *)eobj->linked[k].io;
3412 
3413 						if ((iooo) && ValidIOAddress(iooo))
3414 						{
3415 							EERIE_LINKEDOBJ_UnLinkObjectFromObject(ioo->obj, iooo->obj);
3416 							ARX_INTERACTIVE_DestroyIO(iooo);
3417 						}
3418 					}
3419 				}
3420 			}
3421 
3422 			ARX_INTERACTIVE_DestroyDynamicInfo(ioo);
3423 
3424 			if(ioo->scriptload) {
3425 				delete ioo;
3426 			}
3427 		}
3428 	}
3429 }
3430 
3431 //*************************************************************************************
3432 //
3433 //*************************************************************************************
IsSameObject(Entity * io,Entity * ioo)3434 bool IsSameObject(Entity * io, Entity * ioo)
3435 {
3436 	if ((io == NULL)
3437 	        ||	(ioo == NULL)
3438 	        ||	io->classPath() != ioo->classPath()
3439 	        ||	(io->ioflags & IO_UNIQUE)
3440 	        ||	(io->durability != ioo->durability)
3441 	        ||	(io->max_durability != ioo->max_durability))
3442 		return false;
3443 
3444 	if	((io->ioflags & IO_ITEM)
3445 	        &&	(ioo->ioflags & IO_ITEM)
3446 	        &&	(io->over_script.data == NULL)
3447 	        &&	(ioo->over_script.data == NULL)) {
3448 		if(io->locname == ioo->locname) {
3449 			return true;
3450 		}
3451 	}
3452 
3453 	return false;
3454 }
3455 
intersect(const std::set<std::string> & set1,const std::set<std::string> & set2)3456 static bool intersect(const std::set<std::string> & set1, const std::set<std::string> & set2) {
3457 
3458 	if(set1.empty() || set2.empty()) {
3459 		return false;
3460 	}
3461 
3462 	typedef std::set<std::string>::const_iterator itr;
3463 
3464 	itr it1 = set1.begin();
3465 	itr it2 = set2.begin();
3466 
3467 	if(*it1 > *set2.rbegin() || *it2 > *set1.rbegin()) {
3468 		return false;
3469 	}
3470 
3471 	while(it1 != set1.end() && it2 != set2.end()) {
3472 		if(*it1 == *it2) {
3473 			return true;
3474 		} else if(*it1 < *it2) {
3475 			it1++;
3476 		} else {
3477 			it2++;
3478 		}
3479 	}
3480 
3481 	return false;
3482 }
3483 
HaveCommonGroup(Entity * io,Entity * ioo)3484 bool HaveCommonGroup(Entity * io, Entity * ioo) {
3485 	return io && ioo && intersect(io->groups, ioo->groups);
3486 }
3487 
ARX_INTERACTIVE_GetArmorClass(Entity * io)3488 float ARX_INTERACTIVE_GetArmorClass(Entity * io)
3489 {
3490 	if (!io) return -1;
3491 
3492 	if (!(io->ioflags & IO_NPC)) return -1;
3493 
3494 	float ac = io->_npcdata->armor_class;
3495 
3496 	for (long i = 0; i < io->nb_spells_on; i++)
3497 	{
3498 		long n = io->spells_on[i];
3499 
3500 		if (spells[n].exist)
3501 		{
3502 			switch (spells[n].type)
3503 			{
3504 				case SPELL_ARMOR:
3505 					ac += spells[n].caster_level;
3506 					break;
3507 				case SPELL_LOWER_ARMOR:
3508 					ac -= spells[n].caster_level;
3509 					break;
3510 				default: break;
3511 			}
3512 		}
3513 	}
3514 
3515 	if (ac < 0) ac = 0;
3516 
3517 	return ac;
3518 }
3519 
ARX_INTERACTIVE_ActivatePhysics(long t)3520 void ARX_INTERACTIVE_ActivatePhysics(long t)
3521 {
3522 	if (ValidIONum(t))
3523 	{
3524 		Entity * io = entities[t];
3525 
3526 		if ((io == DRAGINTER)
3527 		        ||	(io->show != SHOW_FLAG_IN_SCENE))
3528 			return;
3529 
3530 		float yy;
3531 		EERIEPOLY * ep = CheckInPoly(io->pos.x, io->pos.y, io->pos.z, &yy);
3532 
3533 		if ((ep) && (yy - io->pos.y < 10.f))
3534 			return;
3535 
3536 		io->obj->pbox->active = 1;
3537 		io->obj->pbox->stopcount = 0;
3538 		Vec3f pos = io->pos;
3539 		io->velocity = Vec3f::ZERO;
3540 		io->stopped = 1;
3541 		Vec3f fallvector = Vec3f(0.0f, 0.000001f, 0.f);
3542 		io->show = SHOW_FLAG_IN_SCENE;
3543 		io->soundtime = 0;
3544 		io->soundcount = 0;
3545 		EERIE_PHYSICS_BOX_Launch(io->obj, &pos, &fallvector);
3546 	}
3547 }
3548 
3549 //-------------------------------------------------------------------------
GetMaterialString(const res::path & texture)3550 string GetMaterialString(const res::path & texture) {
3551 
3552 	const string & origin = texture.string();
3553 
3554 	// need to be precomputed !!!
3555 	if(boost::contains(origin, "stone")) return "stone";
3556 	else if(boost::contains(origin, "marble")) return "stone";
3557 	else if(boost::contains(origin, "rock")) return "stone";
3558 	else if(boost::contains(origin, "wood")) return "wood";
3559 	else if(boost::contains(origin, "wet")) return "wet";
3560 	else if(boost::contains(origin, "mud")) return "wet";
3561 	else if(boost::contains(origin, "blood")) return "wet";
3562 	else if(boost::contains(origin, "bone")) return "wet";
3563 	else if(boost::contains(origin, "flesh")) return "wet";
3564 	else if(boost::contains(origin, "shit")) return "wet";
3565 	else if(boost::contains(origin, "soil")) return "gravel";
3566 	else if(boost::contains(origin, "gravel")) return "gravel";
3567 	else if(boost::contains(origin, "earth")) return "gravel";
3568 	else if(boost::contains(origin, "dust")) return "gravel";
3569 	else if(boost::contains(origin, "sand")) return "gravel";
3570 	else if(boost::contains(origin, "straw")) return "gravel";
3571 	else if(boost::contains(origin, "metal")) return "metal";
3572 	else if(boost::contains(origin, "iron")) return "metal";
3573 	else if(boost::contains(origin, "glass")) return "metal";
3574 	else if(boost::contains(origin, "rust")) return "metal";
3575 	else if(boost::contains(origin, "earth")) return "earth";
3576 	else if(boost::contains(origin, "ice")) return "ice";
3577 	else if(boost::contains(origin, "fabric")) return "carpet";
3578 	else if(boost::contains(origin, "moss")) return "carpet";
3579 	else return "unknown";
3580 }
3581