djcev.com

//

Git Repos / fte_dogmode / qc / triggers / push.qc

Last update to this file was on 2024-09-17 at 12:58.

Show push.qc

////////////////////////////////////////////////////////////////////////////////
// trigger_push -- id1 with additions by dumptruck_ds & CEV
////////////////////////////////////////////////////////////////////////////////

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

#ifdef SSQC
const float TRIGGER_PUSH_ONCE = 1; // only trigger once
const float TRIGGER_PUSH_STARTOFF = 8; // trigger will start off
const float TRIGGER_PUSH_SILENT = 16; // push silently
const float TRIGGER_PUSH_NOISE = 32; // use custom sound via noise key/value
#endif

#if defined(CSQC) || defined(SSQC)
const float TRIGGER_PUSH_STATE_TARGETED = 1;
#endif

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

#ifdef SSQC
vector(float a, float b, float c) solve_quadratic;
#endif

// base_trigger_push
#ifdef CSQC
void(float isnew) base_trigger_push_netreceive;
#endif
#ifdef SSQC
float(entity to, float netflags) base_trigger_push_netsend;
vector(vector org, entity tgt, float ht) base_trigger_push_calculatevelocity;
void() base_trigger_push_findtarget;
#endif
#if defined(CSQC) || defined(SSQC)
void() base_trigger_push_touch;
#endif

// trigger_push
// void() trigger_push_use;
#if defined(CSQC) || defined(SSQC)
void(entity e) trigger_push_init;
#endif
#ifdef SSQC
void() trigger_push;
#endif

// trigger_push_custom
#ifdef SSQC
void() trigger_push_custom_use;
#endif
#if defined(CSQC) || defined(SSQC)
void(entity e) trigger_push_custom_init;
#endif
#ifdef SSQC
void() trigger_push_custom;
#endif

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

#ifdef SSQC
//----------------------------------------------------------------------
// solve_quadratic -- from Nexuiz source code; original can be found at
// https://github.com/smlinux/nexuiz/blob/master/data/qcsrc/common/util.qc
//----------------------------------------------------------------------
vector(float a, float b, float c) solve_quadratic =
{
// ax^2 + bx + c = 0
local vector v = '0 0 0';
local float D;
if (a == 0)
{
if (b != 0)
{
v_x = v_y = -c / b;
v_z = 1;
}
else
{
if (c == 0)
{
// actually, every number solves the equation!
v_z = 1;
}
}
}
else
{
D = b * b - 4 * a * c;
if (D >= 0)
{
D = sqrt (D);
if (a > 0)
{
// put the smaller solution first
v_x = ((-b) - D) / (2 * a);
v_y = ((-b) + D) / (2 * a);
}
else
{
v_x = (-b + D) / (2 * a);
v_y = (-b - D) / (2 * a);
}
v_z = 1;
}
else
{
// complex solutions!
D = sqrt (-D);
v_x = -b / (2 * a);
if (a > 0)
v_y = D / (2 * a);
else
v_y = -D / (2 * a);
v_z = 0;
}
}
return v;
};
#endif

//----------------------------------------------------------------------
// class base_trigger_push: base_trigger
// {
#ifdef CSQC
//--------------------------------------------------------------
void(float isnew) base_trigger_push_netreceive =
{
local float netflags = ReadFloat ();

if (netflags & BASE_TRIGGER_NET_ORIGIN)
{
self.origin_x = ReadCoord ();
self.origin_y = ReadCoord ();
self.origin_z = ReadCoord ();
}

if (netflags & BASE_TRIGGER_NET_SIZE)
{
self.mins_x = ReadCoord ();
self.mins_y = ReadCoord ();
self.mins_z = ReadCoord ();
self.maxs_x = ReadCoord ();
self.maxs_y = ReadCoord ();
self.maxs_z = ReadCoord ();
}

if (netflags & BASE_TRIGGER_NET_MOVEDIR)
{
self.movedir_x = ReadCoord ();
self.movedir_y = ReadCoord ();
self.movedir_z = ReadCoord ();
}

if (netflags & BASE_TRIGGER_NET_STATE)
self.state = ReadByte ();

if (netflags & BASE_TRIGGER_NET_ESTATE)
self.estate = ReadFloat ();

if (isnew)
if (self.classtype == CT_TRIGGER_PUSH)
trigger_push_init (self);
else if (self.classtype == CT_TRIGGER_PUSH_CUSTOM)
trigger_push_custom_init (self);

// make sure size and origin are set -- CEV
if (netflags & BASE_TRIGGER_NET_SIZE)
setsize (self, self.mins, self.maxs);

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

#ifdef SSQC
//--------------------------------------------------------------
float(entity to, float netflags) base_trigger_push_netsend =
{
WriteShort (MSG_ENTITY, self.classtype);
WriteFloat (MSG_ENTITY, netflags);

if (netflags & BASE_TRIGGER_NET_ORIGIN)
{
WriteCoord (MSG_ENTITY, self.origin_x);
WriteCoord (MSG_ENTITY, self.origin_y);
WriteCoord (MSG_ENTITY, self.origin_z);
}

if (netflags & BASE_TRIGGER_NET_SIZE)
{
WriteCoord (MSG_ENTITY, self.mins_x);
WriteCoord (MSG_ENTITY, self.mins_y);
WriteCoord (MSG_ENTITY, self.mins_z);
WriteCoord (MSG_ENTITY, self.maxs_x);
WriteCoord (MSG_ENTITY, self.maxs_y);
WriteCoord (MSG_ENTITY, self.maxs_z);
}

if (netflags & BASE_TRIGGER_NET_MOVEDIR)
{
WriteCoord (MSG_ENTITY, self.movedir_x);
WriteCoord (MSG_ENTITY, self.movedir_y);
WriteCoord (MSG_ENTITY, self.movedir_z);
}

if (netflags & BASE_TRIGGER_NET_STATE)
WriteByte (MSG_ENTITY, self.state);

if (netflags & BASE_TRIGGER_NET_ESTATE)
WriteFloat (MSG_ENTITY, self.estate);

return TRUE;
};

//--------------------------------------------------------------
// trigger_push_calculatevelocity
//
// Arguments:
// org - origin of the object which is to be pushed
// tgt - target entity (can be either a point or a model entity;
// if it is the latter, its midpoint is used)
// ht - jump height, measured from the higher one of org and tgt's
// midpoint
//
// Returns: velocity for the jump
// the global trigger_push_calculatevelocity_flighttime is set to the
// total jump time
//
// copied from Nexuiz source code; original can be found here:
// github.com/smlinux/nexuiz/blob/master/data/qcsrc/server/t_jumppads.qc
//--------------------------------------------------------------
vector(vector org, entity tgt, float ht)
base_trigger_push_calculatevelocity =
{
local float sdist, zdist, vs, vz, jumpheight;
local vector sdir, torg;
local vector solution;
local float flighttime;

torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;

zdist = torg_z - org_z;
sdist = vlen (torg - org - zdist * '0 0 1');
sdir = normalize (torg - org - zdist * '0 0 1');

// how high do we need to push the player?
jumpheight = fabs (ht);
if (zdist > 0)
jumpheight = jumpheight + zdist;

/*
STOP.

You will not understand the following equations anyway...
But here is what I did to get them.

I used the functions

s(t) = t * vs
z(t) = t * vz - 1/2 grav t^2

and solved for:

s(ti) = sdist
z(ti) = zdist
max(z, ti) = jumpheight

From these three equations, you will find the three
parameters vs, vz and ti.
*/

// push him so high... NOTE: sqrt(positive)!
vz = sqrt (2 * world_gravity * jumpheight);

// we start with downwards velocity only if it's a downjump
// and the jump apex should be outside the jump!
if (ht < 0)
if (zdist < 0)
vz = -vz;

// equation "z(ti) = zdist"
solution = solve_quadratic (0.5 * world_gravity, -vz, zdist);
// ALWAYS solvable because jumpheight >= zdist
if (!solution_z)
// just in case it is not solvable due to roundoff
// errors, assume two equal solutions at their center
// (this is mainly for the usual case with ht == 0)
solution_y = solution_x;
if (zdist == 0)
// solution_x is 0 in this case, so don't use it, but
// rather use solution_y (which will be
// sqrt(0.5 * jumpheight / grav), actually)
solution_x = solution_y;

if (zdist < 0)
{
// down-jump
if (ht < 0)
{
// almost straight line type
// jump apex is before the jump
// we must take the larger one
flighttime = solution_y;
}
else
{
// regular jump
// jump apex is during the jump
// we must take the larger one too
flighttime = solution_y;
}
}
else
{
// up-jump
if (ht < 0)
{
// almost straight line type
// jump apex is after the jump
// we must take the smaller one
flighttime = solution_x;
}
else
{
// regular jump
// jump apex is during the jump
// we must take the larger one
flighttime = solution_y;
}
}
vs = sdist / flighttime;

// finally calculate the velocity
return sdir * vs + '0 0 1' * vz;
};

//--------------------------------------------------------------
void() base_trigger_push_findtarget =
{
self.enemy = find (world, targetname, self.target);
if (self.enemy)
{
local vector org;
org = (self.absmin + self.absmax) * 0.5;
org_z = self.absmax_z - -24; // - PL_MIN_z;
self.movedir = base_trigger_push_calculatevelocity (
org, self.enemy, self.height);
self.state = TRIGGER_PUSH_STATE_TARGETED;
self.SendFlags |= BASE_TRIGGER_NET_MOVEDIR;
self.SendFlags |= BASE_TRIGGER_NET_STATE;
}
};
#endif

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void() base_trigger_push_touch =
{
if (self.estate != STATE_ACTIVE)
return;

// from Copper -- dumptruck_ds
if (other.movetype == MOVETYPE_NOCLIP)
return;

#ifdef SSQC
// try to find target again if necessary
if (self.target && (!self.enemy))
base_trigger_push_findtarget ();

// we have a target, set movedir accordingly
if (self.target && self.enemy)
{
self.movedir = base_trigger_push_calculatevelocity (
other.origin, self.enemy, self.height);
self.state = TRIGGER_PUSH_STATE_TARGETED;
self.SendFlags |= BASE_TRIGGER_NET_MOVEDIR |
BASE_TRIGGER_NET_STATE;
}
#endif

if (other.classtype == CT_PROJECTILE_GRENADE)
{
if (self.state == TRIGGER_PUSH_STATE_TARGETED)
other.velocity = self.movedir;
else
other.velocity = self.speed * self.movedir * 10;
}
#if defined(CSQC)
else if (other.classtype == CT_PLAYER)
#elif defined(SSQC)
else if (other.health > 0)
#endif
{
if (self.state == TRIGGER_PUSH_STATE_TARGETED)
other.velocity = self.movedir;
else
other.velocity = self.speed * self.movedir * 10;

if (other.classtype == CT_PLAYER)
{
// signal pmove that player has touched a
// push brush -- CEV
other.pm_flags |= PMF_PUSHED;
other.pm_timer = 0;

#ifdef SSQC
// carry on with normal push sound behavior
if (!(self.spawnflags & TRIGGER_PUSH_SILENT) &&
other.fly_sound < time)
{
if (!(self.spawnflags &
TRIGGER_PUSH_NOISE))
{
other.fly_sound = time + 1.5;
sound (other, CHAN_AUTO,
"ambience/windfly.wav",
VOL_HIGH, ATTN_NORM);
}
else
{
other.fly_sound = time + 1.5;
sound (other, CHAN_AUTO,
self.noise,
VOL_HIGH, ATTN_NORM);
}
}
#endif
}
}

#ifdef SSQC
if (self.spawnflags & TRIGGER_PUSH_ONCE)
remove (self);
#endif
};
#endif
// };

/*QUAKED trigger_push (.5 .5 .5) ? TRIGGER_PUSH_ONCE 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
Pushes the player
*/
//----------------------------------------------------------------------
// class trigger_push: base_trigger_push
// {
//--------------------------------------------------------------
// dumptruck_ds
//--------------------------------------------------------------
/*
void() trigger_push_use =
{
self.is_waiting = !self.is_waiting;
}
*/

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) trigger_push_init =
{
e.classname = "trigger_push";
e.classtype = CT_TRIGGER_PUSH;
e.touch = base_trigger_push_touch;

base_trigger_init (e);

#ifdef SSQC
precache_sound ("ambience/windfly.wav");

if (!e.speed)
e.speed = 1000;

if (e.target != __NULL__ && e.target != "")
{
// attempt to find the target
base_trigger_push_findtarget ();
if (!e.enemy)
if (!e.movedir)
// TODO CEV is this up? I don't remember
e.movedir = '0 0 180';
}

sub_checkwaiting (e);

e.SendEntity = base_trigger_push_netsend;
e.SendFlags |= BASE_TRIGGER_NET_ORIGIN |
BASE_TRIGGER_NET_SIZE |
BASE_TRIGGER_NET_STATE |
BASE_TRIGGER_NET_ESTATE |
BASE_TRIGGER_NET_SPEED;
if (e.movedir)
e.SendFlags |= BASE_TRIGGER_NET_MOVEDIR;
#endif
};
#endif

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

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

/*QUAKED trigger_push_custom (.5 .5 .5) ? TRIGGER_PUSH_ONCE TRIGGER_PUSH_STARTOFF TRIGGER_PUSH_SILENT 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


dumptruck_ds

trigger_push_custom is a new entity. This can be used to create traps,
jumppads, currents in water and more.

If TRIGGER_PUSH_STARTOFF flag is set, this disables the trigger. This can
be targeted and toggled off and on. If the TRIGGER_PUSH_SILENT flag is set
it won't make the windfly sound. Use TRIGGER_PUSH_NOISE spawnflag and the
noise key/value to use a custom push sound. Custom sounds should be "one
off" sounds NOT be looping.

Adapted from Hipnotic's func_togglewall
*/
//----------------------------------------------------------------------
// class trigger_push_custom: base_trigger_push
// {
#ifdef SSQC
//--------------------------------------------------------------
// dumptruck_ds was based on hipnotic blocker_use now Alkaline estate
//--------------------------------------------------------------
void() trigger_push_custom_use =
{
if (self.estate != STATE_ACTIVE)
self.estate = STATE_ACTIVE;
else
self.estate = STATE_INACTIVE;
};
#endif

#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) trigger_push_custom_init =
{
e.classname = "trigger_push_custom";
e.classtype = CT_TRIGGER_PUSH_CUSTOM;
e.touch = base_trigger_push_touch;

#ifdef SSQC
e.use = trigger_push_custom_use;
#endif

base_trigger_init (e);

#ifdef SSQC
precache_sound ("ambience/windfly.wav");

if (e.spawnflags & TRIGGER_PUSH_STARTOFF)
e.estate = STATE_INACTIVE;

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

if (!e.speed)
e.speed = 1000;

if (e.target != __NULL__ && e.target != "")
{
// attempt to find the target
base_trigger_push_findtarget ();
if (!e.enemy)
if (!e.movedir)
// TODO CEV is this up? I don't remember
e.movedir = '0 0 180';
}

sub_checkwaiting (e);

e.SendEntity = base_trigger_push_netsend;
e.SendFlags |= BASE_TRIGGER_NET_ORIGIN |
BASE_TRIGGER_NET_SIZE |
BASE_TRIGGER_NET_STATE |
BASE_TRIGGER_NET_ESTATE |
BASE_TRIGGER_NET_SPEED;
if (e.movedir)
e.SendFlags |= BASE_TRIGGER_NET_MOVEDIR;
#endif
};
#endif

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

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

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

Log push.qc

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