/*
* Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
*
* This file is part of Arx Libertatis.
*
* Arx Libertatis 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 Libertatis 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 Libertatis. If not, see .
*/
/* Based on:
===========================================================================
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.
===========================================================================
*/
// Code: Cyril Meynier
//
// Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
#include "ai/Paths.h"
#include
#include
#include
#include
#include "animation/Animation.h"
#include "core/GameTime.h"
#include "core/Core.h"
#include "game/Spells.h"
#include "game/NPC.h"
#include "game/Player.h"
#include "game/Damage.h"
#include "game/EntityManager.h"
#include "game/Equipment.h"
#include "game/Inventory.h"
#include "graphics/GraphicsModes.h"
#include "graphics/GraphicsTypes.h"
#include "graphics/Math.h"
#include "graphics/Renderer.h"
#include "graphics/effects/SpellEffects.h"
#include "graphics/particle/ParticleEffects.h"
#include "graphics/data/Mesh.h"
#include "graphics/data/TextureContainer.h"
#include "io/resource/ResourcePath.h"
#include "math/Random.h"
#include "platform/Platform.h"
#include "physics/Box.h"
#include "physics/Collisions.h"
#include "scene/GameSound.h"
#include "scene/Interactive.h"
#include "scene/Light.h"
#include "script/Script.h"
using std::min;
using std::max;
using std::string;
extern long CHANGE_LEVEL_ICON;
extern float FrameDiff;
static bool IsPointInField(Vec3f * pos);
ARX_PATH ** ARXpaths = NULL;
ARX_USE_PATH USE_CINEMATICS_PATH;
MASTER_CAMERA_STRUCT MasterCamera;
long nbARXpaths = 0;
long USE_CINEMATICS_CAMERA = 0;
void ARX_PATH_ComputeBB(ARX_PATH * ap) {
ap->bbmin = Vec3f::repeat(9999999999.f);
ap->bbmax = Vec3f::repeat(-9999999999.f);
for(long i = 0; i < ap->nb_pathways; i++) {
ap->bbmin.x = std::min(ap->bbmin.x, ap->pos.x + ap->pathways[i].rpos.x);
ap->bbmax.x = std::max(ap->bbmax.x, ap->pos.x + ap->pathways[i].rpos.x);
ap->bbmin.z = std::min(ap->bbmin.z, ap->pos.z + ap->pathways[i].rpos.z);
ap->bbmax.z = std::max(ap->bbmax.z, ap->pos.z + ap->pathways[i].rpos.z);
}
if(ap->height > 0) {
ap->bbmin.y = ap->pos.y - ap->height;
ap->bbmax.y = ap->pos.y;
} else {
ap->bbmin.y = -99999999.f;
ap->bbmax.y = 99999999.f;
}
}
void ARX_PATH_ComputeAllBoundingBoxes()
{
for (long i = 0; i < nbARXpaths; i++)
{
if (ARXpaths[i])
{
ARX_PATH_ComputeBB(ARXpaths[i]);
}
}
}
long ARX_PATH_IsPosInZone(ARX_PATH * ap, float x, float y, float z)
{
if (x < ap->bbmin.x) return 0;
if (x > ap->bbmax.x) return 0;
if (z < ap->bbmin.z) return 0;
if (z > ap->bbmax.z) return 0;
if (y < ap->bbmin.y) return 0;
if (y > ap->bbmax.y) return 0;
int i, j, c = 0;
x -= ap->pos.x;
z -= ap->pos.z;
ARX_PATHWAY * app = ap->pathways;
for (i = 0, j = ap->nb_pathways - 1; i < ap->nb_pathways; j = i++)
{
Vec3f * pi = &app[i].rpos;
Vec3f * pj = &app[j].rpos;
if ((((pi->z <= z) && (z < pj->z)) ||
((pj->z <= z) && (z < pi->z))) &&
(x < (pj->x - pi->x) *(z - pi->z) / (pj->z - pi->z) + pi->x))
c = !c;
}
return c;
}
ARX_PATH * ARX_PATH_CheckInZone(Entity * io)
{
if (ARXpaths)
{
Vec3f curpos;
GetItemWorldPosition(io, &curpos);
for (long i = 0; i < nbARXpaths; i++)
{
if ((ARXpaths[i]) && (ARXpaths[i]->height != 0))
{
if (ARX_PATH_IsPosInZone(ARXpaths[i], curpos.x, curpos.y, curpos.z))
return ARXpaths[i];
}
}
}
return NULL;
}
ARX_PATH * ARX_PATH_CheckPlayerInZone()
{
if (ARXpaths)
for (long i = 0; i < nbARXpaths; i++)
{
if ((ARXpaths[i]) && (ARXpaths[i]->height != 0))
{
if (ARX_PATH_IsPosInZone(ARXpaths[i], player.pos.x, player.pos.y + 160.f, player.pos.z))
return ARXpaths[i];
}
}
return NULL;
}
long JUST_RELOADED = 0;
void ARX_PATH_UpdateAllZoneInOutInside() {
if(EDITMODE) {
return;
}
static size_t count = 1;
long f = clamp(static_cast(FrameDiff), 10, 50);
if(count >= entities.size()) {
count = 1;
}
if (entities.size() > 1)
for (long tt = 0; tt < f; tt++)
{
long i = count;
Entity * io = entities[i];
if ((count < entities.size()) && (io)
&& (io->ioflags & (IO_NPC | IO_ITEM))
&& (io->show != SHOW_FLAG_MEGAHIDE)
&& (io->show != SHOW_FLAG_DESTROYED)
)
{
ARX_PATH * p = ARX_PATH_CheckInZone(io);
ARX_PATH * op = io->inzone;
if ((op == NULL) && (p == NULL)) goto next; // Not in a zone
if(op == p) { // Stayed inside Zone OP
if (io->show != io->inzone_show)
{
io->inzone_show = io->show;
goto entering;
}
}
else if ((op != NULL) && (p == NULL)) // Leaving Zone OP
{
SendIOScriptEvent(io, SM_LEAVEZONE, op->name);
if (!op->controled.empty())
{
long t = entities.getById(op->controled);
if (t >= 0)
{
string str = io->long_name() + ' ' + op->name;
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, str);
}
}
}
else if ((op == NULL) && (p != NULL)) // Entering Zone P
{
io->inzone_show = io->show;
entering:
if(JUST_RELOADED && (p->name == "ingot_maker" || p->name == "mauld_user")) {
ARX_DEAD_CODE(); // TODO remove JUST_RELOADED global
} else {
SendIOScriptEvent(io, SM_ENTERZONE, p->name);
if (!p->controled.empty())
{
long t = entities.getById(p->controled);
if (t >= 0)
{
string params = io->long_name() + ' ' + p->name;
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, params);
}
}
}
}
else
{
SendIOScriptEvent(io, SM_LEAVEZONE, op->name);
if (!op->controled.empty())
{
long t = entities.getById(op->controled);
if (t >= 0)
{
string str = io->long_name() + ' ' + op->name;
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, str);
}
}
io->inzone_show = io->show;
SendIOScriptEvent(io, SM_ENTERZONE, p->name);
if (!p->controled.empty())
{
long t = entities.getById(p->controled);
if (t >= 0)
{
string str = io->long_name() + ' ' + p->name;
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, str);
}
}
}
io->inzone = p;
}
next:
count++;
if (count >= entities.size()) count = 1;
}
// player check*************************************************
if (entities.player())
{
ARX_PATH * p = ARX_PATH_CheckPlayerInZone();
ARX_PATH * op = (ARX_PATH *)player.inzone;
if ((op == NULL) && (p == NULL)) goto suite; // Not in a zone
if (op == p) // Stayed inside Zone OP
{
}
else if ((op != NULL) && (p == NULL)) // Leaving Zone OP
{
SendIOScriptEvent(entities.player(), SM_LEAVEZONE, op->name);
CHANGE_LEVEL_ICON = -1;
if (!op->controled.empty())
{
long t = entities.getById(op->controled);
if (t >= 0)
{
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, "player " + op->name);
}
}
}
else if ((op == NULL) && (p != NULL)) // Entering Zone P
{
SendIOScriptEvent(entities.player(), SM_ENTERZONE, p->name);
if (p->flags & PATH_AMBIANCE && !p->ambiance.empty())
ARX_SOUND_PlayZoneAmbiance(p->ambiance, ARX_SOUND_PLAY_LOOPED, p->amb_max_vol * ( 1.0f / 100 ));
if (p->flags & PATH_FARCLIP)
{
desired.flags |= GMOD_ZCLIP;
desired.zclip = p->farclip;
}
if (p->flags & PATH_REVERB)
{
}
if (p->flags & PATH_RGB)
{
desired.flags |= GMOD_DCOLOR;
desired.depthcolor = p->rgb;
}
if (!p->controled.empty())
{
long t = entities.getById(p->controled);
if (t >= 0)
{
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, "player " + p->name);
}
}
}
else
{
if (!op->controled.empty())
{
long t = entities.getById(op->controled);
if (t >= 0)
{
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_LEAVE, "player " + p->name);
}
}
if (!op->controled.empty())
{
long t = entities.getById(p->controled);
if (t >= 0)
{
SendIOScriptEvent(entities[t], SM_CONTROLLEDZONE_ENTER, "player " + p->name);
}
}
}
player.inzone = p;
}
suite:
JUST_RELOADED = 0;
}
ARX_PATH::ARX_PATH(const std::string & _name, const Vec3f & _pos)
: name(_name), initpos(_pos), pos(_pos) {
flags = 0;
nb_pathways = 0;
pathways = NULL;
height = 0; // 0 NOT A ZONE
rgb = Color3f::black;
farclip = 0.f;
reverb = 0.f;
amb_max_vol = 0.f;
bbmin = Vec3f::ZERO;
bbmax = Vec3f::ZERO;
}
void ARX_PATH_ClearAllUsePath() {
BOOST_FOREACH(Entity * e, entities) {
if(e && e->usepath) {
free(e->usepath), e->usepath = NULL;
}
}
}
void ARX_PATH_ClearAllControled() {
for(long i = 0; i < nbARXpaths; i++) {
if(ARXpaths[i]) {
ARXpaths[i]->controled.clear();
}
}
}
ARX_PATH * ARX_PATH_GetAddressByName(const string & name) {
// TODO this is almost the same as ARX_PATHS_ExistName()
if(name.empty() || !ARXpaths) {
return NULL;
}
for(long i = 0; i < nbARXpaths; i++) {
if(ARXpaths[i] && ARXpaths[i]->name == name) {
return ARXpaths[i];
}
}
return NULL;
}
void ARX_PATH_ReleaseAllPath() {
ARX_PATH_ClearAllUsePath();
for(long i = 0; i < nbARXpaths; i++) {
if(ARXpaths[i]) {
free(ARXpaths[i]->pathways), ARXpaths[i]->pathways = NULL;
delete ARXpaths[i], ARXpaths[i] = NULL;
}
}
free(ARXpaths), ARXpaths = NULL;
nbARXpaths = 0;
}
ARX_PATH * ARX_PATHS_ExistName(const string & name) {
if(!ARXpaths) {
return NULL;
}
for(long i = 0; i < nbARXpaths; i++) {
if(ARXpaths[i]->name == name) {
return ARXpaths[i];
}
}
return NULL;
}
long ARX_PATHS_Interpolate(ARX_USE_PATH * aup, Vec3f * pos) {
ARX_PATH * ap = aup->path;
// compute Delta Time
float tim = aup->_curtime - aup->_starttime;
if(tim < 0) {
return -1;
}
// set pos to startpos
*pos = Vec3f::ZERO;
if(tim == 0) {
return 0;
}
// we start at reference waypoint 0 (time & rpos = 0 for this waypoint).
long targetwaypoint = 1;
aup->aupflags &= ~ARX_USEPATH_FLAG_FINISHED;
if(ap->pathways) {
ap->pathways[0]._time = 0;
ap->pathways[0].rpos = Vec3f::ZERO;
} else {
return -1;
}
// While we have time left, iterate
while(tim > 0) {
// Path Ended
if(targetwaypoint > ap->nb_pathways - 1) {
*pos += ap->pos;
aup->aupflags |= ARX_USEPATH_FLAG_FINISHED;
return -2;
}
// Manages a Bezier block
if(ap->pathways[targetwaypoint - 1].flag == PATHWAY_BEZIER) {
targetwaypoint += 1;
float delta = tim - ap->pathways[targetwaypoint]._time;
if(delta >= 0) {
tim = delta;
if(targetwaypoint < ap->nb_pathways) {
*pos = ap->pathways[targetwaypoint].rpos;
}
targetwaypoint += 1;
} else {
if(targetwaypoint < ap->nb_pathways) {
if(ap->pathways[targetwaypoint]._time == 0) {
return targetwaypoint - 1;
}
float rel = tim / ap->pathways[targetwaypoint]._time;
float mull = square(rel);
*pos = ap->pos + ap->pathways[targetwaypoint].rpos * mull;
*pos += ap->pathways[targetwaypoint - 1].rpos * (rel - mull);
*pos += ap->pathways[targetwaypoint - 2].rpos * (1 - rel);
}
return targetwaypoint - 1;
}
} else {
// Manages a non-Bezier block
float delta = tim - ap->pathways[targetwaypoint]._time;
if(delta >= 0) {
tim = delta;
if(targetwaypoint < ap->nb_pathways) {
*pos = ap->pathways[targetwaypoint].rpos;
}
targetwaypoint++;
} else {
if(targetwaypoint < ap->nb_pathways) {
if(ap->pathways[targetwaypoint]._time == 0) {
return targetwaypoint - 1;
}
float rel = tim / ap->pathways[targetwaypoint]._time;
*pos += (ap->pathways[targetwaypoint].rpos - *pos) * rel;
}
*pos += ap->pos;
return targetwaypoint - 1;
}
}
}
*pos += ap->pos;
return targetwaypoint;
}
// THROWN OBJECTS MANAGEMENT
ARX_THROWN_OBJECT Thrown[MAX_THROWN_OBJECTS];
long Thrown_Count = 0;
void ARX_THROWN_OBJECT_Kill(long num) {
if(num >= 0 && size_t(num) < MAX_THROWN_OBJECTS) {
Thrown[num].flags = 0;
Thrown_Count--;
delete Thrown[num].pRuban, Thrown[num].pRuban = NULL;
}
}
void ARX_THROWN_OBJECT_KillAll()
{
for (size_t i = 0; i < MAX_THROWN_OBJECTS; i++)
{
ARX_THROWN_OBJECT_Kill(i);
}
Thrown_Count = 0;
}
long ARX_THROWN_OBJECT_GetFree()
{
unsigned long latest_time = (unsigned long)(arxtime);
long latest_obj = -1;
for (size_t i = 0; i < MAX_THROWN_OBJECTS; i++)
{
if (Thrown[i].flags & ATO_EXIST)
{
if (Thrown[i].creation_time < latest_time)
{
latest_obj = i;
latest_time = Thrown[i].creation_time;
}
}
else
{
return i;
}
}
if (latest_obj >= 0)
{
ARX_THROWN_OBJECT_Kill(latest_obj);
return latest_obj;
}
return -1;
}
extern EERIE_3DOBJ * arrowobj;
long ARX_THROWN_OBJECT_Throw(long source, Vec3f * position, Vec3f * vect, Vec3f * upvect,
EERIE_QUAT * quat, float velocity, float damages, float poison) {
long num = ARX_THROWN_OBJECT_GetFree();
if (num >= 0)
{
Thrown[num].damages = damages;
Thrown[num].position = *position;
Thrown[num].initial_position = *position;
Thrown[num].vector = *vect;
Thrown[num].upvect = *upvect;
Quat_Copy(&Thrown[num].quat, quat);
Thrown[num].source = source;
Thrown[num].obj = NULL;
Thrown[num].velocity = velocity;
Thrown[num].poisonous = poison;
Thrown[num].pRuban = new CRuban();
Thrown[num].pRuban->Create(num, 2000);
Thrown[num].obj = arrowobj;
if (Thrown[num].obj)
{
Thrown[num].creation_time = (unsigned long)(arxtime);
Thrown[num].flags |= ATO_EXIST | ATO_MOVING;
Thrown_Count++;
}
if ((source == 0)
&& (player.equiped[EQUIP_SLOT_WEAPON] != 0)
&& (ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])))
{
Entity * tio = entities[player.equiped[EQUIP_SLOT_WEAPON]];
if (tio->ioflags & IO_FIERY)
Thrown[num].flags |= ATO_FIERY;
}
}
return num;
}
float ARX_THROWN_ComputeDamages(long thrownum, long source, long target)
{
float distance_limit = 1000.f;
Entity * io_target = entities[target];
Entity * io_source = entities[source];
SendIOScriptEvent(io_target, SM_AGGRESSION);
float distance = fdist(Thrown[thrownum].position, Thrown[thrownum].initial_position);
float distance_modifier = 1.f;
if (distance < distance_limit * 2.f)
{
distance_modifier = distance / distance_limit;
if (distance_modifier < 0.5f)
distance_modifier = 0.5f;
}
else distance_modifier = 2.f;
float attack, dmgs, backstab, critical, ac;
backstab = 1.f;
critical = false;
if (source == 0)
{
attack = Thrown[thrownum].damages;
if(rnd() * 100 <= float(player.Full_Attribute_Dexterity - 9) * 2.f
+ float(player.Full_Skill_Projectile * 0.2f)) {
if (SendIOScriptEvent(io_source, SM_CRITICAL, "bow") != REFUSE)
critical = true;
}
dmgs = attack;
if (io_target->_npcdata->npcflags & NPCFLAG_BACKSTAB)
{
if (rnd() * 100.f <= player.Full_Skill_Stealth)
{
if (SendIOScriptEvent(io_source, SM_BACKSTAB, "bow") != REFUSE)
backstab = 1.5f;
}
}
}
else
{
// TODO treat NPC !!!
ARX_DEAD_CODE();
attack = 0;
dmgs = 0;
}
float absorb;
if (target == 0)
{
ac = player.Full_armor_class;
absorb = player.Full_Skill_Defense * .5f;
}
else
{
ac = ARX_INTERACTIVE_GetArmorClass(io_target);
absorb = io_target->_npcdata->absorb;
}
char wmat[64];
string _amat = "flesh";
const string * amat = &_amat;
strcpy(wmat, "dagger");
if(!io_target->armormaterial.empty()) {
amat = &io_target->armormaterial;
}
if(io_target == entities.player()) {
if(player.equiped[EQUIP_SLOT_ARMOR] > 0) {
Entity * io = entities[player.equiped[EQUIP_SLOT_ARMOR]];
if(io && !io->armormaterial.empty()) {
amat = &io->armormaterial;
}
}
}
float power;
power = dmgs * ( 1.0f / 20 );
if (power > 1.f) power = 1.f;
power = power * 0.15f + 0.85f;
ARX_SOUND_PlayCollision(*amat, wmat, power, 1.f, &Thrown[thrownum].position, io_source);
dmgs *= backstab;
dmgs -= dmgs * (absorb * ( 1.0f / 100 ));
float chance = 100.f - (ac - attack);
float dice = rnd() * 100.f;
if(dice <= chance) {
if (dmgs > 0.f)
{
if (critical)
dmgs *= 1.5f;
dmgs *= distance_modifier;
return dmgs;
}
}
return 0.f;
}
EERIEPOLY * CheckArrowPolyCollision(Vec3f * start, Vec3f * end) {
EERIE_TRI pol;
pol.v[0] = *start;
pol.v[2] = *end - Vec3f(2.f, 15.f, 2.f);
pol.v[1] = *end;
long px = end->x * ACTIVEBKG->Xmul;
long pz = end->z * ACTIVEBKG->Zmul;
long ix = std::max(px - 2, 0L);
long ax = std::min(px + 2, ACTIVEBKG->Xsize - 1L);
long iz = std::max(pz - 2, 0L);
long az = std::min(pz + 2, ACTIVEBKG->Zsize - 1L);
for(long zz = iz; zz <= az; zz++) for(long xx = ix; xx <= ax; xx++) {
FAST_BKG_DATA * feg = &ACTIVEBKG->fastdata[xx][zz];
for(long k = 0; k < feg->nbpolyin; k++) {
EERIEPOLY * ep = feg->polyin[k];
if(ep->type & (POLY_WATER | POLY_TRANS | POLY_NOCOL)) {
continue;
}
EERIE_TRI pol2;
pol2.v[0] = ep->v[0].p;
pol2.v[1] = ep->v[1].p;
pol2.v[2] = ep->v[2].p;
if(Triangles_Intersect(&pol2, &pol)) {
return ep;
}
if(ep->type & POLY_QUAD) {
pol2.v[0] = ep->v[1].p;
pol2.v[1] = ep->v[3].p;
pol2.v[2] = ep->v[2].p;
if(Triangles_Intersect(&pol2, &pol)) {
return ep;
}
}
}
}
return NULL;
}
void CheckExp(long i) {
if((Thrown[i].flags & ATO_FIERY) && !(Thrown[i].flags & ATO_UNDERWATER)) {
ARX_BOOMS_Add(&Thrown[i].position);
LaunchFireballBoom(&Thrown[i].position, 10);
DoSphericDamage(&Thrown[i].position, 4.f * 2, 50.f,
DAMAGE_AREA, DAMAGE_TYPE_FIRE | DAMAGE_TYPE_MAGICAL, 0);
ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, &Thrown[i].position);
ARX_NPC_SpawnAudibleSound(&Thrown[i].position, entities.player());
long id = GetFreeDynLight();
if(id != -1 && FrameDiff > 0) {
DynLight[id].exist = 1;
DynLight[id].intensity = 3.9f;
DynLight[id].fallstart = 400.f;
DynLight[id].fallend = 440.f;
DynLight[id].rgb = Color3f(1.f - rnd() * .2f, .8f - rnd() * .2f, .6f - rnd() * .2f);
DynLight[id].pos = Thrown[i].position;
DynLight[id].ex_flaresize = 40.f;
DynLight[id].duration = 1500;
}
}
}
extern long FRAME_COUNT;
void ARX_THROWN_OBJECT_Manage(unsigned long time_offset)
{
if (Thrown_Count <= 0) return;
GRenderer->SetRenderState(Renderer::DepthWrite, true);
GRenderer->SetRenderState(Renderer::DepthTest, true);
for (size_t i = 0; i < MAX_THROWN_OBJECTS; i++)
{
if (Thrown[i].flags & ATO_EXIST)
{
// Is Object Visible & Near ?
if(fartherThan(ACTIVECAM->pos, Thrown[i].position, ACTIVECAM->cdepth * fZFogEnd + 50.f)) {
continue;
}
long xx, yy;
xx = (Thrown[i].position.x)*ACTIVEBKG->Xmul;
yy = (Thrown[i].position.z)*ACTIVEBKG->Zmul;
if (xx < 0)
continue;
if (xx >= ACTIVEBKG->Xsize)
continue;
if (yy < 0)
continue;
if (yy >= ACTIVEBKG->Zsize)
continue;
FAST_BKG_DATA * feg = (FAST_BKG_DATA *)&ACTIVEBKG->fastdata[xx][yy];
if (!feg->treat)
continue;
// Now render object !
if (!Thrown[i].obj)
continue;
EERIEMATRIX mat;
MatrixFromQuat(&mat, &Thrown[i].quat);
long ccount = FRAME_COUNT;
FRAME_COUNT = 0;
DrawEERIEInterMatrix(Thrown[i].obj, &mat, &Thrown[i].position, NULL);
if((Thrown[i].flags & ATO_FIERY) && (Thrown[i].flags & ATO_MOVING)
&& !(Thrown[i].flags & ATO_UNDERWATER)) {
long id = GetFreeDynLight();
if(id != -1 && FrameDiff > 0) {
DynLight[id].exist = 1;
DynLight[id].intensity = 1.f;
DynLight[id].fallstart = 100.f;
DynLight[id].fallend = 240.f;
DynLight[id].rgb = Color3f(1.f - rnd() * .2f, .8f - rnd() * .2f, .6f - rnd() * .2f);
DynLight[id].pos = Thrown[i].position;
DynLight[id].ex_flaresize = 40.f;
DynLight[id].extras |= EXTRAS_FLARE;
DynLight[id].duration = static_cast(FrameDiff * 0.5f);
}
float p = 3.f;
while (p > 0.f)
{
p -= 0.5f;
if (Thrown[i].obj)
{
Vec3f pos;
long notok = 10;
std::vector::iterator it;
while (notok-- > 0)
{
it = Random::getIterator(Thrown[i].obj->facelist);
arx_assert(it != Thrown[i].obj->facelist.end());
if (it->facetype & POLY_HIDE) continue;
notok = -1;
}
if (notok < 0)
{
pos = Thrown[i].obj->vertexlist3[it->vid[0]].v;
for(long nn = 0; nn < 2; nn++) {
if(rnd() >= 0.4f) {
continue;
}
PARTICLE_DEF * pd = createParticle();
if(!pd) {
break;
}
pd->ov = pos;
pd->move = Vec3f(2.f - 4.f * rnd(), 2.f - 22.f * rnd(),
2.f - 4.f * rnd());
pd->siz = 7.f;
pd->tolive = Random::get(500, 1500);
pd->special = FIRE_TO_SMOKE | ROTATING | MODULATE_ROTATION;
pd->tc = fire2;
pd->fparam = 0.1f - rnd() * 0.2f;
pd->scale = Vec3f::repeat(-8.f);
pd->rgb = Color3f(0.71f, 0.43f, 0.29f);
pd->delay = nn * 180;
}
}
}
}
}
if (Thrown[i].pRuban)
{
Thrown[i].pRuban->Update();
Thrown[i].pRuban->Render();
}
FRAME_COUNT = ccount;
Vec3f original_pos;
if (Thrown[i].flags & ATO_MOVING)
{
long need_kill = 0;
float mod = (float)time_offset * Thrown[i].velocity;
original_pos = Thrown[i].position;
Thrown[i].position.x += Thrown[i].vector.x * mod;
float gmod = 1.f - Thrown[i].velocity;
if (gmod > 1.f) gmod = 1.f;
else if (gmod < 0.f) gmod = 0.f;
Thrown[i].position.y += Thrown[i].vector.y * mod + (time_offset * gmod);
Thrown[i].position.z += Thrown[i].vector.z * mod;
CheckForIgnition(&original_pos, 10.f, 0, 2);
Vec3f wpos = Thrown[i].position;
wpos.y += 20.f;
EERIEPOLY * ep = EEIsUnderWater(&wpos);
if (Thrown[i].flags & ATO_UNDERWATER)
{
if (ep == NULL)
{
Thrown[i].flags &= ~ATO_UNDERWATER;
ARX_SOUND_PlaySFX(SND_PLOUF, &Thrown[i].position);
}
}
else if (ep != NULL)
{
Thrown[i].flags |= ATO_UNDERWATER;
ARX_SOUND_PlaySFX(SND_PLOUF, &Thrown[i].position);
}
// Check for collision MUST be done after DRAWING !!!!
long nbact = Thrown[i].obj->actionlist.size();
for (long j = 0; j < nbact; j++)
{ // TODO iterator
float rad = -1;
rad = GetHitValue(Thrown[i].obj->actionlist[j].name);
rad *= .5f;
if (rad == -1) continue;
Vec3f * v0 = &Thrown[i].obj->vertexlist3[Thrown[i].obj->actionlist[j].idx].v;
Vec3f dest = original_pos + Thrown[i].vector * 95.f;
Vec3f orgn = original_pos - Thrown[i].vector * 25.f;
EERIEPOLY * ep = CheckArrowPolyCollision(&orgn, &dest);
if (ep)
{
ARX_PARTICLES_Spawn_Spark(v0, 14, 0);
CheckExp(i);
if (ValidIONum(Thrown[i].source))
ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
Thrown[i].flags &= ~ATO_MOVING;
Thrown[i].velocity = 0.f;
char weapon_material[64] = "dagger";
string bkg_material = "earth";
if (ep && ep->tex && !ep->tex->m_texName.empty())
bkg_material = GetMaterialString(ep->tex->m_texName);
if (ValidIONum(Thrown[i].source))
ARX_SOUND_PlayCollision(weapon_material, bkg_material, 1.f, 1.f, v0,
entities[Thrown[i].source]);
Thrown[i].position = original_pos;
j = 200;
}
else if (IsPointInField(v0))
{
ARX_PARTICLES_Spawn_Spark(v0, 24, 0);
CheckExp(i);
if (ValidIONum(Thrown[i].source))
ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
Thrown[i].flags &= ~ATO_MOVING;
Thrown[i].velocity = 0.f;
char weapon_material[64] = "dagger";
char bkg_material[64] = "earth";
if (ValidIONum(Thrown[i].source))
ARX_SOUND_PlayCollision(weapon_material, bkg_material, 1.f, 1.f, v0,
entities[Thrown[i].source]);
Thrown[i].position = original_pos;
j = 200;
need_kill = 1;
}
else
for (float precision = 0.5f; precision <= 6.f; precision += 0.5f)
{
EERIE_SPHERE sphere;
sphere.origin = *v0 + Thrown[i].vector * precision * 4.5f;
sphere.radius = rad + 3.f;
if (CheckEverythingInSphere(&sphere, Thrown[i].source, -1))
{
for (size_t jj = 0; jj < MAX_IN_SPHERE_Pos; jj++)
{
if ((ValidIONum(EVERYTHING_IN_SPHERE[jj])
&& (EVERYTHING_IN_SPHERE[jj] != Thrown[i].source)))
{
Entity * target = entities[EVERYTHING_IN_SPHERE[jj]];
if (target->ioflags & IO_NPC)
{
Vec3f pos;
Color color = Color::none;
long hitpoint = -1;
float curdist = 999999.f;
for (size_t ii = 0 ; ii < target->obj->facelist.size() ; ii++)
{
if (target->obj->facelist[ii].facetype & POLY_HIDE) continue;
short vid = target->obj->facelist[ii].vid[0];
float d = dist(sphere.origin, target->obj->vertexlist3[vid].v);
if (d < curdist)
{
hitpoint = target->obj->facelist[ii].vid[0];
curdist = d;
}
}
if (hitpoint >= 0)
{
color = target->_npcdata->blood_color;
pos = target->obj->vertexlist3[hitpoint].v;
}
if (Thrown[i].source == 0)
{
float damages = ARX_THROWN_ComputeDamages(i, Thrown[i].source,
EVERYTHING_IN_SPHERE[jj]);
if (damages > 0.f)
{
arx_assert(hitpoint >= 0);
if (target->ioflags & IO_NPC)
{
target->_npcdata->SPLAT_TOT_NB = 0;
ARX_PARTICLES_Spawn_Blood2(original_pos, damages, color, target);
}
ARX_PARTICLES_Spawn_Blood2(pos, damages, color, target);
ARX_DAMAGES_DamageNPC(target, damages, Thrown[i].source, 0, &pos);
if (rnd() * 100.f > target->_npcdata->resist_poison)
{
target->_npcdata->poisonned += Thrown[i].poisonous;
}
CheckExp(i);
}
else
{
ARX_PARTICLES_Spawn_Spark(v0, 14, 0);
ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
}
}
}
else // not NPC
{
if (target->ioflags & IO_FIX)
{
if (ValidIONum(Thrown[i].source))
ARX_DAMAGES_DamageFIX(target, 0.1f, Thrown[i].source, 0);
}
ARX_PARTICLES_Spawn_Spark(v0, 14, 0);
if (ValidIONum(Thrown[i].source))
ARX_NPC_SpawnAudibleSound(v0, entities[Thrown[i].source]);
CheckExp(i);
}
// Need to deal damages !
Thrown[i].flags &= ~ATO_MOVING;
Thrown[i].velocity = 0.f;
need_kill = 1;
precision = 500.f;
j = 200;
}
}
}
}
}
if (need_kill) ARX_THROWN_OBJECT_Kill(i);
}
}
}
}
// RUBAN
void CRuban::Create(int _iNumThrow, int _iDuration)
{
iNumThrow = _iNumThrow;
key = 1;
duration = _iDuration;
currduration = 0;
nbrubandef = 0;
int nb = 2048;
while (nb--)
{
truban[nb].actif = 0;
}
float col = 0.1f + (rnd() * 0.1f);
float size = 2.f + (2.f * rnd());
int taille = Random::get(8, 16);
AddRubanDef(0, size, taille, col, col, col, 0.f, 0.f, 0.f);
}
void CRuban::AddRubanDef(int origin, float size, int dec, float r, float g, float b,
float r2, float g2, float b2) {
if (nbrubandef > 255) return;
trubandef[nbrubandef].first = -1;
trubandef[nbrubandef].origin = origin;
trubandef[nbrubandef].size = size;
trubandef[nbrubandef].dec = dec;
trubandef[nbrubandef].r = r;
trubandef[nbrubandef].g = g;
trubandef[nbrubandef].b = b;
trubandef[nbrubandef].r2 = r2;
trubandef[nbrubandef].g2 = g2;
trubandef[nbrubandef].b2 = b2;
nbrubandef++;
}
int CRuban::GetFreeRuban()
{
int nb = 2048;
while (nb--)
{
if (!truban[nb].actif) return nb;
}
return -1;
}
void CRuban::AddRuban(int * f, int dec) {
int num = GetFreeRuban();
if (num >= 0)
{
truban[num].actif = 1;
truban[num].pos = Thrown[iNumThrow].position;
if (*f < 0)
{
*f = num;
truban[num].next = -1;
}
else
{
truban[num].next = *f;
*f = num;
}
int nb = 0, oldnum = 0;
while (num != -1)
{
nb++;
oldnum = num;
num = truban[num].next;
}
if (nb > dec)
{
truban[oldnum].actif = 0;
num = *f;
nb -= 2;
while (nb--)
{
num = truban[num].next;
}
truban[num].next = -1;
}
}
}
void CRuban::Update() {
int nb, num;
if (arxtime.is_paused()) return;
num = 0;
nb = nbrubandef;
while (nb--)
{
AddRuban(&trubandef[num].first, trubandef[num].dec);
num++;
}
}
void CRuban::DrawRuban(int num, float size, int dec, float r, float g, float b,
float r2, float g2, float b2) {
int numsuiv;
float dsize = size / (float)(dec + 1);
int r1 = ((int)(r * 255.f)) << 16;
int g1 = ((int)(g * 255.f)) << 16;
int b1 = ((int)(b * 255.f)) << 16;
int rr2 = ((int)(r2 * 255.f)) << 16;
int gg2 = ((int)(g2 * 255.f)) << 16;
int bb2 = ((int)(b2 * 255.f)) << 16;
int dr = (rr2 - r1) / dec;
int dg = (gg2 - g1) / dec;
int db = (bb2 - b1) / dec;
for (;;)
{
numsuiv = truban[num].next;
if ((num >= 0) && (numsuiv >= 0))
{
Draw3DLineTex2(truban[num].pos, truban[numsuiv].pos, size,
Color(r1 >> 16, g1 >> 16, b1 >> 16, 0),
Color((r1 + dr) >> 16, (g1 + dg) >> 16, (b1 + db) >> 16, 0));
r1 += dr;
g1 += dg;
b1 += db;
size -= dsize;
}
else
{
break;
}
num = numsuiv;
}
}
float CRuban::Render()
{
GRenderer->SetCulling(Renderer::CullNone);
GRenderer->SetRenderState(Renderer::AlphaBlending, true);
GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
GRenderer->ResetTexture(0);
for (int i = 0; i < nbrubandef; i++)
{
this->DrawRuban(trubandef[i].first,
trubandef[i].size,
trubandef[i].dec,
trubandef[i].r, trubandef[i].g, trubandef[i].b,
trubandef[i].r2, trubandef[i].g2, trubandef[i].b2);
}
GRenderer->SetRenderState(Renderer::AlphaBlending, false);
GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendZero);
return 0;
}
extern bool IsValidPos3(Vec3f * pos);
static EERIEPOLY * LAST_COLLISION_POLY = NULL;
extern long CUR_COLLISION_MATERIAL;
float VELOCITY_THRESHOLD = 850.f;
void ARX_ApplySpring(PHYSVERT * phys, long k, long l, float PHYSICS_constant,
float PHYSICS_Damp) {
Vec3f deltaP, deltaV, springforce;
PHYSVERT * pv_k = &phys[k];
PHYSVERT * pv_l = &phys[l];
float Dterm, Hterm;
float restlength = dist(pv_k->initpos, pv_l->initpos);
// Computes Spring Magnitude
deltaP = pv_k->pos - pv_l->pos;
float dist = deltaP.length(); // Magnitude of delta
dist = std::max(dist, 0.000001f); //TODO workaround for division by zero
float divdist = 1.f / dist;
Hterm = (dist - restlength) * PHYSICS_constant;
deltaV = pv_k->velocity - pv_l->velocity; // Delta Velocity Vector
Dterm = dot(deltaV, deltaP) * PHYSICS_Damp * divdist; // Damping Term
Dterm = (-(Hterm + Dterm));
divdist *= Dterm;
springforce = deltaP * divdist; // Normalize Distance Vector & Calc Force
pv_k->force += springforce; // + force on particle 1
pv_l->force -= springforce; // - force on particle 2
}
void ComputeForces(PHYSVERT * phys, long nb) {
const Vec3f PHYSICS_Gravity(0.f, 65.f, 0.f);
const float PHYSICS_Damping = 0.5f;
float lastmass = 1.f;
float div = 1.f;
for(long k = 0; k < nb; k++) {
PHYSVERT * pv = &phys[k];
// Reset Force
pv->force = pv->inertia;
// Apply Gravity
if(pv->mass > 0.f) {
// need to be precomputed...
if(lastmass != pv->mass) {
div = 1.f / pv->mass;
lastmass = pv->mass;
}
pv->force += (PHYSICS_Gravity * div);
}
// Apply Damping
pv->force += pv->velocity * -PHYSICS_Damping;
}
for(int k = 0; k < nb; k++) {
// Now Resolves Spring System
for(long l = 0; l < nb; l++) {
if(l != k) {
ARX_ApplySpring(phys, l, k, 15.f, 0.99f);
}
}
}
}
bool ARX_INTERACTIVE_CheckFULLCollision(EERIE_3DOBJ * obj, long source);
//! Calculate new Positions and Velocities given a deltatime
//! @param DeltaTime that has passed since last iteration
void RK4Integrate(EERIE_3DOBJ * obj, float DeltaTime) {
PHYSVERT * source, * target, * accum1, * accum2, * accum3, * accum4;
float halfDeltaT, sixthDeltaT;
halfDeltaT = DeltaTime * .5f; // some time values i will need
sixthDeltaT = ( 1.0f / 6 );
PHYSVERT m_TempSys[5][32];
for(long jj = 0; jj < 4; jj++) {
arx_assert(size_t(obj->pbox->nb_physvert) <= ARRAY_SIZE(m_TempSys[jj + 1]));
memcpy(m_TempSys[jj + 1], obj->pbox->vert, sizeof(PHYSVERT) * obj->pbox->nb_physvert);
if(jj == 3) {
halfDeltaT = DeltaTime;
}
for(long kk = 0; kk < obj->pbox->nb_physvert; kk++) {
source = &obj->pbox->vert[kk];
accum1 = &m_TempSys[jj + 1][kk];
target = &m_TempSys[0][kk];
accum1->force = source->force * (source->mass * halfDeltaT);
accum1->velocity = source->velocity * halfDeltaT;
// determine the new velocity for the particle over 1/2 time
target->velocity = source->velocity + accum1->force;
target->mass = source->mass;
// set the new position
target->pos = source->pos + accum1->velocity;
}
ComputeForces(m_TempSys[0], obj->pbox->nb_physvert); // compute the new forces
}
for(long kk = 0; kk < obj->pbox->nb_physvert; kk++) {
source = &obj->pbox->vert[kk]; // current state of particle
target = &obj->pbox->vert[kk];
accum1 = &m_TempSys[1][kk];
accum2 = &m_TempSys[2][kk];
accum3 = &m_TempSys[3][kk];
accum4 = &m_TempSys[4][kk];
// determine the new velocity for the particle using rk4 formula
Vec3f dv = accum1->force + ((accum2->force + accum3->force) * 2.f) + accum4->force;
target->velocity = source->velocity + (dv * sixthDeltaT);
// determine the new position for the particle using rk4 formula
Vec3f dp = accum1->velocity + ((accum2->velocity + accum3->velocity) * 2.f)
+ accum4->velocity;
target->pos = source->pos + (dp * sixthDeltaT * 1.2f);
}
}
static bool IsPointInField(Vec3f * pos) {
for(size_t i = 0; i < MAX_SPELLS; i++) {
if(spells[i].exist && spells[i].type == SPELL_CREATE_FIELD) {
if(ValidIONum(spells[i].longinfo)) {
Entity * pfrm = entities[spells[i].longinfo];
EERIE_CYLINDER cyl;
cyl.height = -35.f;
cyl.radius = 35.f;
cyl.origin = *pos + Vec3f(0.f, 17.5f, 0.f);
if(CylinderPlatformCollide(&cyl, pfrm) != 0.f) {
return true;
}
}
}
}
return false;
}
static bool IsObjectInField(EERIE_3DOBJ * obj) {
for(size_t i = 0; i < MAX_SPELLS; i++) {
if(spells[i].exist && spells[i].type == SPELL_CREATE_FIELD) {
if(ValidIONum(spells[i].longinfo)) {
Entity * pfrm = entities[spells[i].longinfo];
EERIE_CYLINDER cyl;
cyl.height = -35.f;
cyl.radius = 35.f;
for(long k = 0; k < obj->pbox->nb_physvert; k++) {
PHYSVERT * pv = &obj->pbox->vert[k];
cyl.origin = pv->pos + Vec3f(0.f, 17.5f, 0.f);
if(CylinderPlatformCollide(&cyl, pfrm) != 0.f) {
return true;
}
}
}
}
}
return false;
}
static bool IsObjectVertexCollidingPoly(EERIE_3DOBJ * obj, EERIEPOLY * ep,
long k, long * validd) {
Vec3f pol[3];
pol[0] = ep->v[0].p;
pol[1] = ep->v[1].p;
pol[2] = ep->v[2].p;
if(ep->type & POLY_QUAD) {
if(IsObjectVertexCollidingTriangle(obj, pol, k, validd)) {
return true;
}
pol[1] = ep->v[2].p;
pol[2] = ep->v[3].p;
if(IsObjectVertexCollidingTriangle(obj, pol, k, validd)) {
return true;
}
return false;
}
if(IsObjectVertexCollidingTriangle(obj, pol, k, validd)) {
return true;
}
return false;
}
static bool IsFULLObjectVertexInValidPosition(EERIE_3DOBJ * obj) {
bool ret = true;
long px, pz;
float x = obj->pbox->vert[0].pos.x;
px = x * ACTIVEBKG->Xmul;
float z = obj->pbox->vert[0].pos.z;
pz = z * ACTIVEBKG->Zmul;
long ix, iz, ax, az;
long n;
n = obj->pbox->radius * ( 1.0f / 100 );
n = min(1L, n + 1);
ix = max(px - n, 0L);
ax = min(px + n, ACTIVEBKG->Xsize - 1L);
iz = max(pz - n, 0L);
az = min(pz + n, ACTIVEBKG->Zsize - 1L);
LAST_COLLISION_POLY = NULL;
EERIEPOLY * ep;
EERIE_BKG_INFO * eg;
float rad = obj->pbox->radius;
for (pz = iz; pz <= az; pz++)
for (px = ix; px <= ax; px++)
{
eg = &ACTIVEBKG->Backg[px+pz*ACTIVEBKG->Xsize];
for (long k = 0; k < eg->nbpoly; k++)
{
ep = &eg->polydata[k];
if ( (ep->area > 190.f)
&& (!(ep->type & (POLY_WATER)))
&& (!(ep->type & (POLY_TRANS)))
&& (!(ep->type & (POLY_NOCOL)))
)
{
if (fartherThan(ep->center, obj->pbox->vert[0].pos, rad + 75.f))
continue;
for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
{
float radd = 4.f;
if(!fartherThan(obj->pbox->vert[kk].pos, ep->center, radd)
|| !fartherThan(obj->pbox->vert[kk].pos, ep->v[0].p, radd)
|| !fartherThan(obj->pbox->vert[kk].pos, ep->v[1].p, radd)
|| !fartherThan(obj->pbox->vert[kk].pos, ep->v[2].p, radd)
|| !fartherThan(obj->pbox->vert[kk].pos, (ep->v[0].p + ep->v[1].p) * .5f, radd)
|| !fartherThan(obj->pbox->vert[kk].pos, (ep->v[2].p + ep->v[1].p) * .5f, radd)
|| !fartherThan(obj->pbox->vert[kk].pos, (ep->v[0].p + ep->v[2].p) * .5f, radd)) {
LAST_COLLISION_POLY = ep;
if (ep->type & POLY_METAL) CUR_COLLISION_MATERIAL = MATERIAL_METAL;
else if (ep->type & POLY_WOOD) CUR_COLLISION_MATERIAL = MATERIAL_WOOD;
else if (ep->type & POLY_STONE) CUR_COLLISION_MATERIAL = MATERIAL_STONE;
else if (ep->type & POLY_GRAVEL) CUR_COLLISION_MATERIAL = MATERIAL_GRAVEL;
else if (ep->type & POLY_WATER) CUR_COLLISION_MATERIAL = MATERIAL_WATER;
else if (ep->type & POLY_EARTH) CUR_COLLISION_MATERIAL = MATERIAL_EARTH;
else CUR_COLLISION_MATERIAL = MATERIAL_STONE;
return false;
}
// Last addon
for (long kl = 1; kl < obj->pbox->nb_physvert; kl++)
{
if (kl != kk)
{
Vec3f pos = (obj->pbox->vert[kk].pos + obj->pbox->vert[kl].pos) * .5f;
if(!fartherThan(pos, ep->center, radd)
|| !fartherThan(pos, ep->v[0].p, radd)
|| !fartherThan(pos, ep->v[1].p, radd)
|| !fartherThan(pos, ep->v[2].p, radd)
|| !fartherThan(pos, (ep->v[0].p + ep->v[1].p) * .5f, radd)
|| !fartherThan(pos, (ep->v[2].p + ep->v[1].p) * .5f, radd)
|| !fartherThan(pos, (ep->v[0].p + ep->v[2].p) * .5f, radd)) {
LAST_COLLISION_POLY = ep;
if (ep->type & POLY_METAL) CUR_COLLISION_MATERIAL = MATERIAL_METAL;
else if (ep->type & POLY_WOOD) CUR_COLLISION_MATERIAL = MATERIAL_WOOD;
else if (ep->type & POLY_STONE) CUR_COLLISION_MATERIAL = MATERIAL_STONE;
else if (ep->type & POLY_GRAVEL) CUR_COLLISION_MATERIAL = MATERIAL_GRAVEL;
else if (ep->type & POLY_WATER) CUR_COLLISION_MATERIAL = MATERIAL_WATER;
else if (ep->type & POLY_EARTH) CUR_COLLISION_MATERIAL = MATERIAL_EARTH;
else CUR_COLLISION_MATERIAL = MATERIAL_STONE;
return false;
}
}
}
}
if (IsObjectVertexCollidingPoly(obj, ep, -1, NULL))
{
LAST_COLLISION_POLY = ep;
if (ep->type & POLY_METAL) CUR_COLLISION_MATERIAL = MATERIAL_METAL;
else if (ep->type & POLY_WOOD) CUR_COLLISION_MATERIAL = MATERIAL_WOOD;
else if (ep->type & POLY_STONE) CUR_COLLISION_MATERIAL = MATERIAL_STONE;
else if (ep->type & POLY_GRAVEL) CUR_COLLISION_MATERIAL = MATERIAL_GRAVEL;
else if (ep->type & POLY_WATER) CUR_COLLISION_MATERIAL = MATERIAL_WATER;
else if (ep->type & POLY_EARTH) CUR_COLLISION_MATERIAL = MATERIAL_EARTH;
else CUR_COLLISION_MATERIAL = MATERIAL_STONE;
return false;
}
}
}
}
return ret;
}
static bool ARX_EERIE_PHYSICS_BOX_Compute(EERIE_3DOBJ * obj, float framediff, long source) {
PHYSVERT * pv;
Vec3f oldpos[32];
long COUNT = 0;
COUNT++;
for (long kk = 0; kk < obj->pbox->nb_physvert; kk++)
{
pv = &obj->pbox->vert[kk];
oldpos[kk] = pv->pos;
pv->inertia = Vec3f::ZERO;
if (pv->velocity.x > VELOCITY_THRESHOLD) pv->velocity.x = VELOCITY_THRESHOLD;
else if (pv->velocity.x < -VELOCITY_THRESHOLD) pv->velocity.x = -VELOCITY_THRESHOLD;
if (pv->velocity.y > VELOCITY_THRESHOLD) pv->velocity.y = VELOCITY_THRESHOLD;
else if (pv->velocity.y < -VELOCITY_THRESHOLD) pv->velocity.y = -VELOCITY_THRESHOLD;
if (pv->velocity.z > VELOCITY_THRESHOLD) pv->velocity.z = VELOCITY_THRESHOLD;
else if (pv->velocity.z < -VELOCITY_THRESHOLD) pv->velocity.z = -VELOCITY_THRESHOLD;
}
CUR_COLLISION_MATERIAL = MATERIAL_STONE;
RK4Integrate(obj, framediff);
EERIE_SPHERE sphere;
pv = &obj->pbox->vert[0];
sphere.origin = pv->pos;
sphere.radius = obj->pbox->radius;
long colidd = 0;
for (int kk = 0; kk < obj->pbox->nb_physvert; kk += 2)
{
pv = &obj->pbox->vert[kk];
if (!IsValidPos3(&pv->pos))
{
colidd = 1;
break;
}
}
if ((!IsFULLObjectVertexInValidPosition(obj))
|| ARX_INTERACTIVE_CheckFULLCollision(obj, source)
|| colidd
|| (IsObjectInField(obj))
)
{
colidd = 1;
float power = (EEfabs(obj->pbox->vert[0].velocity.x)
+ EEfabs(obj->pbox->vert[0].velocity.y)
+ EEfabs(obj->pbox->vert[0].velocity.z)) * .01f;
if (ValidIONum(source) && (entities[source]->ioflags & IO_BODY_CHUNK))
{
}
else
ARX_TEMPORARY_TrySound(0.4f + power);
if(!LAST_COLLISION_POLY) {
for(long k = 0; k < obj->pbox->nb_physvert; k++) {
pv = &obj->pbox->vert[k];
{
pv->velocity.x *= -0.3f;
pv->velocity.z *= -0.3f;
pv->velocity.y *= -0.4f;
}
pv->pos = oldpos[k];
}
} else {
for(long k = 0; k < obj->pbox->nb_physvert; k++) {
pv = &obj->pbox->vert[k];
float t = dot(LAST_COLLISION_POLY->norm, pv->velocity);
pv->velocity -= LAST_COLLISION_POLY->norm * (2.f * t);
pv->velocity.x *= 0.3f;
pv->velocity.z *= 0.3f;
pv->velocity.y *= 0.4f;
pv->pos = oldpos[k];
}
}
}
if (colidd)
{
obj->pbox->stopcount += 1;
}
else
{
obj->pbox->stopcount -= 2;
if (obj->pbox->stopcount < 0)
obj->pbox->stopcount = 0;
}
return true;
}
long ARX_PHYSICS_BOX_ApplyModel(EERIE_3DOBJ * obj, float framediff, float rubber, long source) {
VELOCITY_THRESHOLD = 400.f;
long ret = 0;
if ((!obj) || (!obj->pbox)) return ret;
if (obj->pbox->active == 2) return ret;
if (framediff == 0.f) return ret;
PHYSVERT * pv;
// Memorizes initpos
for(long k = 0; k < obj->pbox->nb_physvert; k++) {
pv = &obj->pbox->vert[k];
pv->temp = pv->pos;
}
float timing = obj->pbox->storedtiming + framediff * rubber * 0.0055f;
float t_threshold = 0.18f;
if (timing < t_threshold)
{
obj->pbox->storedtiming = timing;
return 1;
}
else
{
while(timing >= t_threshold) {
ComputeForces(obj->pbox->vert, obj->pbox->nb_physvert);
if (!ARX_EERIE_PHYSICS_BOX_Compute(obj, std::min(0.11f, timing * 10), source))
ret = 1;
timing -= t_threshold;
}
obj->pbox->storedtiming = timing;
}
if (obj->pbox->stopcount < 16) return ret;
obj->pbox->active = 2;
obj->pbox->stopcount = 0;
if (ValidIONum(source))
{
entities[source]->soundcount = 0;
entities[source]->soundtime = (unsigned long)(arxtime) + 2000;
}
return ret;
}
void ARX_PrepareBackgroundNRMLs()
{
long i, j, k, mai, maj, mii, mij;
long i2, j2, k2;
EERIE_BKG_INFO * eg;
EERIE_BKG_INFO * eg2;
EERIEPOLY * ep;
EERIEPOLY * ep2;
Vec3f nrml;
Vec3f cur_nrml;
float count;
long nbvert;
long nbvert2;
for (j = 0; j < ACTIVEBKG->Zsize; j++)
for (i = 0; i < ACTIVEBKG->Xsize; i++)
{
eg = &ACTIVEBKG->Backg[i+j*ACTIVEBKG->Xsize];
for (long l = 0; l < eg->nbpoly; l++)
{
ep = &eg->polydata[l];
if (ep->type & POLY_QUAD) nbvert = 4;
else nbvert = 3;
for (k = 0; k < nbvert; k++)
{
float ttt = 1.f;
if(k == 3) {
nrml = ep->norm2;
count = 1.f;
} else if(k > 0 && nbvert > 3) {
nrml = (ep->norm + ep->norm2);
count = 2.f;
ttt = .5f;
} else {
nrml = ep->norm;
count = 1.f;
}
cur_nrml = nrml * ttt;
mai = i + 4;
maj = j + 4;
mii = i - 4;
mij = j - 4;
if (mij < 0) mij = 0;
if (mii < 0) mii = 0;
if (maj >= ACTIVEBKG->Zsize) maj = ACTIVEBKG->Zsize - 1;
if (mai >= ACTIVEBKG->Xsize) mai = ACTIVEBKG->Xsize - 1;
for (j2 = mij; j2 < maj; j2++)
for (i2 = mii; i2 < mai; i2++)
{
eg2 = &ACTIVEBKG->Backg[i2+j2*ACTIVEBKG->Xsize];
for (long kr = 0; kr < eg2->nbpoly; kr++)
{
ep2 = &eg2->polydata[kr];
if (ep2->type & POLY_QUAD) nbvert2 = 4;
else nbvert2 = 3;
if (ep != ep2)
for (k2 = 0; k2 < nbvert2; k2++)
{
if ((EEfabs(ep2->v[k2].p.x - ep->v[k].p.x) < 2.f)
&& (EEfabs(ep2->v[k2].p.y - ep->v[k].p.y) < 2.f)
&& (EEfabs(ep2->v[k2].p.z - ep->v[k].p.z) < 2.f))
{
if(k2 == 3) {
if(LittleAngularDiff(&cur_nrml, &ep2->norm2)) {
nrml += ep2->norm2;
count += 1.f;
nrml += cur_nrml;
count += 1.f;
}
} else if(k2 > 0 && nbvert2 > 3) {
Vec3f tnrml = (ep2->norm + ep2->norm2) * .5f;
if(LittleAngularDiff(&cur_nrml, &tnrml)) {
nrml += tnrml * 2.f;
count += 2.f;
}
} else {
if(LittleAngularDiff(&cur_nrml, &ep2->norm)) {
nrml += ep2->norm;
count += 1.f;
}
}
}
}
}
}
count = 1.f / count;
ep->tv[k].p = nrml * count;
}
}
}
for (j = 0; j < ACTIVEBKG->Zsize; j++)
for (i = 0; i < ACTIVEBKG->Xsize; i++)
{
eg = &ACTIVEBKG->Backg[i+j*ACTIVEBKG->Xsize];
for (long l = 0; l < eg->nbpoly; l++)
{
ep = &eg->polydata[l];
if (ep->type & POLY_QUAD) nbvert = 4;
else nbvert = 3;
for(k = 0; k < nbvert; k++) {
ep->nrml[k] = ep->tv[k].p;
}
float d = 0.f;
for(long ii = 0; ii < nbvert; ii++) {
d = max(d, dist(ep->center, ep->v[ii].p));
}
ep->v[0].rhw = d;
}
}
}
void EERIE_PHYSICS_BOX_Launch_NOCOL(Entity * io, EERIE_3DOBJ * obj, Vec3f * pos,
Vec3f * vect, long flags, Anglef * angle) {
io->gameFlags |= GFLAG_NO_PHYS_IO_COL;
EERIE_PHYSICS_BOX_Launch(obj, pos, vect, flags, angle);
}