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 "ai/Paths.h"
48
49 #include <cstdlib>
50 #include <cstring>
51 #include <algorithm>
52
53 #include <boost/foreach.hpp>
54
55 #include "animation/Animation.h"
56
57 #include "core/GameTime.h"
58 #include "core/Core.h"
59
60 #include "game/Spells.h"
61 #include "game/NPC.h"
62 #include "game/Player.h"
63 #include "game/Damage.h"
64 #include "game/EntityManager.h"
65 #include "game/Equipment.h"
66 #include "game/Inventory.h"
67
68 #include "graphics/GraphicsModes.h"
69 #include "graphics/GraphicsTypes.h"
70 #include "graphics/Math.h"
71 #include "graphics/Renderer.h"
72 #include "graphics/effects/SpellEffects.h"
73 #include "graphics/particle/ParticleEffects.h"
74 #include "graphics/data/Mesh.h"
75 #include "graphics/data/TextureContainer.h"
76
77 #include "io/resource/ResourcePath.h"
78
79 #include "math/Random.h"
80
81 #include "platform/Platform.h"
82
83 #include "physics/Box.h"
84 #include "physics/Collisions.h"
85
86 #include "scene/GameSound.h"
87 #include "scene/Interactive.h"
88 #include "scene/Light.h"
89
90 #include "script/Script.h"
91
92 using std::min;
93 using std::max;
94 using std::string;
95
96 extern long CHANGE_LEVEL_ICON;
97 extern float FrameDiff;
98 static bool IsPointInField(Vec3f * pos);
99 ARX_PATH ** ARXpaths = NULL;
100 ARX_USE_PATH USE_CINEMATICS_PATH;
101 MASTER_CAMERA_STRUCT MasterCamera;
102 long nbARXpaths = 0;
103 long USE_CINEMATICS_CAMERA = 0;
104
ARX_PATH_ComputeBB(ARX_PATH * ap)105 void ARX_PATH_ComputeBB(ARX_PATH * ap) {
106
107 ap->bbmin = Vec3f::repeat(9999999999.f);
108 ap->bbmax = Vec3f::repeat(-9999999999.f);
109
110 for(long i = 0; i < ap->nb_pathways; i++) {
111 ap->bbmin.x = std::min(ap->bbmin.x, ap->pos.x + ap->pathways[i].rpos.x);
112 ap->bbmax.x = std::max(ap->bbmax.x, ap->pos.x + ap->pathways[i].rpos.x);
113 ap->bbmin.z = std::min(ap->bbmin.z, ap->pos.z + ap->pathways[i].rpos.z);
114 ap->bbmax.z = std::max(ap->bbmax.z, ap->pos.z + ap->pathways[i].rpos.z);
115 }
116
117 if(ap->height > 0) {
118 ap->bbmin.y = ap->pos.y - ap->height;
119 ap->bbmax.y = ap->pos.y;
120 } else {
121 ap->bbmin.y = -99999999.f;
122 ap->bbmax.y = 99999999.f;
123 }
124 }
125
ARX_PATH_ComputeAllBoundingBoxes()126 void ARX_PATH_ComputeAllBoundingBoxes()
127 {
128 for (long i = 0; i < nbARXpaths; i++)
129 {
130 if (ARXpaths[i])
131 {
132 ARX_PATH_ComputeBB(ARXpaths[i]);
133 }
134 }
135 }
ARX_PATH_IsPosInZone(ARX_PATH * ap,float x,float y,float z)136 long ARX_PATH_IsPosInZone(ARX_PATH * ap, float x, float y, float z)
137 {
138 if (x < ap->bbmin.x) return 0;
139
140 if (x > ap->bbmax.x) return 0;
141
142 if (z < ap->bbmin.z) return 0;
143
144 if (z > ap->bbmax.z) return 0;
145
146 if (y < ap->bbmin.y) return 0;
147
148 if (y > ap->bbmax.y) return 0;
149
150 int i, j, c = 0;
151
152 x -= ap->pos.x;
153 z -= ap->pos.z;
154
155 ARX_PATHWAY * app = ap->pathways;
156
157 for (i = 0, j = ap->nb_pathways - 1; i < ap->nb_pathways; j = i++)
158 {
159 Vec3f * pi = &app[i].rpos;
160 Vec3f * pj = &app[j].rpos;
161
162 if ((((pi->z <= z) && (z < pj->z)) ||
163 ((pj->z <= z) && (z < pi->z))) &&
164 (x < (pj->x - pi->x) *(z - pi->z) / (pj->z - pi->z) + pi->x))
165 c = !c;
166 }
167
168 return c;
169 }
ARX_PATH_CheckInZone(Entity * io)170 ARX_PATH * ARX_PATH_CheckInZone(Entity * io)
171 {
172 if (ARXpaths)
173 {
174 Vec3f curpos;
175 GetItemWorldPosition(io, &curpos);
176
177 for (long i = 0; i < nbARXpaths; i++)
178 {
179 if ((ARXpaths[i]) && (ARXpaths[i]->height != 0))
180 {
181 if (ARX_PATH_IsPosInZone(ARXpaths[i], curpos.x, curpos.y, curpos.z))
182 return ARXpaths[i];
183 }
184 }
185 }
186
187 return NULL;
188 }
ARX_PATH_CheckPlayerInZone()189 ARX_PATH * ARX_PATH_CheckPlayerInZone()
190 {
191 if (ARXpaths)
192 for (long i = 0; i < nbARXpaths; i++)
193 {
194 if ((ARXpaths[i]) && (ARXpaths[i]->height != 0))
195 {
196 if (ARX_PATH_IsPosInZone(ARXpaths[i], player.pos.x, player.pos.y + 160.f, player.pos.z))
197 return ARXpaths[i];
198 }
199 }
200
201 return NULL;
202 }
203 long JUST_RELOADED = 0;
204
ARX_PATH_UpdateAllZoneInOutInside()205 void ARX_PATH_UpdateAllZoneInOutInside() {
206
207 if(EDITMODE) {
208 return;
209 }
210
211 static size_t count = 1;
212
213 long f = clamp(static_cast<long>(FrameDiff), 10, 50);
214
215 if(count >= entities.size()) {
216 count = 1;
217 }
218
219 if (entities.size() > 1)
220 for (long tt = 0; tt < f; tt++)
221 {
222 long i = count;
223 Entity * io = entities[i];
224
225 if ((count < entities.size()) && (io)
226 && (io->ioflags & (IO_NPC | IO_ITEM))
227 && (io->show != SHOW_FLAG_MEGAHIDE)
228 && (io->show != SHOW_FLAG_DESTROYED)
229
230 )
231 {
232 ARX_PATH * p = ARX_PATH_CheckInZone(io);
233 ARX_PATH * op = io->inzone;
234
235 if ((op == NULL) && (p == NULL)) goto next; // Not in a zone
236
237 if(op == p) { // Stayed inside Zone OP
238 if (io->show != io->inzone_show)
239 {
240 io->inzone_show = io->show;
241 goto entering;
242 }
243 }
244 else if ((op != NULL) && (p == NULL)) // Leaving Zone OP
245 {
246 SendIOScriptEvent(io, SM_LEAVEZONE, op->name);
247
248 if (!op->controled.empty())
249 {
250 long t = entities.getById(op->controled);
251
252 if (t >= 0)
253 {
254 string str = io->long_name() + ' ' + op->name;
255 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, str);
256 }
257 }
258 }
259 else if ((op == NULL) && (p != NULL)) // Entering Zone P
260 {
261 io->inzone_show = io->show;
262 entering:
263
264 if(JUST_RELOADED && (p->name == "ingot_maker" || p->name == "mauld_user")) {
265 ARX_DEAD_CODE(); // TODO remove JUST_RELOADED global
266 } else {
267 SendIOScriptEvent(io, SM_ENTERZONE, p->name);
268
269 if (!p->controled.empty())
270 {
271 long t = entities.getById(p->controled);
272
273 if (t >= 0)
274 {
275 string params = io->long_name() + ' ' + p->name;
276 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, params);
277 }
278 }
279 }
280 }
281 else
282 {
283 SendIOScriptEvent(io, SM_LEAVEZONE, op->name);
284
285 if (!op->controled.empty())
286 {
287 long t = entities.getById(op->controled);
288
289 if (t >= 0)
290 {
291 string str = io->long_name() + ' ' + op->name;
292 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, str);
293 }
294 }
295
296 io->inzone_show = io->show;
297 SendIOScriptEvent(io, SM_ENTERZONE, p->name);
298
299 if (!p->controled.empty())
300 {
301 long t = entities.getById(p->controled);
302
303 if (t >= 0)
304 {
305 string str = io->long_name() + ' ' + p->name;
306 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, str);
307 }
308 }
309 }
310
311 io->inzone = p;
312 }
313
314 next:
315 count++;
316
317 if (count >= entities.size()) count = 1;
318 }
319
320 // player check*************************************************
321 if (entities.player())
322 {
323 ARX_PATH * p = ARX_PATH_CheckPlayerInZone();
324 ARX_PATH * op = (ARX_PATH *)player.inzone;
325
326 if ((op == NULL) && (p == NULL)) goto suite; // Not in a zone
327
328 if (op == p) // Stayed inside Zone OP
329 {
330
331 }
332 else if ((op != NULL) && (p == NULL)) // Leaving Zone OP
333 {
334 SendIOScriptEvent(entities.player(), SM_LEAVEZONE, op->name);
335 CHANGE_LEVEL_ICON = -1;
336
337 if (!op->controled.empty())
338 {
339 long t = entities.getById(op->controled);
340
341 if (t >= 0)
342 {
343 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, "player " + op->name);
344 }
345 }
346 }
347 else if ((op == NULL) && (p != NULL)) // Entering Zone P
348 {
349 SendIOScriptEvent(entities.player(), SM_ENTERZONE, p->name);
350
351 if (p->flags & PATH_AMBIANCE && !p->ambiance.empty())
352 ARX_SOUND_PlayZoneAmbiance(p->ambiance, ARX_SOUND_PLAY_LOOPED, p->amb_max_vol * ( 1.0f / 100 ));
353
354 if (p->flags & PATH_FARCLIP)
355 {
356 desired.flags |= GMOD_ZCLIP;
357 desired.zclip = p->farclip;
358 }
359
360 if (p->flags & PATH_REVERB)
361 {
362 }
363
364 if (p->flags & PATH_RGB)
365 {
366 desired.flags |= GMOD_DCOLOR;
367 desired.depthcolor = p->rgb;
368 }
369
370 if (!p->controled.empty())
371 {
372 long t = entities.getById(p->controled);
373
374 if (t >= 0)
375 {
376 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, "player " + p->name);
377 }
378 }
379 }
380 else
381 {
382
383 if (!op->controled.empty())
384 {
385 long t = entities.getById(op->controled);
386
387 if (t >= 0)
388 {
389 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, "player " + p->name);
390 }
391 }
392
393 if (!op->controled.empty())
394 {
395 long t = entities.getById(p->controled);
396
397 if (t >= 0)
398 {
399 SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, "player " + p->name);
400 }
401 }
402 }
403
404 player.inzone = p;
405 }
406
407
408 suite:
409 JUST_RELOADED = 0;
410 }
411
ARX_PATH(const std::string & _name,const Vec3f & _pos)412 ARX_PATH::ARX_PATH(const std::string & _name, const Vec3f & _pos)
413 : name(_name), initpos(_pos), pos(_pos) {
414
415 flags = 0;
416 nb_pathways = 0;
417 pathways = NULL;
418 height = 0; // 0 NOT A ZONE
419
420 rgb = Color3f::black;
421 farclip = 0.f;
422 reverb = 0.f;
423 amb_max_vol = 0.f;
424 bbmin = Vec3f::ZERO;
425 bbmax = Vec3f::ZERO;
426
427 }
428
ARX_PATH_ClearAllUsePath()429 void ARX_PATH_ClearAllUsePath() {
430 BOOST_FOREACH(Entity * e, entities) {
431 if(e && e->usepath) {
432 free(e->usepath), e->usepath = NULL;
433 }
434 }
435 }
436
ARX_PATH_ClearAllControled()437 void ARX_PATH_ClearAllControled() {
438 for(long i = 0; i < nbARXpaths; i++) {
439 if(ARXpaths[i]) {
440 ARXpaths[i]->controled.clear();
441 }
442 }
443 }
444
ARX_PATH_GetAddressByName(const string & name)445 ARX_PATH * ARX_PATH_GetAddressByName(const string & name) {
446
447 // TODO this is almost the same as ARX_PATHS_ExistName()
448
449 if(name.empty() || !ARXpaths) {
450 return NULL;
451 }
452
453 for(long i = 0; i < nbARXpaths; i++) {
454 if(ARXpaths[i] && ARXpaths[i]->name == name) {
455 return ARXpaths[i];
456 }
457 }
458
459 return NULL;
460 }
461
ARX_PATH_ReleaseAllPath()462 void ARX_PATH_ReleaseAllPath() {
463
464 ARX_PATH_ClearAllUsePath();
465
466 for(long i = 0; i < nbARXpaths; i++) {
467 if(ARXpaths[i]) {
468 free(ARXpaths[i]->pathways), ARXpaths[i]->pathways = NULL;
469 delete ARXpaths[i], ARXpaths[i] = NULL;
470 }
471 }
472
473 free(ARXpaths), ARXpaths = NULL;
474 nbARXpaths = 0;
475 }
476
ARX_PATHS_ExistName(const string & name)477 ARX_PATH * ARX_PATHS_ExistName(const string & name) {
478
479 if(!ARXpaths) {
480 return NULL;
481 }
482
483 for(long i = 0; i < nbARXpaths; i++) {
484 if(ARXpaths[i]->name == name) {
485 return ARXpaths[i];
486 }
487 }
488
489 return NULL;
490 }
491
ARX_PATHS_Interpolate(ARX_USE_PATH * aup,Vec3f * pos)492 long ARX_PATHS_Interpolate(ARX_USE_PATH * aup, Vec3f * pos) {
493
494 ARX_PATH * ap = aup->path;
495
496 // compute Delta Time
497 float tim = aup->_curtime - aup->_starttime;
498
499 if(tim < 0) {
500 return -1;
501 }
502
503 // set pos to startpos
504 *pos = Vec3f::ZERO;
505
506 if(tim == 0) {
507 return 0;
508 }
509
510 // we start at reference waypoint 0 (time & rpos = 0 for this waypoint).
511 long targetwaypoint = 1;
512 aup->aupflags &= ~ARX_USEPATH_FLAG_FINISHED;
513
514 if(ap->pathways) {
515 ap->pathways[0]._time = 0;
516 ap->pathways[0].rpos = Vec3f::ZERO;
517 } else {
518 return -1;
519 }
520
521 // While we have time left, iterate
522 while(tim > 0) {
523
524 // Path Ended
525 if(targetwaypoint > ap->nb_pathways - 1) {
526 *pos += ap->pos;
527 aup->aupflags |= ARX_USEPATH_FLAG_FINISHED;
528 return -2;
529 }
530
531 // Manages a Bezier block
532 if(ap->pathways[targetwaypoint - 1].flag == PATHWAY_BEZIER) {
533
534 targetwaypoint += 1;
535 float delta = tim - ap->pathways[targetwaypoint]._time;
536
537 if(delta >= 0) {
538
539 tim = delta;
540
541 if(targetwaypoint < ap->nb_pathways) {
542 *pos = ap->pathways[targetwaypoint].rpos;
543 }
544
545 targetwaypoint += 1;
546
547 } else {
548
549 if(targetwaypoint < ap->nb_pathways) {
550
551 if(ap->pathways[targetwaypoint]._time == 0) {
552 return targetwaypoint - 1;
553 }
554
555 float rel = tim / ap->pathways[targetwaypoint]._time;
556 float mull = square(rel);
557
558 *pos = ap->pos + ap->pathways[targetwaypoint].rpos * mull;
559 *pos += ap->pathways[targetwaypoint - 1].rpos * (rel - mull);
560 *pos += ap->pathways[targetwaypoint - 2].rpos * (1 - rel);
561 }
562
563 return targetwaypoint - 1;
564 }
565
566 } else {
567
568 // Manages a non-Bezier block
569 float delta = tim - ap->pathways[targetwaypoint]._time;
570
571 if(delta >= 0) {
572
573 tim = delta;
574
575 if(targetwaypoint < ap->nb_pathways) {
576 *pos = ap->pathways[targetwaypoint].rpos;
577 }
578
579 targetwaypoint++;
580
581 } else {
582
583 if(targetwaypoint < ap->nb_pathways) {
584
585 if(ap->pathways[targetwaypoint]._time == 0) {
586 return targetwaypoint - 1;
587 }
588
589 float rel = tim / ap->pathways[targetwaypoint]._time;
590
591 *pos += (ap->pathways[targetwaypoint].rpos - *pos) * rel;
592 }
593
594 *pos += ap->pos;
595
596 return targetwaypoint - 1;
597 }
598 }
599 }
600
601 *pos += ap->pos;
602
603 return targetwaypoint;
604 }
605
606 // THROWN OBJECTS MANAGEMENT
607
608 ARX_THROWN_OBJECT Thrown[MAX_THROWN_OBJECTS];
609 long Thrown_Count = 0;
ARX_THROWN_OBJECT_Kill(long num)610 void ARX_THROWN_OBJECT_Kill(long num) {
611 if(num >= 0 && size_t(num) < MAX_THROWN_OBJECTS) {
612 Thrown[num].flags = 0;
613 Thrown_Count--;
614 delete Thrown[num].pRuban, Thrown[num].pRuban = NULL;
615 }
616 }
617
ARX_THROWN_OBJECT_KillAll()618 void ARX_THROWN_OBJECT_KillAll()
619 {
620 for (size_t i = 0; i < MAX_THROWN_OBJECTS; i++)
621 {
622 ARX_THROWN_OBJECT_Kill(i);
623 }
624
625 Thrown_Count = 0;
626 }
627
ARX_THROWN_OBJECT_GetFree()628 long ARX_THROWN_OBJECT_GetFree()
629 {
630 unsigned long latest_time = (unsigned long)(arxtime);
631 long latest_obj = -1;
632
633 for (size_t i = 0; i < MAX_THROWN_OBJECTS; i++)
634 {
635 if (Thrown[i].flags & ATO_EXIST)
636 {
637 if (Thrown[i].creation_time < latest_time)
638 {
639 latest_obj = i;
640 latest_time = Thrown[i].creation_time;
641 }
642 }
643 else
644 {
645 return i;
646 }
647 }
648
649 if (latest_obj >= 0)
650 {
651 ARX_THROWN_OBJECT_Kill(latest_obj);
652 return latest_obj;
653 }
654
655 return -1;
656 }
657
658 extern EERIE_3DOBJ * arrowobj;
659
ARX_THROWN_OBJECT_Throw(long source,Vec3f * position,Vec3f * vect,Vec3f * upvect,EERIE_QUAT * quat,float velocity,float damages,float poison)660 long ARX_THROWN_OBJECT_Throw(long source, Vec3f * position, Vec3f * vect, Vec3f * upvect,
661 EERIE_QUAT * quat, float velocity, float damages, float poison) {
662
663 long num = ARX_THROWN_OBJECT_GetFree();
664
665 if (num >= 0)
666 {
667
668 Thrown[num].damages = damages;
669 Thrown[num].position = *position;
670 Thrown[num].initial_position = *position;
671 Thrown[num].vector = *vect;
672 Thrown[num].upvect = *upvect;
673 Quat_Copy(&Thrown[num].quat, quat);
674 Thrown[num].source = source;
675 Thrown[num].obj = NULL;
676 Thrown[num].velocity = velocity;
677 Thrown[num].poisonous = poison;
678 Thrown[num].pRuban = new CRuban();
679 Thrown[num].pRuban->Create(num, 2000);
680
681 Thrown[num].obj = arrowobj;
682
683 if (Thrown[num].obj)
684 {
685 Thrown[num].creation_time = (unsigned long)(arxtime);
686 Thrown[num].flags |= ATO_EXIST | ATO_MOVING;
687 Thrown_Count++;
688 }
689
690 if ((source == 0)
691 && (player.equiped[EQUIP_SLOT_WEAPON] != 0)
692 && (ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])))
693 {
694 Entity * tio = entities[player.equiped[EQUIP_SLOT_WEAPON]];
695
696 if (tio->ioflags & IO_FIERY)
697 Thrown[num].flags |= ATO_FIERY;
698 }
699
700 }
701
702 return num;
703 }
704
ARX_THROWN_ComputeDamages(long thrownum,long source,long target)705 float ARX_THROWN_ComputeDamages(long thrownum, long source, long target)
706 {
707 float distance_limit = 1000.f;
708 Entity * io_target = entities[target];
709 Entity * io_source = entities[source];
710
711 SendIOScriptEvent(io_target, SM_AGGRESSION);
712
713 float distance = fdist(Thrown[thrownum].position, Thrown[thrownum].initial_position);
714 float distance_modifier = 1.f;
715
716 if (distance < distance_limit * 2.f)
717 {
718 distance_modifier = distance / distance_limit;
719
720 if (distance_modifier < 0.5f)
721 distance_modifier = 0.5f;
722 }
723 else distance_modifier = 2.f;
724
725 float attack, dmgs, backstab, critical, ac;
726
727 backstab = 1.f;
728 critical = false;
729
730 if (source == 0)
731 {
732 attack = Thrown[thrownum].damages;
733
734 if(rnd() * 100 <= float(player.Full_Attribute_Dexterity - 9) * 2.f
735 + float(player.Full_Skill_Projectile * 0.2f)) {
736 if (SendIOScriptEvent(io_source, SM_CRITICAL, "bow") != REFUSE)
737 critical = true;
738 }
739
740 dmgs = attack;
741
742 if (io_target->_npcdata->npcflags & NPCFLAG_BACKSTAB)
743 {
744 if (rnd() * 100.f <= player.Full_Skill_Stealth)
745 {
746 if (SendIOScriptEvent(io_source, SM_BACKSTAB, "bow") != REFUSE)
747 backstab = 1.5f;
748 }
749 }
750 }
751 else
752 {
753 // TODO treat NPC !!!
754
755 ARX_DEAD_CODE();
756 attack = 0;
757 dmgs = 0;
758
759 }
760
761 float absorb;
762
763 if (target == 0)
764 {
765 ac = player.Full_armor_class;
766 absorb = player.Full_Skill_Defense * .5f;
767 }
768 else
769 {
770 ac = ARX_INTERACTIVE_GetArmorClass(io_target);
771 absorb = io_target->_npcdata->absorb;
772 }
773
774 char wmat[64];
775
776 string _amat = "flesh";
777 const string * amat = &_amat;
778
779 strcpy(wmat, "dagger");
780
781 if(!io_target->armormaterial.empty()) {
782 amat = &io_target->armormaterial;
783 }
784
785 if(io_target == entities.player()) {
786 if(player.equiped[EQUIP_SLOT_ARMOR] > 0) {
787 Entity * io = entities[player.equiped[EQUIP_SLOT_ARMOR]];
788 if(io && !io->armormaterial.empty()) {
789 amat = &io->armormaterial;
790 }
791 }
792 }
793
794 float power;
795 power = dmgs * ( 1.0f / 20 );
796
797 if (power > 1.f) power = 1.f;
798
799 power = power * 0.15f + 0.85f;
800
801 ARX_SOUND_PlayCollision(*amat, wmat, power, 1.f, &Thrown[thrownum].position, io_source);
802
803 dmgs *= backstab;
804 dmgs -= dmgs * (absorb * ( 1.0f / 100 ));
805
806 float chance = 100.f - (ac - attack);
807 float dice = rnd() * 100.f;
808
809 if(dice <= chance) {
810 if (dmgs > 0.f)
811 {
812 if (critical)
813 dmgs *= 1.5f;
814
815 dmgs *= distance_modifier;
816 return dmgs;
817 }
818 }
819
820 return 0.f;
821 }
822
CheckArrowPolyCollision(Vec3f * start,Vec3f * end)823 EERIEPOLY * CheckArrowPolyCollision(Vec3f * start, Vec3f * end) {
824
825 EERIE_TRI pol;
826 pol.v[0] = *start;
827 pol.v[2] = *end - Vec3f(2.f, 15.f, 2.f);
828 pol.v[1] = *end;
829
830 long px = end->x * ACTIVEBKG->Xmul;
831 long pz = end->z * ACTIVEBKG->Zmul;
832
833 long ix = std::max(px - 2, 0L);
834 long ax = std::min(px + 2, ACTIVEBKG->Xsize - 1L);
835 long iz = std::max(pz - 2, 0L);
836 long az = std::min(pz + 2, ACTIVEBKG->Zsize - 1L);
837
838 for(long zz = iz; zz <= az; zz++) for(long xx = ix; xx <= ax; xx++) {
839
840 FAST_BKG_DATA * feg = &ACTIVEBKG->fastdata[xx][zz];
841
842 for(long k = 0; k < feg->nbpolyin; k++) {
843
844 EERIEPOLY * ep = feg->polyin[k];
845
846 if(ep->type & (POLY_WATER | POLY_TRANS | POLY_NOCOL)) {
847 continue;
848 }
849
850 EERIE_TRI pol2;
851 pol2.v[0] = ep->v[0].p;
852 pol2.v[1] = ep->v[1].p;
853 pol2.v[2] = ep->v[2].p;
854
855 if(Triangles_Intersect(&pol2, &pol)) {
856 return ep;
857 }
858
859 if(ep->type & POLY_QUAD) {
860 pol2.v[0] = ep->v[1].p;
861 pol2.v[1] = ep->v[3].p;
862 pol2.v[2] = ep->v[2].p;
863 if(Triangles_Intersect(&pol2, &pol)) {
864 return ep;
865 }
866 }
867
868 }
869 }
870
871 return NULL;
872 }
873
CheckExp(long i)874 void CheckExp(long i) {
875
876 if((Thrown[i].flags & ATO_FIERY) && !(Thrown[i].flags & ATO_UNDERWATER)) {
877
878 ARX_BOOMS_Add(&Thrown[i].position);
879 LaunchFireballBoom(&Thrown[i].position, 10);
880 DoSphericDamage(&Thrown[i].position, 4.f * 2, 50.f,
881 DAMAGE_AREA, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL, 0);
882 ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, &Thrown[i].position);
883 ARX_NPC_SpawnAudibleSound(&Thrown[i].position, entities.player());
884 long id = GetFreeDynLight();
885
886 if(id != -1 && FrameDiff > 0) {
887 DynLight[id].exist = 1;
888 DynLight[id].intensity = 3.9f;
889 DynLight[id].fallstart = 400.f;
890 DynLight[id].fallend = 440.f;
891 DynLight[id].rgb = Color3f(1.f - rnd() * .2f, .8f - rnd() * .2f, .6f - rnd() * .2f);
892 DynLight[id].pos = Thrown[i].position;
893 DynLight[id].ex_flaresize = 40.f;
894 DynLight[id].duration = 1500;
895 }
896 }
897 }
898
899 extern long FRAME_COUNT;
900
ARX_THROWN_OBJECT_Manage(unsigned long time_offset)901 void ARX_THROWN_OBJECT_Manage(unsigned long time_offset)
902 {
903 if (Thrown_Count <= 0) return;
904
905 GRenderer->SetRenderState(Renderer::DepthWrite, true);
906 GRenderer->SetRenderState(Renderer::DepthTest, true);
907
908 for (size_t i = 0; i < MAX_THROWN_OBJECTS; i++)
909 {
910 if (Thrown[i].flags & ATO_EXIST)
911 {
912 // Is Object Visible & Near ?
913 if(fartherThan(ACTIVECAM->pos, Thrown[i].position, ACTIVECAM->cdepth * fZFogEnd + 50.f)) {
914 continue;
915 }
916
917 long xx, yy;
918 xx = (Thrown[i].position.x)*ACTIVEBKG->Xmul;
919 yy = (Thrown[i].position.z)*ACTIVEBKG->Zmul;
920
921 if (xx < 0)
922 continue;
923
924 if (xx >= ACTIVEBKG->Xsize)
925 continue;
926
927 if (yy < 0)
928 continue;
929
930 if (yy >= ACTIVEBKG->Zsize)
931 continue;
932
933 FAST_BKG_DATA * feg = (FAST_BKG_DATA *)&ACTIVEBKG->fastdata[xx][yy];
934
935 if (!feg->treat)
936 continue;
937
938 // Now render object !
939 if (!Thrown[i].obj)
940 continue;
941
942 EERIEMATRIX mat;
943 MatrixFromQuat(&mat, &Thrown[i].quat);
944 long ccount = FRAME_COUNT;
945 FRAME_COUNT = 0;
946 DrawEERIEInterMatrix(Thrown[i].obj, &mat, &Thrown[i].position, NULL);
947
948 if((Thrown[i].flags & ATO_FIERY) && (Thrown[i].flags & ATO_MOVING)
949 && !(Thrown[i].flags & ATO_UNDERWATER)) {
950
951 long id = GetFreeDynLight();
952 if(id != -1 && FrameDiff > 0) {
953 DynLight[id].exist = 1;
954 DynLight[id].intensity = 1.f;
955 DynLight[id].fallstart = 100.f;
956 DynLight[id].fallend = 240.f;
957 DynLight[id].rgb = Color3f(1.f - rnd() * .2f, .8f - rnd() * .2f, .6f - rnd() * .2f);
958 DynLight[id].pos = Thrown[i].position;
959 DynLight[id].ex_flaresize = 40.f;
960 DynLight[id].extras |= EXTRAS_FLARE;
961 DynLight[id].duration = static_cast<long>(FrameDiff * 0.5f);
962 }
963
964 float p = 3.f;
965
966 while (p > 0.f)
967 {
968 p -= 0.5f;
969
970 if (Thrown[i].obj)
971 {
972 Vec3f pos;
973 long notok = 10;
974 std::vector<EERIE_FACE>::iterator it;
975
976 while (notok-- > 0)
977 {
978 it = Random::getIterator(Thrown[i].obj->facelist);
979 arx_assert(it != Thrown[i].obj->facelist.end());
980
981 if (it->facetype & POLY_HIDE) continue;
982
983 notok = -1;
984 }
985
986 if (notok < 0)
987 {
988 pos = Thrown[i].obj->vertexlist3[it->vid[0]].v;
989
990 for(long nn = 0; nn < 2; nn++) {
991
992 if(rnd() >= 0.4f) {
993 continue;
994 }
995
996 PARTICLE_DEF * pd = createParticle();
997 if(!pd) {
998 break;
999 }
1000
1001 pd->ov = pos;
1002 pd->move = Vec3f(2.f - 4.f * rnd(), 2.f - 22.f * rnd(),
1003 2.f - 4.f * rnd());
1004 pd->siz = 7.f;
1005 pd->tolive = Random::get(500, 1500);
1006 pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
1007 pd->tc = fire2;
1008 pd->fparam = 0.1f - rnd() * 0.2f;
1009 pd->scale = Vec3f::repeat(-8.f);
1010 pd->rgb = Color3f(0.71f, 0.43f, 0.29f);
1011 pd->delay = nn * 180;
1012 }
1013
1014 }
1015
1016 }
1017 }
1018 }
1019
1020 if (Thrown[i].pRuban)
1021 {
1022
1023 Thrown[i].pRuban->Update();
1024
1025 Thrown[i].pRuban->Render();
1026 }
1027
1028 FRAME_COUNT = ccount;
1029 Vec3f original_pos;
1030
1031 if (Thrown[i].flags & ATO_MOVING)
1032 {
1033 long need_kill = 0;
1034 float mod = (float)time_offset * Thrown[i].velocity;
1035 original_pos = Thrown[i].position;
1036 Thrown[i].position.x += Thrown[i].vector.x * mod;
1037 float gmod = 1.f - Thrown[i].velocity;
1038
1039 if (gmod > 1.f) gmod = 1.f;
1040 else if (gmod < 0.f) gmod = 0.f;
1041
1042 Thrown[i].position.y += Thrown[i].vector.y * mod + (time_offset * gmod);
1043 Thrown[i].position.z += Thrown[i].vector.z * mod;
1044
1045 CheckForIgnition(&original_pos, 10.f, 0, 2);
1046
1047 Vec3f wpos = Thrown[i].position;
1048 wpos.y += 20.f;
1049 EERIEPOLY * ep = EEIsUnderWater(&wpos);
1050
1051 if (Thrown[i].flags & ATO_UNDERWATER)
1052 {
1053 if (ep == NULL)
1054 {
1055 Thrown[i].flags &= ~ATO_UNDERWATER;
1056 ARX_SOUND_PlaySFX(SND_PLOUF, &Thrown[i].position);
1057 }
1058 }
1059 else if (ep != NULL)
1060 {
1061 Thrown[i].flags |= ATO_UNDERWATER;
1062 ARX_SOUND_PlaySFX(SND_PLOUF, &Thrown[i].position);
1063 }
1064
1065 // Check for collision MUST be done after DRAWING !!!!
1066 long nbact = Thrown[i].obj->actionlist.size();
1067
1068 for (long j = 0; j < nbact; j++)
1069 { // TODO iterator
1070 float rad = -1;
1071 rad = GetHitValue(Thrown[i].obj->actionlist[j].name);
1072 rad *= .5f;
1073
1074 if (rad == -1) continue;
1075
1076 Vec3f * v0 = &Thrown[i].obj->vertexlist3[Thrown[i].obj->actionlist[j].idx].v;
1077 Vec3f dest = original_pos + Thrown[i].vector * 95.f;
1078 Vec3f orgn = original_pos - Thrown[i].vector * 25.f;
1079 EERIEPOLY * ep = CheckArrowPolyCollision(&orgn, &dest);
1080
1081 if (ep)
1082 {
1083 ARX_PARTICLES_Spawn_Spark(v0, 14, 0);
1084 CheckExp(i);
1085
1086 if (ValidIONum(Thrown[i].source))
1087 ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
1088
1089 Thrown[i].flags &= ~ATO_MOVING;
1090 Thrown[i].velocity = 0.f;
1091 char weapon_material[64] = "dagger";
1092 string bkg_material = "earth";
1093
1094 if (ep && ep->tex && !ep->tex->m_texName.empty())
1095 bkg_material = GetMaterialString(ep->tex->m_texName);
1096
1097 if (ValidIONum(Thrown[i].source))
1098 ARX_SOUND_PlayCollision(weapon_material, bkg_material, 1.f, 1.f, v0,
1099 entities[Thrown[i].source]);
1100
1101 Thrown[i].position = original_pos;
1102 j = 200;
1103
1104 }
1105 else if (IsPointInField(v0))
1106 {
1107 ARX_PARTICLES_Spawn_Spark(v0, 24, 0);
1108 CheckExp(i);
1109
1110 if (ValidIONum(Thrown[i].source))
1111 ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
1112
1113 Thrown[i].flags &= ~ATO_MOVING;
1114 Thrown[i].velocity = 0.f;
1115 char weapon_material[64] = "dagger";
1116 char bkg_material[64] = "earth";
1117
1118 if (ValidIONum(Thrown[i].source))
1119 ARX_SOUND_PlayCollision(weapon_material, bkg_material, 1.f, 1.f, v0,
1120 entities[Thrown[i].source]);
1121
1122 Thrown[i].position = original_pos;
1123 j = 200;
1124 need_kill = 1;
1125 }
1126 else
1127 for (float precision = 0.5f; precision <= 6.f; precision += 0.5f)
1128 {
1129 EERIE_SPHERE sphere;
1130 sphere.origin = *v0 + Thrown[i].vector * precision * 4.5f;
1131 sphere.radius = rad + 3.f;
1132
1133 if (CheckEverythingInSphere(&sphere, Thrown[i].source, -1))
1134 {
1135 for (size_t jj = 0; jj < MAX_IN_SPHERE_Pos; jj++)
1136 {
1137
1138 if ((ValidIONum(EVERYTHING_IN_SPHERE[jj])
1139 && (EVERYTHING_IN_SPHERE[jj] != Thrown[i].source)))
1140 {
1141
1142 Entity * target = entities[EVERYTHING_IN_SPHERE[jj]];
1143
1144 if (target->ioflags & IO_NPC)
1145 {
1146 Vec3f pos;
1147 Color color = Color::none;
1148 long hitpoint = -1;
1149 float curdist = 999999.f;
1150
1151 for (size_t ii = 0 ; ii < target->obj->facelist.size() ; ii++)
1152 {
1153 if (target->obj->facelist[ii].facetype & POLY_HIDE) continue;
1154
1155 short vid = target->obj->facelist[ii].vid[0];
1156 float d = dist(sphere.origin, target->obj->vertexlist3[vid].v);
1157
1158 if (d < curdist)
1159 {
1160 hitpoint = target->obj->facelist[ii].vid[0];
1161 curdist = d;
1162 }
1163 }
1164
1165 if (hitpoint >= 0)
1166 {
1167 color = target->_npcdata->blood_color;
1168 pos = target->obj->vertexlist3[hitpoint].v;
1169 }
1170
1171 if (Thrown[i].source == 0)
1172 {
1173 float damages = ARX_THROWN_ComputeDamages(i, Thrown[i].source,
1174 EVERYTHING_IN_SPHERE[jj]);
1175
1176 if (damages > 0.f)
1177 {
1178 arx_assert(hitpoint >= 0);
1179
1180 if (target->ioflags & IO_NPC)
1181 {
1182 target->_npcdata->SPLAT_TOT_NB = 0;
1183 ARX_PARTICLES_Spawn_Blood2(original_pos, damages, color, target);
1184 }
1185
1186 ARX_PARTICLES_Spawn_Blood2(pos, damages, color, target);
1187 ARX_DAMAGES_DamageNPC(target, damages, Thrown[i].source, 0, &pos);
1188
1189 if (rnd() * 100.f > target->_npcdata->resist_poison)
1190 {
1191 target->_npcdata->poisonned += Thrown[i].poisonous;
1192 }
1193
1194 CheckExp(i);
1195 }
1196 else
1197 {
1198 ARX_PARTICLES_Spawn_Spark(v0, 14, 0);
1199 ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
1200 }
1201 }
1202 }
1203 else // not NPC
1204 {
1205 if (target->ioflags & IO_FIX)
1206 {
1207 if (ValidIONum(Thrown[i].source))
1208 ARX_DAMAGES_DamageFIX(target, 0.1f, Thrown[i].source, 0);
1209 }
1210
1211 ARX_PARTICLES_Spawn_Spark(v0, 14, 0);
1212
1213 if (ValidIONum(Thrown[i].source))
1214 ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
1215
1216 CheckExp(i);
1217 }
1218
1219 // Need to deal damages !
1220 Thrown[i].flags &= ~ATO_MOVING;
1221 Thrown[i].velocity = 0.f;
1222 need_kill = 1;
1223 precision = 500.f;
1224 j = 200;
1225 }
1226 }
1227 }
1228 }
1229 }
1230
1231 if (need_kill) ARX_THROWN_OBJECT_Kill(i);
1232 }
1233 }
1234 }
1235 }
1236
1237 // RUBAN
Create(int _iNumThrow,int _iDuration)1238 void CRuban::Create(int _iNumThrow, int _iDuration)
1239 {
1240 iNumThrow = _iNumThrow;
1241
1242 key = 1;
1243 duration = _iDuration;
1244 currduration = 0;
1245
1246 nbrubandef = 0;
1247
1248 int nb = 2048;
1249
1250 while (nb--)
1251 {
1252 truban[nb].actif = 0;
1253 }
1254
1255 float col = 0.1f + (rnd() * 0.1f);
1256 float size = 2.f + (2.f * rnd());
1257 int taille = Random::get(8, 16);
1258 AddRubanDef(0, size, taille, col, col, col, 0.f, 0.f, 0.f);
1259
1260 }
1261
AddRubanDef(int origin,float size,int dec,float r,float g,float b,float r2,float g2,float b2)1262 void CRuban::AddRubanDef(int origin, float size, int dec, float r, float g, float b,
1263 float r2, float g2, float b2) {
1264
1265 if (nbrubandef > 255) return;
1266
1267 trubandef[nbrubandef].first = -1;
1268 trubandef[nbrubandef].origin = origin;
1269 trubandef[nbrubandef].size = size;
1270 trubandef[nbrubandef].dec = dec;
1271 trubandef[nbrubandef].r = r;
1272 trubandef[nbrubandef].g = g;
1273 trubandef[nbrubandef].b = b;
1274 trubandef[nbrubandef].r2 = r2;
1275 trubandef[nbrubandef].g2 = g2;
1276 trubandef[nbrubandef].b2 = b2;
1277 nbrubandef++;
1278 }
1279
GetFreeRuban()1280 int CRuban::GetFreeRuban()
1281 {
1282 int nb = 2048;
1283
1284 while (nb--)
1285 {
1286 if (!truban[nb].actif) return nb;
1287 }
1288
1289 return -1;
1290 }
1291
AddRuban(int * f,int dec)1292 void CRuban::AddRuban(int * f, int dec) {
1293
1294 int num = GetFreeRuban();
1295
1296 if (num >= 0)
1297 {
1298 truban[num].actif = 1;
1299
1300 truban[num].pos = Thrown[iNumThrow].position;
1301
1302 if (*f < 0)
1303 {
1304 *f = num;
1305 truban[num].next = -1;
1306 }
1307 else
1308 {
1309 truban[num].next = *f;
1310 *f = num;
1311 }
1312
1313 int nb = 0, oldnum = 0;
1314
1315 while (num != -1)
1316 {
1317 nb++;
1318 oldnum = num;
1319 num = truban[num].next;
1320 }
1321
1322 if (nb > dec)
1323 {
1324
1325 truban[oldnum].actif = 0;
1326 num = *f;
1327 nb -= 2;
1328
1329 while (nb--)
1330 {
1331 num = truban[num].next;
1332 }
1333
1334 truban[num].next = -1;
1335 }
1336 }
1337 }
1338
Update()1339 void CRuban::Update() {
1340
1341 int nb, num;
1342
1343 if (arxtime.is_paused()) return;
1344
1345 num = 0;
1346 nb = nbrubandef;
1347
1348 while (nb--)
1349 {
1350 AddRuban(&trubandef[num].first, trubandef[num].dec);
1351 num++;
1352 }
1353 }
1354
DrawRuban(int num,float size,int dec,float r,float g,float b,float r2,float g2,float b2)1355 void CRuban::DrawRuban(int num, float size, int dec, float r, float g, float b,
1356 float r2, float g2, float b2) {
1357
1358 int numsuiv;
1359
1360 float dsize = size / (float)(dec + 1);
1361 int r1 = ((int)(r * 255.f)) << 16;
1362 int g1 = ((int)(g * 255.f)) << 16;
1363 int b1 = ((int)(b * 255.f)) << 16;
1364 int rr2 = ((int)(r2 * 255.f)) << 16;
1365 int gg2 = ((int)(g2 * 255.f)) << 16;
1366 int bb2 = ((int)(b2 * 255.f)) << 16;
1367 int dr = (rr2 - r1) / dec;
1368 int dg = (gg2 - g1) / dec;
1369 int db = (bb2 - b1) / dec;
1370
1371 for (;;)
1372 {
1373 numsuiv = truban[num].next;
1374
1375 if ((num >= 0) && (numsuiv >= 0))
1376 {
1377 Draw3DLineTex2(truban[num].pos, truban[numsuiv].pos, size,
1378 Color(r1 >> 16, g1 >> 16, b1 >> 16, 0),
1379 Color((r1 + dr) >> 16, (g1 + dg) >> 16, (b1 + db) >> 16, 0));
1380 r1 += dr;
1381 g1 += dg;
1382 b1 += db;
1383 size -= dsize;
1384 }
1385 else
1386 {
1387 break;
1388 }
1389
1390 num = numsuiv;
1391 }
1392 }
1393
Render()1394 float CRuban::Render()
1395 {
1396 GRenderer->SetCulling(Renderer::CullNone);
1397 GRenderer->SetRenderState(Renderer::AlphaBlending, true);
1398 GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1399 GRenderer->ResetTexture(0);
1400
1401 for (int i = 0; i < nbrubandef; i++)
1402 {
1403 this->DrawRuban(trubandef[i].first,
1404 trubandef[i].size,
1405 trubandef[i].dec,
1406 trubandef[i].r, trubandef[i].g, trubandef[i].b,
1407 trubandef[i].r2, trubandef[i].g2, trubandef[i].b2);
1408 }
1409
1410 GRenderer->SetRenderState(Renderer::AlphaBlending, false);
1411 GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendZero);
1412
1413 return 0;
1414 }
1415
1416 extern bool IsValidPos3(Vec3f * pos);
1417
1418 static EERIEPOLY * LAST_COLLISION_POLY = NULL;
1419 extern long CUR_COLLISION_MATERIAL;
1420
1421 float VELOCITY_THRESHOLD = 850.f;
1422
ARX_ApplySpring(PHYSVERT * phys,long k,long l,float PHYSICS_constant,float PHYSICS_Damp)1423 void ARX_ApplySpring(PHYSVERT * phys, long k, long l, float PHYSICS_constant,
1424 float PHYSICS_Damp) {
1425
1426 Vec3f deltaP, deltaV, springforce;
1427 PHYSVERT * pv_k = &phys[k];
1428 PHYSVERT * pv_l = &phys[l];
1429 float Dterm, Hterm;
1430
1431 float restlength = dist(pv_k->initpos, pv_l->initpos);
1432 // Computes Spring Magnitude
1433 deltaP = pv_k->pos - pv_l->pos;
1434 float dist = deltaP.length(); // Magnitude of delta
1435 dist = std::max(dist, 0.000001f); //TODO workaround for division by zero
1436 float divdist = 1.f / dist;
1437 Hterm = (dist - restlength) * PHYSICS_constant;
1438
1439 deltaV = pv_k->velocity - pv_l->velocity; // Delta Velocity Vector
1440 Dterm = dot(deltaV, deltaP) * PHYSICS_Damp * divdist; // Damping Term
1441 Dterm = (-(Hterm + Dterm));
1442 divdist *= Dterm;
1443 springforce = deltaP * divdist; // Normalize Distance Vector & Calc Force
1444
1445 pv_k->force += springforce; // + force on particle 1
1446
1447 pv_l->force -= springforce; // - force on particle 2
1448 }
1449
ComputeForces(PHYSVERT * phys,long nb)1450 void ComputeForces(PHYSVERT * phys, long nb) {
1451
1452 const Vec3f PHYSICS_Gravity(0.f, 65.f, 0.f);
1453 const float PHYSICS_Damping = 0.5f;
1454
1455 float lastmass = 1.f;
1456 float div = 1.f;
1457
1458 for(long k = 0; k < nb; k++) {
1459
1460 PHYSVERT * pv = &phys[k];
1461
1462 // Reset Force
1463 pv->force = pv->inertia;
1464
1465 // Apply Gravity
1466 if(pv->mass > 0.f) {
1467
1468 // need to be precomputed...
1469 if(lastmass != pv->mass) {
1470 div = 1.f / pv->mass;
1471 lastmass = pv->mass;
1472 }
1473
1474 pv->force += (PHYSICS_Gravity * div);
1475 }
1476
1477 // Apply Damping
1478 pv->force += pv->velocity * -PHYSICS_Damping;
1479 }
1480
1481 for(int k = 0; k < nb; k++) {
1482 // Now Resolves Spring System
1483 for(long l = 0; l < nb; l++) {
1484 if(l != k) {
1485 ARX_ApplySpring(phys, l, k, 15.f, 0.99f);
1486 }
1487 }
1488 }
1489 }
1490
1491 bool ARX_INTERACTIVE_CheckFULLCollision(EERIE_3DOBJ * obj, long source);
1492
1493 //! Calculate new Positions and Velocities given a deltatime
1494 //! @param DeltaTime that has passed since last iteration
RK4Integrate(EERIE_3DOBJ * obj,float DeltaTime)1495 void RK4Integrate(EERIE_3DOBJ * obj, float DeltaTime) {
1496
1497 PHYSVERT * source, * target, * accum1, * accum2, * accum3, * accum4;
1498 float halfDeltaT, sixthDeltaT;
1499 halfDeltaT = DeltaTime * .5f; // some time values i will need
1500 sixthDeltaT = ( 1.0f / 6 );
1501
1502 PHYSVERT m_TempSys[5][32];
1503
1504 for(long jj = 0; jj < 4; jj++) {
1505
1506 arx_assert(size_t(obj->pbox->nb_physvert) <= ARRAY_SIZE(m_TempSys[jj + 1]));
1507 memcpy(m_TempSys[jj + 1], obj->pbox->vert, sizeof(PHYSVERT) * obj->pbox->nb_physvert);
1508
1509 if(jj == 3) {
1510 halfDeltaT = DeltaTime;
1511 }
1512
1513 for(long kk = 0; kk < obj->pbox->nb_physvert; kk++) {
1514
1515 source = &obj->pbox->vert[kk];
1516 accum1 = &m_TempSys[jj + 1][kk];
1517 target = &m_TempSys[0][kk];
1518
1519 accum1->force = source->force * (source->mass * halfDeltaT);
1520 accum1->velocity = source->velocity * halfDeltaT;
1521
1522 // determine the new velocity for the particle over 1/2 time
1523 target->velocity = source->velocity + accum1->force;
1524 target->mass = source->mass;
1525
1526 // set the new position
1527 target->pos = source->pos + accum1->velocity;
1528 }
1529
1530 ComputeForces(m_TempSys[0], obj->pbox->nb_physvert); // compute the new forces
1531 }
1532
1533 for(long kk = 0; kk < obj->pbox->nb_physvert; kk++) {
1534
1535 source = &obj->pbox->vert[kk]; // current state of particle
1536 target = &obj->pbox->vert[kk];
1537 accum1 = &m_TempSys[1][kk];
1538 accum2 = &m_TempSys[2][kk];
1539 accum3 = &m_TempSys[3][kk];
1540 accum4 = &m_TempSys[4][kk];
1541
1542 // determine the new velocity for the particle using rk4 formula
1543 Vec3f dv = accum1->force + ((accum2->force + accum3->force) * 2.f) + accum4->force;
1544 target->velocity = source->velocity + (dv * sixthDeltaT);
1545 // determine the new position for the particle using rk4 formula
1546 Vec3f dp = accum1->velocity + ((accum2->velocity + accum3->velocity) * 2.f)
1547 + accum4->velocity;
1548 target->pos = source->pos + (dp * sixthDeltaT * 1.2f);
1549 }
1550
1551 }
1552
IsPointInField(Vec3f * pos)1553 static bool IsPointInField(Vec3f * pos) {
1554
1555 for(size_t i = 0; i < MAX_SPELLS; i++) {
1556
1557 if(spells[i].exist && spells[i].type == SPELL_CREATE_FIELD) {
1558
1559 if(ValidIONum(spells[i].longinfo)) {
1560
1561 Entity * pfrm = entities[spells[i].longinfo];
1562 EERIE_CYLINDER cyl;
1563 cyl.height = -35.f;
1564 cyl.radius = 35.f;
1565 cyl.origin = *pos + Vec3f(0.f, 17.5f, 0.f);
1566
1567 if(CylinderPlatformCollide(&cyl, pfrm) != 0.f) {
1568 return true;
1569 }
1570 }
1571 }
1572 }
1573
1574 return false;
1575 }
1576
IsObjectInField(EERIE_3DOBJ * obj)1577 static bool IsObjectInField(EERIE_3DOBJ * obj) {
1578
1579 for(size_t i = 0; i < MAX_SPELLS; i++) {
1580
1581 if(spells[i].exist && spells[i].type == SPELL_CREATE_FIELD) {
1582
1583 if(ValidIONum(spells[i].longinfo)) {
1584
1585 Entity * pfrm = entities[spells[i].longinfo];
1586 EERIE_CYLINDER cyl;
1587 cyl.height = -35.f;
1588 cyl.radius = 35.f;
1589
1590 for(long k = 0; k < obj->pbox->nb_physvert; k++) {
1591 PHYSVERT * pv = &obj->pbox->vert[k];
1592 cyl.origin = pv->pos + Vec3f(0.f, 17.5f, 0.f);
1593 if(CylinderPlatformCollide(&cyl, pfrm) != 0.f) {
1594 return true;
1595 }
1596 }
1597 }
1598 }
1599 }
1600
1601 return false;
1602 }
1603
IsObjectVertexCollidingPoly(EERIE_3DOBJ * obj,EERIEPOLY * ep,long k,long * validd)1604 static bool IsObjectVertexCollidingPoly(EERIE_3DOBJ * obj, EERIEPOLY * ep,
1605 long k, long * validd) {
1606
1607 Vec3f pol[3];
1608 pol[0] = ep->v[0].p;
1609 pol[1] = ep->v[1].p;
1610 pol[2] = ep->v[2].p;
1611
1612 if(ep->type & POLY_QUAD) {
1613
1614 if(IsObjectVertexCollidingTriangle(obj, pol, k, validd)) {
1615 return true;
1616 }
1617
1618 pol[1] = ep->v[2].p;
1619 pol[2] = ep->v[3].p;
1620
1621 if(IsObjectVertexCollidingTriangle(obj, pol, k, validd)) {
1622 return true;
1623 }
1624
1625 return false;
1626 }
1627
1628 if(IsObjectVertexCollidingTriangle(obj, pol, k, validd)) {
1629 return true;
1630 }
1631
1632 return false;
1633 }
1634
IsFULLObjectVertexInValidPosition(EERIE_3DOBJ * obj)1635 static bool IsFULLObjectVertexInValidPosition(EERIE_3DOBJ * obj) {
1636
1637 bool ret = true;
1638 long px, pz;
1639 float x = obj->pbox->vert[0].pos.x;
1640 px = x * ACTIVEBKG->Xmul;
1641 float z = obj->pbox->vert[0].pos.z;
1642 pz = z * ACTIVEBKG->Zmul;
1643 long ix, iz, ax, az;
1644 long n;
1645 n = obj->pbox->radius * ( 1.0f / 100 );
1646 n = min(1L, n + 1);
1647 ix = max(px - n, 0L);
1648 ax = min(px + n, ACTIVEBKG->Xsize - 1L);
1649 iz = max(pz - n, 0L);
1650 az = min(pz + n, ACTIVEBKG->Zsize - 1L);
1651 LAST_COLLISION_POLY = NULL;
1652 EERIEPOLY * ep;
1653 EERIE_BKG_INFO * eg;
1654
1655 float rad = obj->pbox->radius;
1656
1657 for (pz = iz; pz <= az; pz++)
1658 for (px = ix; px <= ax; px++)
1659 {
1660 eg = &ACTIVEBKG->Backg[px+pz*ACTIVEBKG->Xsize];
1661
1662 for (long k = 0; k < eg->nbpoly; k++)
1663 {
1664
1665 ep = &eg->polydata[k];
1666
1667 if ( (ep->area > 190.f)
1668 && (!(ep->type & (POLY_WATER)))
1669 && (!(ep->type & (POLY_TRANS)))
1670 && (!(ep->type & (POLY_NOCOL)))
1671 )
1672 {
1673 if (fartherThan(ep->center, obj->pbox->vert[0].pos, rad + 75.f))
1674 continue;
1675
1676 for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
1677 {
1678 float radd = 4.f;
1679
1680 if(!fartherThan(obj->pbox->vert[kk].pos, ep->center, radd)
1681 || !fartherThan(obj->pbox->vert[kk].pos, ep->v[0].p, radd)
1682 || !fartherThan(obj->pbox->vert[kk].pos, ep->v[1].p, radd)
1683 || !fartherThan(obj->pbox->vert[kk].pos, ep->v[2].p, radd)
1684 || !fartherThan(obj->pbox->vert[kk].pos, (ep->v[0].p + ep->v[1].p) * .5f, radd)
1685 || !fartherThan(obj->pbox->vert[kk].pos, (ep->v[2].p + ep->v[1].p) * .5f, radd)
1686 || !fartherThan(obj->pbox->vert[kk].pos, (ep->v[0].p + ep->v[2].p) * .5f, radd)) {
1687
1688 LAST_COLLISION_POLY = ep;
1689
1690 if (ep->type & POLY_METAL) CUR_COLLISION_MATERIAL = MATERIAL_METAL;
1691 else if (ep->type & POLY_WOOD) CUR_COLLISION_MATERIAL = MATERIAL_WOOD;
1692 else if (ep->type & POLY_STONE) CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1693 else if (ep->type & POLY_GRAVEL) CUR_COLLISION_MATERIAL = MATERIAL_GRAVEL;
1694 else if (ep->type & POLY_WATER) CUR_COLLISION_MATERIAL = MATERIAL_WATER;
1695 else if (ep->type & POLY_EARTH) CUR_COLLISION_MATERIAL = MATERIAL_EARTH;
1696 else CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1697
1698 return false;
1699 }
1700
1701 // Last addon
1702 for (long kl = 1; kl < obj->pbox->nb_physvert; kl++)
1703 {
1704 if (kl != kk)
1705 {
1706 Vec3f pos = (obj->pbox->vert[kk].pos + obj->pbox->vert[kl].pos) * .5f;
1707
1708 if(!fartherThan(pos, ep->center, radd)
1709 || !fartherThan(pos, ep->v[0].p, radd)
1710 || !fartherThan(pos, ep->v[1].p, radd)
1711 || !fartherThan(pos, ep->v[2].p, radd)
1712 || !fartherThan(pos, (ep->v[0].p + ep->v[1].p) * .5f, radd)
1713 || !fartherThan(pos, (ep->v[2].p + ep->v[1].p) * .5f, radd)
1714 || !fartherThan(pos, (ep->v[0].p + ep->v[2].p) * .5f, radd)) {
1715
1716 LAST_COLLISION_POLY = ep;
1717
1718 if (ep->type & POLY_METAL) CUR_COLLISION_MATERIAL = MATERIAL_METAL;
1719 else if (ep->type & POLY_WOOD) CUR_COLLISION_MATERIAL = MATERIAL_WOOD;
1720 else if (ep->type & POLY_STONE) CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1721 else if (ep->type & POLY_GRAVEL) CUR_COLLISION_MATERIAL = MATERIAL_GRAVEL;
1722 else if (ep->type & POLY_WATER) CUR_COLLISION_MATERIAL = MATERIAL_WATER;
1723 else if (ep->type & POLY_EARTH) CUR_COLLISION_MATERIAL = MATERIAL_EARTH;
1724 else CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1725
1726 return false;
1727 }
1728 }
1729 }
1730 }
1731
1732
1733 if (IsObjectVertexCollidingPoly(obj, ep, -1, NULL))
1734 {
1735
1736 LAST_COLLISION_POLY = ep;
1737
1738 if (ep->type & POLY_METAL) CUR_COLLISION_MATERIAL = MATERIAL_METAL;
1739 else if (ep->type & POLY_WOOD) CUR_COLLISION_MATERIAL = MATERIAL_WOOD;
1740 else if (ep->type & POLY_STONE) CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1741 else if (ep->type & POLY_GRAVEL) CUR_COLLISION_MATERIAL = MATERIAL_GRAVEL;
1742 else if (ep->type & POLY_WATER) CUR_COLLISION_MATERIAL = MATERIAL_WATER;
1743 else if (ep->type & POLY_EARTH) CUR_COLLISION_MATERIAL = MATERIAL_EARTH;
1744 else CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1745
1746 return false;
1747 }
1748 }
1749 }
1750 }
1751
1752 return ret;
1753 }
1754
ARX_EERIE_PHYSICS_BOX_Compute(EERIE_3DOBJ * obj,float framediff,long source)1755 static bool ARX_EERIE_PHYSICS_BOX_Compute(EERIE_3DOBJ * obj, float framediff, long source) {
1756
1757 PHYSVERT * pv;
1758 Vec3f oldpos[32];
1759 long COUNT = 0;
1760 COUNT++;
1761
1762 for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
1763 {
1764 pv = &obj->pbox->vert[kk];
1765 oldpos[kk] = pv->pos;
1766 pv->inertia = Vec3f::ZERO;
1767
1768 if (pv->velocity.x > VELOCITY_THRESHOLD) pv->velocity.x = VELOCITY_THRESHOLD;
1769 else if (pv->velocity.x < -VELOCITY_THRESHOLD) pv->velocity.x = -VELOCITY_THRESHOLD;
1770
1771 if (pv->velocity.y > VELOCITY_THRESHOLD) pv->velocity.y = VELOCITY_THRESHOLD;
1772 else if (pv->velocity.y < -VELOCITY_THRESHOLD) pv->velocity.y = -VELOCITY_THRESHOLD;
1773
1774 if (pv->velocity.z > VELOCITY_THRESHOLD) pv->velocity.z = VELOCITY_THRESHOLD;
1775 else if (pv->velocity.z < -VELOCITY_THRESHOLD) pv->velocity.z = -VELOCITY_THRESHOLD;
1776 }
1777
1778 CUR_COLLISION_MATERIAL = MATERIAL_STONE;
1779
1780 RK4Integrate(obj, framediff);
1781
1782 EERIE_SPHERE sphere;
1783 pv = &obj->pbox->vert[0];
1784 sphere.origin = pv->pos;
1785 sphere.radius = obj->pbox->radius;
1786 long colidd = 0;
1787
1788 for (int kk = 0; kk < obj->pbox->nb_physvert; kk += 2)
1789 {
1790 pv = &obj->pbox->vert[kk];
1791
1792 if (!IsValidPos3(&pv->pos))
1793 {
1794 colidd = 1;
1795 break;
1796 }
1797 }
1798
1799 if ((!IsFULLObjectVertexInValidPosition(obj))
1800 || ARX_INTERACTIVE_CheckFULLCollision(obj, source)
1801 || colidd
1802 || (IsObjectInField(obj))
1803 )
1804 {
1805 colidd = 1;
1806 float power = (EEfabs(obj->pbox->vert[0].velocity.x)
1807 + EEfabs(obj->pbox->vert[0].velocity.y)
1808 + EEfabs(obj->pbox->vert[0].velocity.z)) * .01f;
1809
1810
1811 if (ValidIONum(source) && (entities[source]->ioflags & IO_BODY_CHUNK))
1812 {
1813 }
1814 else
1815 ARX_TEMPORARY_TrySound(0.4f + power);
1816
1817
1818 if(!LAST_COLLISION_POLY) {
1819
1820 for(long k = 0; k < obj->pbox->nb_physvert; k++) {
1821 pv = &obj->pbox->vert[k];
1822
1823 {
1824 pv->velocity.x *= -0.3f;
1825 pv->velocity.z *= -0.3f;
1826 pv->velocity.y *= -0.4f;
1827 }
1828
1829 pv->pos = oldpos[k];
1830 }
1831
1832 } else {
1833
1834 for(long k = 0; k < obj->pbox->nb_physvert; k++) {
1835
1836 pv = &obj->pbox->vert[k];
1837
1838 float t = dot(LAST_COLLISION_POLY->norm, pv->velocity);
1839 pv->velocity -= LAST_COLLISION_POLY->norm * (2.f * t);
1840
1841 pv->velocity.x *= 0.3f;
1842 pv->velocity.z *= 0.3f;
1843 pv->velocity.y *= 0.4f;
1844
1845 pv->pos = oldpos[k];
1846 }
1847 }
1848 }
1849
1850 if (colidd)
1851 {
1852 obj->pbox->stopcount += 1;
1853 }
1854 else
1855 {
1856 obj->pbox->stopcount -= 2;
1857
1858 if (obj->pbox->stopcount < 0)
1859 obj->pbox->stopcount = 0;
1860 }
1861
1862 return true;
1863 }
1864
ARX_PHYSICS_BOX_ApplyModel(EERIE_3DOBJ * obj,float framediff,float rubber,long source)1865 long ARX_PHYSICS_BOX_ApplyModel(EERIE_3DOBJ * obj, float framediff, float rubber, long source) {
1866
1867 VELOCITY_THRESHOLD = 400.f;
1868 long ret = 0;
1869
1870 if ((!obj) || (!obj->pbox)) return ret;
1871
1872 if (obj->pbox->active == 2) return ret;
1873
1874 if (framediff == 0.f) return ret;
1875
1876 PHYSVERT * pv;
1877
1878 // Memorizes initpos
1879 for(long k = 0; k < obj->pbox->nb_physvert; k++) {
1880 pv = &obj->pbox->vert[k];
1881 pv->temp = pv->pos;
1882 }
1883
1884 float timing = obj->pbox->storedtiming + framediff * rubber * 0.0055f;
1885 float t_threshold = 0.18f;
1886
1887 if (timing < t_threshold)
1888 {
1889 obj->pbox->storedtiming = timing;
1890 return 1;
1891 }
1892 else
1893 {
1894
1895 while(timing >= t_threshold) {
1896
1897 ComputeForces(obj->pbox->vert, obj->pbox->nb_physvert);
1898
1899 if (!ARX_EERIE_PHYSICS_BOX_Compute(obj, std::min(0.11f, timing * 10), source))
1900 ret = 1;
1901
1902 timing -= t_threshold;
1903 }
1904
1905 obj->pbox->storedtiming = timing;
1906 }
1907
1908
1909 if (obj->pbox->stopcount < 16) return ret;
1910
1911 obj->pbox->active = 2;
1912 obj->pbox->stopcount = 0;
1913
1914 if (ValidIONum(source))
1915 {
1916 entities[source]->soundcount = 0;
1917 entities[source]->soundtime = (unsigned long)(arxtime) + 2000;
1918 }
1919
1920 return ret;
1921 }
1922
ARX_PrepareBackgroundNRMLs()1923 void ARX_PrepareBackgroundNRMLs()
1924 {
1925 long i, j, k, mai, maj, mii, mij;
1926 long i2, j2, k2;
1927 EERIE_BKG_INFO * eg;
1928 EERIE_BKG_INFO * eg2;
1929 EERIEPOLY * ep;
1930 EERIEPOLY * ep2;
1931 Vec3f nrml;
1932 Vec3f cur_nrml;
1933 float count;
1934 long nbvert;
1935 long nbvert2;
1936
1937 for (j = 0; j < ACTIVEBKG->Zsize; j++)
1938 for (i = 0; i < ACTIVEBKG->Xsize; i++)
1939 {
1940 eg = &ACTIVEBKG->Backg[i+j*ACTIVEBKG->Xsize];
1941
1942 for (long l = 0; l < eg->nbpoly; l++)
1943 {
1944 ep = &eg->polydata[l];
1945
1946 if (ep->type & POLY_QUAD) nbvert = 4;
1947 else nbvert = 3;
1948
1949 for (k = 0; k < nbvert; k++)
1950 {
1951 float ttt = 1.f;
1952
1953 if(k == 3) {
1954 nrml = ep->norm2;
1955 count = 1.f;
1956 } else if(k > 0 && nbvert > 3) {
1957 nrml = (ep->norm + ep->norm2);
1958 count = 2.f;
1959 ttt = .5f;
1960 } else {
1961 nrml = ep->norm;
1962 count = 1.f;
1963 }
1964
1965 cur_nrml = nrml * ttt;
1966
1967 mai = i + 4;
1968 maj = j + 4;
1969 mii = i - 4;
1970 mij = j - 4;
1971
1972 if (mij < 0) mij = 0;
1973
1974 if (mii < 0) mii = 0;
1975
1976 if (maj >= ACTIVEBKG->Zsize) maj = ACTIVEBKG->Zsize - 1;
1977
1978 if (mai >= ACTIVEBKG->Xsize) mai = ACTIVEBKG->Xsize - 1;
1979
1980 for (j2 = mij; j2 < maj; j2++)
1981 for (i2 = mii; i2 < mai; i2++)
1982 {
1983 eg2 = &ACTIVEBKG->Backg[i2+j2*ACTIVEBKG->Xsize];
1984
1985 for (long kr = 0; kr < eg2->nbpoly; kr++)
1986 {
1987 ep2 = &eg2->polydata[kr];
1988
1989 if (ep2->type & POLY_QUAD) nbvert2 = 4;
1990 else nbvert2 = 3;
1991
1992 if (ep != ep2)
1993
1994 for (k2 = 0; k2 < nbvert2; k2++)
1995 {
1996 if ((EEfabs(ep2->v[k2].p.x - ep->v[k].p.x) < 2.f)
1997 && (EEfabs(ep2->v[k2].p.y - ep->v[k].p.y) < 2.f)
1998 && (EEfabs(ep2->v[k2].p.z - ep->v[k].p.z) < 2.f))
1999 {
2000 if(k2 == 3) {
2001
2002 if(LittleAngularDiff(&cur_nrml, &ep2->norm2)) {
2003 nrml += ep2->norm2;
2004 count += 1.f;
2005 nrml += cur_nrml;
2006 count += 1.f;
2007 }
2008
2009 } else if(k2 > 0 && nbvert2 > 3) {
2010
2011 Vec3f tnrml = (ep2->norm + ep2->norm2) * .5f;
2012 if(LittleAngularDiff(&cur_nrml, &tnrml)) {
2013 nrml += tnrml * 2.f;
2014 count += 2.f;
2015 }
2016
2017 } else {
2018
2019 if(LittleAngularDiff(&cur_nrml, &ep2->norm)) {
2020 nrml += ep2->norm;
2021 count += 1.f;
2022 }
2023 }
2024 }
2025 }
2026 }
2027 }
2028
2029 count = 1.f / count;
2030 ep->tv[k].p = nrml * count;
2031
2032 }
2033 }
2034 }
2035
2036 for (j = 0; j < ACTIVEBKG->Zsize; j++)
2037 for (i = 0; i < ACTIVEBKG->Xsize; i++)
2038 {
2039 eg = &ACTIVEBKG->Backg[i+j*ACTIVEBKG->Xsize];
2040
2041 for (long l = 0; l < eg->nbpoly; l++)
2042 {
2043 ep = &eg->polydata[l];
2044
2045 if (ep->type & POLY_QUAD) nbvert = 4;
2046 else nbvert = 3;
2047
2048 for(k = 0; k < nbvert; k++) {
2049 ep->nrml[k] = ep->tv[k].p;
2050 }
2051
2052 float d = 0.f;
2053
2054 for(long ii = 0; ii < nbvert; ii++) {
2055 d = max(d, dist(ep->center, ep->v[ii].p));
2056 }
2057
2058 ep->v[0].rhw = d;
2059 }
2060 }
2061
2062 }
2063
EERIE_PHYSICS_BOX_Launch_NOCOL(Entity * io,EERIE_3DOBJ * obj,Vec3f * pos,Vec3f * vect,long flags,Anglef * angle)2064 void EERIE_PHYSICS_BOX_Launch_NOCOL(Entity * io, EERIE_3DOBJ * obj, Vec3f * pos,
2065 Vec3f * vect, long flags, Anglef * angle) {
2066 io->gameFlags |= GFLAG_NO_PHYS_IO_COL;
2067 EERIE_PHYSICS_BOX_Launch(obj, pos, vect, flags, angle);
2068 }
2069