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