openmohaa/code/fgame/mover.cpp
2025-02-15 14:39:39 +01:00

273 lines
6.5 KiB
C++

/*
===========================================================================
Copyright (C) 2025 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
===========================================================================
*/
// mover.cpp: Base class for any object that needs to move to specific locations over a
// period of time. This class is kept separate from most entities to keep
// class size down for objects that don't need such behavior.
//
#include "g_local.h"
#include "entity.h"
#include "trigger.h"
#include "mover.h"
#include "level.h"
#include "g_phys.h"
#define MOVE_ANGLES 1
#define MOVE_ORIGIN 2
CLASS_DECLARATION(Trigger, Mover, "mover") {
{&EV_MoveDone, &Mover::MoveDone},
{NULL, NULL }
};
Mover::Mover()
{
endevent = NULL;
}
Mover::~Mover() {}
void Mover::MoveDone(Event *ev)
{
Event *event;
Vector move;
Vector amove;
// zero out the movement
avelocity = vec_zero;
if (moveflags & MOVE_ANGLES) {
amove = angledest - localangles;
} else {
amove = vec_zero;
}
velocity = vec_zero;
if (moveflags & MOVE_ORIGIN) {
move = finaldest - localorigin;
} else {
move = vec_zero;
}
accel = vec_zero;
aaccel = vec_zero;
if (!G_PushMove(this, move, amove)) {
// Added in OPM
// Check to make sure the event isn't pending
// blocked events called by G_PushMove() might alter
// the mover
if (!EventPending(EV_MoveDone)) {
// Delay finish till we can move into the final position
PostEvent(EV_MoveDone, FRAMETIME);
}
return;
}
//
// After moving, set origin to exact final destination
//
if (moveflags & MOVE_ORIGIN) {
setLocalOrigin(finaldest);
}
if (moveflags & MOVE_ANGLES) {
localangles = angledest;
if ((localangles.x >= 360) || (localangles.x < 0)) {
localangles.x -= ((int)localangles.x / 360) * 360;
}
if ((localangles.y >= 360) || (localangles.y < 0)) {
localangles.y -= ((int)localangles.y / 360) * 360;
}
if ((localangles.z >= 360) || (localangles.z < 0)) {
localangles.z -= ((int)localangles.z / 360) * 360;
}
}
event = endevent;
endevent = NULL;
if (event) {
ProcessEvent(event);
}
}
/*
=============
MoveTo
calculate self.velocity and self.nextthink to reach dest from
self.origin traveling at speed
===============
*/
void Mover::MoveTo(Vector tdest, Vector angdest, float tspeed, Event& event)
{
Vector vdestdelta;
Vector angdestdelta;
float len;
float traveltime;
assert(tspeed >= 0.0f);
if (!tspeed) {
error("MoveTo", "No speed is defined!");
}
if (tspeed < 0.0f) {
error("MoveTo", "Speed is negative!");
}
// Cancel previous moves
CancelEventsOfType(EV_MoveDone);
moveflags = 0;
if (endevent) {
delete endevent;
}
endevent = new Event(event);
finaldest = tdest;
angledest = angdest;
if (finaldest != localorigin) {
moveflags |= MOVE_ORIGIN;
}
if (angledest != localangles) {
moveflags |= MOVE_ANGLES;
}
if (!moveflags) {
// stop the object from moving
velocity = vec_zero;
avelocity = vec_zero;
// post the event so we don't wait forever
PostEvent(EV_MoveDone, FRAMETIME);
return;
}
// set destdelta to the vector needed to move
vdestdelta = tdest - localorigin;
angdestdelta[0] = angledist(angdest[0] - localangles[0]);
angdestdelta[1] = angledist(angdest[1] - localangles[1]);
angdestdelta[2] = angledist(angdest[2] - localangles[2]);
if (tdest == localorigin) {
// calculate length of vector based on angles
len = angdestdelta.length();
} else {
// calculate length of vector based on distance
len = vdestdelta.length();
}
// divide by speed to get time to reach dest
traveltime = len / tspeed;
if (traveltime < level.frametime) {
traveltime = level.frametime;
vdestdelta = vec_zero;
angdestdelta = vec_zero;
}
// scale the destdelta vector by the time spent traveling to get velocity
if (moveflags & MOVE_ORIGIN) {
velocity = vdestdelta * (1.0f / traveltime);
}
if (moveflags & MOVE_ANGLES) {
avelocity = angdestdelta * (1.0f / traveltime);
}
PostEvent(EV_MoveDone, traveltime);
}
/*
=============
LinearInterpolate
===============
*/
void Mover::LinearInterpolate(Vector tdest, Vector angdest, float time, Event& event)
{
Vector vdestdelta;
Vector angdestdelta;
float t;
if (endevent) {
delete endevent;
}
endevent = new Event(event);
finaldest = tdest;
angledest = angdest;
// Cancel previous moves
CancelEventsOfType(EV_MoveDone);
// Quantize to FRAMETIME
if (time < FRAMETIME) {
time = FRAMETIME;
}
moveflags = 0;
t = 1 / time;
// scale the destdelta vector by the time spent traveling to get velocity
if (finaldest != localorigin) {
vdestdelta = tdest - localorigin;
velocity = vdestdelta * t;
moveflags |= MOVE_ORIGIN;
}
if (angledest != localangles) {
angdestdelta = angdest - localangles;
avelocity = angdestdelta * t;
moveflags |= MOVE_ANGLES;
}
if (g_bBeforeThinks) {
time -= FRAMETIME;
}
PostEvent(EV_MoveDone, time);
}
/*
=============
Stop
===============
*/
void Mover::Stop()
{
if (endevent) {
delete endevent;
endevent = NULL;
}
CancelEventsOfType(EV_MoveDone);
moveflags = 0;
avelocity = vec_zero;
velocity = vec_zero;
accel = vec_zero;
aaccel = vec_zero;
}