djcev.com

//

Git Repos / fte_dogmode / qc / func / train.qc

Last update to this file was on 2024-06-26 at 03:47.

Show train.qc

//==============================================================================
// func_train
//==============================================================================

//======================================================================
// constants
//======================================================================

#ifdef SSQC
const float TRAIN_RETRIGGER = 1;
const float TRAIN_MOVEONTRIGGER = 2;
const float TRAIN_STOPONTRIGGER = 4;
const float TRAIN_NONSOLID = 8;
const float TRAIN_NOROTATE = 16;
const float TRAIN_ROTATEY = 32;
const float TRAIN_CUSTOMALIGN = 64;

const float TRAIN_NEXT_WAIT = 0; // normal movement
const float TRAIN_NEXT_STOP = 1; // force a stop on the next path_corner
const float TRAIN_NEXT_CONTINUE = 2; // force continue on the next
// path_corner (ignores wait time)
#endif

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

// base_func_train
#ifdef SSQC
void() base_func_train_blocked;
void() base_func_train_use_stopped;
void() base_func_train_use_moving;
void() base_func_train_wait;
void() base_func_train_next;
void() base_func_train_find;
#endif
#if defined(CSQC) || defined(SSQC)
void(entity e) base_func_train_init;
#endif
#ifdef SSQC
strip void() base_func_train;
#endif

// func_train
#ifdef CSQC
void(float isnew) func_train_netreceive;
#endif
#if defined(CSQC) || defined(SSQC)
void(entity e) func_train_init;
#endif
#ifdef SSQC
void() func_train;
#endif

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

//----------------------------------------------------------------------
// class base_func_train: base_func
// {
#ifdef SSQC
//--------------------------------------------------------------
void() base_func_train_blocked =
{
// don't block on projectiles -- CEV
if (other.classgroup & CG_PROJECTILE)
{
if (other.mins != '0 0 0' || other.maxs != '0 0 0')
setsize (other, '0 0 0', '0 0 0');

return;
}

if (time < self.attack_finished)
return;

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

//--------------------------------------------------------------
// Use function for when the train is stopped.
// Makes the train continue on path_corners with wait -1,
// or forces a continue if the "move on trigger" spawnflag is set.
//--------------------------------------------------------------
void() base_func_train_use_stopped =
{
if (self.spawnflags & TRAIN_MOVEONTRIGGER || self.wait < 0)
{
base_func_train_next ();
return;
}

// Train has already moved after startup, and has no
// "stop on trigger" flag, so ignore activation.
if (self.think != base_func_train_find &&
self.cnt != TRAIN_NEXT_STOP)
{
return;
}

base_func_train_next ();
};

//--------------------------------------------------------------
// Use function for when the train is moving.
// Forces a stop or an instant continue on the next path_corner
// depending on the spawnflag set
//--------------------------------------------------------------
void() base_func_train_use_moving =
{
if (self.spawnflags & TRAIN_MOVEONTRIGGER || self.wait < 0)
self.cnt = TRAIN_NEXT_CONTINUE;
else if (self.spawnflags & TRAIN_STOPONTRIGGER)
self.cnt = TRAIN_NEXT_STOP;
};

//--------------------------------------------------------------
// path_corner has been reached, so decide what to do next
//--------------------------------------------------------------
void() base_func_train_wait =
{
local float localtime;

if (self.movetype == MOVETYPE_PUSH)
localtime = self.ltime;
else
localtime = time;

// ready to be re-triggered
self.use = base_func_train_use_stopped;

// from Copper
// Trains now fire their path_corners' targets on arrival.
// If a player is riding the train, treat them as activator.
activator = nextent (world);

while (!func_door_entities_touching(self, activator) &&
activator.classtype == CT_PLAYER)
{
activator = nextent (activator);
}

if (activator.classtype != CT_PLAYER)
// default to player1 wherever they are
activator = nextent (world);

// was SUB_UseEntTargets -- CEV
sub_runvoidas (self.enemy, sub_usetargets);

if (self.enemy.speed)
// sets the speed from the current path_corner
self.speed2 = self.enemy.speed;

// oof. -- CEV
if (self.classtype == CT_MISC_MODELTRAIN)
{
if (self.enemy.first_frame)
self.first_frame = self.enemy.first_frame;
if (self.enemy.last_frame)
self.last_frame = self.enemy.last_frame;
if (self.enemy.first_frame2)
self.first_frame2 = self.enemy.first_frame2;
if (self.enemy.last_frame2)
self.last_frame2 = self.enemy.last_frame2;
if (self.enemy.frtime)
self.frtime = self.enemy.frtime;
if (self.enemy.frtime2)
self.frtime2 = self.enemy.frtime2;
if (self.enemy.animtype)
self.animtype = self.enemy.animtype;
if (self.enemy.animtype2)
self.animtype2 = self.enemy.animtype2;
if (self.enemy.multiplier)
self.multiplier = self.enemy.multiplier;
}

self.think = base_func_train_next;

// train is moving normally and path_corner has a wait set,
// so pause for that time.
if (self.wait > 0 && self.cnt == TRAIN_NEXT_WAIT &&
!(self.spawnflags & TRAIN_RETRIGGER))
{
// state: stopped
self.state = 0;

self.SendFlags |= BASE_FUNC_NET_ORIGIN |
BASE_FUNC_NET_VELOCITY | BASE_FUNC_NET_SPEED;

// oof -- CEV
if (self.classtype == CT_MISC_MODELTRAIN)
{
sub_runvoidas (self.animcontroller,
self.animcontroller.think);
}

self.nextthink = localtime + self.wait;

// play stopping sound. If path_corner has a custom
// sound, play that instead
if (self.enemy.noise != "")
sound (self, CHAN_WEAPON, self.enemy.noise,
VOL_MHI, ATTN_NORM);
else
sound (self, CHAN_VOICE, self.noise,
VOL_MHI, ATTN_NORM);
}
// train is moving normally and path_corner has no wait time,
// or has been forced to move instantly through a triggering.
else if (self.cnt != TRAIN_NEXT_STOP && !self.wait &&
!(self.spawnflags & TRAIN_RETRIGGER))
{
// play "passing by" sound, if any. If path_corner has
// a custom one, play that instead
if (self.enemy.noise2 != "")
sound (self, CHAN_WEAPON, self.enemy.noise2,
VOL_MHI, ATTN_NORM);
else if (self.noise2 != "")
sound (self, CHAN_WEAPON, self.noise2,
VOL_MHI, ATTN_NORM);

// move instantly
self.nextthink = -1;
base_func_train_next ();
}
// path_corner has wait -1, or train has been forced to stop
// through a triggering. Also catches the backwards compatible
// case for the original rubicon2 "retrigger" flag.
else
{
// play stopping sound. If path_corner has a custom
// sound, play that instead
if (self.enemy.noise != "")
sound (self, CHAN_WEAPON, self.enemy.noise,
VOL_MHI, ATTN_NORM);
else
sound (self, CHAN_VOICE, self.noise,
VOL_MHI, ATTN_NORM);

// state: stopped
self.state = 0;

self.SendFlags |= BASE_FUNC_NET_ORIGIN |
BASE_FUNC_NET_VELOCITY | BASE_FUNC_NET_SPEED;

// oof -- CEV
if (self.classtype == CT_MISC_MODELTRAIN)
{
sub_runvoidas (self.animcontroller,
self.animcontroller.think);
}

self.nextthink = -1;
}
};

//--------------------------------------------------------------
// searches for the next path_corner and sends the train on its way
//--------------------------------------------------------------
void() base_func_train_next =
{
local entity targ;
local vector destang = '0 0 0';
local vector displ = '0 0 0';

targ = find (world, targetname, self.target);

if (!targ || self.target == "")
objerror ("train_next: no next target");

self.target = targ.target;

if (targ.wait)
{
// get the wait time from the upcoming path_corner

// wait -2 on the path_corner forces it to continue
// moving (no wait), even if the train has a default
// wait time
if (targ.wait == -2)
self.wait = 0;
else
self.wait = targ.wait;
}
else
{
// use train's current wait time otherwise
self.wait = self.pausetime;
}

self.enemy = targ;

sound (self, CHAN_VOICE, self.noise1, VOL_MHI, ATTN_NORM);

if (!(!self.wait && self.cnt == TRAIN_NEXT_CONTINUE))
self.cnt = TRAIN_NEXT_WAIT;

// store up any premature triggerings until current
// movement is finished
self.use = base_func_train_use_moving;

// oof -- CEV
if (self.classtype == CT_MISC_MODELTRAIN)
{
if (!(self.spawnflags & TRAIN_NOROTATE))
{
destang = vectoangles (
targ.origin - self.origin);

if (self.spawnflags & TRAIN_ROTATEY)
{
destang_x = self.angles_x;
destang_z = self.angles_z;
}

if (self.multiplier > 0)
{
// TODO CEV
sub_calcanglemovecontroller (self,
destang,
self.speed2 * self.multiplier,
sub_null,
self.rotatecontroller);
}
else
{
self.angles = destang;
}
}
}

if (!self.state)
{
self.state = 1;

if (self.classtype == CT_MISC_MODELTRAIN &&
self.style != TRAIN_STYLE_SINGLEANIM)
{
sub_runvoidas (self.animcontroller,
self.animcontroller.think);
}
}

// if the TRAIN_CUSTOMALIGN flag is checked then the train
// should align with its path_corners based on the location
// of an "origin" brush added to the train by the mapper
// instead of the mins corner -therektafire
local float do_displace;
if (self.spawnflags & TRAIN_CUSTOMALIGN)
do_displace = 0.0;
else
do_displace = 1.0;

if (self.classtype != CT_MISC_MODELTRAIN)
displ = self.mins;

sub_calcmove (self, targ.origin - (displ * do_displace),
self.speed2, base_func_train_wait);

self.SendFlags |= BASE_FUNC_NET_ORIGIN |
BASE_FUNC_NET_VELOCITY | BASE_FUNC_NET_SPEED;
};

//--------------------------------------------------------------
// searches for the first path_corner after the train entity is
// initialized
//--------------------------------------------------------------
void() base_func_train_find =
{
local entity targ;
local float localtime;
local vector displ = '0 0 0';

if (self.movetype == MOVETYPE_PUSH)
localtime = self.ltime;
else
localtime = time;

targ = find (world, targetname, self.target);
self.target = targ.target;

local float do_displace;
if (self.spawnflags & TRAIN_CUSTOMALIGN)
do_displace = 0.0;
else
do_displace = 1.0;

if (self.classtype != CT_MISC_MODELTRAIN)
displ = self.mins;

self.enemy = targ;
setorigin (self, targ.origin - (displ * do_displace));

// If you set SendFlags here it will cause a movement error
// on the client; let the next think take care of it -- CEV
self.SendFlags |= BASE_FUNC_NET_ORIGIN |
BASE_FUNC_NET_VELOCITY | BASE_FUNC_NET_SPEED;

if (targ.speed)
// uses speed from the 1st path corner if set
self.speed2 = targ.speed;

if (!self.targetname)
{
// not triggered, so start immediately
self.think = base_func_train_next;
self.nextthink = localtime + 0.1;
}
};
#endif

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

#ifdef SSQC
if (!e.speed)
e.speed = 100;
if (!e.target)
objerror ("base_func_train_init: no target\n");
if (!e.dmg)
e.dmg = 2;

if (e.spawnflags & TRAIN_STOPONTRIGGER &&
e.spawnflags & TRAIN_MOVEONTRIGGER)
{
objerror ("base_func_train_init: Stop and move on "
"trigger set at the same time\n");
}

if (e.sounds == 1)
{
if (e.noise == "")
e.noise = "plats/train2.wav";
if (e.noise1 == "")
e.noise1 = "plats/train1.wav";
}
else if (e.sounds == 2)
{
// base door sound
if (e.noise == "")
e.noise = "doors/hydro2.wav";
if (e.noise1 == "")
e.noise1 = "doors/hydro1.wav";
}
else
{
if (e.noise == "")
e.noise = "misc/null.wav";
if (e.noise1 == "")
e.noise1 = "misc/null.wav";
}

// backwards compatibility with previous version
if (e.noise3 != "")
e.noise = e.noise3;
if (e.noise4 != "")
e.noise1 = e.noise4;

precache_sound (e.noise);
precache_sound (e.noise1);
if (e.noise2 != "")
precache_sound (e.noise2);

e.cnt = TRAIN_NEXT_WAIT;

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

e.blocked = base_func_train_blocked;
e.use = base_func_train_use_stopped;

setmodel (e, e.model);
setsize (e, e.mins , e.maxs);
setorigin (e, e.origin);

e.speed2 = e.speed;

// start trains on the second frame, to make sure their
// targets have had a chance to spawn

e.think = base_func_train_find;

if (e.movetype == MOVETYPE_PUSH)
e.nextthink = e.ltime + 0.1;
else
e.nextthink = time + 0.1;

if (e.solid == SOLID_BSP && e.classtype == CT_FUNC_TRAIN)
{
// e.SendEntity = base_func_netsend;
e.SendFlags = BASE_FUNC_NET_ORIGIN |
BASE_FUNC_NET_SIZE | BASE_FUNC_NET_MODEL |
BASE_FUNC_NET_SPEED;
}
#endif

#ifdef CSQC
// networked trains are always SOLID_BSP & MOVETYPE_PUSH
e.drawmask = DRAWMASK_NORMAL;
e.predraw = base_func_predraw_solidpush;
e.solid = SOLID_BSP;
e.movetype = MOVETYPE_PUSH;

// make sure model, size, and origin are set -- CEV
setmodelindex (e, e.modelindex);
setsize (e, e.mins , e.maxs);
setorigin (e, e.origin);
#endif

};
#endif

#ifdef SSQC
//--------------------------------------------------------------
strip void() base_func_train =
{
base_func_train_init (self);
};
#endif
// };

/*QUAKED func_train (0 .5 .8) ? RETRIGGER
Trains are moving platforms that players can ride.
The targets origin specifies the min point of the train at each corner.
The train spawns at the first target it is pointing at.
If the train is the target of a button or trigger, it will not begin moving until activated.

RETRIGGER: stop at each path_corner and don't resume until triggered again (ignores wait time)

speed default 100
dmg default 2
sounds
1) ratchet metal
2) base

*/
//----------------------------------------------------------------------
// class func_train: base_func_train
// {
#ifdef CSQC
//--------------------------------------------------------------
void(float isnew) func_train_netreceive =
{
local float netflags = ReadFloat ();
base_func_netreceive_read (isnew, netflags);

if (isnew)
func_train_init (self);

if (netflags & BASE_FUNC_NET_VELOCITY)
{
if (self.velocity != self.velocity_net)
{
if (self.velocity)
{
self.origin_start = self.origin;
self.movetime = time;
}
else
{
self.movetime = 0;
}
}
}

self.origin_net = self.origin;
self.velocity_net = self.velocity;

if (netflags & BASE_FUNC_NET_MODEL)
setmodelindex (self, self.modelindex);

if (netflags & BASE_FUNC_NET_SIZE)
setsize (self, self.mins, self.maxs);

if (netflags & BASE_FUNC_NET_ORIGIN)
setorigin (self, self.origin);
};
#endif

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) func_train_init =
{
e.classname = "func_train";
e.classtype = CT_FUNC_TRAIN;

base_func_train_init (e);
};
#endif

#ifdef SSQC
//--------------------------------------------------------------
void() func_train =
{
// new spawnflags for all entities -- iw
if (SUB_Inhibit())
return;

func_train_init (self);
};
#endif
// };

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

Log train.qc

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