/*
===========================================================================
ARX FATALIS GPL Source Code
Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
.
In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
//////////////////////////////////////////////////////////////////////////////////////
// @@ @@@ @@@ @@ @@@@@ //
// @@@ @@@@@@ @@@ @@ @@@@ @@@ @@@ //
// @@@ @@@@@@@ @@@ @@@@ @@@@ @@ @@@@ //
// @@@ @@ @@@@ @@@ @@@@@ @@@@@@ @@@ @@@ //
// @@@@@ @@ @@@@ @@@ @@@@@ @@@@@@@ @@@ @ @@@ //
// @@@@@ @@ @@@@ @@@@@@@@ @@@@ @@@ @@@@@ @@ @@@@@@@ //
// @@ @@@ @@ @@@@ @@@@@@@ @@@ @@@ @@@@@@ @@ @@@@ //
// @@@ @@@ @@@ @@@@ @@@@@ @@@@@@@@@ @@@@@@@ @@@ @@@@ //
// @@@ @@@@ @@@@@@@ @@@@@@ @@@ @@@@ @@@ @@@ @@@ @@@@ //
// @@@@@@@@ @@@@@ @@@@@@@@@@ @@@ @@@ @@@ @@@ @@@ @@@@@ //
// @@@ @@@@ @@@@ @@@ @@@@@@@ @@@ @@@ @@@@ @@@ @@@@ @@@@@ //
//@@@ @@@@ @@@@@ @@@ @@@@@@ @@ @@@ @@@@ @@@@@@@ @@@@@ @@@@@ //
//@@@ @@@@@ @@@@@ @@@@ @@@ @@ @@ @@@@ @@@@@@@ @@@@@@@@@ //
//@@@ @@@@ @@@@@@@ @@@@ @@ @@ @@@@ @@@@@ @@@@@ //
//@@@ @@@@ @@@@@@@ @@@@ @@ @@ @@@@ @@@@@ @@ //
//@@@ @@@ @@@ @@@@@ @@ @@@ //
// @@@ @@@ @@ @@ STUDIOS //
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// ARX_NPC
//////////////////////////////////////////////////////////////////////////////////////
//
// Description:
// ARX NPC Management
//
// Updates: (date) (person) (update)
//
// Code: Cyril Meynier
//
// Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
//////////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ARX_Script.h"
#include "ARX_Collisions.h"
#include "ARX_Damages.h"
#include "ARX_Equipment.h"
#include "ARX_interface.h"
#include "ARX_interactive.h"
#include "ARX_Speech.h"
#include "ARX_Paths.h"
#include "ARX_Paths.h"
#include "ARX_Particles.h"
#include "ARX_Scene.h"
#include "ARX_Sound.h"
#include "ARX_Spells.h"
#include "ARX_Time.h"
#include "EERIECollisionSpheres.h"
#include "EERIEMeshTweak.h"
#define _CRTDBG_MAP_ALLOC
#include
void CheckNPCEx(INTERACTIVE_OBJ * io);
static const float ARX_NPC_ON_HEAR_MAX_DISTANCE_STEP(600.0F);
static const float ARX_NPC_ON_HEAR_MAX_DISTANCE_ITEM(800.0F);
extern long LastSelectedIONum;
extern long APPLY_PUSH;
void StareAtTarget(INTERACTIVE_OBJ * io);
#define RUN_WALK_RADIUS 450
void ARX_NPC_Kill_Spell_Launch(INTERACTIVE_OBJ * io)
{
if (io)
{
if (io->flarecount)
{
for (long i = 0; i < MAX_FLARES; i++)
{
if ((flare[i].exist) && (flare[i].io == io))
flare[i].io = NULL;
}
}
io->spellcast_data.castingspell = -1;
}
}
//***********************************************************************************************
// Releases Pathfinder info from an NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_ReleasePathFindInfo(INTERACTIVE_OBJ * io)
{
// Checks for valid IO/NPC
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
// Releases data & resets vars
if (io->_npcdata->pathfind.list)
free(io->_npcdata->pathfind.list);
io->_npcdata->pathfind.list = NULL;
io->_npcdata->pathfind.listnb = -1;
io->_npcdata->pathfind.listpos = 0;
io->_npcdata->pathfind.pathwait = 0;
}
//***********************************************************************************************
// Creates an extra rotations structure for a NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_CreateExRotateData(INTERACTIVE_OBJ * io)
{
if ((!io)
|| (!(io->ioflags & IO_NPC))
|| (io->_npcdata->ex_rotate))
return;
io->_npcdata->ex_rotate = (EERIE_EXTRA_ROTATE *)malloc(sizeof(EERIE_EXTRA_ROTATE));
io->head_rot = 0;
if (io->_npcdata->ex_rotate)
{
io->_npcdata->ex_rotate->group_number[0] = (short)EERIE_OBJECT_GetGroup(io->obj, "head");
io->_npcdata->ex_rotate->group_number[1] = (short)EERIE_OBJECT_GetGroup(io->obj, "neck");
io->_npcdata->ex_rotate->group_number[2] = (short)EERIE_OBJECT_GetGroup(io->obj, "chest");
io->_npcdata->ex_rotate->group_number[3] = (short)EERIE_OBJECT_GetGroup(io->obj, "belt");
for (long n = 0; n < MAX_EXTRA_ROTATE; n++)
{
io->_npcdata->ex_rotate->group_rotate[n].a = 0;
io->_npcdata->ex_rotate->group_rotate[n].b = 0;
io->_npcdata->ex_rotate->group_rotate[n].g = 0;
}
io->_npcdata->ex_rotate->flags = 0;
}
io->_npcdata->look_around_inc = 0.f;
}
//***********************************************************************************************
// Resurects an NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_Revive(INTERACTIVE_OBJ * io, long flags)
{
if ((TSecondaryInventory) && (TSecondaryInventory->io == io))
{
TSecondaryInventory = NULL;
}
ARX_SCRIPT_SetMainEvent(io, "MAIN");
if (io->ioflags & IO_NPC)
{
io->ioflags &= ~IO_NO_COLLISIONS;
io->_npcdata->life = io->_npcdata->maxlife;
ARX_SCRIPT_ResetObject(io, 1);
io->_npcdata->life = io->_npcdata->maxlife;
}
if (flags & 1)
{
io->room_flags |= 1;
io->pos.x = io->initpos.x;
io->pos.y = io->initpos.y;
io->pos.z = io->initpos.z;
}
long goretex = -1;
for (long i = 0; i < io->obj->nbmaps; i++)
{
if (io->obj->texturecontainer
&& io->obj->texturecontainer[i]
&& (IsIn(io->obj->texturecontainer[i]->m_strName, "GORE")))
{
goretex = i;
break;
}
}
for (long ll = 0; ll < io->obj->nbfaces; ll++)
{
if (io->obj->facelist[ll].texid != goretex)
{
io->obj->facelist[ll].facetype &= ~POLY_HIDE;
}
else
{
io->obj->facelist[ll].facetype |= POLY_HIDE;
}
}
if (io->ioflags & IO_NPC)
io->_npcdata->cuts = 0;
}
//***********************************************************************************************
// Sets a new behaviour for NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_Behaviour_Change(INTERACTIVE_OBJ * io, long behavior, long behavior_param)
{
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT) && !(behavior & BEHAVIOUR_FIGHT))
{
ANIM_USE * ause1 = &io->animlayer[1];
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ause1->cur_anim = NULL;
}
if ((behavior & BEHAVIOUR_NONE) || (behavior == 0))
{
ANIM_USE * ause0 = &io->animlayer[0];
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ause0->cur_anim = NULL;
ANIM_Set(ause0, io->anims[ANIM_DEFAULT]);
ause0->flags &= ~EA_LOOP;
ANIM_USE * ause1 = &io->animlayer[1];
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ause1->cur_anim = NULL;
ause1->flags &= ~EA_LOOP;
ANIM_USE * ause2 = &io->animlayer[2];
AcquireLastAnim(io);
FinishAnim(io, ause2->cur_anim);
ause2->cur_anim = NULL;
ause2->flags &= ~EA_LOOP;
}
if (behavior & BEHAVIOUR_FRIENDLY)
{
ANIM_USE * ause0 = &io->animlayer[0];
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, io->anims[ANIM_DEFAULT]);
ause0->altidx_cur = 0;
}
io->_npcdata->behavior = behavior;
io->_npcdata->behavior_param = (float)behavior_param;
}
//***********************************************************************************************
// Resets all behaviour data from a NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_Behaviour_Reset(INTERACTIVE_OBJ * io)
{
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
io->_npcdata->behavior = BEHAVIOUR_NONE;
for (long i = 0; i < MAX_STACKED_BEHAVIOR; i++)
io->_npcdata->stacked[i].exist = 0;
}
//***********************************************************************************************
// Reset all Behaviours from all NPCs
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_Behaviour_ResetAll()
{
for (long i = 0; i < inter.nbmax; i++)
{
if (inter.iobj[i])
ARX_NPC_Behaviour_Reset(inter.iobj[i]);
}
}
//***********************************************************************************************
// Stacks an NPC behaviour
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_Behaviour_Stack(INTERACTIVE_OBJ * io)
{
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
for (long i = 0; i < MAX_STACKED_BEHAVIOR; i++)
{
IO_BEHAVIOR_DATA * bd = &io->_npcdata->stacked[i];
if (bd->exist == 0)
{
bd->behavior = io->_npcdata->behavior;
bd->behavior_param = io->_npcdata->behavior_param;
bd->tactics = io->_npcdata->tactics;
if (io->_npcdata->pathfind.listnb > 0)
bd->target = io->_npcdata->pathfind.truetarget;
else
bd->target = io->targetinfo;
bd->movemode = io->_npcdata->movemode;
bd->exist = 1;
return;
}
}
}
//***********************************************************************************************
// Unstacks One stacked behaviour from an NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_Behaviour_UnStack(INTERACTIVE_OBJ * io)
{
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
for (long i = MAX_STACKED_BEHAVIOR - 1; i >= 0; i--)
{
IO_BEHAVIOR_DATA * bd = &io->_npcdata->stacked[i];
if (bd->exist)
{
AcquireLastAnim(io);
io->_npcdata->behavior = bd->behavior;
io->_npcdata->behavior_param = bd->behavior_param;
io->_npcdata->tactics = bd->tactics;
io->targetinfo = bd->target;
io->_npcdata->movemode = bd->movemode;
bd->exist = 0;
ARX_NPC_LaunchPathfind(io, bd->target);
if (io->_npcdata->behavior & BEHAVIOUR_NONE)
{
memcpy(io->animlayer, bd->animlayer, sizeof(ANIM_USE)*MAX_ANIM_LAYERS);
}
return;
}
}
}
extern void GetIOCyl(INTERACTIVE_OBJ * io, EERIE_CYLINDER * cyl);
//***********************************************************************************************
// long ARX_NPC_GetNextAttainableNodeIncrement(INTERACTIVE_OBJ * io)
// Description:
// Checks for any direct shortcut between NPC and future anchors...
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
long ARX_NPC_GetNextAttainableNodeIncrement(INTERACTIVE_OBJ * io)
{
if ((!io)
|| (!(io->ioflags & IO_NPC))
|| (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND))
return 0;
float dist = EEDistance3D(&io->pos, &ACTIVECAM->pos);
if (dist > ACTIVECAM->cdepth * DIV2)
return 0;
long MAX_TEST;
if (dist < ACTIVECAM->cdepth * DIV4)
MAX_TEST = 6; //4;
else
MAX_TEST = 4; //3;
for (long l_try = MAX_TEST; l_try > 1; l_try--)
{
if (io->_npcdata->pathfind.listpos + l_try > io->_npcdata->pathfind.listnb - 1) continue;
float tot = 0.f;
for (long aa = l_try; aa > 1; aa--)
{
long v = io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos+aa];
tot += ACTIVEBKG->anchors[v].nblinked;
if (aa == l_try)
tot += ACTIVEBKG->anchors[v].nblinked;
}
tot /= (float)(l_try + 1);
if (tot <= 3.5f) continue;
io->physics.startpos.x = io->pos.x;
io->physics.startpos.y = io->pos.y;
io->physics.startpos.z = io->pos.z;
long pos = io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos+l_try];
io->physics.targetpos.x = ACTIVEBKG->anchors[pos].pos.x;
io->physics.targetpos.y = ACTIVEBKG->anchors[pos].pos.y;
io->physics.targetpos.z = ACTIVEBKG->anchors[pos].pos.z;
if (EEfabs(io->physics.startpos.y - io->physics.targetpos.y) > 60.f) continue;
io->physics.targetpos.y += 60.f; // FAKE Gravity !
IO_PHYSICS phys;
memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
GetIOCyl(io, &phys.cyl);
// Now we try the physical move for real
if (Vector_Compare(&io->physics.startpos, &io->physics.targetpos)
|| ((ARX_COLLISION_Move_Cylinder(&phys, io, 40, CFLAG_JUST_TEST | CFLAG_NPC))))
{
float dist = EEDistance3D(&phys.cyl.origin, &ACTIVEBKG->anchors[pos].pos);
if (dist < 30)
{
return l_try;
}
}
}
return 0;
}
//*****************************************************************************
// Checks for nearest VALID anchor for a cylinder from a position
//*****************************************************************************
long AnchorData_GetNearest(EERIE_3D * pos, EERIE_CYLINDER * cyl)
{
long returnvalue = -1;
float distmax = 99999999.f;
EERIE_BACKGROUND * eb = ACTIVEBKG;
for (long i = 0; i < eb->nbanchors; i++)
{
if (eb->anchors[i].nblinked)
{
float d = EEDistance3D(&eb->anchors[i].pos, pos);
if ((d < distmax) && (eb->anchors[i].height <= cyl->height)
&& (eb->anchors[i].radius >= cyl->radius)
&& (!(eb->anchors[i].flags & ANCHOR_FLAG_BLOCKED)))
{
returnvalue = i;
distmax = d;
}
}
}
return returnvalue;
}
long AnchorData_GetNearest_2(float beta, EERIE_3D * pos, EERIE_CYLINDER * cyl)
{
EERIE_3D vect;
float d = DEG2RAD(beta);
vect.x = -EEsin(d);
vect.y = 0;
vect.z = EEcos(d);
TRUEVector_Normalize(&vect);
EERIE_3D posi;
posi.x = pos->x + vect.x * 50.f;
posi.y = pos->y;
posi.z = pos->z + vect.x * 50.f;
return AnchorData_GetNearest(&posi, cyl);
}
long AnchorData_GetNearest_Except(EERIE_3D * pos, EERIE_CYLINDER * cyl, long except)
{
long returnvalue = -1;
float distmax = 99999999.f;
EERIE_BACKGROUND * eb = ACTIVEBKG;
for (long i = 0; i < eb->nbanchors; i++)
{
if (i == except) continue;
if (eb->anchors[i].nblinked)
{
float d = EEDistance3D(&eb->anchors[i].pos, pos);
if ((d < distmax) && (eb->anchors[i].height <= cyl->height)
&& (eb->anchors[i].radius >= cyl->radius)
&& (!(eb->anchors[i].flags & ANCHOR_FLAG_BLOCKED)))
{
returnvalue = i;
distmax = d;
}
}
}
return returnvalue;
}
BOOL ARX_NPC_LaunchPathfind(INTERACTIVE_OBJ * io, long target)
{
// Check Validity
if ((!io) || (!(io->ioflags & IO_NPC)))
return FALSE;
long MUST_SELECT_Start_Anchor = -1;
io->physics.cyl.origin.x = io->pos.x;
io->physics.cyl.origin.y = io->pos.y;
io->physics.cyl.origin.z = io->pos.z;
long old_target = io->targetinfo;
if ((!(io->ioflags & IO_PHYSICAL_OFF)))
{
if (!ForceNPC_Above_Ground(io))
{
io->_npcdata->pathfind.pathwait = 0;
return FALSE;
}
}
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)
{
io->targetinfo = target;
io->_npcdata->pathfind.pathwait = 0;
return FALSE;
}
EERIE_3D pos1, pos2;
if (io->_npcdata->pathfind.listnb > 0)
{
io->_npcdata->pathfind.listnb = -1;
io->_npcdata->pathfind.listpos = 0;
io->_npcdata->pathfind.pathwait = 0;
io->_npcdata->pathfind.truetarget = TARGET_NONE;
if (io->_npcdata->pathfind.list) free(io->_npcdata->pathfind.list);
io->_npcdata->pathfind.list = NULL;
}
if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
{
pos1.x = io->pos.x;
pos1.y = io->pos.y;
pos1.z = io->pos.z;
pos2.x = io->pos.x + 1000.f;
pos2.y = io->pos.y;
pos2.z = io->pos.z + 1000.f;
goto wander;
}
if ((target < 0) || (io->_npcdata->behavior & BEHAVIOUR_GO_HOME))
{
target = GetInterNum(io);
if (target == -1)
goto failure;
io->_npcdata->pathfind.truetarget = target;
pos1.x = io->pos.x;
pos1.y = io->pos.y;
pos1.z = io->pos.z;
if (io->_npcdata->behavior & BEHAVIOUR_GO_HOME)
{
pos2.x = io->initpos.x;
pos2.y = io->initpos.y;
pos2.z = io->initpos.z;
}
else
{
pos2.x = pos1.x;
pos2.y = pos1.y;
pos2.z = pos1.z;
}
goto suite;
}
if ((ValidIONum(target))
&& (inter.iobj[target] == io))
{
io->_npcdata->pathfind.pathwait = 0;
return FALSE; // cannot pathfind self...
}
if (old_target != target)
io->_npcdata->reachedtarget = 0;
EVENT_SENDER = NULL;
pos1.x = io->pos.x;
pos1.y = io->pos.y;
pos1.z = io->pos.z;
if (io->_npcdata->behavior & BEHAVIOUR_GO_HOME)
{
pos2.x = io->initpos.x;
pos2.y = io->initpos.y;
pos2.z = io->initpos.z;
}
else
{
if (ValidIONum(target))
{
INTERACTIVE_OBJ * io2;
io2 = inter.iobj[target];
pos2.x = io2->pos.x;
pos2.y = io2->pos.y;
pos2.z = io2->pos.z;
}
else
{
pos2.x = io->pos.x;
pos2.y = io->pos.y;
pos2.z = io->pos.z;
}
}
io->_npcdata->pathfind.truetarget = target;
float dist;
dist = EEDistance3D(&pos1, &pos2);
if ((EEDistance3D(&pos1, &ACTIVECAM->pos) < ACTIVECAM->cdepth * DIV2)
&& (EEfabs(pos1.y - pos2.y) < 50.f)
&& (dist < 520) && (io->_npcdata->behavior & BEHAVIOUR_MOVE_TO)
&& (!(io->_npcdata->behavior & BEHAVIOUR_SNEAK))
&& (!(io->_npcdata->behavior & BEHAVIOUR_FLEE))
)
{
// COLLISION Management START *********************************************************************
io->physics.startpos.x = pos1.x;
io->physics.startpos.y = pos1.y;
io->physics.startpos.z = pos1.z;
io->physics.targetpos.x = pos2.x;
io->physics.targetpos.y = pos2.y;
io->physics.targetpos.z = pos2.z;
IO_PHYSICS phys;
memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
GetIOCyl(io, &phys.cyl);
// Now we try the physical move for real
if (Vector_Compare(&io->physics.startpos, &io->physics.targetpos) // optim
|| ((ARX_COLLISION_Move_Cylinder(&phys, io, 40, CFLAG_JUST_TEST | CFLAG_NPC | CFLAG_NO_HEIGHT_MOD)) ))
{
dist = TRUEEEDistance3D(&phys.cyl.origin, &pos2);
if (dist < 100)
{
io->_npcdata->pathfind.pathwait = 0;
return FALSE;
}
}
}
suite:
;
wander:
;
io->targetinfo = target;
io->_npcdata->pathfind.truetarget = target;
long from;
if (MUST_SELECT_Start_Anchor == -1)
{
if ((io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
|| (io->_npcdata->behavior & BEHAVIOUR_FLEE))
from = AnchorData_GetNearest(&pos1, &io->physics.cyl);
else
from = AnchorData_GetNearest_2(io->angle.b, &pos1, &io->physics.cyl);
}
else from = MUST_SELECT_Start_Anchor;
long to;
if (io->_npcdata->behavior & BEHAVIOUR_FLEE)
to = AnchorData_GetNearest_Except(&pos2, &io->physics.cyl, from);
else if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
to = from;
else to = AnchorData_GetNearest(&pos2, &io->physics.cyl);
if ((from != -1) && (to != -1))
{
if ((from == to) && !(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND))
return TRUE;
float dist1 = EEDistance3D(&pos1, &ACTIVEBKG->anchors[from].pos);
if ((io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND) ||
(dist1 < 200.f))
{
if (!(io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
&& !(io->_npcdata->behavior & BEHAVIOUR_FLEE))
{
float dist2 = EEDistance3D(&pos2, &ACTIVEBKG->anchors[to].pos);
float dist3 = EEDistance3D(&ACTIVEBKG->anchors[from].pos, &ACTIVEBKG->anchors[to].pos);
if (dist3 < 200.f) return FALSE;
if ((dist2 > 200.f))
goto failure;
}
if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
{
io->_npcdata->pathfind.truetarget = TARGET_NONE;
}
io->_npcdata->pathfind.listnb = -1;
io->_npcdata->pathfind.listpos = 0;
io->_npcdata->pathfind.pathwait = 1;
if (io->_npcdata->pathfind.list) free(io->_npcdata->pathfind.list);
io->_npcdata->pathfind.list = NULL;
PATHFINDER_REQUEST tpr;
tpr.from = from;
tpr.to = to;
tpr.returnlist = &io->_npcdata->pathfind.list;
tpr.returnnumber = &io->_npcdata->pathfind.listnb;
tpr.ioid = io;
tpr.isvalid = TRUE;
if (EERIE_PATHFINDER_Add_To_Queue(&tpr))
return TRUE;
}
}
failure:
;
io->_npcdata->pathfind.pathwait = 0;
if (io->_npcdata->pathfind.list) ARX_NPC_ReleasePathFindInfo(io);
io->_npcdata->pathfind.listnb = -2;
if (io->_npcdata->pathfind.flags & BEHAVIOUR_NONE) return FALSE;
SendIOScriptEvent(io, SM_PATHFINDER_FAILURE, "", NULL);
return FALSE;
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_NPC_SetStat(INTERACTIVE_OBJ * io, char * statname, float value)
{
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
if (!stricmp(statname, "ARMOR_CLASS"))
{
if (value < 0) value = 0;
io->_npcdata->armor_class = value;
}
else if (!stricmp(statname, "BACKSTAB_SKILL"))
{
if (value < 0) value = 0;
io->_npcdata->backstab_skill = value;
}
else if (!stricmp(statname, "BACKSTAB"))
{
if (value == 0) io->_npcdata->npcflags &= ~NPCFLAG_BACKSTAB;
else io->_npcdata->npcflags |= NPCFLAG_BACKSTAB;
}
else if (!stricmp(statname, "REACH"))
{
if (value < 0) value = 0;
io->_npcdata->reach = value;
}
else if (!stricmp(statname, "CRITICAL"))
{
if (value < 0) value = 0;
io->_npcdata->critical = value;
}
else if (!stricmp(statname, "ABSORB"))
{
if (value < 0) value = 0;
io->_npcdata->absorb = value;
}
else if (!stricmp(statname, "DAMAGES"))
{
if (value < 0) value = 0;
io->_npcdata->damages = value;
}
else if (!stricmp(statname, "TOHIT"))
{
if (value < 0) value = 0;
io->_npcdata->tohit = value;
}
else if (!stricmp(statname, "AIMTIME"))
{
if (value < 0) value = 0;
io->_npcdata->aimtime = value;
}
else if (!stricmp(statname, "LIFE"))
{
if (value < 0) value = 0.0000001f;
io->_npcdata->maxlife = io->_npcdata->life = value;
}
else if (!stricmp(statname, "MANA"))
{
if (value < 0) value = 0;
io->_npcdata->maxmana = io->_npcdata->mana = value;
}
else if (!stricmp(statname, "RESISTFIRE"))
{
if (value < 0) value = 0;
else if (value > 100) value = 100;
io->_npcdata->resist_fire = (unsigned char)value;
}
else if (!stricmp(statname, "RESISTPOISON"))
{
if (value < 0) value = 0;
else if (value > 100) value = 100;
io->_npcdata->resist_poison = (unsigned char)value;
}
else if (!stricmp(statname, "RESISTMAGIC"))
{
if (value < 0) value = 0;
else if (value > 100) value = 100;
io->_npcdata->resist_magic = (unsigned char)value;
}
}
extern long CUR_COLLISION_MATERIAL;
INTERACTIVE_OBJ * PHYSICS_CURIO = NULL;
//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_TEMPORARY_TrySound(float volume)
{
if (PHYSICS_CURIO)
{
if (PHYSICS_CURIO->ioflags & IO_BODY_CHUNK)
return;
unsigned long at = ARXTimeUL();
if (at > PHYSICS_CURIO->soundtime)
{
long material;
PHYSICS_CURIO->soundcount++;
if (PHYSICS_CURIO->soundcount < 5)
{
if ( EEIsUnderWaterFast( &PHYSICS_CURIO->pos ) ) material = MATERIAL_WATER; //ARX: jycorbel (2010-08-20) - rendering issues with bGATI8500: optimize time to render;
else if (PHYSICS_CURIO->material) material = PHYSICS_CURIO->material;
else material = MATERIAL_STONE;
if (volume > 1.f)
volume = 1.f;
PHYSICS_CURIO->soundtime = at + (ARX_SOUND_PlayCollision(material, CUR_COLLISION_MATERIAL, volume, 1.f, &PHYSICS_CURIO->pos, PHYSICS_CURIO) >> 4) + 50;
}
}
}
}
//***********************************************************************************************
// Sets a New MoveMode for a NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_ChangeMoveMode(INTERACTIVE_OBJ * io, long MOVEMODE)
{
if ((!io)
|| (!(io->ioflags & IO_NPC)))
return;
ANIM_USE * ause0 = &io->animlayer[0];
ANIM_HANDLE ** alist = io->anims;
switch (MOVEMODE)
{
case RUNMODE:
if (((ause0->cur_anim == alist[ANIM_WALK]) && (alist[ANIM_WALK]))
|| ((ause0->cur_anim == alist[ANIM_WALK_SNEAK]) && (alist[ANIM_WALK_SNEAK])))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_RUN]);
ause0->altidx_cur = 0;
}
break;
case WALKMODE:
if (((ause0->cur_anim == alist[ANIM_RUN]) && (alist[ANIM_RUN]))
|| ((ause0->cur_anim == alist[ANIM_WALK_SNEAK]) && (alist[ANIM_WALK_SNEAK])))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WALK]);
}
break;
case NOMOVEMODE:
if (((ause0->cur_anim == alist[ANIM_WALK]) && (alist[ANIM_WALK]))
|| ((ause0->cur_anim == alist[ANIM_RUN]) && (alist[ANIM_RUN]))
|| ((ause0->cur_anim == alist[ANIM_WALK_SNEAK]) && (alist[ANIM_WALK_SNEAK])))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WAIT]);
ause0->altidx_cur = 0;
}
break;
case SNEAKMODE:
if (((ause0->cur_anim == alist[ANIM_WALK]) && (alist[ANIM_WALK]))
|| ((ause0->cur_anim == alist[ANIM_RUN]) && (alist[ANIM_RUN])))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WALK_SNEAK]);
}
break;
}
io->_npcdata->movemode = MOVEMODE;
}
//***********************************************************************************************
// Diminishes life of a Poisoned NPC
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ARX_NPC_ManagePoison(INTERACTIVE_OBJ * io)
{
float cp = io->_npcdata->poisonned;
cp *= DIV2 * _framedelay * DIV1000 * DIV2;
float faster = 10.f - io->_npcdata->poisonned;
if (faster < 0.f) faster = 0.f;
if (rnd() * 100.f > io->_npcdata->resist_poison + faster)
{
float dmg = cp * DIV3;
if ((io->_npcdata->life > 0) && (io->_npcdata->life - dmg <= 0.f))
{
long xp = io->_npcdata->xpvalue;
ARX_DAMAGES_DamageNPC(io, dmg, -1, 0, NULL);
ARX_PLAYER_Modify_XP(xp);
}
else io->_npcdata->life -= dmg;
io->_npcdata->poisonned -= cp * DIV10;
}
else io->_npcdata->poisonned -= cp;
if (io->_npcdata->poisonned < 0.1f) io->_npcdata->poisonned = 0.f;
}
//*************************************************************************************
//*************************************************************************************
extern void ARX_PARTICLES_Spawn_Splat(EERIE_3D * pos, float dmgs, D3DCOLOR col, long vert, INTERACTIVE_OBJ * io, long flags);
extern void ARX_PARTICLES_Spawn_Blood3(EERIE_3D * pos, float dmgs, D3DCOLOR col, long vert, INTERACTIVE_OBJ * io, long flags);
extern float MAX_ALLOWED_PER_SECOND;
long REACTIVATION_COUNT = 0;
void ARX_PHYSICS_Apply()
{
static long CURRENT_DETECT = 0;
REACTIVATION_COUNT++;
if (REACTIVATION_COUNT > 12) REACTIVATION_COUNT = 0;
CURRENT_DETECT++;
if (CURRENT_DETECT > TREATZONE_CUR) CURRENT_DETECT = 1;
long needkill = 0;
for (long i = 1; i < TREATZONE_CUR; i++) // We don't manage Player(0) this way
{
if (treatio[i].show != 1) continue;
if (treatio[i].ioflags & (IO_FIX | IO_JUST_COLLIDE))
continue;
INTERACTIVE_OBJ * io = treatio[i].io;
if (!io) continue;
if ((io->ioflags & IO_NPC) && (io->_npcdata->poisonned > 0.f)) ARX_NPC_ManagePoison(io);
if ((io->ioflags & IO_ITEM)
&& (io->show != SHOW_FLAG_DESTROYED)
&& ((io->GameFlags & GFLAG_GOREEXPLODE)
&& (ARXTime - io->lastanimtime > 300))
&& ((io->obj)
&& (io->obj->nbvertex))
)
{
long idx;
long cnt = (io->obj->nbvertex << 12) + 1;
if (cnt < 2) cnt = 2;
if (cnt > 10) cnt = 10;
for (long nn = 0; nn < cnt; nn++)
{
F2L(rnd()*(float)io->obj->nbvertex, &idx);
if (idx >= io->obj->nbvertex) idx = io->obj->nbvertex - 1;
EERIE_3D vector;
vector.x = io->obj->vertexlist3[idx].v.x - io->obj->vertexlist3[0].v.x + (rnd() - rnd()) * 3.f;
vector.y = io->obj->vertexlist3[idx].v.y - io->obj->vertexlist3[0].v.y + (rnd() - rnd()) * 3.f;
vector.z = io->obj->vertexlist3[idx].v.z - io->obj->vertexlist3[0].v.z + (rnd() - rnd()) * 3.f;
TRUEVector_Normalize(&vector);
ARX_PARTICLES_Spawn_Splat(&io->obj->vertexlist3[idx].v, 20, 0xFFFF0000, idx, io, 1);
ARX_PARTICLES_Spawn_Blood(&io->obj->vertexlist3[idx].v, &vector, 20, GetInterNum(io));
needkill = 1;
}
ARX_INTERACTIVE_DestroyIO(io);
continue;
}
EERIEPOLY * ep = EECheckInPoly(&io->pos);
if ((ep)
&& (ep->type & POLY_LAVA)
&& (EEfabs(ep->center.y - io->pos.y) < 40))
{
ARX_PARTICLES_Spawn_Lava_Burn(&io->pos, 0.6f, io);
if (io->ioflags & IO_NPC)
{
#define LAVA_DAMAGE 10.f
ARX_DAMAGES_DamageNPC(io, LAVA_DAMAGE * FrameDiff * DIV100, -1, 0, NULL);
}
}
CheckUnderWaterIO(io);
if (io->obj->pbox)
{
io->GameFlags &= ~GFLAG_NOCOMPUTATION;
if ((io->obj->pbox->active == 1))
{
PHYSICS_CURIO = io;
long flags = 0;
if (io->GameFlags & GFLAG_NO_PHYS_IO_COL) flags = 1;
if (EERIE_PHYSICS_BOX_ApplyModel(io->obj, (float)FrameDiff, io->rubber, flags, treatio[i].num))
{
if (io->damagedata >= 0)
{
damages[io->damagedata].active = 1;
ARX_DAMAGES_UpdateDamage(io->damagedata, (float)ARXTime);
damages[io->damagedata].exist = 0;
io->damagedata = -1;
}
}
if (io->soundcount > 12)
{
io->soundtime = 0;
io->soundcount = 0;
for (long k = 0; k < io->obj->pbox->nb_physvert; k++)
{
PHYSVERT * pv = &io->obj->pbox->vert[k];
pv->velocity.x = 0.f;
pv->velocity.y = 0.f;
pv->velocity.z = 0.f;
}
io->obj->pbox->active = 2;
io->obj->pbox->stopcount = 0;
}
io->room_flags |= 1;
io->pos.x = io->obj->pbox->vert[0].pos.x;
io->pos.y = io->obj->pbox->vert[0].pos.y;
io->pos.z = io->obj->pbox->vert[0].pos.z;
continue;
}
}
if (IsDeadNPC(io)) continue;
if (io->ioflags & IO_PHYSICAL_OFF)
{
if (io->ioflags & IO_NPC)
{
ANIM_USE * ause0 = &io->animlayer[0];
if ((ause0->cur_anim == 0) || (ause0->flags & EA_ANIMEND))
{
ANIM_Set(ause0, io->anims[ANIM_WAIT]);
ause0->altidx_cur = 0;
}
GetTargetPos(io);
if ((!ARXPausedTimer) && (!(ause0->flags & EA_FORCEPLAY)))
{
if (io->_npcdata->behavior & BEHAVIOUR_STARE_AT)
StareAtTarget(io);
else FaceTarget2(io);
}
}
continue;
}
if ((io->ioflags & IO_NPC)
&& (!EDITMODE))
{
if ((io->_npcdata->climb_count != 0.f) && (FrameDiff > 0))
{
io->_npcdata->climb_count -= MAX_ALLOWED_PER_SECOND * (float)FrameDiff * DIV1000;
if (io->_npcdata->climb_count < 0)
io->_npcdata->climb_count = 0.f;
}
if (io->_npcdata->pathfind.pathwait) // Waiting For Pathfinder Answer
{
if ((ValidIONum(LastSelectedIONum)) &&
(io == inter.iobj[LastSelectedIONum])) ShowIOPath(io);
if (io->_npcdata->pathfind.listnb == 0) // Not Found
{
SendIOScriptEvent(io, SM_PATHFINDER_FAILURE, "", NULL);
io->_npcdata->pathfind.pathwait = 0;
if (io->_npcdata->pathfind.list)
ARX_NPC_ReleasePathFindInfo(io);
io->_npcdata->pathfind.listnb = -2;
}
else if (io->_npcdata->pathfind.listnb > 0) // Found
{
SendIOScriptEvent(io, SM_PATHFINDER_SUCCESS, "", NULL);
io->_npcdata->pathfind.pathwait = 0;
io->_npcdata->pathfind.listpos += (unsigned short)ARX_NPC_GetNextAttainableNodeIncrement(io);
if (io->_npcdata->pathfind.listpos >= io->_npcdata->pathfind.listnb)
io->_npcdata->pathfind.listpos = 0;
}
}
ManageNPCMovement(io);
CheckNPC(io);
if (CURRENT_DETECT == i) CheckNPCEx(io);
}
}
}
//*************************************************************************************
//*************************************************************************************
void FaceTarget2(INTERACTIVE_OBJ * io)
{
EERIE_3D tv;
if (!io->show) return;
if (io->ioflags & IO_NPC)
{
if (io->_npcdata->life <= 0.f) return;
if (io->_npcdata->behavior & BEHAVIOUR_NONE) return;
if ((io->_npcdata->pathfind.listnb <= 0)
&& (io->_npcdata->behavior & BEHAVIOUR_FLEE)) return;
}
GetTargetPos(io);
tv.x = io->pos.x;
tv.y = io->pos.y;
tv.z = io->pos.z;
if (Distance2D(tv.x, tv.z, io->target.x, io->target.z) <= 5.f) return;
float cangle, tangle;
tangle = MAKEANGLE(180.f + RAD2DEG(GetAngle(io->target.x, io->target.z, tv.x, tv.z)));
cangle = io->angle.b;
float tt = (cangle - tangle);
if (tt == 0) return;
float rot = 0.33f * _framedelay;
if (EEfabs(tt) < rot) rot = (float)EEfabs(tt);
rot = -rot;
if ((tt > 0.f) && (tt < 180.f)) rot = -rot;
else if ((tt < -180.f)) rot = -rot;
if (rot != 0)
{
EERIE_3D temp;
temp.x = io->move.x;
temp.y = io->move.y;
temp.z = io->move.z;
Vector_RotateY(&io->move, &temp, rot);
temp.x = io->lastmove.x;
temp.y = io->lastmove.y;
temp.z = io->lastmove.z;
Vector_RotateY(&io->lastmove, &temp, rot);
}
// Needed angle to turn toward target
io->angle.b = MAKEANGLE(io->angle.b - rot); // -tt
}
//***********************************************************************************************
//***********************************************************************************************
void StareAtTarget(INTERACTIVE_OBJ * io)
{
if (io->_npcdata->ex_rotate == NULL)
{
ARX_NPC_CreateExRotateData(io);
}
EERIE_3D tv;
if (!io->show) return;
if (io->ioflags & IO_NPC)
{
if (io->_npcdata->life <= 0.f) return;
if ((io->_npcdata->pathfind.listnb <= 0) && (io->_npcdata->behavior & BEHAVIOUR_FLEE)) return;
}
if (io->_npcdata->behavior & BEHAVIOUR_NONE) return;
GetTargetPos(io);
tv.x = io->pos.x;
tv.y = io->pos.y;
tv.z = io->pos.z;
if (TRUEEEDistance3D(&tv, &io->target) <= 20.f) return; // To fix "stupid" rotation near target
if (((io->target.x - tv.x) == 0) && ((io->target.z - tv.z) == 0)) return;
float rot = 0.27f * _framedelay;
float alpha = MAKEANGLE(io->angle.b);
float beta = -io->head_rot;
float pouet = MAKEANGLE(180.f + RAD2DEG(GetAngle(io->target.x, io->target.z, tv.x, tv.z)));
float A = MAKEANGLE((MAKEANGLE(alpha + beta) - pouet));
float B = MAKEANGLE(alpha - pouet);
if (A == 0.f) rot = 0.f;
if ((B < 180) && (B > 90))
{
rot = rot;
if (rot > A) rot = A;
}
else if ((B > 180) && (B < 270))
{
if (rot > 360 - A) rot = -(360 - A);
else rot = -rot;
}
else if (A < 180)
{
rot = rot;
if (rot > A) rot = A;
}
else
{
if (rot > 360 - A) rot = -(360 - A);
else rot = -rot;
}
// Needed angle to turn toward target
rot *= DIV2;
float HEAD_ANGLE_THRESHOLD;
if ((io) && (io->ioflags & IO_NPC))
HEAD_ANGLE_THRESHOLD = 45.f * io->_npcdata->stare_factor;
else HEAD_ANGLE_THRESHOLD = 45.f;
io->head_rot += rot;
if (io->head_rot > 120.f) io->head_rot = 120.f;
if (io->head_rot < -120.f) io->head_rot = -120.f;
io->_npcdata->ex_rotate->group_rotate[0].b = io->head_rot * 1.5f;
if (io->_npcdata->ex_rotate->group_rotate[0].b > HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[0].b = HEAD_ANGLE_THRESHOLD;
if (io->_npcdata->ex_rotate->group_rotate[0].b < -HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[0].b = -HEAD_ANGLE_THRESHOLD;
io->_npcdata->ex_rotate->group_rotate[1].b = io->head_rot * DIV2;
if (io->_npcdata->ex_rotate->group_rotate[1].b > HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[1].b = HEAD_ANGLE_THRESHOLD;
if (io->_npcdata->ex_rotate->group_rotate[1].b < -HEAD_ANGLE_THRESHOLD) io->_npcdata->ex_rotate->group_rotate[1].b = -HEAD_ANGLE_THRESHOLD;
//MAKEANGLE(io->angle.b-rot); // -tt
return;
}
//***********************************************************************************************
//***********************************************************************************************
float GetTRUETargetDist(INTERACTIVE_OBJ * io)
{
long t;
if (io->ioflags & IO_NPC)
t = io->_npcdata->pathfind.truetarget;
else t = io->targetinfo;
if ((t >= 0) && (t < inter.nbmax) && (inter.iobj[t] != NULL))
{
if (io->_npcdata->behavior & BEHAVIOUR_GO_HOME)
return TRUEEEDistance3D(&io->pos, &io->initpos);
return TRUEEEDistance3D(&io->pos, &inter.iobj[t]->pos);
}
return 99999999.f;
}
extern TextureContainer * sphere_particle;
extern INTERACTIVE_OBJ * EVENT_SENDER;
//***********************************************************************************************
// Checks If a NPC is dead
//***********************************************************************************************
BOOL IsDeadNPC(INTERACTIVE_OBJ * io)
{
if (!io) return FALSE;
if (!(io->ioflags & IO_NPC)) return FALSE;
if (io->_npcdata->life <= 0) return TRUE;
if ((io->mainevent) && !strcmp(io->mainevent, "DEAD")) return TRUE;
return FALSE;
}
//***********************************************************************************************
//***********************************************************************************************
long IsInGroup(EERIE_3DOBJ * obj, long vert, long tw)
{
if (obj == NULL) return -1;
if (tw < 0) return -1;
if (tw > obj->nbgroups) return -1;
if (vert < 0) return -1;
for (long i = 0; i < obj->grouplist[tw].nb_index; i++)
{
if (obj->grouplist[tw].indexes[i] == vert)
return i;
}
return -1;
}
//***********************************************************************************************
//***********************************************************************************************
long IsNearSelection(EERIE_3DOBJ * obj, long vert, long tw)
{
if (obj == NULL) return -1;
if (tw < 0) return -1;
if (vert < 0) return -1;
for (long i = 0; i < obj->selections[tw].nb_selected; i++)
{
float d = TRUEEEDistance3D(&obj->vertexlist[obj->selections[tw].selected[i]].v,
&obj->vertexlist[vert].v);
if (d < 8.f)
return i;
}
return -1;
}
//***********************************************************************************************
// Spawns a body part from NPC
//***********************************************************************************************
void ARX_NPC_SpawnMember(INTERACTIVE_OBJ * ioo, long num)
{
if (!ioo) return;
EERIE_3DOBJ * from = ioo->obj;
if ((!from)
|| (num < 0)
|| (num > from->nbselections))
return;
EERIE_3DOBJ * nouvo;
nouvo = (EERIE_3DOBJ *)malloc(sizeof(EERIE_3DOBJ));
if (!nouvo)
return;
memset(nouvo, 0, sizeof(EERIE_3DOBJ));
nouvo->nbvertex = from->selections[num].nb_selected;
long gore = -1;
for (long k = 0; k < from->nbmaps; k++)
{
if (from->texturecontainer
&& from->texturecontainer[k]
&& (IsIn(from->texturecontainer[k]->m_strName, "GORE")))
{
gore = k;
break;
}
}
for (int k = 0; k < from->nbfaces; k++)
{
if (from->facelist[k].texid == gore)
{
if ((IsNearSelection(from, from->facelist[k].vid[0], num) >= 0)
|| (IsNearSelection(from, from->facelist[k].vid[1], num) >= 0)
|| (IsNearSelection(from, from->facelist[k].vid[2], num) >= 0))
nouvo->nbvertex += 3;
}
}
nouvo->vertexlist = (EERIE_VERTEX *)malloc(sizeof(EERIE_VERTEX) * nouvo->nbvertex);
if (!nouvo->vertexlist)
{
free(nouvo);
return;
}
nouvo->vertexlist3 = (EERIE_VERTEX *)malloc(sizeof(EERIE_VERTEX) * nouvo->nbvertex);
if (!nouvo->vertexlist3)
{
free(nouvo->vertexlist);
free(nouvo);
return;
}
long inpos = 0;
long * equival = (long *)malloc(sizeof(long) * from->nbvertex);
if (!equival)
{
free(nouvo->vertexlist3);
free(nouvo->vertexlist);
free(nouvo);
}
for (int k = 0 ; k < from->nbvertex ; k++)
equival[k] = -1;
ARX_CHECK(0 < from->selections[num].nb_selected);
for (int k = 0 ; k < from->selections[num].nb_selected ; k++)
{
inpos = from->selections[num].selected[k];
equival[from->selections[num].selected[k]] = k;
memcpy(&nouvo->vertexlist[k], &from->vertexlist[from->selections[num].selected[k]], sizeof(EERIE_VERTEX));
nouvo->vertexlist[k].v.x = nouvo->vertexlist[k].vert.sx = from->vertexlist3[from->selections[num].selected[k]].v.x - ioo->pos.x;
nouvo->vertexlist[k].v.y = nouvo->vertexlist[k].vert.sy = from->vertexlist3[from->selections[num].selected[k]].v.y - ioo->pos.y;
nouvo->vertexlist[k].v.z = nouvo->vertexlist[k].vert.sz = from->vertexlist3[from->selections[num].selected[k]].v.z - ioo->pos.z;
nouvo->vertexlist[k].vert.color = from->vertexlist[k].vert.color;
nouvo->vertexlist[k].vert.tu = from->vertexlist[k].vert.tu;
nouvo->vertexlist[k].vert.tv = from->vertexlist[k].vert.tv;
memcpy(&nouvo->vertexlist3[k], &nouvo->vertexlist[k], sizeof(EERIE_VERTEX));
}
long count = from->selections[num].nb_selected;
for (int k = 0; k < from->nbfaces; k++)
{
if (from->facelist[k].texid == gore)
{
if ((IsNearSelection(from, from->facelist[k].vid[0], num) >= 0)
|| (IsNearSelection(from, from->facelist[k].vid[1], num) >= 0)
|| (IsNearSelection(from, from->facelist[k].vid[2], num) >= 0))
{
for (long j = 0; j < 3; j++)
{
equival[from->facelist[k].vid[j]] = count;
if (count < nouvo->nbvertex)
{
memcpy(&nouvo->vertexlist[count], &from->vertexlist[from->facelist[k].vid[j]], sizeof(EERIE_VERTEX));
nouvo->vertexlist[count].v.x = nouvo->vertexlist[count].vert.sx = from->vertexlist3[from->facelist[k].vid[j]].v.x - ioo->pos.x;
nouvo->vertexlist[count].v.y = nouvo->vertexlist[count].vert.sy = from->vertexlist3[from->facelist[k].vid[j]].v.y - ioo->pos.y;
nouvo->vertexlist[count].v.z = nouvo->vertexlist[count].vert.sz = from->vertexlist3[from->facelist[k].vid[j]].v.z - ioo->pos.z;
memcpy(&nouvo->vertexlist3[count], &nouvo->vertexlist[count], sizeof(EERIE_VERTEX));
}
else
equival[from->facelist[k].vid[j]] = -1;
count++;
}
}
}
}
float min = nouvo->vertexlist[0].vert.sy;
long nummm = 0;
for (int k = 1; k < nouvo->nbvertex; k++)
{
if (nouvo->vertexlist[k].vert.sy > min)
{
min = nouvo->vertexlist[k].vert.sy;
nummm = k;
}
}
nouvo->origin = nummm;
nouvo->point0.x = nouvo->vertexlist[nouvo->origin].v.x;
nouvo->point0.y = nouvo->vertexlist[nouvo->origin].v.y;
nouvo->point0.z = nouvo->vertexlist[nouvo->origin].v.z;
for (int k = 0; k < nouvo->nbvertex; k++)
{
nouvo->vertexlist[k].vert.sx = nouvo->vertexlist[k].v.x -= nouvo->point0.x;
nouvo->vertexlist[k].vert.sy = nouvo->vertexlist[k].v.y -= nouvo->point0.y;
nouvo->vertexlist[k].vert.sz = nouvo->vertexlist[k].v.z -= nouvo->point0.z;
nouvo->vertexlist[k].vert.color = 0xFFFFFFFF;
}
nouvo->point0.x = 0;
nouvo->point0.y = 0;
nouvo->point0.z = 0;
nouvo->pbox = NULL;
nouvo->pdata = NULL;
nouvo->cdata = NULL;
nouvo->sdata = NULL;
nouvo->ndata = NULL;
nouvo->nbfaces = 0;
for (int k = 0; k < from->nbfaces; k++)
{
if ((equival[from->facelist[k].vid[0]] != -1)
&& (equival[from->facelist[k].vid[1]] != -1)
&& (equival[from->facelist[k].vid[2]] != -1))
nouvo->nbfaces++;
}
if (nouvo->nbfaces)
{
nouvo->facelist = (EERIE_FACE *)malloc(sizeof(EERIE_FACE) * nouvo->nbfaces);
long pos = 0;
for (long k = 0; k < from->nbfaces; k++)
{
if ((equival[from->facelist[k].vid[0]] != -1)
&& (equival[from->facelist[k].vid[1]] != -1)
&& (equival[from->facelist[k].vid[2]] != -1))
{
memcpy(&nouvo->facelist[pos], &from->facelist[k], sizeof(EERIE_FACE));
nouvo->facelist[pos].vid[0] = (unsigned short)equival[from->facelist[k].vid[0]];
nouvo->facelist[pos].vid[1] = (unsigned short)equival[from->facelist[k].vid[1]];
nouvo->facelist[pos].vid[2] = (unsigned short)equival[from->facelist[k].vid[2]];
pos++;
}
}
long gore = -1;
for (int k = 0; k < from->nbmaps; k++)
{
if (from->texturecontainer
&& from->texturecontainer[k]
&& (IsIn(from->texturecontainer[k]->m_strName, "GORE")))
{
gore = k;
break;
}
}
for (int k = 0; k < nouvo->nbfaces; k++)
{
nouvo->facelist[k].facetype &= ~POLY_HIDE;
if (nouvo->facelist[k].texid == gore)
nouvo->facelist[k].facetype |= POLY_DOUBLESIDED;
}
}
free(equival);
nouvo->nbmaps = from->nbmaps;
if (from->nbmaps)
{
nouvo->texturecontainer = (TextureContainer **)malloc(sizeof(TextureContainer *) * nouvo->nbmaps);
if (!nouvo->texturecontainer)
{
free(equival);
free(nouvo->vertexlist3);
free(nouvo->vertexlist);
free(nouvo);
}
memcpy(nouvo->texturecontainer, from->texturecontainer, sizeof(TextureContainer *)*nouvo->nbmaps);
}
nouvo->linked = NULL;
nouvo->nblinked = 0;
nouvo->originaltextures = NULL;
INTERACTIVE_OBJ * io = CreateFreeInter();
if (io == NULL)
{
ReleaseEERIE3DObj(nouvo);
return;
}
io->_itemdata = (IO_ITEMDATA *)malloc(sizeof(IO_ITEMDATA));
memset(io->_itemdata, 0, sizeof(IO_ITEMDATA));
io->ioflags = IO_ITEM;
io->script.size = 0;
io->script.data = NULL;
io->GameFlags |= GFLAG_NO_PHYS_IO_COL;
strcpy(io->filename, "NoName");
EERIE_COLLISION_Cylinder_Create(io);
EERIE_PHYSICS_BOX_Create(nouvo);
if (nouvo->pbox == NULL)
{
ReleaseEERIE3DObj(nouvo);
return;
}
io->infracolor.r = 0.f;
io->infracolor.g = 0.f;
io->infracolor.b = 0.8f;
io->collision = 1;
io->inv = NULL;
io->scriptload = 1;
io->obj = nouvo;
io->lastpos.x = io->initpos.x = io->pos.x = ioo->obj->vertexlist3[inpos].v.x;
io->lastpos.y = io->initpos.y = io->pos.y = ioo->obj->vertexlist3[inpos].v.y;
io->lastpos.z = io->initpos.z = io->pos.z = ioo->obj->vertexlist3[inpos].v.z;
io->angle.a = ioo->angle.a;
io->angle.b = ioo->angle.b;
io->angle.g = ioo->angle.g;
io->GameFlags = ioo->GameFlags;
memcpy(&io->halo, &ioo->halo, sizeof(IO_HALO));
ioo->halo.dynlight = -1;
io->ioflags |= IO_MOVABLE;
io->angle.a = rnd() * 40.f + 340.f;
io->angle.b = rnd() * 360.f;
io->angle.g = 0;
io->obj->pbox->active = 1;
io->obj->pbox->stopcount = 0;
EERIE_3D pos, vector;
io->velocity.x = 0.f;
io->velocity.y = 0.f;
io->velocity.z = 0.f;
io->stopped = 1;
vector.x = -(float)EEsin(DEG2RAD(io->angle.b));
vector.y = EEsin(DEG2RAD(io->angle.a)) * 2.f;
vector.z = (float)EEcos(DEG2RAD(io->angle.b));
Vector_Normalize(&vector);
pos.x = io->pos.x;
pos.y = io->pos.y;
pos.z = io->pos.z;
io->rubber = 0.6f;
long long_no_collide = GetInterNum(ioo);
ARX_CHECK_SHORT(long_no_collide);
io->no_collide = ARX_CLEAN_WARN_CAST_SHORT(long_no_collide);
io->GameFlags |= GFLAG_GOREEXPLODE;
io->lastanimtime = ARXTimeUL();//treat warning C4244 conversion from 'float' to 'unsigned long'
io->soundtime = 0;
io->soundcount = 0;
EERIE_PHYSICS_BOX_Launch(io->obj, &pos, &vector, 3, &io->angle);
return;
}
extern long GORE_MODE;
#define FLAG_CUT_HEAD (1)
#define FLAG_CUT_TORSO (1<<1)
#define FLAG_CUT_LARM (1<<2)
#define FLAG_CUT_RARM (1<<3)
#define FLAG_CUT_LLEG (1<<4)
#define FLAG_CUT_RLEG (1<<5)
short GetCutFlag(char * str)
{
if (!stricmp(str, "CUT_HEAD"))
return FLAG_CUT_HEAD;
if (!stricmp(str, "CUT_TORSO"))
return FLAG_CUT_TORSO;
if (!stricmp(str, "CUT_LARM"))
return FLAG_CUT_LARM;
if (!stricmp(str, "CUT_RARM"))
return FLAG_CUT_HEAD;
if (!stricmp(str, "CUT_LLEG"))
return FLAG_CUT_LLEG;
if (!stricmp(str, "CUT_RLEG"))
return FLAG_CUT_RLEG;
return 0;
}
long GetCutSelection(INTERACTIVE_OBJ * io, short flag)
{
if ((!io) || (!(io->ioflags & IO_NPC)) || flag == 0)
return -1;
char tx[64];
tx[0] = 0;
if (flag == FLAG_CUT_HEAD)
strcpy(tx, "CUT_HEAD");
else if (flag == FLAG_CUT_TORSO)
strcpy(tx, "CUT_TORSO");
else if (flag == FLAG_CUT_LARM)
strcpy(tx, "CUT_LARM");
if (flag == FLAG_CUT_RARM)
strcpy(tx, "CUT_RARM");
if (flag == FLAG_CUT_LLEG)
strcpy(tx, "CUT_LLEG");
if (flag == FLAG_CUT_RLEG)
strcpy(tx, "CUT_RLEG");
if (tx[0])
{
for (long i = 0; i < io->obj->nbselections; i++)
{
if ((io->obj->selections[i].nb_selected > 0)
&& (!stricmp(io->obj->selections[i].name, tx)))
return i;
}
}
return -1;
}
void ReComputeCutFlags(INTERACTIVE_OBJ * io)
{
if ((!io) || (!(io->ioflags & IO_NPC)))
return;
if (io->_npcdata->cuts & FLAG_CUT_TORSO)
{
io->_npcdata->cuts &= ~FLAG_CUT_HEAD;
io->_npcdata->cuts &= ~FLAG_CUT_LARM;
io->_npcdata->cuts &= ~FLAG_CUT_RARM;
}
}
bool IsAlreadyCut(INTERACTIVE_OBJ * io, short fl)
{
if (io->_npcdata->cuts & fl)
return true;
if (io->_npcdata->cuts & FLAG_CUT_TORSO)
{
if (fl == FLAG_CUT_HEAD)
return true;
if (fl == FLAG_CUT_LARM)
return true;
if (fl == FLAG_CUT_RARM)
return true;
}
return false;
}
long ARX_NPC_ApplyCuts(INTERACTIVE_OBJ * io)
{
if ((!io) || (!(io->ioflags & IO_NPC)))
return 0 ;
if (!GORE_MODE)
return 0;
if (io->_npcdata->cuts == 0)
return 0; // No cuts
ReComputeCutFlags(io);
long goretex = -1;
for (long i = 0; i < io->obj->nbmaps; i++)
{
if (io->obj->texturecontainer
&& io->obj->texturecontainer[i]
&& (IsIn(io->obj->texturecontainer[i]->m_strName, "GORE")))
{
goretex = i;
break;
}
}
long hid = 0;
for (long nn = 0; nn < io->obj->nbfaces; nn++)
{
io->obj->facelist[nn].facetype &= ~POLY_HIDE;
}
for (long jj = 0; jj < 6; jj++)
{
short flg = 1 << jj;
long numsel = GetCutSelection(io, flg);
if ((io->_npcdata->cuts & flg) && (numsel >= 0))
{
for (long ll = 0; ll < io->obj->nbfaces; ll++)
{
if ((IsInSelection(io->obj, io->obj->facelist[ll].vid[0], numsel) != -1)
|| (IsInSelection(io->obj, io->obj->facelist[ll].vid[1], numsel) != -1)
|| (IsInSelection(io->obj, io->obj->facelist[ll].vid[2], numsel) != -1)
)
{
if (!(io->obj->facelist[ll].facetype & POLY_HIDE))
{
if (io->obj->facelist[ll].texid != goretex)
hid = 1;
}
io->obj->facelist[ll].facetype |= POLY_HIDE;
}
}
io->_npcdata->cut = 1;
}
}
return hid;
}
//***********************************************************************************************
// Attempt to cut something on NPC
//***********************************************************************************************
void ARX_NPC_TryToCutSomething(INTERACTIVE_OBJ * target, EERIE_3D * pos)
{
//return;
if (!target) return;
if (!(target->ioflags & IO_NPC)) return;
if (!GORE_MODE)
return;
if (target->GameFlags & GFLAG_NOGORE)
return;
float mindist = 9999.f;
long numsel = -1;
long goretex = -1;
for (long i = 0; i < target->obj->nbmaps; i++)
{
if (target->obj->texturecontainer
&& target->obj->texturecontainer[i]
&& (IsIn(target->obj->texturecontainer[i]->m_strName, "GORE")))
{
goretex = i;
break;
}
}
for (int i = 0; i < target->obj->nbselections; i++)
{
if ((target->obj->selections[i].nb_selected > 0)
&& (IsIn(target->obj->selections[i].name, "CUT_")))
{
short fll = GetCutFlag(target->obj->selections[i].name);
if (IsAlreadyCut(target, fll))
continue;
long out = 0;
for (long ll = 0; ll < target->obj->nbfaces; ll++)
{
if (target->obj->facelist[ll].texid != goretex)
{
if ((IsInSelection(target->obj, target->obj->facelist[ll].vid[0], i) != -1)
|| (IsInSelection(target->obj, target->obj->facelist[ll].vid[1], i) != -1)
|| (IsInSelection(target->obj, target->obj->facelist[ll].vid[2], i) != -1)
)
{
if (target->obj->facelist[ll].facetype & POLY_HIDE)
{
out++;
}
}
}
}
if (out < 3)
{
float dist = EEDistance3D(pos, &target->obj->vertexlist3[target->obj->selections[i].selected[0]].v);
if (dist < mindist)
{
mindist = dist;
numsel = i;
}
}
}
}
if (numsel == -1) return; // Nothing to cut...
long hid = 0;
if (mindist < 60) // can only cut a close part...
{
short fl = GetCutFlag(target->obj->selections[numsel].name);
if ((fl)
&& (!(target->_npcdata->cuts & fl)))
{
target->_npcdata->cuts |= fl;
hid = ARX_NPC_ApplyCuts(target);
}
}
if (hid)
{
ARX_SOUND_PlayCinematic("Flesh_Critical.wav");
ARX_NPC_SpawnMember(target, numsel);
}
}
extern float STRIKE_AIMTIME;
//***********************************************************************************************
// IsPlayerStriking()
// Checks if Player is currently striking.
//***********************************************************************************************
BOOL IsPlayerStriking()
{
INTERACTIVE_OBJ * io = inter.iobj[0];
if (!io) return FALSE;
ANIM_USE * useanim = &io->animlayer[1];
long weapontype = ARX_EQUIPMENT_GetPlayerWeaponType();
long j;
switch (weapontype)
{
case WEAPON_BARE:
for (j = 0; j < 4; j++)
{
if ((STRIKE_AIMTIME > 300)
&& (useanim->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+j*3])) return TRUE;
if (useanim->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT+j*3]) return TRUE;
}
break;
case WEAPON_DAGGER:
for (j = 0; j < 4; j++)
{
if ((STRIKE_AIMTIME > 300)
&& (useanim->cur_anim == io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+j*3])) return TRUE;
if (useanim->cur_anim == io->anims[ANIM_DAGGER_STRIKE_LEFT+j*3]) return TRUE;
}
break;
case WEAPON_1H:
for (j = 0; j < 4; j++)
{
if ((STRIKE_AIMTIME > 300)
&& (useanim->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+j*3])) return TRUE;
if (useanim->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT+j*3]) return TRUE;
}
break;
case WEAPON_2H:
for (j = 0; j < 4; j++)
{
if ((STRIKE_AIMTIME > 300)
&& (useanim->cur_anim == io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+j*3])) return TRUE;
if (useanim->cur_anim == io->anims[ANIM_2H_STRIKE_LEFT+j*3]) return TRUE;
}
break;
}
return FALSE;
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_NPC_Manage_NON_Fight(INTERACTIVE_OBJ * io)
{
ANIM_USE * ause1 = &io->animlayer[1];
if ((ause1->flags & EA_ANIMEND) && (ause1->cur_anim != NULL))
{
if (!(ause1->flags & EA_FORCEPLAY))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ause1->cur_anim = NULL;
}
}
}
//***********************************************************************************************
//***********************************************************************************************
void Strike_StartTickCount(INTERACTIVE_OBJ * io)
{
io->_npcdata->strike_time = 0;
}
//***********************************************************************************************
//***********************************************************************************************
// NPC IS in fight mode and close to target...
void ARX_NPC_Manage_Fight(INTERACTIVE_OBJ * io)
{
if (!(io->ioflags & IO_NPC)) return;
INTERACTIVE_OBJ * ioo = (INTERACTIVE_OBJ *)io->_npcdata->weapon;
if (ioo)
io->_npcdata->weapontype = ioo->type_flags;
else io->_npcdata->weapontype = 0;
if ((io->_npcdata->weapontype != 0) && (io->_npcdata->weaponinhand != 1))
return;
ANIM_USE * ause = &io->animlayer[1];
{
// BARE HANDS fight !!! *******************************
if (io->_npcdata->weapontype == 0)
{
if (((ause->cur_anim != io->anims[ANIM_BARE_WAIT])
&& (ause->cur_anim != io->anims[ANIM_BARE_READY])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START+3 ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START+6 ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_START+9 ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+3])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+6])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+9])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT+3 ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT+6 ])
&& (ause->cur_anim != io->anims[ANIM_BARE_STRIKE_LEFT+9 ])
&& (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_CAST])
&& (ause->cur_anim != io->anims[ANIM_CAST_END])
&& (ause->cur_anim != io->anims[ANIM_CAST_START]))
|| (ause->cur_anim == NULL))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_BARE_WAIT]);
Strike_StartTickCount(io);
ause->flags |= EA_LOOP;
}
}
// DAGGER fight !!! ***********************************
else if (io->_npcdata->weapontype & OBJECT_TYPE_DAGGER)
{
if (((ause->cur_anim != io->anims[ANIM_DAGGER_WAIT])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_READY_PART_1])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_READY_PART_2])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START+3 ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START+6 ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_START+9 ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+3])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+6])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT_CYCLE+9])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT+3 ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT+6 ])
&& (ause->cur_anim != io->anims[ANIM_DAGGER_STRIKE_LEFT+9 ])
&& (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_CAST])
&& (ause->cur_anim != io->anims[ANIM_CAST_END])
&& (ause->cur_anim != io->anims[ANIM_CAST_START]))
|| (ause->cur_anim == NULL))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_DAGGER_WAIT]);
Strike_StartTickCount(io);
ause->flags |= EA_LOOP;
}
}
// 1H fight !!! ***************************************
else if (io->_npcdata->weapontype & OBJECT_TYPE_1H)
{
if (((ause->cur_anim != io->anims[ANIM_1H_WAIT])
&& (ause->cur_anim != io->anims[ANIM_1H_READY_PART_1])
&& (ause->cur_anim != io->anims[ANIM_1H_READY_PART_2])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START+3 ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START+6 ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_START+9 ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+3])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+6])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+9])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT+3 ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT+6 ])
&& (ause->cur_anim != io->anims[ANIM_1H_STRIKE_LEFT+9 ])
&& (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_CAST])
&& (ause->cur_anim != io->anims[ANIM_CAST_END])
&& (ause->cur_anim != io->anims[ANIM_CAST_START]))
|| (ause->cur_anim == NULL))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_1H_WAIT]);
Strike_StartTickCount(io);
ause->flags |= EA_LOOP;
}
}
// 2H fight !!! ***************************************
else if (io->_npcdata->weapontype & OBJECT_TYPE_2H)
{
if (((ause->cur_anim != io->anims[ANIM_2H_WAIT])
&& (ause->cur_anim != io->anims[ANIM_2H_READY_PART_1])
&& (ause->cur_anim != io->anims[ANIM_2H_READY_PART_2])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START+3 ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START+6 ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_START+9 ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+3])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+6])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT_CYCLE+9])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT+3 ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT+6 ])
&& (ause->cur_anim != io->anims[ANIM_2H_STRIKE_LEFT+9 ])
&& (ause->cur_anim != io->anims[ANIM_CAST_CYCLE])
&& (ause->cur_anim != io->anims[ANIM_CAST])
&& (ause->cur_anim != io->anims[ANIM_CAST_END])
&& (ause->cur_anim != io->anims[ANIM_CAST_START]))
|| (ause->cur_anim == NULL))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_2H_WAIT]);
ause->flags |= EA_LOOP;
}
}
// BOW fight !!! **************************************
else if (io->_npcdata->weapontype & OBJECT_TYPE_BOW)
{
////////////// later...
}
}
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_NPC_Manage_Anims_End(INTERACTIVE_OBJ * io)
{
ANIM_USE * ause = &io->animlayer[0];
if ((ause->flags & EA_ANIMEND) && (ause->cur_anim != NULL))
{
if (ause->flags & EA_FORCEPLAY)
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause->altidx_cur = 0;
}
// some specific code for combat animation end management
if (ause->cur_anim == io->anims[ANIM_FIGHT_STRAFE_LEFT])
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_FIGHT_WAIT]);
ause->flags |= EA_LOOP;
}
if (ause->cur_anim == io->anims[ANIM_FIGHT_STRAFE_RIGHT])
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_FIGHT_WAIT]);
ause->flags |= EA_LOOP;
}
if (ause->cur_anim == io->anims[ANIM_FIGHT_WALK_BACKWARD])
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[ANIM_FIGHT_WAIT]);
ause->flags |= EA_LOOP;
}
}
}
bool TryIOAnimMove(INTERACTIVE_OBJ * io, long animnum)
{
if ((!io) || (!io->anims[animnum])) return false;
EERIE_3D trans, trans2;
GetAnimTotalTranslate(io->anims[animnum], 0, &trans);
float temp = DEG2RAD(MAKEANGLE(180.f - io->angle.b));
_YRotatePoint(&trans, &trans2, (float)EEcos(temp), (float)EEsin(temp));
IO_PHYSICS phys;
memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
GetIOCyl(io, &phys.cyl);
phys.startpos.x = io->pos.x;
phys.startpos.y = io->pos.y;
phys.startpos.z = io->pos.z;
phys.targetpos.x = io->pos.x + trans2.x;
phys.targetpos.y = io->pos.y + trans2.y;
phys.targetpos.z = io->pos.z + trans2.z;
BOOL res = ARX_COLLISION_Move_Cylinder(&phys, io, 30, CFLAG_JUST_TEST | CFLAG_NPC);
if (res && (EEfabs(phys.cyl.origin.y - io->pos.y) < 20.f))
return true;
return false;
}
void TryAndCheckAnim(INTERACTIVE_OBJ * io, long animnum, long layer)
{
if (!io) return;
ANIM_USE * ause = &io->animlayer[layer];
if ((ause->cur_anim != io->anims[animnum])
&& (ause->cur_anim != NULL))
{
if (TryIOAnimMove(io, animnum))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
ANIM_Set(ause, io->anims[animnum]);
}
}
}
//Define Time of Strike Damage
#define STRIKE_MUL 0.25f
#define STRIKE_MUL2 0.8f
#define STRIKE_DISTANCE 220
// Main animations management
//***********************************************************************************************
//***********************************************************************************************
void ARX_NPC_Manage_Anims(INTERACTIVE_OBJ * io, float TOLERANCE)
{
io->_npcdata->strike_time += (short)FrameDiff;
ANIM_USE * ause = &io->animlayer[0];
ANIM_USE * ause1 = &io->animlayer[1];
float tdist = 999999.f;
if (ValidIONum(io->targetinfo))
{
tdist = EEDistance3D(&io->pos, &inter.iobj[io->targetinfo]->pos);
}
if ((io->_npcdata->pathfind.listnb)
&& (ValidIONum(io->_npcdata->pathfind.truetarget)))
{
tdist = EEDistance3D(&io->pos, &inter.iobj[io->_npcdata->pathfind.truetarget]->pos);
}
INTERACTIVE_OBJ * ioo = (INTERACTIVE_OBJ *)io->_npcdata->weapon;
if (ValidIOAddress(ioo))
io->_npcdata->weapontype = ioo->type_flags;
else
io->_npcdata->weapontype = 0;
if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
&& (tdist <= TOLERANCE + 10)
&& ((tdist <= TOLERANCE - 20) || (rnd() > 0.97f)))
{
{
if ((ause->cur_anim == io->anims[ANIM_FIGHT_WAIT])
&& (ause->cur_anim != NULL))
{
float r = rnd();
if (tdist < TOLERANCE - 20) r = 0;
if (r < 0.1f)
TryAndCheckAnim(io, ANIM_FIGHT_WALK_BACKWARD, 0);
else if (r < 0.55f)
TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_LEFT, 0);
else
TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_RIGHT, 0);
}
}
}
//Decides fight moves
else if ((io->_npcdata->behavior & (BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT))
|| (io->spellcast_data.castingspell >= 0))
{
if (rnd() > 0.85f)
{
if ((ause->cur_anim == io->anims[ANIM_FIGHT_WAIT])
&& (ause->cur_anim != NULL))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
float r = rnd();
if (tdist < 340) r = 0;
if (r < 0.33f)
TryAndCheckAnim(io, ANIM_FIGHT_WALK_BACKWARD, 0);
else if (r < 0.66f)
TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_LEFT, 0);
else
TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_RIGHT, 0);
}
}
}
if (IsPlayerStriking())
{
if ((ause->cur_anim == io->anims[ANIM_FIGHT_WAIT])
&& (ause->cur_anim != NULL))
{
AcquireLastAnim(io);
FinishAnim(io, ause->cur_anim);
float r = rnd();
if (r < 0.2f)
TryAndCheckAnim(io, ANIM_FIGHT_WALK_BACKWARD, 0);
else if (r < 0.6f)
TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_LEFT, 0);
else
TryAndCheckAnim(io, ANIM_FIGHT_STRAFE_RIGHT, 0);
}
}
long j;
// MAGICAL FIGHT
if ((ause1->cur_anim == io->anims[ANIM_CAST])
&& (io->anims[ANIM_CAST])
&& (ause1->flags & EA_ANIMEND))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_CAST_END]);
}
else if ((ause1->cur_anim == io->anims[ANIM_CAST_END])
&& (io->anims[ANIM_CAST_END])
&& (ause1->flags & EA_ANIMEND))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ause1->cur_anim = NULL;
}
if ((io->spellcast_data.castingspell < 0)
&& (ause1->cur_anim == io->anims[ANIM_CAST_START])
&& (io->anims[ANIM_CAST_START]))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_CAST]);
}
if ((ause1->cur_anim == io->anims[ANIM_CAST_START])
&& (io->anims[ANIM_CAST_START]))
{
return;
}
if ((ause1->cur_anim == io->anims[ANIM_CAST_CYCLE])
&& (io->anims[ANIM_CAST_CYCLE]))
{
return;
}
if (io->spellcast_data.castingspell >= 0) return;
if (ause1->cur_anim)
{
if ((ause1->cur_anim == io->anims[ANIM_CAST_CYCLE])
|| (ause1->cur_anim == io->anims[ANIM_CAST])
|| (ause1->cur_anim == io->anims[ANIM_CAST_END])
|| (ause1->cur_anim == io->anims[ANIM_CAST_START]))
return;
}
// BARE HANDS fight !!! *******************************
if (io->_npcdata->weapontype == 0)
{
if (io->_npcdata->weaponinhand == -1)
{
io->_npcdata->weaponinhand = 1;
}
if ((ause1->cur_anim == io->anims[ANIM_BARE_WAIT])
&& (ause1->cur_anim))
{
if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
&& (tdist < STRIKE_DISTANCE)
&& (io->_npcdata->strike_time > 0))
{
AcquireLastAnim(io);
F2L(rnd() * 4, &j);
if (j < 0) j = 0;
if (j > 3) j = 3;
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_BARE_STRIKE_LEFT_START+j*3]);
}
}
for (j = 0; j < 4; j++)
{
if ((ause1->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT_START+j*3])
&& (ause1->cur_anim)
&& (ause1->flags & EA_ANIMEND))
{
io->ioflags &= ~IO_HIT;
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+j*3]);
io->_npcdata->aiming_start = lARXTime;
ause1->flags |= EA_LOOP;
}
else if ((ause1->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT_CYCLE+j*3])
&& (ause1->cur_anim))
{
if (((ARXTime > io->_npcdata->aiming_start + io->_npcdata->aimtime) || ((ARXTime > io->_npcdata->aiming_start + io->_npcdata->aimtime * DIV2) && (rnd() > 0.9f)))
&& (tdist < STRIKE_DISTANCE))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_BARE_STRIKE_LEFT+j*3]);
SendIOScriptEvent(io, SM_STRIKE, "BARE");
}
}
else if ((ause1->cur_anim == io->anims[ANIM_BARE_STRIKE_LEFT+j*3])
&& (ause1->cur_anim))
{
if (ause1->flags & EA_ANIMEND)
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_BARE_WAIT]);
Strike_StartTickCount(io);
io->ioflags &= ~IO_HIT;
if ((io->ioflags & IO_NPC) && (!io->_npcdata->reachedtarget))
ause1->cur_anim = NULL;
}
else if (!(io->ioflags & IO_HIT))
{
if ((ause1->ctime > ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL)
&& (ause1->ctime <= ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL2))
{
CheckHit(io, 1.f);
io->ioflags |= IO_HIT;
}
}
}
}
}
// 1H fight !!! ***************************************
else if (io->_npcdata->weapontype & (OBJECT_TYPE_1H | OBJECT_TYPE_2H | OBJECT_TYPE_DAGGER))
{
long ANIMBase = 0;
if (io->_npcdata->weapontype & OBJECT_TYPE_1H)
ANIMBase = 0;
else if (io->_npcdata->weapontype & OBJECT_TYPE_2H)
ANIMBase = ANIM_2H_READY_PART_1 - ANIM_1H_READY_PART_1;
else if (io->_npcdata->weapontype & OBJECT_TYPE_DAGGER)
ANIMBase = ANIM_DAGGER_READY_PART_1 - ANIM_1H_READY_PART_1;
// desire to remove weapon
if (io->_npcdata->weaponinhand == 2)
{
if ((ause1->cur_anim == io->anims[ANIM_1H_UNREADY_PART_1+ANIMBase])
&& (ause1->cur_anim)
&& (ause1->flags & EA_ANIMEND))
{
if (io->_npcdata->weaponname[0] != 0)
SetWeapon_Back(io);
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_UNREADY_PART_2+ANIMBase]);
}
else if ((ause1->cur_anim == io->anims[ANIM_1H_UNREADY_PART_2+ANIMBase])
&& (ause1->cur_anim)
&& (ause1->flags & EA_ANIMEND))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ause1->cur_anim = NULL;
io->_npcdata->weaponinhand = 0;
}
else if ((ause1->cur_anim != io->anims[ANIM_1H_UNREADY_PART_1+ANIMBase])
&& (ause1->cur_anim != io->anims[ANIM_1H_UNREADY_PART_2+ANIMBase]))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_UNREADY_PART_1+ANIMBase]);
}
}
// Desire to have weapon in hand...
else if (io->_npcdata->weaponinhand == -1)
{
if ((ause1->cur_anim == io->anims[ANIM_1H_READY_PART_1+ANIMBase])
&& (ause1->cur_anim)
&& (ause1->flags & EA_ANIMEND))
{
if (io->_npcdata->weaponname[0] != 0)
SetWeapon_On(io);
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_READY_PART_2+ANIMBase]);
}
else if ((ause1->cur_anim == io->anims[ANIM_1H_READY_PART_2+ANIMBase])
&& (ause1->cur_anim)
&& (ause1->flags & EA_ANIMEND))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
if (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
{
ANIM_Set(ause1, io->anims[ANIM_1H_WAIT+ANIMBase]);
Strike_StartTickCount(io);
ause1->flags |= EA_LOOP;
}
else ause1->cur_anim = NULL;
io->_npcdata->weaponinhand = 1;
}
else if ((!ause1->cur_anim)
|| ((ause1->cur_anim) && (ause1->flags & EA_ANIMEND)))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_READY_PART_1+ANIMBase]);
}
}
// Weapon in hand... ready to strike
else if (io->_npcdata->weaponinhand > 0)
{
if ((ause1->cur_anim == io->anims[ANIM_1H_WAIT+ANIMBase])
&& (ause1->cur_anim))
{
if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
&& (tdist < STRIKE_DISTANCE)
&& (io->_npcdata->strike_time > 0))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
F2L(rnd() * 4, &j);
if (j < 0) j = 0;
if (j > 3) j = 3;
ANIM_Set(ause1, io->anims[ANIM_1H_STRIKE_LEFT_START+j*3+ANIMBase]);
}
}
for (j = 0; j < 4; j++)
{
if ((ause1->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT_START+j*3+ANIMBase])
&& (ause1->cur_anim)
&& (ause1->flags & EA_ANIMEND))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+j*3+ANIMBase]);
io->_npcdata->aiming_start = lARXTime;
ause1->flags |= EA_LOOP;
}
else if ((ause1->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT_CYCLE+j*3+ANIMBase])
&& (ause1->cur_anim))
{
if (((ARXTime > io->_npcdata->aiming_start + io->_npcdata->aimtime) || ((ARXTime > io->_npcdata->aiming_start + io->_npcdata->aimtime * DIV2) && (rnd() > 0.9f)))
&& (tdist < STRIKE_DISTANCE))
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_STRIKE_LEFT+j*3+ANIMBase]);
if (io->_npcdata->weapontype & OBJECT_TYPE_1H)
SendIOScriptEvent(io, SM_STRIKE, "1H");
if (io->_npcdata->weapontype & OBJECT_TYPE_2H)
SendIOScriptEvent(io, SM_STRIKE, "2H");
if (io->_npcdata->weapontype & OBJECT_TYPE_DAGGER)
SendIOScriptEvent(io, SM_STRIKE, "DAGGER");
}
}
else if ((ause1->cur_anim == io->anims[ANIM_1H_STRIKE_LEFT+j*3+ANIMBase])
&& (ause1->cur_anim))
{
if (ause1->flags & EA_ANIMEND)
{
AcquireLastAnim(io);
FinishAnim(io, ause1->cur_anim);
ANIM_Set(ause1, io->anims[ANIM_1H_WAIT+ANIMBase]);
Strike_StartTickCount(io);
io->ioflags &= ~IO_HIT;
}
else
{
if ((ause1->ctime > ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL)
&& (ause1->ctime <= ause1->cur_anim->anims[0]->anim_time * STRIKE_MUL2))
{
if (!(io->ioflags & IO_HIT))
{
if (ARX_EQUIPMENT_Strike_Check(io, (INTERACTIVE_OBJ *)io->_npcdata->weapon, 1, 0, io->targetinfo))
io->ioflags |= IO_HIT;
}
else
ARX_EQUIPMENT_Strike_Check(io, (INTERACTIVE_OBJ *)io->_npcdata->weapon, 1, 1, io->targetinfo);
}
}
}
}
}
}
// BOW fight !!! **************************************
else if (io->_npcdata->weapontype & OBJECT_TYPE_BOW)
{
////////////// later...
}
}
float GetIOHeight(INTERACTIVE_OBJ * io)
{
if (io == inter.iobj[0])
{
return io->physics.cyl.height;
}
float v = (io->original_height * io->scale);
if (v < -165.f) return -165.f;
return __min(v, -45.f);
}
float GetIORadius(INTERACTIVE_OBJ * io)
{
if (io == inter.iobj[0])
return PLAYER_BASE_RADIUS;
float v = __max(io->original_radius * io->scale, 25.f);
if (v > 60.f) return 60.f;
return v;
}
void GetIOCyl(INTERACTIVE_OBJ * io, EERIE_CYLINDER * cyl)
{
cyl->height = GetIOHeight(io);
cyl->radius = GetIORadius(io);
if (io == inter.iobj[0])
{
cyl->origin.x = inter.iobj[0]->pos.x;
cyl->origin.y = inter.iobj[0]->pos.y;
cyl->origin.z = inter.iobj[0]->pos.z;
}
else
Vector_Copy(&cyl->origin, &io->pos);
}
//***********************************************************************************************
// Computes distance tolerance between NPC and its target
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/15)
//***********************************************************************************************
void ComputeTolerance(INTERACTIVE_OBJ * io, long targ, float * dst)
{
float TOLERANCE = 30.f;
if ((targ >= 0)
&& (targ < inter.nbmax)
&& (inter.iobj[targ]))
{
float self_dist, targ_dist;
// Compute min target close-dist
if (inter.iobj[targ]->ioflags & IO_NO_COLLISIONS)
targ_dist = 0.f;
else targ_dist = __max(inter.iobj[targ]->physics.cyl.radius, GetIORadius(inter.iobj[targ])); //inter.iobj[targ]->physics.cyl.radius;
// Compute min self close-dist
if (io->ioflags & IO_NO_COLLISIONS)
self_dist = 0.f;
else self_dist = __max(io->physics.cyl.radius, GetIORadius(io)); //io->physics.cyl.radius;
// Base tolerance = radius added
TOLERANCE = targ_dist + self_dist + 5.f;
if (TOLERANCE < 0.f)
{
TOLERANCE = 0.f;
}
if (inter.iobj[targ]->ioflags & IO_FIX)
TOLERANCE += 100.f;
// If target is a NPC add another tolerance
if (inter.iobj[targ]->_npcdata)
{
TOLERANCE += 20.f;
}
// If target is the player improve again tolerance
if (io->targetinfo == 0) // PLAYER TARGET
{
TOLERANCE += 10.f;
}
if (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
{
TOLERANCE += io->_npcdata->reach * 0.7f;
}
// If distant of magic behavior Maximize tolerance
if ((io->_npcdata->behavior & (BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT)) || (io->spellcast_data.castingspell >= 0))
TOLERANCE += 300.f;
// if target is a marker set to a minimal tolerance
if (inter.iobj[targ]->ioflags & IO_MARKER)
TOLERANCE = 21.f + (float)io->_npcdata->moveproblem * DIV10;
}
// Tolerance is modified by current moveproblem status
TOLERANCE += (float)io->_npcdata->moveproblem * DIV10;
// Now fill our return value with TOLERANCE
*dst = TOLERANCE;
}
extern long FRAME_COUNT;
//now APOS is computed in Anim but used here and mustn't be used elsewhere...
//***********************************************************************************************
//***********************************************************************************************
void ManageNPCMovement(INTERACTIVE_OBJ * io)
{
float dis = FLT_MAX;
IO_PHYSICS phys;
float TOLERANCE = 0.f;
float TOLERANCE2 = 0.f;
// Ignores invalid or dead IO
if ((!io)
|| (!io->show)
|| (!(io->ioflags & IO_NPC)))
return;
// AnchorData_GetNearest_2(io->angle.b,&io->pos,&io->physics.cyl);
// Specific USEPATH management
ARX_USE_PATH * aup = (ARX_USE_PATH *)io->usepath;
if ((aup)
&& (aup->aupflags & ARX_USEPATH_WORM_SPECIFIC))
{
io->room_flags |= 1;
EERIE_3D tv;
if (aup->_curtime - aup->_starttime > 500)
{
aup->_curtime -= 500;
ARX_PATHS_Interpolate(aup, &tv);
aup->_curtime += 500;
io->angle.b = MAKEANGLE(RAD2DEG(GetAngle(tv.x, tv.z, io->pos.x, io->pos.z)));
}
else
{
aup->_curtime += 500;
ARX_PATHS_Interpolate(aup, &tv);
aup->_curtime -= 500;
io->angle.b = MAKEANGLE(180.f + RAD2DEG(GetAngle(tv.x, tv.z, io->pos.x, io->pos.z)));
}
return;
}
// Frozen ?
if (io->ioflags & IO_FREEZESCRIPT)
return;
// Dead ?
if (IsDeadNPC(io))
{
io->ioflags |= IO_NO_COLLISIONS;
io->animlayer[0].next_anim = NULL;
io->animlayer[1].next_anim = NULL;
io->animlayer[2].next_anim = NULL;
io->animlayer[3].next_anim = NULL;
return;
}
ANIM_USE * ause0 = &io->animlayer[0];
ANIM_HANDLE ** alist = io->anims;
// Using USER animation ?
if ((ause0->cur_anim)
&& (ause0->flags & EA_FORCEPLAY)
&& (ause0->cur_anim != alist[ANIM_DIE])
&& (ause0->cur_anim != alist[ANIM_HIT1])
&& (ause0->cur_anim != alist[ANIM_HIT_SHORT])
&& !(ause0->flags & EA_ANIMEND))
{
io->room_flags |= 1;
io->lastpos.x = io->pos.x += io->move.x;
io->lastpos.y = io->pos.y += io->move.y;
io->lastpos.z = io->pos.z += io->move.z;
return;
}
if ((io->_npcdata->pathfind.listnb > 0)
&& (!io->_npcdata->pathfind.list))
io->_npcdata->pathfind.listnb = 0;
// waiting for pathfinder ? or pathfinder failure ? ---> Wait Anim
if ((io->_npcdata->pathfind.pathwait) // waiting for pathfinder
|| (io->_npcdata->pathfind.listnb == -2)) // pathfinder failure
{
if (io->_npcdata->pathfind.listnb == -2)
{
if (!io->_npcdata->pathfind.pathwait)
{
if (io->_npcdata->pathfind.flags & PATHFIND_NO_UPDATE)
{
io->_npcdata->reachedtarget = 1;
io->_npcdata->reachedtime = ARXTimeUL();//treat warning C4244 conversion from 'float' to 'unsigned long'
if (io->targetinfo != GetInterNum(io))
SendIOScriptEvent(io, SM_REACHEDTARGET, "");
}
else if ((ause0->cur_anim == alist[ANIM_WAIT]) && (ause0->flags & EA_ANIMEND))
{
io->_npcdata->pathfind.listnb = -1;
io->_npcdata->pathfind.pathwait = 0;
ARX_NPC_LaunchPathfind(io, io->targetinfo);
goto afterthat;
}
}
}
if (!(io->_npcdata->behavior & BEHAVIOUR_FIGHT))
{
if ((ause0->cur_anim == alist[ANIM_WALK])
|| (ause0->cur_anim == alist[ANIM_RUN])
|| (ause0->cur_anim == alist[ANIM_WALK_SNEAK]))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WAIT]);
ause0->altidx_cur = 0;
return;
}
else if (ause0->cur_anim == alist[ANIM_WAIT])
{
if (ause0->flags & EA_ANIMEND)
{
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WAIT]);
ause0->altidx_cur = 0;
}
return;
}
}
afterthat:
;
}
if ((io->_npcdata->behavior & BEHAVIOUR_NONE))
{
ARX_NPC_Manage_Anims(io, 0);
if ((ause0->cur_anim == NULL)
|| ((ause0->cur_anim) && (ause0->flags & EA_ANIMEND)))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WAIT]);
ause0->flags &= ~EA_LOOP;
}
return;
}
// First retrieves current position of target...
GetTargetPos(io);
ARX_NPC_Manage_Anims_End(io);
if ((io->_npcdata->behavior & BEHAVIOUR_LOOK_AROUND)
&& (io->_npcdata->pathfind.listnb <= 0)
&& !(io->_npcdata->pathfind.pathwait))
{
ARX_NPC_LaunchPathfind(io, io->targetinfo);
}
if ((io->_npcdata->behavior & (BEHAVIOUR_FRIENDLY | BEHAVIOUR_NONE))
&& ((ause0->cur_anim == alist[ANIM_WALK])
|| (ause0->cur_anim == alist[ANIM_WALK_SNEAK])
|| (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
|| (ause0->cur_anim == alist[ANIM_RUN])
|| (ause0->cur_anim == alist[ANIM_RUN])
|| (ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_LEFT])
|| (ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_RIGHT])
|| (ause0->cur_anim == alist[ANIM_FIGHT_WALK_BACKWARD])
))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WAIT]);
ause0->altidx_cur = 0;
}
// look around if finished fleeing or being looking around !
if ((io->_npcdata->behavior & BEHAVIOUR_LOOK_AROUND)
&& (EEDistance3D(&io->pos, &io->target) > 150.f))
{
if (!io->_npcdata->ex_rotate)
{
ARX_NPC_CreateExRotateData(io);
}
else // already created
{
if (((ause0->cur_anim == alist[ANIM_WAIT])
|| (ause0->cur_anim == alist[ANIM_WALK])
|| (ause0->cur_anim == alist[ANIM_WALK_SNEAK])
|| (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
|| (ause0->cur_anim == alist[ANIM_RUN]))
|| (ause0->cur_anim != NULL))
{
io->_npcdata->look_around_inc = 0.f;
for (long n = 0; n < 4; n++)
{
io->_npcdata->ex_rotate->group_rotate[n].b -= io->_npcdata->ex_rotate->group_rotate[n].b * DIV3;
if (fabs(io->_npcdata->ex_rotate->group_rotate[n].b) < 0.01f)
io->_npcdata->ex_rotate->group_rotate[n].b = 0.f;
}
}
else
{
if (io->_npcdata->look_around_inc == 0.f)
{
io->_npcdata->look_around_inc = (rnd() - 0.5f) * 0.08f;
}
for (long n = 0; n < 4; n++)
{
float t = 1.5f - (float)n * DIV5;
io->_npcdata->ex_rotate->group_rotate[n].b += io->_npcdata->look_around_inc * _framedelay * t;
}
if (io->_npcdata->ex_rotate->group_rotate[0].b > 30)
io->_npcdata->look_around_inc = -io->_npcdata->look_around_inc;
if (io->_npcdata->ex_rotate->group_rotate[0].b < -30)
io->_npcdata->look_around_inc = -io->_npcdata->look_around_inc;
}
}
}
else
{
if ((!(io->_npcdata->behavior & BEHAVIOUR_STARE_AT))
&& (io->_npcdata->ex_rotate != NULL))
{
io->_npcdata->look_around_inc = 0.f;
for (long n = 0; n < 4; n++)
{
io->_npcdata->ex_rotate->group_rotate[n].b -= io->_npcdata->ex_rotate->group_rotate[n].b * DIV3;
if (fabs(io->_npcdata->ex_rotate->group_rotate[n].b) < 0.01f)
io->_npcdata->ex_rotate->group_rotate[n].b = 0.f;
}
}
}
ause0->flags &= ~EA_STATICANIM;
if ((ause0->cur_anim)
&& ((ause0->cur_anim == alist[ANIM_HIT1]) || (ause0->cur_anim == alist[ANIM_HIT_SHORT])))
{
if (ause0->flags & EA_ANIMEND)
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
if (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
ANIM_Set(ause0, alist[ANIM_FIGHT_WAIT]);
else
ANIM_Set(ause0, alist[ANIM_WAIT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)
ause0->altidx_cur = 0;
}
else
return;
}
float dist = FLT_MAX;
long CHANGE = 0;
// GetTargetPos MUST be called before FaceTarget2
if ((io->_npcdata->pathfind.listnb > 0)
&& (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
&& (io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb - 2)
&& (io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos] == io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos+1]))
{
if (ause0->cur_anim != io->anims[ANIM_DEFAULT])
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
}
else if (ause0->flags & EA_ANIMEND)
{
ause0->flags &= ~EA_FORCEPLAY;
goto argh;
}
return;
}
// XS : Moved to top of func
dist = TRUEDistance2D(io->pos.x, io->pos.z, io->target.x, io->target.z);
dis = dist;
if (io->_npcdata->pathfind.listnb > 0)
dis = GetTRUETargetDist(io);
if (io->_npcdata->behavior & BEHAVIOUR_FLEE)
dis = 9999999;
// Force to flee/wander again
if ((!io->_npcdata->pathfind.pathwait)
&& (io->_npcdata->pathfind.listnb <= 0))
{
if (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
{
ARX_NPC_LaunchPathfind(io, io->targetinfo);
}
else if ((dis > 220)
&& (io->_npcdata->behavior & BEHAVIOUR_MOVE_TO)
&& (!(io->_npcdata->behavior & (BEHAVIOUR_FIGHT | BEHAVIOUR_MAGIC | BEHAVIOUR_SNEAK))))
{
ARX_NPC_LaunchPathfind(io, io->targetinfo);
}
}
if ((io->_npcdata->pathfind.listnb <= 0)
&& (io->_npcdata->behavior & BEHAVIOUR_FLEE))
{
if (ause0->cur_anim != alist[ANIM_DEFAULT])
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
}
}
else // Force object to walk if using pathfinder !!!
{
if (io->_npcdata->behavior & (BEHAVIOUR_FRIENDLY | BEHAVIOUR_NONE))
{
}
else if (!io->_npcdata->reachedtarget)
{
if (
((io->targetinfo != -2)
|| (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
|| (io->_npcdata->behavior & BEHAVIOUR_FLEE)
|| (io->_npcdata->behavior & BEHAVIOUR_GO_HOME))
&& (io->_npcdata->pathfind.listnb > 0)
&& (ause0->cur_anim != alist[ANIM_WALK])
&& (ause0->cur_anim != alist[ANIM_FIGHT_WALK_FORWARD])
&& (ause0->cur_anim != alist[ANIM_RUN])
&& (ause0->cur_anim != alist[ANIM_WALK_SNEAK])
&& (!(ause0->flags & EA_FORCEPLAY))
)
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
if ((dis <= RUN_WALK_RADIUS) && (io->_npcdata->behavior & BEHAVIOUR_FIGHT))
{
ANIM_Set(ause0, alist[ANIM_FIGHT_WALK_FORWARD]);
}
else
switch (io->_npcdata->movemode)
{
case SNEAKMODE:
ANIM_Set(ause0, alist[ANIM_WALK_SNEAK]);
break;
case WALKMODE:
ANIM_Set(ause0, alist[ANIM_WALK]);
break;
case RUNMODE:
if (dis <= RUN_WALK_RADIUS)
{
ANIM_Set(ause0, alist[ANIM_WALK]);
}
else
{
if (alist[ANIM_RUN])
ANIM_Set(ause0, alist[ANIM_RUN]);
else ANIM_Set(ause0, alist[ANIM_WALK]);
}
break;
case NOMOVEMODE:
ANIM_Set(ause0, alist[ANIM_WAIT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
break;
}
ause0->flags |= EA_LOOP;
io->_npcdata->walk_start_time = 0;
}
}
else // our object has reached its target...
{
// Put it in Fighting stance if using Fight Behavior
if ((io->_npcdata->behavior & (BEHAVIOUR_FIGHT | BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT))
&& io->anims[ANIM_FIGHT_WAIT])
{
if ((ause0->cur_anim != alist[ANIM_FIGHT_WAIT])
&& (ause0->cur_anim != alist[ANIM_FIGHT_STRAFE_LEFT])
&& (ause0->cur_anim != alist[ANIM_FIGHT_STRAFE_RIGHT])
&& (ause0->cur_anim != alist[ANIM_FIGHT_WALK_BACKWARD])
&& (ause0->cur_anim != alist[ANIM_FIGHT_WALK_FORWARD])
)
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_FIGHT_WAIT]);
ause0->flags |= EA_LOOP;
}
// Stop it and put it in Wait anim after finishing his walk anim...
else
{
if (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
{
ause0->flags &= ~EA_LOOP;
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
ause0->flags |= EA_LOOP;
}
else if ((ause0->cur_anim == alist[ANIM_WALK])
|| (ause0->cur_anim == alist[ANIM_RUN])
|| (ause0->cur_anim == alist[ANIM_WALK_SNEAK]))
{
ause0->flags &= ~EA_LOOP;
if (io->_npcdata->reachedtime + 500 < ARXTime)
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
ause0->flags |= EA_LOOP;
}
}
if (!(ause0->flags & EA_FORCEPLAY)
&& (ause0->cur_anim != alist[ANIM_DEFAULT])
&& (ause0->cur_anim != alist[ANIM_FIGHT_WAIT])
&& (ause0->flags & EA_ANIMEND)
&& !(ause0->flags & EA_LOOP))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
}
}
}
}
// Force Run when far from target and using RUNMODE
if ((dis > RUN_WALK_RADIUS) && (io->_npcdata->movemode == RUNMODE)
&& (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
&& alist[ANIM_RUN])
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_RUN]);
}
// Reset WAIT Animation if reached end !
if ((ause0->cur_anim == alist[ANIM_DEFAULT])
&& (ause0->cur_anim != NULL)
&& (ause0->flags & EA_ANIMEND))
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
}
// Can only change direction during some specific animations
if (((ause0->cur_anim == alist[ANIM_DEFAULT]) && (ause0->altidx_cur == 0))
|| (ause0->cur_anim == alist[ANIM_FIGHT_WAIT])
|| (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
|| (ause0->cur_anim == alist[ANIM_WALK])
|| (ause0->cur_anim == alist[ANIM_WALK_SNEAK])
|| (ause0->cur_anim == alist[ANIM_RUN])
|| (ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_LEFT])
|| (ause0->cur_anim == alist[ANIM_FIGHT_STRAFE_RIGHT])
|| (ause0->cur_anim == alist[ANIM_FIGHT_WALK_BACKWARD])
|| (ause0->cur_anim == NULL)
) CHANGE = 1;
// Tries to face/stare at target
if ((!ARXPausedTimer) && (CHANGE)
&& (!(ause0->flags & EA_FORCEPLAY)))
{
if (io->_npcdata->behavior & BEHAVIOUR_STARE_AT)
StareAtTarget(io);
else FaceTarget2(io);
}
// Choose tolerance value depending on target...
if ((io->_npcdata->pathfind.listnb > 0)
&& (io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb))
{
ComputeTolerance(io, io->_npcdata->pathfind.list[io->_npcdata->pathfind.listpos], &TOLERANCE);
ComputeTolerance(io, io->_npcdata->pathfind.truetarget, &TOLERANCE2);
}
else
{
ComputeTolerance(io, io->targetinfo, &TOLERANCE);
TOLERANCE2 = TOLERANCE;
}
// COLLISION Management START *********************************************************************
// EERIE_CYLINDER cyl;
// Try physics from last valid pos to current desired pos...
// For this frame we want to try a move from startpos (valid pos)
// to targetpos (potentially invalid pos)
io->physics.startpos.x = io->physics.cyl.origin.x = io->pos.x;
io->physics.startpos.y = io->physics.cyl.origin.y = io->pos.y;
io->physics.startpos.z = io->physics.cyl.origin.z = io->pos.z;
EERIE_3D ForcedMove;
if ((io->forcedmove.x == 0.f) && (io->forcedmove.y == 0.f) && (io->forcedmove.z == 0.f))
Vector_Init(&ForcedMove);
else
{
EERIE_3D vect;
Vector_Copy(&vect, &io->forcedmove);
float d = TRUEVector_Magnitude(&vect);
TRUEVector_Normalize(&vect);
float dd = __min(d, (float)FrameDiff * DIV6);
ForcedMove.x = vect.x * dd;
ForcedMove.y = vect.y * dd;
ForcedMove.z = vect.z * dd;
}
// Sets Target position to desired position...
io->physics.targetpos.x = io->pos.x + io->move.x + ForcedMove.x;
io->physics.targetpos.z = io->pos.z + io->move.z + ForcedMove.z;
// IO_PHYSICS phys; // XS : Moved to func beginning
memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
GetIOCyl(io, &phys.cyl);
long levitate;
levitate = 0;
if (ARX_SPELLS_GetSpellOn(io, SPELL_LEVITATE) >= 0)
{
levitate = 1;
io->physics.targetpos.y = io->pos.y + io->move.y + ForcedMove.y;
}
else // Gravity 'simulation'
{
phys.cyl.origin.y += 10.f;
float anything = CheckAnythingInCylinder(&phys.cyl, io, CFLAG_JUST_TEST | CFLAG_NPC);
if (anything >= 0)
{
io->physics.targetpos.y = io->pos.y + (float)FrameDiff * 1.5f + ForcedMove.y;
}
else io->physics.targetpos.y = io->pos.y + ForcedMove.y;
phys.cyl.origin.y -= 10.f;
}
memcpy(&phys, &io->physics, sizeof(IO_PHYSICS));
GetIOCyl(io, &phys.cyl);
io->forcedmove.x -= ForcedMove.x;
io->forcedmove.y -= ForcedMove.y;
io->forcedmove.z -= ForcedMove.z;
// Some visual debug stuff
if (DEBUGNPCMOVE)
{
EERIE_CYLINDER cyll;
cyll.height = GetIOHeight(io);
cyll.radius = GetIORadius(io);
cyll.origin.x = phys.startpos.x;
cyll.origin.y = phys.startpos.y;
cyll.origin.z = phys.startpos.z;
EERIEDraw3DCylinder(GDevice, &cyll, 0xFF00FF00);
if (!(AttemptValidCylinderPos(&cyll, io, levitate | CFLAG_NPC)))
{
cyll.height = -40.f;
EERIEDraw3DCylinder(GDevice, &cyll, 0xFF0000FF);
cyll.height = GetIOHeight(io);
}
cyll.origin.x = io->physics.targetpos.x;
cyll.origin.y = io->physics.targetpos.y;
cyll.origin.z = io->physics.targetpos.z;
EERIEDraw3DCylinder(GDevice, &cyll, 0xFFFF0000);
if (!(AttemptValidCylinderPos(&cyll, io, levitate | CFLAG_NPC)))
{
cyll.height = GetIOHeight(io);
}
}
APPLY_PUSH = 0;
DIRECT_PATH = TRUE;
// Now we try the physical move for real
if (Vector_Compare(&io->physics.startpos, &io->physics.targetpos) // optim
|| (ARX_COLLISION_Move_Cylinder(&phys, io, 40, levitate | CFLAG_NPC)))
{
// Successfull move now validate it
if (!DIRECT_PATH) io->_npcdata->moveproblem += 1;
else io->_npcdata->moveproblem = 0;
io->_npcdata->collid_state = 0;
}
else // Object was unable to move to target... Stop it
{
io->_npcdata->moveproblem += 3;
}
io->room_flags |= 1;
io->physics.cyl.origin.x = io->pos.x = phys.cyl.origin.x;
io->physics.cyl.origin.y = io->pos.y = phys.cyl.origin.y;
io->physics.cyl.origin.z = io->pos.z = phys.cyl.origin.z;
io->physics.cyl.radius = GetIORadius(io);
io->physics.cyl.height = GetIOHeight(io);
APPLY_PUSH = 0;
// Compute distance 2D to target.
dist = TRUEDistance2D(io->pos.x, io->pos.z, io->target.x, io->target.z);
dis = dist;
if (io->_npcdata->pathfind.listnb > 0)
dis = GetTRUETargetDist(io);
if (io->_npcdata->behavior & BEHAVIOUR_FLEE)
dis = 9999999;
// Tries to solve Moveproblems... sort of...
if (io->_npcdata->moveproblem > 11)
{
if ((dist > TOLERANCE) && (!io->_npcdata->pathfind.pathwait))
{
long targ;
if (io->_npcdata->pathfind.listnb > 0)
targ = io->_npcdata->pathfind.truetarget;
else
targ = io->targetinfo;
ARX_NPC_LaunchPathfind(io, targ);
}
io->_npcdata->moveproblem = 0;
}
// Checks if pathfind final target is still locked on true target
if ((FRAME_COUNT <= 0)
&& (!io->_npcdata->pathfind.pathwait)
&& !(io->_npcdata->pathfind.flags & PATHFIND_ONCE)
&& !(io->_npcdata->pathfind.flags & PATHFIND_NO_UPDATE)
&& (io->_npcdata->pathfind.listnb > 0)
&& (io->_npcdata->pathfind.listpos < io->_npcdata->pathfind.listnb)
&& (io->_npcdata->behavior & BEHAVIOUR_MOVE_TO)
&& !(io->_npcdata->behavior & BEHAVIOUR_FLEE)
)
{
if (ValidIONum(io->_npcdata->pathfind.truetarget))
{
EERIE_3D * p = &inter.iobj[io->_npcdata->pathfind.truetarget]->pos;
long t = AnchorData_GetNearest(p, &io->physics.cyl);
if ((t != -1) && (t != io->_npcdata->pathfind.list[io->_npcdata->pathfind.listnb-1]))
{
float dist = TRUEEEDistance3D(&ACTIVEBKG->anchors[t].pos, &ACTIVEBKG->anchors[io->_npcdata->pathfind.list[io->_npcdata->pathfind.listnb-1]].pos);
if (dist > 200.f)
ARX_NPC_LaunchPathfind(io, io->_npcdata->pathfind.truetarget);
}
}
}
// We are still too far from our target...
if (io->_npcdata->pathfind.pathwait == 0)
{
if ((dist > TOLERANCE) && (dis > TOLERANCE2))
{
if ((io->_npcdata->reachedtarget))
{
if (ValidIONum(io->targetinfo))
EVENT_SENDER = inter.iobj[io->targetinfo];
else
EVENT_SENDER = NULL;
SendIOScriptEvent(io, SM_LOSTTARGET, "");
io->_npcdata->reachedtarget = 0;
}
// if not blocked & not Flee-Pathfinding
if ( !((io->_npcdata->pathfind.listnb <= 0) && (io->_npcdata->behavior & BEHAVIOUR_FLEE))
|| (io->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND)
|| (io->_npcdata->behavior & BEHAVIOUR_GO_HOME))
{
ANIM_HANDLE * desiredanim = NULL;
//long desiredloop=1;
if ((dis <= RUN_WALK_RADIUS) && (io->_npcdata->behavior & BEHAVIOUR_FIGHT)
&& (ause0->cur_anim != alist[ANIM_RUN]))
{
float fCalc = io->_npcdata->walk_start_time + FrameDiff ;
ARX_CHECK_SHORT(fCalc);
io->_npcdata->walk_start_time = ARX_CLEAN_WARN_CAST_SHORT(fCalc);
if (io->_npcdata->walk_start_time > 600)
{
desiredanim = alist[ANIM_FIGHT_WALK_FORWARD];
io->_npcdata->walk_start_time = 0;
}
}
else switch (io->_npcdata->movemode)
{
case SNEAKMODE:
desiredanim = alist[ANIM_WALK_SNEAK];
break;
case WALKMODE:
desiredanim = alist[ANIM_WALK];
break;
case RUNMODE:
if (dis <= RUN_WALK_RADIUS)
{
desiredanim = alist[ANIM_WALK];
}
else
desiredanim = alist[ANIM_RUN];
break;
case NOMOVEMODE:
desiredanim = alist[ANIM_DEFAULT];
break;
}
if (io->targetinfo == -2) desiredanim = alist[ANIM_DEFAULT];
if ((desiredanim)
&& (desiredanim != ause0->cur_anim)
&& (!(ause0->flags & EA_FORCEPLAY))
&&
((ause0->cur_anim == alist[ANIM_DEFAULT])
|| (ause0->cur_anim == alist[ANIM_FIGHT_WAIT]))
)
{
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, desiredanim);
if ((desiredanim == alist[ANIM_DEFAULT])
&& (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY)) ause0->altidx_cur = 0;
ause0->flags |= EA_LOOP;
}
}
}
// Near target
else
{
if (dis <= TOLERANCE2)
{
io->_npcdata->pathfind.listpos = 0;
io->_npcdata->pathfind.listnb = -1;
io->_npcdata->pathfind.pathwait = 0;
if (io->_npcdata->pathfind.list) MemFree(io->_npcdata->pathfind.list);
io->_npcdata->pathfind.list = NULL;
if (ause0->cur_anim == alist[ANIM_FIGHT_WALK_FORWARD])
{
ause0->flags &= ~EA_LOOP;
AcquireLastAnim(io);
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_DEFAULT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
ause0->flags |= EA_LOOP;
}
}
if (io->_npcdata->pathfind.listnb > 0)
{
argh:;
long lMax = __max(ARX_NPC_GetNextAttainableNodeIncrement(io), 1L);
ARX_CHECK_USHORT(io->_npcdata->pathfind.listpos + lMax);
io->_npcdata->pathfind.listpos += ARX_CLEAN_WARN_CAST_USHORT(lMax);
if ((io->_npcdata->pathfind.listpos >= io->_npcdata->pathfind.listnb)) // || (dis<=120.f))
{
io->_npcdata->pathfind.listpos = 0;
io->_npcdata->pathfind.listnb = -1;
io->_npcdata->pathfind.pathwait = 0;
if (io->_npcdata->pathfind.list) MemFree(io->_npcdata->pathfind.list);
io->_npcdata->pathfind.list = NULL;
EVENT_SENDER = NULL;
if ((io->_npcdata->behavior & BEHAVIOUR_FLEE)
&& (!io->_npcdata->pathfind.pathwait))
SendIOScriptEvent(io, 0, "", "FLEE_END");
if ((io->_npcdata->pathfind.flags & PATHFIND_NO_UPDATE) &&
(io->_npcdata->pathfind.pathwait == 0))
{
if (!io->_npcdata->reachedtarget)
{
long num = GetInterNum(io);
io->_npcdata->reachedtarget = 1;
io->_npcdata->reachedtime = ARXTimeUL();
if (io->targetinfo != num)
{
SendIOScriptEvent(io, SM_REACHEDTARGET, "FAKE");
io->targetinfo = num;
}
}
}
else
{
io->targetinfo = io->_npcdata->pathfind.truetarget;
GetTargetPos(io);
if (fabs(io->pos.y - io->target.y) > 200.f)
{
io->_npcdata->pathfind.listnb = -2;
}
}
}
}
else if (!io->_npcdata->reachedtarget)
{
if (ValidIONum(io->targetinfo))
EVENT_SENDER = inter.iobj[io->targetinfo];
else
EVENT_SENDER = NULL;
io->_npcdata->reachedtarget = 1;
io->_npcdata->reachedtime = ARXTimeUL();//treat warning C4244 conversion from 'float' to 'unsigned long'
if (io->animlayer[1].flags & EA_ANIMEND)
io->animlayer[1].cur_anim = NULL;
if (io->targetinfo != GetInterNum(io))
SendIOScriptEvent(io, SM_REACHEDTARGET, "");
}
}
}
if (dis < 280.f)
{
if ((io->_npcdata->behavior & BEHAVIOUR_FIGHT)
&& !(io->_npcdata->behavior & BEHAVIOUR_FLEE))
{
ARX_NPC_Manage_Fight(io);
}
else
{
ARX_NPC_Manage_NON_Fight(io);
}
}
ARX_NPC_Manage_Anims(io, TOLERANCE2);
// Puts at least WAIT anim on NPC if he has no main animation...
if (ause0->cur_anim == NULL)
{
if (io->_npcdata->behavior & (BEHAVIOUR_FIGHT | BEHAVIOUR_MAGIC | BEHAVIOUR_DISTANT))
{
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_FIGHT_WAIT]);
ause0->flags |= EA_LOOP;
}
else
{
FinishAnim(io, ause0->cur_anim);
ANIM_Set(ause0, alist[ANIM_WAIT]);
if (io->_npcdata->behavior & BEHAVIOUR_FRIENDLY) ause0->altidx_cur = 0;
}
}
// Now update lastpos values for next call use...
io->lastpos.x = io->pos.x;
io->lastpos.y = io->pos.y;
io->lastpos.z = io->pos.z;
}
float AngularDifference(float a1, float a2)
{
a1 = MAKEANGLE(a1);
a2 = MAKEANGLE(a2);
if (a1 == a2) return 0;
float ret;
if (a1 < a2)
{
ret = a2;
a2 = a1;
a1 = ret;
}
ret = a1 - a2;
ret = __min(ret, (a2 + 360) - a1);
return ret;
}
extern float CURRENT_PLAYER_COLOR;
//***********************************************************************************************
// INTERACTIVE_OBJ * ARX_NPC_GetFirstNPCInSight(INTERACTIVE_OBJ * ioo)
//-----------------------------------------------------------------------------------------------
// FUNCTION:
// returns the "first" NPC in sight for another NPC (ioo)
//***********************************************************************************************
INTERACTIVE_OBJ * ARX_NPC_GetFirstNPCInSight(INTERACTIVE_OBJ * ioo)
{
if (!ioo) return NULL;
// Basic Clipping to avoid performance loss
float dist_cam = EEDistance3D(&ACTIVECAM->pos, &ioo->pos);
if (dist_cam > 2500) return NULL;
INTERACTIVE_OBJ * found_io = NULL;
float found_dist = 9999999999999.f;
for (long i = 0; i < inter.nbmax; i++)
{
INTERACTIVE_OBJ * io = inter.iobj[i];
if ((!io)
|| (IsDeadNPC(io))
|| (io == ioo)
|| (!(io->ioflags & IO_NPC))
|| (io->show != SHOW_FLAG_IN_SCENE))
continue;
float dist_io = EEDistance3D(&io->pos, &ioo->pos);
if ((dist_io > found_dist)
|| (dist_io > 1800))
continue; // too far
if (dist_io < 130)
{
if (found_dist > dist_io)
{
found_io = io;
found_dist = dist_io;
}
continue;
}
EERIE_3D orgn, dest;
float ab = MAKEANGLE(ioo->angle.b);
long grp = ioo->obj->fastaccess.head_group_origin;
if (grp < 0)
{
orgn.x = ioo->pos.x;
orgn.y = ioo->pos.y - 90.f;
orgn.z = ioo->pos.z;
if (ioo == inter.iobj[0]) orgn.y = player.pos.y + 90.f;
}
else
GetVertexPos(ioo, ioo->obj->fastaccess.head_group_origin, &orgn);
grp = io->obj->fastaccess.head_group_origin;
if (grp < 0)
{
dest.x = io->pos.x;
dest.y = io->pos.y - 90.f;
dest.z = io->pos.z;
if (io == inter.iobj[0]) dest.y = player.pos.y + 90.f;
}
else
GetVertexPos(io, io->obj->fastaccess.head_group_origin, &dest);
float aa = GetAngle(orgn.x, orgn.z, dest.x, dest.z);
aa = MAKEANGLE(RAD2DEG(aa));
if (EEfabs(AngularDifference(aa, ab)) < 110.f)
{
if (dist_io < 200)
{
if (found_dist > dist_io)
{
found_io = io;
found_dist = dist_io;
}
continue;
}
float grnd_color = CURRENT_PLAYER_COLOR - GetPlayerStealth();
if (grnd_color > 0)
{
EERIE_3D ppos;
EERIEPOLY * epp = NULL;
if (IO_Visible(&orgn, &dest, epp, &ppos))
{
if (found_dist > dist_io)
{
found_io = io;
found_dist = dist_io;
}
continue;
}
else if (EEDistance3D(&ppos, &dest) < 25.f)
{
if (found_dist > dist_io)
{
found_io = io;
found_dist = dist_io;
}
continue;
}
}
}
}
return found_io;
}
extern float CURRENT_PLAYER_COLOR;
//***********************************************************************************************
// void CheckNPC(INTERACTIVE_OBJ * io)
//-----------------------------------------------------------------------------------------------
// FUNCTION
// Checks if a NPC is dead to prevent further Layers Animation
//***********************************************************************************************
void CheckNPC(INTERACTIVE_OBJ * io)
{
if ((!io)
|| (io->show != SHOW_FLAG_IN_SCENE))
return;
if (IsDeadNPC(io))
{
io->animlayer[1].cur_anim = NULL;
io->animlayer[2].cur_anim = NULL;
io->animlayer[3].cur_anim = NULL;
io->animlayer[0].next_anim = NULL;
io->animlayer[1].next_anim = NULL;
io->animlayer[2].next_anim = NULL;
io->animlayer[3].next_anim = NULL;
}
}
//***********************************************************************************************
// void CheckUnderWaterIO(INTERACTIVE_OBJ * io)
//-----------------------------------------------------------------------------------------------
// FUNCTION:
// Checks if the bottom of an IO is underwater.
// RESULT:
// Plays Water sounds
// Decrease/stops Ignition of this IO if necessary
//-----------------------------------------------------------------------------------------------
// WARNINGS:
// io must be valid (no check !)
//***********************************************************************************************
void CheckUnderWaterIO(INTERACTIVE_OBJ * io)
{
EERIE_3D ppos;
ppos.x = io->pos.x;
ppos.y = io->pos.y;
ppos.z = io->pos.z;
EERIEPOLY * ep = EEIsUnderWater(&ppos);
if (io->ioflags & IO_UNDERWATER)
{
if (!ep)
{
io->ioflags &= ~IO_UNDERWATER;
ARX_SOUND_PlaySFX(SND_PLOUF, &ppos);
ARX_PARTICLES_SpawnWaterSplash(&ppos);
}
}
else if (ep)
{
io->ioflags |= IO_UNDERWATER;
ARX_SOUND_PlaySFX(SND_PLOUF, &ppos);
ARX_PARTICLES_SpawnWaterSplash(&ppos);
if (io->ignition > 0.f)
{
ARX_SOUND_PlaySFX(SND_TORCH_END, &ppos);
if (ValidDynLight(io->ignit_light))
DynLight[io->ignit_light].exist = 0;
io->ignit_light = -1;
if (io->ignit_sound != ARX_SOUND_INVALID_RESOURCE)
{
ARX_SOUND_Stop(io->ignit_sound);
io->ignit_sound = ARX_SOUND_INVALID_RESOURCE;
}
io->ignition = 0;
}
}
}
extern long GLOBAL_Player_Room;
//***********************************************************************************************
// void CheckNPCEx(INTERACTIVE_OBJ * io)
// ----------------------------------------------------------------------------------------------
// FUNCTION:
// Checks an NPC Visibility Field (Player Detect)
// NECESSARY:
// Uses Invisibility/Confuse/Torch infos.
// RESULT:
// Sends appropriate Detectplayer/Undetectplayer events to the IO
//-----------------------------------------------------------------------------------------------
// WARNINGS:
// io and io->obj must be valid (no check !)
//***********************************************************************************************
void CheckNPCEx(INTERACTIVE_OBJ * io)
{
// Distance Between Player and IO
float dist = Distance3D(io->pos.x, io->pos.y, io->pos.z, player.pos.x, player.pos.y - PLAYER_BASE_HEIGHT, player.pos.z);
// Start as not visible
long Visible = 0;
// Check visibility only if player is visible, not too far and not dead
if ((!(inter.iobj[0]->invisibility > 0.f)) && (dist < 2000.f) && (player.life > 0))
{
// checks for near contact +/- 15 cm --> force visibility
if (io->room_flags & 1)
UpdateIORoom(io);
if (GLOBAL_Player_Room == -1)
GLOBAL_Player_Room = ARX_PORTALS_GetRoomNumForPosition(&player.pos, 1);
float fdist = SP_GetRoomDist(&io->pos, &player.pos, io->room, GLOBAL_Player_Room);
// Use Portal Room Distance for Extra Visibility Clipping.
if ((GLOBAL_Player_Room > -1) && (io->room > -1) && (fdist > 2000.f))
{
}
else if ((dist < GetIORadius(io) + GetIORadius(inter.iobj[0]) + 15.f)
&& (EEfabs(player.pos.y - io->pos.y) < 200.f))
{
Visible = 1;
}
else // Make full visibility test
{
EERIE_3D orgn, dest;
// Retreives Head group position for "eye" pos.
long grp = io->obj->fastaccess.head_group_origin;
if (grp < 0)
{
orgn.x = io->pos.x;
orgn.y = io->pos.y - 90.f;
orgn.z = io->pos.z;
}
else
{
orgn.x = io->pos.x;
orgn.y = io->pos.y - 120.f;
orgn.z = io->pos.z;
}
dest.x = player.pos.x;
dest.y = player.pos.y + 90.f;
dest.z = player.pos.z;
// Check for Field of vision angle
float aa = GetAngle(orgn.x, orgn.z, dest.x, dest.z);
aa = MAKEANGLE(RAD2DEG(aa));
float ab = MAKEANGLE(io->angle.b);
if (EEfabs(AngularDifference(aa, ab)) < 110.f)
{
// Check for Darkness/Stealth
if ((CURRENT_PLAYER_COLOR > GetPlayerStealth())
|| SHOW_TORCH
|| (dist < 200))
{
EERIEPOLY * epp = NULL;
EERIE_3D ppos;
// Check for Geometrical Visibility
if ((IO_Visible(&orgn, &dest, epp, &ppos))
|| (EEDistance3D(&ppos, &dest) < 25.f))
Visible = 1;
}
}
}
if (Visible)
{
if (!io->_npcdata->detect)
{
// if visible but was NOT visible, sends an Detectplayer Event
EVENT_SENDER = NULL;
SendIOScriptEvent(io, SM_DETECTPLAYER, "");
io->_npcdata->detect = 1;
}
}
}
// if not visible but was visible, sends an Undetectplayer Event
if ((!Visible) && (io->_npcdata->detect))
{
EVENT_SENDER = NULL;
SendIOScriptEvent(io, SM_UNDETECTPLAYER, "");
io->_npcdata->detect = 0;
}
}
//-------------------------------------------------------------------------
void GetMaterialString(char * origin, char * dest)
{
// need to be precomputed !!!
if (IsIn(origin, "STONE")) strcpy(dest, "STONE");
else if (IsIn(origin, "MARBLE")) strcpy(dest, "STONE");
else if (IsIn(origin, "ROCK")) strcpy(dest, "STONE");
else if (IsIn(origin, "WOOD")) strcpy(dest, "WOOD");
else if (IsIn(origin, "WET")) strcpy(dest, "WET");
else if (IsIn(origin, "MUD")) strcpy(dest, "WET");
else if (IsIn(origin, "BLOOD")) strcpy(dest, "WET");
else if (IsIn(origin, "BONE")) strcpy(dest, "WET");
else if (IsIn(origin, "FLESH")) strcpy(dest, "WET");
else if (IsIn(origin, "SHIT")) strcpy(dest, "WET");
else if (IsIn(origin, "SOIL")) strcpy(dest, "GRAVEL");
else if (IsIn(origin, "GRAVEL")) strcpy(dest, "GRAVEL");
else if (IsIn(origin, "EARTH")) strcpy(dest, "GRAVEL");
else if (IsIn(origin, "DUST")) strcpy(dest, "GRAVEL");
else if (IsIn(origin, "SAND")) strcpy(dest, "GRAVEL");
else if (IsIn(origin, "STRAW")) strcpy(dest, "GRAVEL");
else if (IsIn(origin, "METAL")) strcpy(dest, "METAL");
else if (IsIn(origin, "IRON")) strcpy(dest, "METAL");
else if (IsIn(origin, "GLASS")) strcpy(dest, "METAL");
else if (IsIn(origin, "RUST")) strcpy(dest, "METAL");
else if (IsIn(origin, "EARTH")) strcpy(dest, "EARTH");
else if (IsIn(origin, "ICE")) strcpy(dest, "ICE");
else if (IsIn(origin, "FABRIC")) strcpy(dest, "CARPET");
else if (IsIn(origin, "MOSS")) strcpy(dest, "CARPET");
else strcpy(dest, "UNKNOWN");
}
//-------------------------------------------------------------------------
void ARX_NPC_NeedStepSound(INTERACTIVE_OBJ * io, EERIE_3D * pos, const float volume, const float power)
{
char step_material[64] = "Foot_bare";
char floor_material[64] = "EARTH";
if (IsUnderWater(pos->x, pos->y, pos->z))
strcpy(floor_material, "WATER");
else
{
EERIEPOLY * ep;
ep = CheckInPoly(pos->x, pos->y - 100.0F, pos->z);
if (ep && ep->tex && ep->tex->m_texName)
GetMaterialString(ep->tex->m_texName, floor_material);
}
if (io && io->stepmaterial)
strcpy(step_material, io->stepmaterial);
if ((io == inter.iobj[0]) && (player.equiped[EQUIP_SLOT_LEGGINGS] > 0))
{
if (ValidIONum(player.equiped[EQUIP_SLOT_LEGGINGS]))
{
INTERACTIVE_OBJ * ioo = inter.iobj[player.equiped[EQUIP_SLOT_LEGGINGS]];
if (ioo->stepmaterial)
strcpy(step_material, ioo->stepmaterial);
}
}
ARX_SOUND_PlayCollision(step_material, floor_material, volume, power, pos, io);
}
//-------------------------------------------------------------------------
//***********************************************************************************************
// ARX_NPC_SpawnAudibleSound
// Sends ON HEAR events to NPCs for audible sounds
// factor > 1.0F harder to hear, < 0.0F easier to hear
//***********************************************************************************************
void ARX_NPC_SpawnAudibleSound(EERIE_3D * pos, INTERACTIVE_OBJ * source, const float factor, const float presence)
{
float max_distance;
if (source == inter.iobj[0])
max_distance = ARX_NPC_ON_HEAR_MAX_DISTANCE_STEP;
else if (source && source->ioflags & IO_ITEM)
max_distance = ARX_NPC_ON_HEAR_MAX_DISTANCE_ITEM;
else return;
max_distance *= presence;
max_distance /= factor;
EVENT_SENDER = source;
long Source_Room = ARX_PORTALS_GetRoomNumForPosition(pos, 1);
for (long i = 0; i < inter.nbmax; i++)
if ((inter.iobj[i])
&& (inter.iobj[i]->ioflags & IO_NPC)
&& (inter.iobj[i]->GameFlags & GFLAG_ISINTREATZONE)
&& (inter.iobj[i] != source)
&& ((inter.iobj[i]->show == SHOW_FLAG_IN_SCENE)
|| (inter.iobj[i]->show == SHOW_FLAG_HIDDEN))
&& (inter.iobj[i]->_npcdata->life > 0.f)
)
{
float distance(EEDistance3D(pos, &inter.iobj[i]->pos));
if (distance < max_distance)
{
if (inter.iobj[i]->room_flags & 1)
UpdateIORoom(inter.iobj[i]);
float fdist;
if ((Source_Room > -1) && (inter.iobj[i]->room > -1))
{
fdist = SP_GetRoomDist(pos, &inter.iobj[i]->pos, Source_Room, inter.iobj[i]->room);
if (fdist < max_distance * 1.5f)
{
long ldistance;
char temp[64];
F2L(fdist, &ldistance);
sprintf(temp, "%d", ldistance);
SendIOScriptEvent(inter.iobj[i], SM_HEAR, temp);
}
}
else
{
long ldistance;
char temp[64];
F2L(distance, &ldistance);
sprintf(temp, "%d", ldistance);
SendIOScriptEvent(inter.iobj[i], SM_HEAR, temp);
}
}
}
}
extern INTERACTIVE_OBJ * CURRENT_TORCH;
//-------------------------------------------------------------------------
void ManageIgnition(INTERACTIVE_OBJ * io)
{
if (!io) return;
if (CURRENT_TORCH == io)
{
if (ValidDynLight(io->ignit_light))
DynLight[io->ignit_light].exist = 0;
io->ignit_light = -1;
if (io->ignit_sound != ARX_SOUND_INVALID_RESOURCE)
{
ARX_SOUND_Stop(io->ignit_sound);
io->ignit_sound = ARX_SOUND_INVALID_RESOURCE;
}
return;
}
// Torch Management
INTERACTIVE_OBJ * plw = NULL;
if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
&& (ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])))
plw = inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]];
if ((io->ioflags & IO_FIERY)
&& (!(io->type_flags & OBJECT_TYPE_BOW))
&& ((io->show == SHOW_FLAG_IN_SCENE) || (io == plw)))
{
float p = io->ignition = 25.f;
while (p > 0.f)
{
p -= 6.f;
if ((io) && (io->obj) && (io->obj->nbfaces))
{
EERIE_3D pos;
long notok = 10;
long num = 0;
while (notok-- > 0)
{
F2L((float)(rnd() *(float)io->obj->nbfaces), &num);
if ((num >= 0) && (num < io->obj->nbfaces))
{
if (io->obj->facelist[num].facetype & POLY_HIDE) continue;
notok = -1;
}
}
if (notok < 0)
{
Vector_Copy(&pos, &io->obj->vertexlist3[io->obj->facelist[num].vid[0]].v);
for (long nn = 0 ; nn < 1 ; nn++)
{
long j = ARX_PARTICLES_GetFree();
if ((j != -1) && (!ARXPausedTimer) && (rnd() < 0.4f))
{
ParticleCount++;
PARTICLE_DEF * pd = &particle[j];
pd->exist = TRUE;
pd->zdec = 0;
Vector_Copy(&pd->ov, &pos);
pd->move.x = (2.f - 4.f * rnd());
pd->move.y = (2.f - 22.f * rnd());
pd->move.z = (2.f - 4.f * rnd());
pd->siz = 7.f;
pd->tolive = 500 + (unsigned long)(rnd() * 1000.f);
pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
pd->tc = fire2;//tc;
pd->fparam = 0.1f - rnd() * 0.2f;
pd->scale.x = -8.f;
pd->scale.y = -8.f;
pd->scale.z = -8.f;
pd->timcreation = lARXTime;
pd->r = 0.71f;
pd->g = 0.43f;
pd->b = 0.29f;
//pd->delay=nn*180;
}
}
}
}
}
}
else if (io->obj && (io->obj->fastaccess.fire >= 0) && (io->ignition > 0.f))
{
io->ignition = 25.f;
io->durability -= FrameDiff * DIV10000;
if (io->durability <= 0.F)
{
if (ValidDynLight(io->ignit_light))
DynLight[io->ignit_light].exist = 0;
io->ignit_light = -1;
if (io->ignit_sound != ARX_SOUND_INVALID_RESOURCE)
{
ARX_SOUND_Stop(io->ignit_sound);
io->ignit_sound = ARX_SOUND_INVALID_RESOURCE;
}
ARX_SPEECH_ReleaseIOSpeech(io);
// Need To Kill timers
ARX_SCRIPT_Timer_Clear_By_IO(io);
io->show = SHOW_FLAG_KILLED;
io->GameFlags &= ~GFLAG_ISINTREATZONE;
RemoveFromAllInventories(io);
ARX_INTERACTIVE_DestroyDynamicInfo(io);
ARX_SOUND_PlaySFX(SND_TORCH_END, &io->pos);
if (io == DRAGINTER)
Set_DragInter(NULL);
ARX_INTERACTIVE_DestroyIO(io);
return;
}
EERIE_3D pos;
Vector_Copy(&pos, &io->obj->vertexlist3[io->obj->fastaccess.fire].v);
for (long nn = 0; nn < 2; nn++)
{
long j = ARX_PARTICLES_GetFree();
if ((j != -1) && (!ARXPausedTimer) && (rnd() < 0.4f))
{
ParticleCount++;
PARTICLE_DEF * pd = &particle[j];
pd->exist = TRUE;
pd->zdec = 0;
Vector_Copy(&pd->ov, &pos);
pd->move.x = (2.f - 4.f * rnd());
pd->move.y = (2.f - 22.f * rnd());
pd->move.z = (2.f - 4.f * rnd());
pd->siz = 7.f;
pd->tolive = 500 + (unsigned long)(rnd() * 1000.f);
pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
pd->tc = fire2;
pd->fparam = 0.1f - rnd() * 0.2f;
pd->scale.x = -8.f;
pd->scale.y = -8.f;
pd->scale.z = -8.f;
pd->timcreation = lARXTime;
pd->r = 0.71f;
pd->g = 0.43f;
pd->b = 0.29f;
pd->delay = nn * 2;
}
}
}
else
{
io->ignition -= _framedelay * DIV100;
if ((!io) || (!io->obj)) return;
float p = io->ignition * _framedelay * DIV1000 * io->obj->nbfaces * DIV1000;
if (p > 5.f)
p = 5.f;
while (p > 0.f)
{
p -= 0.5f;
if ((io) && (io->obj) && (io->obj->nbfaces))
{
EERIE_3D pos;
long notok = 10;
long num = 0;
while (notok-- > 0)
{
F2L((float)(rnd() *(float)io->obj->nbfaces), &num);
if ((num >= 0) && (num < io->obj->nbfaces))
{
if (io->obj->facelist[num].facetype & POLY_HIDE) continue;
notok = -1;
}
}
if (notok < 0)
{
Vector_Copy(&pos, &io->obj->vertexlist3[io->obj->facelist[num].vid[0]].v);
for (long nn = 0 ; nn < 6 ; nn++)
{
long j = ARX_PARTICLES_GetFree();
if ((j != -1) && (!ARXPausedTimer) && (rnd() < 0.4f))
{
ParticleCount++;
PARTICLE_DEF * pd = &particle[j];
pd->exist = TRUE;
pd->zdec = 0;
Vector_Copy(&pd->ov, &pos);
pd->move.x = (2.f - 4.f * rnd());
pd->move.y = (2.f - 22.f * rnd());
pd->move.z = (2.f - 4.f * rnd());
pd->siz = 7.f;
pd->tolive = 500 + (unsigned long)(rnd() * 1000.f);
pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
pd->tc = fire2;//tc;
pd->fparam = 0.1f - rnd() * 0.2f;
pd->scale.x = -8.f;
pd->scale.y = -8.f;
pd->scale.z = -8.f;
pd->timcreation = lARXTime;
pd->r = 0.71f;
pd->g = 0.43f;
pd->b = 0.29f;
pd->delay = nn * 180;
}
}
}
}
}
}
ManageIgnition_2(io);
}
//-------------------------------------------------------------------------
void ManageIgnition_2(INTERACTIVE_OBJ * io)
{
if (!io) return;
if (io->ignition > 0.f)
{
if (io->ignition > 100.f)
io->ignition = 100.f;
EERIE_3D position;
if (io->obj && (io->obj->fastaccess.fire >= 0))
{
if (io == DRAGINTER)
Vector_Copy(&position, &player.pos);
else
{
Vector_Copy(&position, &io->obj->vertexlist3[io->obj->fastaccess.fire].v);
}
}
else
{
Vector_Copy(&position, &io->pos);
}
if (io->ignit_light == -1)
io->ignit_light = GetFreeDynLight();
if (io->ignit_light != -1)
{
long id = io->ignit_light;
DynLight[id].exist = 1;
DynLight[id].intensity = __max(io->ignition * DIV10, 1.f);
DynLight[id].fallstart = __max(io->ignition * 10.f, 100.f);
DynLight[id].fallend = __max(io->ignition * 25.f, 240.f);
float v = __max((io->ignition * DIV10), 0.5f);
v = __min(v, 1.f);
DynLight[id].rgb.r = (1.f - rnd() * 0.2f) * v;
DynLight[id].rgb.g = (0.8f - rnd() * 0.2f) * v;
DynLight[id].rgb.b = (0.6f - rnd() * 0.2f) * v;
DynLight[id].pos.x = position.x;
DynLight[id].pos.y = position.y - 30.f;
DynLight[id].pos.z = position.z;
DynLight[id].ex_flaresize = 40.f; //16.f;
DynLight[id].extras |= EXTRAS_FLARE;
}
if (io->ignit_sound == ARX_SOUND_INVALID_RESOURCE)
{
io->ignit_sound = SND_FIREPLACE;
ARX_SOUND_PlaySFX(io->ignit_sound, &position, 0.95F + 0.1F * rnd(), ARX_SOUND_PLAY_LOOPED);
}
else ARX_SOUND_RefreshPosition(io->ignit_sound, &position);
if (rnd() > 0.9f) CheckForIgnition(&position, io->ignition, 1);
}
else
{
if (ValidDynLight(io->ignit_light))
DynLight[io->ignit_light].exist = 0;
io->ignit_light = -1;
if (io->ignit_sound != ARX_SOUND_INVALID_RESOURCE)
{
ARX_SOUND_Stop(io->ignit_sound);
io->ignit_sound = ARX_SOUND_INVALID_RESOURCE;
}
}
}
//-------------------------------------------------------------------------