djcev.com

//

Git Repos / fte_dogmode / qc / base_func.qc

Last update to this file was on 2025-08-13 at 05:20.

Show base_func.qc

//==============================================================================
// base_func.qc -- func base class
//==============================================================================

//======================================================================
// Constants
//======================================================================

#if defined(CSQC) || defined(SSQC)
//----------------------------------------------------------------------
// func_ entity states, used for buttons & doors & plats -- CEV
//----------------------------------------------------------------------
typedef enum
{
FMF_TOP = 0, // top position
FMF_BOTTOM = 1, // bottom position
FMF_UP = 2, // moving up
FMF_DOWN = 3 // moving down
} base_func_moveflags;
#endif

//======================================================================
// fields
//======================================================================

#ifdef SSQC
.vector finaldest; // used by calcmove
.vector finalangle; // used by calcanglemove
#endif

//======================================================================
// forward declarations
//======================================================================

// base_func
#ifdef CSQC
// BASE_FUNC_NETRECEIVE(initfn)
float() base_func_predraw;
#endif
#ifdef SSQC
void() base_func_neteval;
#endif
#ifdef SSQC
void() base_func_calcmove_done;
void(entity e, vector tdest, float tspeed, void() newthink) base_func_calcmove;
void() sub_calcanglemovecontroller_done;
void(entity e, vector destangle, float tspeed, void() func, entity c)
sub_calcanglemovecontroller;
#endif
#ifdef SSQC
// BASE_FUNC_PREINIT(func)
#endif
#if defined(CSQC) || defined(SSQC)
void(entity e) base_func_init;
#endif
#ifdef SSQC
strip void() base_func;
#endif

//------------------------------------------------------------------------------

//----------------------------------------------------------------------
// class base_func: base_mapentity
// {
//==============================================================
// Drawing & Networking
//==============================================================

#ifdef CSQC
//--------------------------------------------------------------
#define BASE_FUNC_NETRECEIVE(initfn) \
/* { */ \
local float netflags = base_entity_netreceive (isnew); \
if (NETFLAG_BASE_ENTITY_FRAME) \
self.frame = self.frame_net; \
if (isnew && !(self.predraw)) \
{ \
initfn (self); \
} \
else \
{ \
if (netflags & NETFLAG_BASE_ENTITY_MODEL) { \
if (self.modelindex) \
{ \
setmodelindex (self, self.modelindex); \
setsize (self, self.mins, self.maxs); \
} } \
else if (netflags & NETFLAG_BASE_ENTITY_SOLID) \
{ \
if (self.solid) \
setsize (self, self.mins, self.maxs); \
else \
setsize (self, '0 0 0', '0 0 0'); \
if (!(netflags & NETFLAG_BASE_ENTITY_ORIGIN)) \
setorigin (self, self.origin); \
} \
if (netflags & NETFLAG_BASE_ENTITY_ORIGIN) \
setorigin (self, self.origin); \
} \
/* } */

//--------------------------------------------------------------
float() base_func_predraw =
{
// func_ entities are (I hope) not changing .frame often
// enough to need interpolation -- CEV

// interpolate angles, origins -- func ents do this a bit
// differently than others, they seem to need different
// timestamps, not sure why. MOVETYPE_PUSH might have
// something to do with it. -- CEV
local float lf = 0;

// this is similar to/inspired by the logic used in Xonotic;
// see lib/csqcmodel/interpolate.qc in the Xonotic QuakeC src
// -- CEV
if (self.origin_net_time && self.origin_prev_time &&
self.origin_net_time != self.origin_prev_time)
{
lf = (time - self.origin_prev_time) /
(self.origin_net_time - self.origin_prev_time);

if (time - self.origin_net_time >= 0.2)
setorigin (self, self.origin_net);
else if (lf <= 0)
setorigin (self, self.origin_prev);
else if (lf >= 1)
setorigin (self, self.origin_net);
else
setorigin (self, (1 - lf) * self.origin_prev +
lf * self.origin_net);
}

// same as BASE_ENTITY_LERP_ANGLES but copied here -- CEV
if (self.angles_net_time && self.angles_prev_time &&
self.angles_net_time != self.angles_prev_time)
{
lf = (time - self.angles_prev_time) /
(self.angles_net_time - self.angles_prev_time);
lf = bound (0, lf, 1);

local float ang;

// correctly wrap angles when interpolating. this is
// the same logic used in Ironwail 0.7.0; see
// Quake/cl_main.c line 547 -- CEV
for (float angi = 0; angi < 3; angi++)
{
ang = self.angles_net[angi] -
self.angles_prev[angi];

if (ang > 180)
ang -= 360;
else if (ang < -180)
ang += 360;

self.angles[angi] = self.angles_prev[angi] +
ang * lf;
}
}

// draw this entity -- CEV
addentity (self);

// reset to latest server position after drawing (for player
// prediction collision checking) -- CEV
if (self.origin != self.origin_net)
setorigin (self, self.origin_net);

// go to next without auto-drawing this entity -- CEV
return PREDRAW_NEXT;
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
// flag moving func_ entities to transmit origin -- CEV
//--------------------------------------------------------------
void() base_func_neteval =
{
if (!(self.SendFlags & NETFLAG_BASE_ENTITY_ORIGIN)) {
if (self.origin != self.origin_net)
{
// flag this entity to transmit origin -- CEV
self.SendFlags |= NETFLAG_BASE_ENTITY_ORIGIN;
} }
};
#endif

//==============================================================
// Subs
//==============================================================

#ifdef SSQC
//--------------------------------------------------------------
void() base_func_calcmove_done =
{
setorigin (self, self.finaldest);
self.velocity = '0 0 0';
self.nextthink = -1;

#if 0
dprint (sprintf("base_func_calcmove_done: %s completed "
"at %g\n", self.classname, time));
#endif

// don't need custom physics anymore -- CEV
if (self.customphysics == base_entity_movetype_push)
self.customphysics = __NULL__;

if (self.think1)
self.think1 ();
};

//--------------------------------------------------------------
// SUB_CalcMove
//--------------------------------------------------------------
void(entity e, vector tdest, float tspeed, void() newthink)
base_func_calcmove =
{
local vector vdestdelta;
local float len, traveltime, localtime;

if (!tspeed)
objerror ("base_func_calcmove: No speed is defined!");

if (e.movetype == MOVETYPE_PUSH)
{
localtime = e.ltime;

// use a custom movetype_push that will update
// SendFlags on this and nearby entities -- CEV
e.customphysics = base_entity_movetype_push;
}
else
{
localtime = time;
}

e.think1 = newthink;
e.finaldest = tdest;
e.think = base_func_calcmove_done;

if (tdest == e.origin)
{
e.velocity = '0 0 0';
e.nextthink = localtime + 0.1;
return;
}

// set destdelta to the vector needed to move
vdestdelta = tdest - e.origin;

// calculate length of vector
len = vlen (vdestdelta);

// divide by speed to get time to reach tdest
traveltime = len / tspeed;

if (traveltime < 0.1)
{
e.velocity = '0 0 0';
e.nextthink = localtime + 0.1;
return;
}

// set nextthink to trigger a think when d is reached
e.nextthink = localtime + traveltime;

// scale the destdelta vector by the time spent traveling
// to get velocity
// qcc won't take vec/float
e.velocity = vdestdelta * (1 / traveltime);
};

//--------------------------------------------------------------
// SUB_CalcAngleMoveDoneController
// After rotating, set angle to exact final angle
//--------------------------------------------------------------
void() sub_calcanglemovecontroller_done =
{
self.owner.angles = self.finalangle;
self.owner.avelocity = '0 0 0';
self.nextthink = -1;
if (self.think1)
sub_runvoidas (self.owner, self.think1);
};

//--------------------------------------------------------------
// SUB_CalcAngleMoveController -- Same as SUB_CalcAngleMove, but
// using a separate controller entity to not lose track of current
// think functions.
//--------------------------------------------------------------
void(entity e, vector destangle, float tspeed, void() func,
entity c) sub_calcanglemovecontroller =
{
local vector destdelta;
local float len, traveltime;

if (!tspeed)
objerror("sub_calcanglemove: No speed is defined!\n");

// set destdelta to the vector needed to move
destdelta = normalize_angles180 (destangle - e.angles);

/*
dprint (sprintf("destangle: %v\n", destangle));
dprint (sprintf("self.angles: %v\n", self.angles));
dprint (sprintf("destdelta: %v\n", destdelta));
*/

// calculate length of vector
len = vlen (destdelta);

// divide by speed to get time to reach dest
traveltime = len / tspeed;

// set nextthink to trigger a think when dest is reached
c.nextthink = time + traveltime;

// scale the destdelta vector by the time spent traveling to
// get velocity
e.avelocity = destdelta * (1 / traveltime);

// Makes sure controller.owner points to self so it can be
// referenced later in the think function
c.owner = e;
c.think1 = func;
c.finalangle = destangle;
c.think = sub_calcanglemovecontroller_done;
};
#endif

//==============================================================
// Constructor & Spawn Functions
//==============================================================

#ifdef SSQC
//--------------------------------------------------------------
#define BASE_FUNC_PREINIT(func) \
/* { */ \
base_mapentity_init_spawndata (func); \
/* new spawnflags for all entities -- iw */ \
if (SUB_Inhibit()) \
return; \
/* } */
#endif

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) base_func_init =
{
base_mapentity_init (e);
e.classgroup |= CG_FUNC;
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
strip void() base_func =
{
base_func_init (self);
};
#endif
// };

Return to the top of this page or return to the overview of this repo.

Log base_func.qc

Return to the top of this page or return to the overview of this repo.