djcev.com

//

Git Repos / fte_dogmode / qc / func / bob.qc

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

Show bob.qc

//==============================================================================
// func_bob -- pd3 code attributed to RennyC, MG1 code by MachineGames
//==============================================================================

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

#ifdef SSQC
//----------------------------------------------------------------------
// func_bob spawnflags -- CEV
//----------------------------------------------------------------------
typedef enumflags
{
SPAWNFLAG_FUNC_BOB_MG1_NONSOLID = 1, // mg1 nonsolid
SPAWNFLAG_FUNC_BOB_COLLISION = 2, // pd3 collision for misc_bob
SPAWNFLAG_FUNC_BOB_MG1_START_ON = 2, // mg1 start on
SPAWNFLAG_FUNC_BOB_NONSOLID = 4 // pd3 nonsolid for func_bob
// SPAWNFLAG_NOT_ON_EASY = 256, // see base_entities.qc -- CEV
// SPAWNFLAG_NOT_ON_NORMAL = 512,
// SPAWNFLAG_NOT_ON_HARD_OR_NIGHTMARE = 1024,
// SPAWNFLAG_NOT_IN_DEATHMATCH = 2048,
// SPAWNFLAG_NOT_IN_COOP = 4096,
// SPAWNFLAG_NOT_IN_SP = 8192,
// SPAWNFLAG_NOT_ON_SKILL2 = 32768, // see base_entities.qc -- CEV
// SPAWNFLAG_NOT_ON_SKILL3 = 65536, // see base_entities.qc -- CEV
// SPAWNFLAG_CENTERPRINTALL = 131072 // see base_entities.qc -- CEV
} base_func_bob_spawnflags;
#endif

#ifdef SSQC
const float FUNC_BOB_START_OFF = 1; // pd3 style key
const float FUNC_BOB_THINKINTERVAL = 0.05;
#endif

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

// base_func_bob
#ifdef CSQC
void(float isnew) base_func_bob_netreceive;
#endif
#ifdef SSQC
// BASE_FUNC_BOB_GETPOSITIONFORANGLE(an)
void() base_func_bob_on;
void() base_func_bob_off;
void() base_func_bob_blocked;
void() base_func_bob_think_mg1;
void() base_func_bob_think_timer;
void() base_func_bob_use_mg1;
void() base_func_bob_tick_mg1;
#endif
#ifdef SSQC
void(string key, string value) base_func_bob_init_field;
#endif
#if defined(CSQC) || defined(SSQC)
void(entity e) base_func_bob_init;
#ifdef SSQC
strip void() base_func_bob;
#endif

// func_bob
#if defined(CSQC) || defined(SSQC)
void(entity e) func_bob_init;
#endif
#ifdef SSQC
void() func_bob;
#endif

// misc_bob
#ifdef SSQC
void(entity e) misc_bob_init;
void() misc_bob;
#endif

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

//----------------------------------------------------------------------
// class base_func_bob: base_func
// {
#ifdef CSQC
//--------------------------------------------------------------
void(float isnew) base_func_bob_netreceive =
{
// creates the netflag variable -- CEV
BASE_FUNC_NETRECEIVE (base_func_bob_init)

if (isnew)
base_entity_que_add (self, QUE_TYPE_ACTOR);
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
#define BASE_FUNC_BOB_GETPOSITIONFORANGLE(an) \
{ \
self.count += (an); \
self.count = mod (self.count, 360); \
/* MG1 uses makevectors to calculate sin and cos -- CEV */ \
makevectors ([0, self.count, 0]); \
/* v_forward_y is sin */ \
local vector offs = self.pos1 * v_forward_y; \
if (self.pos2) \
{ \
/* v_forward_x is cos */ \
offs += (self.pos2 * v_forward_x); \
} \
}

//--------------------------------------------------------------
void() base_func_bob_on =
{
// This may have been called by a "use" function, so don't
// allow it to be called repeatedly -- iw
self.use = sub_null;

if (self.cnt)
{
self.movetype = MOVETYPE_PUSH;
self.solid = SOLID_BSP;
}
else
{
self.movetype = MOVETYPE_FLY;
self.solid = SOLID_BBOX;
// Reset any onground flags
self.flags = 0;
}

if (self.spawnflags & SPAWNFLAG_FUNC_BOB_NONSOLID)
self.solid = SOLID_NOT;

self.SendFlags |= NETFLAG_BASE_ENTITY_SOLID;

setmodel (self, self.mdl);
setsize (self, self.mins , self.maxs);

self.think = base_func_bob_think_timer;

if (self.cnt)
self.nextthink = self.ltime + 0.1 + self.delay;
else
self.nextthink = time + 0.1 + self.delay;

// add to the frametick group since motion has started -- CEV
self.classgroup |= CG_FRAMETICK;
};

//--------------------------------------------------------------
void() base_func_bob_off =
{
if (self.cnt)
{
self.movetype = MOVETYPE_PUSH;
self.solid = SOLID_BSP;
}
else
{
self.movetype = MOVETYPE_FLY;
self.solid = SOLID_BBOX;
}

if (self.spawnflags & SPAWNFLAG_FUNC_BOB_NONSOLID)
self.solid = SOLID_NOT;

self.SendFlags |= NETFLAG_BASE_ENTITY_SOLID;

setmodel (self, self.mdl);
setsize (self, self.mins , self.maxs);
self.velocity = '0 0 0';

if (self.style & FUNC_BOB_START_OFF)
self.use = base_func_bob_on;

// remove from frametick group since motion has stopped -- CEV
self.classgroup &= ~CG_FRAMETICK;
};

//--------------------------------------------------------------
// was func_bob_blocked in MG1 -- CEV
//--------------------------------------------------------------
void() base_func_bob_blocked =
{
if (self.attack_finished > time)
return;

t_damage2 (other, self, self, self.dmg);
self.attack_finished = time + 0.5;
};

//--------------------------------------------------------------
// was func_bob_think in MG1. 'Used by solid bobbers'. -- CEV
//--------------------------------------------------------------
void() base_func_bob_think_mg1 =
{
local float ang = self.finalangle_x * FUNC_BOB_THINKINTERVAL;
// macros everywhere. creates the variable 'offs' -- CEV
BASE_FUNC_BOB_GETPOSITIONFORANGLE (ang)
local vector diff = offs - self.origin;
self.velocity = diff * (1 / FUNC_BOB_THINKINTERVAL);
self.nextthink = self.ltime + FUNC_BOB_THINKINTERVAL;

// self.tick will take care of SendFlags -- CEV
};

//--------------------------------------------------------------
// was bob_timer -- CEV
//--------------------------------------------------------------
void() base_func_bob_think_timer =
{
self.think = base_func_bob_think_timer;

if (self.cnt)
self.nextthink = self.ltime + 0.1;
else
self.nextthink = time + 0.1;

// Has the cycle completed?
// changed from self.attack_timer to .attack_finished -- CEV
if (self.attack_finished < time)
{
// Setup bob cycle and half way point for slowdown
self.attack_finished = time + self.count;
self.distance = time + (self.count * 0.5);

// Flip direction of bmodel bob
if (self.stateflags & STATE_LEFTY)
{
self.stateflags &= ~STATE_LEFTY;
self.t_length = self.t_height;
}
else
{
self.stateflags |= STATE_LEFTY;
self.t_length = -self.t_height;
}

// Always reset velocity and flags
self.velocity = '0 0 0';
self.flags = 0;
}

// Is the direction set?
// This is a block condition to prevent the bmodel moving
if (!(self.stateflags & STATE_INACTIVE))
{
// Slow down velocity (gradually)
if (self.distance < time)
{
self.velocity = self.velocity * self.speed2;
}
else
{
// Speed up velocity (linear/exponentially)
self.t_length = self.t_length * self.speed;
self.velocity += self.movedir * self.t_length;
}
}
};

//--------------------------------------------------------------
// was func_bob_tick in MG1. 'Used by non-solid bobbers'. -- CEV
//--------------------------------------------------------------
void() base_func_bob_tick_mg1 =
{
if (self.solid == SOLID_NOT)
{
local float ang = self.finalangle_x * server_deltatime;
// macros everywhere. creates the variable 'offs' -- CEV
BASE_FUNC_BOB_GETPOSITIONFORANGLE (ang)
setorigin (self, offs);
}

if (!(self.SendFlags & NETFLAG_BASE_ENTITY_ORIGIN)) {
if (self.origin != self.origin_net)
{
// need to send origin -- CEV
self.SendFlags |= NETFLAG_BASE_ENTITY_ORIGIN;
} }
};

//--------------------------------------------------------------
void() base_func_bob_use_mg1 =
{
if (!(self.stateflags & STATE_INACTIVE))
{
if (self.solid)
{
self.velocity = '0 0 0';
self.nextthink = -1;
}

self.stateflags |= STATE_INACTIVE;
self.classgroup &= ~CG_FRAMETICK;
}
else
{
if (self.solid)
base_func_bob_think_mg1 ();

self.stateflags &= ~STATE_INACTIVE;
self.classgroup |= CG_FRAMETICK;
}
};
#endif

//==============================================================
// Initialization
//==============================================================

#ifdef SSQC
void(string key, string value) base_func_bob_init_field =
{
switch (key)
{
case "bsporigin":
// bsporigin used by progs_dump 3 -- CEV
// "bmodel origins are 0,0,0; check first"
if (!self.cnt)
self.cnt = stof (value);
break;
case "dest":
// dest is used in MG1 -- CEV
if (!self.pos1)
self.pos1 = stov (value);
break;
case "dest2":
// remap dest2, just in case -- CEV
if (!self.pos2)
self.pos2 = stov (value);
break;
case "height":
// remap height to t_height -- CEV
if (!self.t_height)
self.t_height = stof (value);
break;
case "waitmin":
// used by pd3 -- speed up scale -- CEV
if (!self.speed)
self.speed = stof (value);
break;
case "waitmin2":
// used by pd3 -- slow down scale -- CEV
if (!self.speed2)
self.speed2 = stof (value);
break;
}
};
#endif

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) base_func_bob_init =
{
base_func_init (e);

#ifdef CSQC
e.drawmask = DRAWMASK_NORMAL;
e.predraw = base_func_predraw;

setmodelindex (e, e.modelindex);
setsize (e, e.mins, e.maxs);
setorigin (e, e.origin);

// TODO CEV eew
if (e.classtype == CT_MISC_BOB)
e.classname = "misc_bob";
else if (e.classtype == CT_FUNC_BOB)
e.classname = "func_bob";
#endif

#ifdef SSQC
if (known_release == KNOWN_RELEASE_MG1)
{
// MG1 func_bob -- TODO CEV
setmodel (e, e.model);
setorigin (e, e.origin);

if (!e.pos1)
e.pos1 = '0 0 64';
if (!e.wait)
e.wait = 10;
if (!e.dmg)
e.dmg = 1;

// in the public MG1 sourcecode this was .avelocity;
// if you examine the fields of a func_bob entity with
// the released MG1 progs.dat loaded you'll see the
// field used is instead .bob_avelocity. (So there's
// a discrepancy between the released source and
// the released compiled progs.dat). Using .avelocity
// results in buggy func_bob movement in FTEQW; use
// another (not-used, not-special-to-the-engine)
// vector field and they'll work fine. I've chosen
// finalangle here. -- CEV
e.finalangle_x = 360 / e.wait;
e.count = 360 * e.delay;

e.blocked = base_func_bob_blocked;
e.customphysics = base_entity_movetype_push;
e.think = base_func_bob_think_mg1;
e.tick = base_func_bob_tick_mg1;
e.use = base_func_bob_use_mg1;

if (e.spawnflags & SPAWNFLAG_FUNC_BOB_MG1_NONSOLID)
{
e.movetype = MOVETYPE_NONE;
e.solid = SOLID_NOT;
}
else
{
e.movetype = MOVETYPE_PUSH;
e.solid = SOLID_BSP;
}

e.SendEntity = base_entity_netsend;
e.SendFlags = NETFLAG_BASE_ENTITY_FULLSEND;

if (e.spawnflags & SPAWNFLAG_FUNC_BOB_MG1_START_ON)
{
// set inactive so use will toggle on -- CEV
e.stateflags |= STATE_INACTIVE;
sub_runvoidas (e, base_func_bob_use_mg1);
}
}
else
{
e.spawnflags |= SPAWNFLAG_FUNC_BOB_COLLISION;
if (e.spawnflags & SPAWNFLAG_FUNC_BOB_NONSOLID)
e.spawnflags &= ~SPAWNFLAG_FUNC_BOB_COLLISION;

// Using a custom model?
if (e.mdl == "")
{
e.cnt = TRUE;
e.mdl = e.model;
}
else
{
e.cnt = FALSE;
e.modelindex = 0;
e.model = "";
}

sub_setmovedir (e);
e.movedir = normalize (e.movedir);

if (e.t_height <= 0)
// Direction intensity
e.t_height = 8;
if (e.count < 1)
// Direction switch timer
e.count = 2;
if (e.speed <= 0)
// Speed up
e.speed = 1;
if (e.speed2 <= 0)
// Slow down
e.speed2 = 0.75;
if (e.delay < 0)
e.delay = random() + random() + random();

e.SendEntity = base_entity_netsend;
e.SendFlags = NETFLAG_BASE_ENTITY_MODEL |
NETFLAG_BASE_ENTITY_ORIGIN |
NETFLAG_BASE_ENTITY_SOLID;
e.customphysics = base_entity_movetype_push;
e.tick = base_func_neteval;

// added style key 1 for start off -- dumptruck_ds
if (e.style & FUNC_BOB_START_OFF)
sub_runvoidas (e, base_func_bob_off);
else
sub_runvoidas (e, base_func_bob_on);
}
#endif
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
strip void() base_func_bob =
{
base_func_bob_init (self);
};
#endif
// };

/*QUAKED func_bob (0 .5 .8) X X X X X X X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY
A SOLID bmodel that gently moves back and forth
-------- KEYS --------
targetname : trigger entity (works with entity state system)
angle : direction movement, use "360" for angle 0
height : direction intensity (def=8)
count : direction cycle timer (def=2s, minimum=1s)
waitmin : Speed up scale (def=1) 1+=non linear
waitmin2 : Slow down scale (def=0.75)
delay : Starting time delay (def=0, -1=random)
style : If set to 1, starts off and waits for trigger
_dirt : -1 = will be excluded from dirtmapping
_minlight : Minimum light level for any surface of the brush model
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
_shadow : Will cast shadows on other models and itself
_shadowself : Will cast shadows on itself
-------- SPAWNFLAGS --------
STARTOFF : Starts off and waits for trigger - DISABLED, Ripped out ESTATE System (RennyC)
-------- NOTES --------
A SOLID bmodel that gently moves back and forth
*/
//----------------------------------------------------------------------
// class func_bob: base_func_bob
// {
#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) func_bob_init =
{
e.classname = "func_bob";
e.classtype = CT_FUNC_BOB;
base_func_bob_init (e);
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
void() func_bob =
{
BASE_FUNC_PREINIT (base_func_bob_init_field)
func_bob_init (self);
};
#endif
// };

/*QUAKED misc_bob (0 0.5 0.8) (-8 -8 -8) (8 8 8) X FUNC_BOB_COLLISION FUNC_BOB_NONSOLID X X X X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY
{
model ({"path" : mdl, "skin" : skin, "frame": frame});
}
Same as func_bob but uses a custom model instead of a brush. Use the mdl key to set the path of the model.
*/
//----------------------------------------------------------------------
// class misc_bob: base_func_bob
// {
#ifdef SSQC
//--------------------------------------------------------------
void(entity e) misc_bob_init =
{
e.classname = "misc_bob";
e.classtype = CT_MISC_BOB;

if (e.mdl == "")
e.mdl = __NULL__;

precache_model (e.mdl);

base_func_bob_init (e);
};

//--------------------------------------------------------------
void() misc_bob =
{
BASE_FUNC_PREINIT (base_func_bob_init_field)
misc_bob_init (self);
};
#endif
// };

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

Log bob.qc

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