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