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
Date | Commit Message | Author | + | - |
---|---|---|---|---|
2024-09-17 | Ice, improved stairs, other movement changes | cev | +5 | |
2024-07-17 | pmove changes, smooth crouching | cev | +1 | -1 |
2024-07-03 | pmove changes and fixes, improved climbing | cev | +1 | -1 |
2024-06-15 | Major update, committing as-is, will have bugs | cev | +187 | -9 |
2024-04-08 | Registered monsters, projectile bugfixes | cev | +1 | -1 |
2024-03-24 | 2nd pass refactor, rework QC class structure | cev | +130 | -90 |
2024-02-27 | Bullet projectile, pmove changes, misc | cev | +1 | |
2024-02-18 | Client/player, projectiles, entrypoints refactor | cev | +4 | -6 |
2024-01-31 | Class based monster refactor & start projectiles | cev | +26 | -11 |
2024-01-09 | Continue OO / Class-based refactor | cev | +32 | -37 |
2023-12-09 | Start OO / class-based refactor, work on items | cev | +229 | -225 |
2023-11-20 | changes to movement, build environment, file reorg | cev | +1 | -61 |
2023-11-16 | pmove bug fixes, moved q3 compat code, cleanup | cev | +429 |
Return to the top of this page or return to the overview of this repo.