mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-18 22:00:21 +02:00
680 lines
16 KiB
C++
680 lines
16 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA 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 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA 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 OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// gravpath.cpp: Gravity path - Used for underwater currents and wells.
|
|
|
|
#include "g_local.h"
|
|
#include "entity.h"
|
|
#include "gravpath.h"
|
|
#include "../corepp/container.h"
|
|
#include "navigate.h"
|
|
#include "misc.h"
|
|
#include "player.h"
|
|
#include "g_phys.h"
|
|
#include "debuglines.h"
|
|
|
|
GravPathManager gravPathManager;
|
|
|
|
CLASS_DECLARATION(Class, GravPathManager, NULL) {
|
|
{NULL, NULL}
|
|
};
|
|
|
|
GravPathManager::~GravPathManager()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void GravPathManager::Reset(void)
|
|
{
|
|
while (pathList.NumObjects() > 0) {
|
|
delete (GravPath *)pathList.ObjectAt(1);
|
|
}
|
|
|
|
pathList.FreeObjectList();
|
|
}
|
|
|
|
void GravPathManager::AddPath(GravPath *p)
|
|
{
|
|
pathList.AddObject(p);
|
|
}
|
|
|
|
void GravPathManager::RemovePath(GravPath *p)
|
|
{
|
|
pathList.RemoveObject(p);
|
|
}
|
|
|
|
void GravPathManager::DrawGravPaths(void)
|
|
{
|
|
int i;
|
|
int num = pathList.NumObjects();
|
|
|
|
for (i = 1; i <= num; i++) {
|
|
GravPath *p = (GravPath *)pathList.ObjectAt(i);
|
|
|
|
p->DrawPath(1, 0, 0);
|
|
}
|
|
}
|
|
|
|
Vector GravPathManager::CalculateGravityPull(Entity& ent, Vector pos, qboolean *force, float *max_speed)
|
|
{
|
|
int i, num;
|
|
GravPath *p;
|
|
GravPathNode *node;
|
|
Vector point;
|
|
Vector newpoint;
|
|
Vector dir;
|
|
float bestdist = 99999;
|
|
float dist;
|
|
float speed;
|
|
float radius;
|
|
Vector velocity;
|
|
int bestpath = 0;
|
|
int entity_contents, grav_contents;
|
|
|
|
num = pathList.NumObjects();
|
|
if (!num) {
|
|
return vec_zero;
|
|
}
|
|
|
|
entity_contents = gi.pointcontents(ent.origin, 0);
|
|
|
|
for (i = 1; i <= num; i++) {
|
|
p = (GravPath *)pathList.ObjectAt(i);
|
|
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
|
|
// Check to see if path is active
|
|
node = p->GetNode(1);
|
|
if (!node || !node->active) {
|
|
continue;
|
|
}
|
|
|
|
// Check to see if the contents are the same
|
|
grav_contents = gi.pointcontents(node->origin, 0);
|
|
|
|
// If grav node is in water, make sure ent is too.
|
|
if ((grav_contents & CONTENTS_WATER) && !(entity_contents & CONTENTS_WATER)) {
|
|
continue;
|
|
}
|
|
|
|
// Test to see if we are in this path's bounding box
|
|
if ((pos.x < p->maxs.x) && (pos.y < p->maxs.y) && (pos.z < p->maxs.z) && (pos.x > p->mins.x)
|
|
&& (pos.y > p->mins.y) && (pos.z > p->mins.z)) {
|
|
point = p->ClosestPointOnPath(pos, ent, &dist, &speed, &radius);
|
|
|
|
// If the closest distance on the path is greater than the radius, then
|
|
// do not consider this path.
|
|
|
|
if (dist > radius) {
|
|
continue;
|
|
} else if (dist < bestdist) {
|
|
bestpath = i;
|
|
bestdist = dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bestpath) {
|
|
return vec_zero;
|
|
}
|
|
|
|
p = (GravPath *)pathList.ObjectAt(bestpath);
|
|
if (!p) {
|
|
return velocity;
|
|
}
|
|
*force = p->force;
|
|
dist = p->DistanceAlongPath(pos, &speed);
|
|
newpoint = p->PointAtDistance(pos, dist + speed, ent.isSubclassOf(Player), max_speed);
|
|
dir = newpoint - pos;
|
|
dir.normalize();
|
|
velocity = dir * speed;
|
|
|
|
//velocity *= .75;
|
|
return velocity;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*QUAKED info_grav_pathnode (0 0 .5) (-16 -16 0) (16 16 32) HEADNODE FORCE PULL_UPWARDS
|
|
"radius" Radius of the effect of the pull (Default is 256)
|
|
"speed" Speed of the pull (Use negative for a repulsion) (Default is 100)
|
|
|
|
Set HEADNODE to signify the head of the path.
|
|
Set FORCE if you want un-fightable gravity ( i.e. can't go backwards )
|
|
Set PULL_UPWARDS if you want the gravnodes to pull you upwards also
|
|
******************************************************************************/
|
|
|
|
#define PULL_UPWARDS (1 << 2)
|
|
|
|
Event EV_GravPath_Create
|
|
(
|
|
"gravpath_create",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Create the grav path.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GravPath_Activate
|
|
(
|
|
"activate",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Activate the grav path.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GravPath_Deactivate
|
|
(
|
|
"deactivate",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Deactivate the grav path.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GravPath_SetSpeed
|
|
(
|
|
"speed",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"speed",
|
|
"Set the speed of the grav path.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GravPath_SetMaxSpeed
|
|
(
|
|
"maxspeed",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"maxspeed",
|
|
"Set the max speed of the grav path.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GravPath_SetRadius
|
|
(
|
|
"radius",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"radius",
|
|
"Set the radius of the grav path.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
CLASS_DECLARATION(Entity, GravPathNode, "info_grav_pathnode") {
|
|
{&EV_GravPath_Create, &GravPathNode::CreatePath },
|
|
{&EV_GravPath_Activate, &GravPathNode::Activate },
|
|
{&EV_GravPath_Deactivate, &GravPathNode::Deactivate },
|
|
{&EV_GravPath_SetSpeed, &GravPathNode::SetSpeed },
|
|
{&EV_GravPath_SetMaxSpeed, &GravPathNode::SetMaxSpeed},
|
|
{&EV_GravPath_SetRadius, &GravPathNode::SetRadius },
|
|
{NULL, NULL }
|
|
};
|
|
|
|
GravPathNode::GravPathNode()
|
|
{
|
|
if (LoadingSavegame) {
|
|
// all data will be setup by the archive function
|
|
return;
|
|
}
|
|
|
|
setMoveType(MOVETYPE_NONE);
|
|
setSolidType(SOLID_NOT);
|
|
hideModel();
|
|
|
|
speed = 100.0f;
|
|
max_speed = 200.0f;
|
|
radius = 256.0f;
|
|
headnode = spawnflags & 1;
|
|
active = true;
|
|
|
|
// This is the head of a new path, post an event to create the path
|
|
if (headnode) {
|
|
PostEvent(EV_GravPath_Create, 0);
|
|
}
|
|
}
|
|
|
|
void GravPathNode::SetSpeed(Event *ev)
|
|
{
|
|
speed = ev->GetFloat(1);
|
|
}
|
|
|
|
void GravPathNode::SetMaxSpeed(Event *ev)
|
|
{
|
|
max_speed = ev->GetFloat(1);
|
|
}
|
|
|
|
void GravPathNode::SetRadius(Event *ev)
|
|
{
|
|
radius = ev->GetFloat(1);
|
|
}
|
|
|
|
float GravPathNode::Speed(void)
|
|
{
|
|
if (active) {
|
|
return speed;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
float GravPathNode::MaxSpeed(void)
|
|
{
|
|
return max_speed;
|
|
}
|
|
|
|
void GravPathNode::Activate(Event *ev)
|
|
{
|
|
GravPathNode *node;
|
|
Entity *ent;
|
|
const char *target;
|
|
|
|
active = true;
|
|
node = this;
|
|
// Go through the entire path and activate it
|
|
target = node->Target();
|
|
while (target[0]) {
|
|
ent = (Entity *)G_FindTarget(NULL, target);
|
|
if (ent) {
|
|
node = (GravPathNode *)ent;
|
|
assert(node);
|
|
node->active = true;
|
|
} else {
|
|
gi.Error(ERR_DROP, "GravPathNode::CreatePath: target %s not found\n", target);
|
|
}
|
|
target = node->Target();
|
|
}
|
|
}
|
|
|
|
void GravPathNode::Deactivate(Event *ev)
|
|
{
|
|
GravPathNode *node;
|
|
Entity *ent;
|
|
const char *target;
|
|
|
|
active = false;
|
|
node = this;
|
|
// Go through the entire path and activate it
|
|
target = node->Target();
|
|
while (target[0]) {
|
|
ent = (Entity *)G_FindTarget(NULL, target);
|
|
if (ent) {
|
|
node = (GravPathNode *)ent;
|
|
assert(node);
|
|
node->active = false;
|
|
} else {
|
|
gi.Error(ERR_DROP, "GravPathNode::CreatePath: target %s not found\n", target);
|
|
}
|
|
target = node->Target();
|
|
}
|
|
}
|
|
|
|
void GravPathNode::CreatePath(Event *ev)
|
|
{
|
|
const char *target;
|
|
GravPath *path = new GravPath;
|
|
GravPathNode *node;
|
|
Entity *ent;
|
|
|
|
ClearBounds(path->mins, path->maxs);
|
|
|
|
// This node is the head of a path, create a new path in the path manager.
|
|
// and add it in, then add all of it's children in the path.
|
|
node = this;
|
|
path->AddNode(node);
|
|
path->force = spawnflags & 2;
|
|
|
|
// Make the path from the targetlist.
|
|
target = node->Target();
|
|
while (target[0]) {
|
|
ent = (Entity *)G_FindTarget(NULL, target);
|
|
if (ent) {
|
|
node = (GravPathNode *)ent;
|
|
assert(node);
|
|
path->AddNode(node);
|
|
} else {
|
|
gi.Error(ERR_DROP, "GravPathNode::CreatePath: target %s not found\n", target);
|
|
}
|
|
target = node->Target();
|
|
}
|
|
|
|
// Set the origin.
|
|
path->origin = path->mins + path->maxs;
|
|
path->origin *= 0.5f;
|
|
}
|
|
|
|
CLASS_DECLARATION(Listener, GravPath, NULL) {
|
|
{NULL, NULL}
|
|
};
|
|
|
|
GravPath::GravPath()
|
|
{
|
|
pathlength = 0;
|
|
from = NULL;
|
|
to = NULL;
|
|
nextnode = 1;
|
|
|
|
if (!LoadingSavegame) {
|
|
gravPathManager.AddPath(this);
|
|
}
|
|
}
|
|
|
|
GravPath::~GravPath()
|
|
{
|
|
pathlength = 0;
|
|
from = NULL;
|
|
to = NULL;
|
|
nextnode = 1;
|
|
gravPathManager.RemovePath(this);
|
|
}
|
|
|
|
void GravPath::Clear(void)
|
|
{
|
|
nextnode = 1;
|
|
pathlength = 0;
|
|
from = NULL;
|
|
to = NULL;
|
|
pathlist.FreeObjectList();
|
|
}
|
|
|
|
void GravPath::Reset(void)
|
|
{
|
|
nextnode = 1;
|
|
}
|
|
|
|
GravPathNode *GravPath::Start(void)
|
|
{
|
|
return from;
|
|
}
|
|
|
|
GravPathNode *GravPath::End(void)
|
|
{
|
|
return to;
|
|
}
|
|
|
|
void GravPath::AddNode(GravPathNode *node)
|
|
{
|
|
int num;
|
|
Vector r, addp;
|
|
|
|
if (!from) {
|
|
from = node;
|
|
}
|
|
|
|
to = node;
|
|
pathlist.AddObject(GravPathNodePtr(node));
|
|
|
|
num = NumNodes();
|
|
if (num > 1) {
|
|
pathlength += (node->origin - GetNode(num)->origin).length();
|
|
}
|
|
|
|
r.setXYZ(node->Radius(), node->Radius(), node->Radius());
|
|
addp = node->origin + r;
|
|
AddPointToBounds(addp, mins, maxs);
|
|
addp = node->origin - r;
|
|
AddPointToBounds(addp, mins, maxs);
|
|
}
|
|
|
|
GravPathNode *GravPath::GetNode(int num)
|
|
{
|
|
return pathlist.ObjectAt(num);
|
|
}
|
|
|
|
GravPathNode *GravPath::NextNode(void)
|
|
{
|
|
if (nextnode <= NumNodes()) {
|
|
return pathlist.ObjectAt(nextnode++);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Vector GravPath::ClosestPointOnPath(Vector pos, Entity& ent, float *ret_dist, float *speed, float *radius)
|
|
{
|
|
GravPathNode *s;
|
|
GravPathNode *e;
|
|
int num;
|
|
int i;
|
|
float bestdist;
|
|
Vector bestpoint;
|
|
float dist;
|
|
float segmentlength;
|
|
Vector delta;
|
|
Vector p1;
|
|
Vector p2;
|
|
Vector p3;
|
|
float t;
|
|
trace_t trace;
|
|
|
|
num = NumNodes();
|
|
s = GetNode(1);
|
|
trace =
|
|
G_Trace(pos, ent.mins, ent.maxs, s->origin, &ent, MASK_PLAYERSOLID, false, "GravPath::ClosestPointOnPath 1");
|
|
bestpoint = s->origin;
|
|
delta = bestpoint - pos;
|
|
bestdist = delta.length();
|
|
*speed = s->Speed();
|
|
*radius = s->Radius();
|
|
|
|
for (i = 2; i <= num; i++) {
|
|
e = GetNode(i);
|
|
|
|
// check if we're closest to the endpoint
|
|
delta = e->origin - pos;
|
|
dist = delta.length();
|
|
|
|
if (dist < bestdist) {
|
|
trace = G_Trace(
|
|
pos, ent.mins, ent.maxs, e->origin, &ent, MASK_PLAYERSOLID, false, "GravPath::ClosestPointOnPath 2"
|
|
);
|
|
bestdist = dist;
|
|
bestpoint = e->origin;
|
|
*speed = e->Speed();
|
|
*radius = e->Radius();
|
|
}
|
|
|
|
// check if we're closest to the segment
|
|
p1 = e->origin - s->origin;
|
|
segmentlength = p1.length();
|
|
p1 *= 1 / segmentlength;
|
|
p2 = pos - s->origin;
|
|
|
|
t = p1 * p2;
|
|
if ((t > 0) && (t < segmentlength)) {
|
|
p3 = (p1 * t) + s->origin;
|
|
|
|
delta = p3 - pos;
|
|
dist = delta.length();
|
|
if (dist < bestdist) {
|
|
trace = G_Trace(
|
|
pos, ent.mins, ent.maxs, p3, &ent, MASK_PLAYERSOLID, false, "GravPath::ClosestPointOnPath 3"
|
|
);
|
|
bestdist = dist;
|
|
bestpoint = p3;
|
|
*speed = (e->Speed() * t) + (s->Speed() * (1.0f - t));
|
|
*radius = (e->Radius() * t) + (s->Radius() * (1.0f - t));
|
|
}
|
|
}
|
|
|
|
s = e;
|
|
}
|
|
*ret_dist = bestdist;
|
|
return bestpoint;
|
|
}
|
|
|
|
float GravPath::DistanceAlongPath(Vector pos, float *speed)
|
|
{
|
|
GravPathNode *s;
|
|
GravPathNode *e;
|
|
int num;
|
|
int i;
|
|
float bestdist;
|
|
float dist;
|
|
float segmentlength;
|
|
Vector delta;
|
|
Vector segment;
|
|
Vector p1;
|
|
Vector p2;
|
|
Vector p3;
|
|
float t;
|
|
float pathdist;
|
|
float bestdistalongpath;
|
|
float oosl;
|
|
pathdist = 0;
|
|
|
|
num = NumNodes();
|
|
s = GetNode(1);
|
|
delta = s->origin - pos;
|
|
bestdist = delta.length();
|
|
bestdistalongpath = 0;
|
|
*speed = s->Speed();
|
|
|
|
for (i = 2; i <= num; i++) {
|
|
e = GetNode(i);
|
|
|
|
segment = e->origin - s->origin;
|
|
segmentlength = segment.length();
|
|
|
|
// check if we're closest to the endpoint
|
|
delta = e->origin - pos;
|
|
dist = delta.length();
|
|
|
|
if (dist < bestdist) {
|
|
bestdist = dist;
|
|
bestdistalongpath = pathdist + segmentlength;
|
|
*speed = e->Speed();
|
|
}
|
|
|
|
// check if we're closest to the segment
|
|
oosl = (1 / segmentlength);
|
|
p1 = segment * oosl;
|
|
p1.normalize();
|
|
p2 = pos - s->origin;
|
|
|
|
t = p1 * p2;
|
|
if ((t > 0) && (t < segmentlength)) {
|
|
p3 = (p1 * t) + s->origin;
|
|
|
|
delta = p3 - pos;
|
|
dist = delta.length();
|
|
if (dist < bestdist) {
|
|
bestdist = dist;
|
|
bestdistalongpath = pathdist + t;
|
|
|
|
t *= oosl;
|
|
*speed = (e->Speed() * t) + (s->Speed() * (1.0f - t));
|
|
}
|
|
}
|
|
|
|
s = e;
|
|
pathdist += segmentlength;
|
|
}
|
|
|
|
return bestdistalongpath;
|
|
}
|
|
|
|
Vector GravPath::PointAtDistance(Vector pos, float dist, qboolean is_player, float *max_speed)
|
|
{
|
|
GravPathNode *s;
|
|
GravPathNode *e;
|
|
int num;
|
|
int i;
|
|
Vector delta;
|
|
Vector p1;
|
|
float t;
|
|
float pathdist;
|
|
float segmentlength;
|
|
|
|
num = NumNodes();
|
|
s = GetNode(1);
|
|
pathdist = 0;
|
|
|
|
for (i = 2; i <= num; i++) {
|
|
e = GetNode(i);
|
|
|
|
delta = e->origin - s->origin;
|
|
segmentlength = delta.length();
|
|
|
|
if ((pathdist + segmentlength) > dist) {
|
|
t = dist - pathdist;
|
|
|
|
p1 = delta * (t / segmentlength);
|
|
// return p1 + s->origin;
|
|
|
|
if (e->spawnflags & PULL_UPWARDS && is_player) {
|
|
p1.z = p1.length() / 2;
|
|
}
|
|
|
|
*max_speed = e->MaxSpeed();
|
|
|
|
return p1 + pos;
|
|
}
|
|
|
|
s = e;
|
|
pathdist += segmentlength;
|
|
}
|
|
|
|
*max_speed = s->MaxSpeed();
|
|
|
|
// cap it off at start or end of path
|
|
return s->origin;
|
|
}
|
|
|
|
void GravPath::DrawPath(float r, float g, float b)
|
|
{
|
|
Vector s;
|
|
Vector e;
|
|
Vector offset;
|
|
GravPathNode *node;
|
|
int num;
|
|
int i;
|
|
|
|
num = NumNodes();
|
|
node = GetNode(1);
|
|
s = node->origin;
|
|
G_DebugBBox(s, Vector(8, 8, 8), Vector(-8, -8, -8), 0, 1, 0, 1);
|
|
offset = Vector(r, g, b) * 4 + Vector(0, 0, 0);
|
|
offset = Vector(0, 0, 0);
|
|
|
|
for (i = 2; i <= num; i++) {
|
|
node = GetNode(i);
|
|
e = node->origin;
|
|
|
|
G_DebugLine(s + offset, e + offset, r, g, b, 1);
|
|
G_DebugBBox(e, Vector(8, 8, 8), Vector(-8, -8, -8), 0, 1, 0, 1);
|
|
s = e;
|
|
}
|
|
|
|
G_DebugBBox(origin, mins - origin, maxs - origin, 1, 0, 0, 1);
|
|
}
|
|
|
|
int GravPath::NumNodes(void)
|
|
{
|
|
return pathlist.NumObjects();
|
|
}
|
|
|
|
float GravPath::Length(void)
|
|
{
|
|
return pathlength;
|
|
}
|