2020-01-05 02:25:00 +00:00
|
|
|
#include "g_local.h"
|
|
|
|
|
|
|
|
//====================================================================
|
|
|
|
|
|
|
|
/* FUNC_PENDULUM
|
|
|
|
|
|
|
|
SF 1 = START_ON
|
|
|
|
2 = STOP_AT_TOP
|
|
|
|
8 = SLOW (internal use only, used to slow a pendulum to a stop after
|
|
|
|
being blocked)
|
|
|
|
16= STOPPING (internal use only, set when a STOP_AT_TOP pendulum is triggered off)
|
|
|
|
|
|
|
|
radius = length of pendulum, used for motion equation (not impact)
|
|
|
|
distance = total arc that pendulum moves through, must be < 360
|
|
|
|
move_origin = vector from origin to c.g. of pendulum, used for impact
|
|
|
|
mass = mass of pendulum, used for knockback of func_pushables (not players/monsters)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define SF_PENDULUM_STARTON 1
|
|
|
|
#define SF_PENDULUM_STOP_AT_TOP 2
|
|
|
|
#define SF_PENDULUM_SLOW 8
|
|
|
|
#define SF_PENDULUM_STOPPING 16
|
|
|
|
|
|
|
|
void box_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
|
|
|
void pendulum_blocked (edict_t *self, edict_t *other)
|
|
|
|
{
|
|
|
|
trace_t trace;
|
|
|
|
vec3_t angles;
|
|
|
|
vec3_t forward, left, up;
|
|
|
|
vec3_t dir;
|
|
|
|
vec3_t f1, l1, u1;
|
|
|
|
vec3_t point;
|
|
|
|
vec3_t origin;
|
|
|
|
vec3_t new_velocity;
|
|
|
|
vec3_t new_origin;
|
|
|
|
float speed;
|
|
|
|
int damage;
|
|
|
|
|
|
|
|
// Since this routine is called in response to being blocked,
|
|
|
|
// the current s.angles is for the LAST frame, not the proposed
|
|
|
|
// angles for the current frame. Since we're basically overriding
|
|
|
|
// normal physics here, go ahead and move to new location. This
|
|
|
|
// means that later on we have to move blocker NOW rather than
|
|
|
|
// relying on its velocity to get it out of the way.
|
|
|
|
// BUT... trouble is doing this with players ends up giving
|
|
|
|
// goofy direction in some cases. For players/monsters, use old
|
|
|
|
// angles but STILL move 'em out of the way immediately
|
2021-01-26 05:08:08 +00:00
|
|
|
if (other->client || (other->svflags & SVF_MONSTER))
|
2020-01-05 02:25:00 +00:00
|
|
|
VectorCopy(self->s.angles,angles);
|
|
|
|
else
|
|
|
|
VectorMA(self->s.angles,FRAMETIME,self->avelocity,angles);
|
|
|
|
|
|
|
|
AngleVectors(angles,forward,left,up);
|
|
|
|
|
|
|
|
speed = fabs(self->avelocity[ROLL]) * M_PI / 180. * self->radius;
|
2021-01-26 05:08:08 +00:00
|
|
|
if ( (level.time > self->touch_debounce_time) && (speed > 200) )
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
damage = (int)( self->dmg * (speed-200)/100 );
|
|
|
|
self->touch_debounce_time = level.time + 0.5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
damage = 0;
|
|
|
|
|
|
|
|
VectorAdd(other->s.origin,other->origin_offset,origin);
|
|
|
|
VectorCopy(left,dir);
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->avelocity[ROLL] > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
VectorNegate(dir,dir);
|
|
|
|
|
|
|
|
VectorScale(forward,self->move_origin[0],f1);
|
|
|
|
VectorScale(left,-self->move_origin[1],l1);
|
|
|
|
VectorScale(up,self->move_origin[2],u1);
|
|
|
|
VectorAdd(self->s.origin,f1,point);
|
|
|
|
VectorAdd(point,l1,point);
|
|
|
|
VectorAdd(point,u1,point);
|
|
|
|
VectorSubtract(origin,point,point);
|
|
|
|
VectorNormalize(point);
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (other->client || (other->svflags & SVF_MONSTER))
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if ((point[2] < -0.7) && other->groundentity)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, DAMAGE_NO_PROTECTION, MOD_CRUSH);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dir[2] = max(1.0,fabs(dir[2]));
|
|
|
|
VectorNormalize(dir);
|
|
|
|
// Normal kickback takes too long to take effect and allows embedment. Move
|
|
|
|
// the blocker NOW.
|
|
|
|
// Give a minimum speed so we can get the poor fool out of the way
|
|
|
|
speed = max(100,speed);
|
|
|
|
VectorScale(dir,speed,new_velocity);
|
|
|
|
VectorMA(other->s.origin,FRAMETIME,new_velocity,new_origin);
|
|
|
|
other->solid = SOLID_NOT;
|
|
|
|
gi.linkentity(other);
|
|
|
|
trace = gi.trace(other->s.origin,other->mins,other->maxs,new_origin,self,other->clipmask);
|
|
|
|
VectorCopy(trace.endpos,other->s.origin);
|
|
|
|
VectorCopy(new_velocity,other->velocity);
|
|
|
|
other->solid = SOLID_BBOX;
|
|
|
|
gi.linkentity(other);
|
|
|
|
T_Damage (other, self, self, dir, other->s.origin, vec3_origin, damage, 0, 0, MOD_CRUSH);
|
|
|
|
}
|
2021-01-26 05:08:08 +00:00
|
|
|
else if (other->solid == SOLID_BSP)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
// Other is most likely a func_pushable, since almost all other bmodels aren't
|
|
|
|
// clipped to MOVETYPE_PUSH
|
|
|
|
|
|
|
|
vec3_t org, mins, maxs;
|
|
|
|
vec3_t vn2;
|
|
|
|
qboolean block;
|
|
|
|
float e=self->attenuation; // coefficient of restitution
|
|
|
|
float m=(float)(other->mass)/(float)(self->mass);
|
|
|
|
float v11 = speed;
|
|
|
|
float v21; // Initial speed of other in the impact direction
|
|
|
|
float v12, v22;
|
|
|
|
float new_rspeed;
|
|
|
|
float sgor, time, wave;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (v11 >= 100)
|
2020-01-05 02:25:00 +00:00
|
|
|
gi.sound (self, 0, self->noise_index, 1, 1, 0);
|
|
|
|
|
|
|
|
// If other is on the ground, push it UP regardless of dir
|
2021-01-26 05:08:08 +00:00
|
|
|
if (other->groundentity)
|
2020-01-05 02:25:00 +00:00
|
|
|
dir[2] = max(1.0,fabs(dir[2]));
|
|
|
|
VectorNormalize(dir);
|
|
|
|
|
|
|
|
// If pendulum hits crate from above and crate is on the ground,
|
|
|
|
// destroy the crate. This may not be realistic, but there's really
|
|
|
|
// no other way since if we stop the pendulum we'd then have to
|
|
|
|
// continously monitor whether the crate moved away or not.
|
2021-01-26 05:08:08 +00:00
|
|
|
if ((point[2] < -0.7) && (other->velocity[2] == 0))
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
box_die (other, self, self, 100000, point);
|
|
|
|
return;
|
|
|
|
}
|
2021-01-26 05:08:08 +00:00
|
|
|
if (e > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
v21 = VectorLength(other->velocity);
|
2021-01-26 05:08:08 +00:00
|
|
|
if (v21 > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
VectorCopy(other->velocity,vn2);
|
|
|
|
VectorNormalize(vn2);
|
|
|
|
v21 *= DotProduct(dir,vn2);
|
|
|
|
}
|
|
|
|
|
|
|
|
v22 = ( e*(v11-v21) + v11 + m*v21 ) / (1.0 + m);
|
|
|
|
v12 = v22 + e*(v21-v11);
|
|
|
|
// gi.dprintf("v11=%g, v21=%g, v12=%g, v22=%g\n",v11,v21,v12,v22);
|
|
|
|
// gi.dprintf("av[ROLL]=%g, roll=%g\n",self->avelocity[ROLL],angles[ROLL]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
v12 = v11;
|
2021-01-26 05:08:08 +00:00
|
|
|
if (other->mass > self->mass)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
block = true;
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
gi.linkentity(self);
|
|
|
|
goto deadstop;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
v22 = v11 * (float)self->mass/(float)other->mass;
|
|
|
|
}
|
|
|
|
VectorScale(dir,v22,new_velocity);
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (v12 < 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
// Reverse rotation.
|
|
|
|
new_rspeed = fabs(v12) / (M_PI / 180. * self->radius);
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->avelocity[ROLL] > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
self->avelocity[ROLL] = -new_rspeed;
|
|
|
|
else
|
|
|
|
self->avelocity[ROLL] = new_rspeed;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Continuing to move in same direction, though slower.
|
|
|
|
new_rspeed = v12 / (M_PI / 180. * self->radius);
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->avelocity[ROLL] > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
self->avelocity[ROLL] = new_rspeed;
|
|
|
|
else
|
|
|
|
self->avelocity[ROLL] = -new_rspeed;
|
|
|
|
}
|
|
|
|
sgor = sqrt( (float)sv_gravity->value / self->radius );
|
|
|
|
wave = fabs( self->avelocity[ROLL] / (angles[ROLL] * sgor) );
|
|
|
|
wave = atan(wave);
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->avelocity[ROLL] >= 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (angles[ROLL] > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
wave = M_PI - wave;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (angles[ROLL] > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
wave = M_PI + wave;
|
|
|
|
else
|
|
|
|
wave = 2*M_PI - wave;
|
|
|
|
}
|
|
|
|
time = wave/sgor;
|
|
|
|
self->startframe = level.framenum - time*10.;
|
|
|
|
self->moveinfo.start_angles[ROLL] = -fabs(angles[ROLL] / cos(wave));
|
|
|
|
|
|
|
|
// Now we know the new pendulum velocity and crate velocity, *assuming*
|
|
|
|
// nothing else is in the way. Now check to see if crate hits anything
|
|
|
|
// else.
|
|
|
|
|
|
|
|
VectorAdd(other->s.origin,other->origin_offset,org);
|
|
|
|
VectorMA(org,FRAMETIME,new_velocity,new_origin);
|
|
|
|
|
|
|
|
// Temporarily make crate nonsolid so we can ignore pendulum in our trace
|
|
|
|
// (rather than crate)
|
|
|
|
other->solid = SOLID_NOT;
|
|
|
|
gi.linkentity(other);
|
|
|
|
VectorSubtract(other->mins,other->origin_offset,mins);
|
|
|
|
VectorSubtract(other->maxs,other->origin_offset,maxs);
|
|
|
|
trace = gi.trace (org, mins, maxs, new_origin, self, other->clipmask);
|
|
|
|
// restore solidity of crate
|
|
|
|
other->solid = SOLID_BSP;
|
2021-01-26 05:08:08 +00:00
|
|
|
if (trace.startsolid)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
// Things are completely fouled up. Nuke other and go away.
|
|
|
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
|
|
|
|
if (other)
|
|
|
|
BecomeExplosion1 (other);
|
|
|
|
return;
|
|
|
|
}
|
2021-01-26 05:08:08 +00:00
|
|
|
else if (trace.fraction < 1.0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
vec3_t vec;
|
|
|
|
float dist;
|
|
|
|
VectorSubtract(trace.endpos,org,vec);
|
|
|
|
dist = VectorLength(vec);
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if ( (trace.ent->client) || (trace.ent->flags & SVF_MONSTER))
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
float delta=FRAMETIME*VectorLength(new_velocity);
|
|
|
|
|
|
|
|
// If a player or monster is in the way of the crate, AND
|
|
|
|
// the pendulum speed is > 100, throw 'em out of the way.
|
|
|
|
// If pendulum tangential speed is < 100, give up.
|
2021-01-26 05:08:08 +00:00
|
|
|
if (speed < 100)
|
2020-01-05 02:25:00 +00:00
|
|
|
block = true;
|
|
|
|
else
|
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (dist < delta)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
VectorScale(new_velocity,1.25,trace.ent->velocity);
|
|
|
|
VectorMA(trace.ent->s.origin,FRAMETIME,trace.ent->velocity,trace.ent->s.origin);
|
|
|
|
gi.linkentity(trace.ent);
|
|
|
|
}
|
|
|
|
block = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (dist < speed*FRAMETIME)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
block = true;
|
|
|
|
VectorScale(vec,10.,other->velocity);
|
|
|
|
VectorMA(other->s.origin,FRAMETIME,other->velocity,other->s.origin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
block = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
block = false;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!block)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
VectorCopy(new_velocity,other->velocity);
|
|
|
|
VectorMA(other->s.origin,FRAMETIME,other->velocity,other->s.origin);
|
|
|
|
}
|
|
|
|
gi.linkentity(other);
|
|
|
|
|
|
|
|
// Final checks:
|
|
|
|
// 1) If pendulum after-impact speed is < 100, that's too damn slow.
|
|
|
|
// Lie and say it's blocked
|
|
|
|
// 2) If not blocked, in its new position test for intersection
|
|
|
|
// of crate and pendulum. If they intersect, then most likely pendulum is
|
|
|
|
// moving VERY slowly and we need to reverse direction NOW to prevent
|
|
|
|
// embedment.
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!block)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (fabs(v12) < 100)
|
2020-01-05 02:25:00 +00:00
|
|
|
block = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorAdd(other->s.origin,other->origin_offset,org);
|
|
|
|
trace = gi.trace(org,mins,maxs,org,other,MASK_SOLID);
|
2021-01-26 05:08:08 +00:00
|
|
|
if (trace.startsolid)
|
2020-01-05 02:25:00 +00:00
|
|
|
block = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deadstop:
|
|
|
|
T_Damage (other, self, self, dir, other->s.origin, vec3_origin, damage, 0, 0, MOD_CRUSH);
|
2021-01-26 05:08:08 +00:00
|
|
|
if ( block )
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
// Then this sucker will still be in the way. Reverse rotation, slow, or stop
|
2021-01-26 05:08:08 +00:00
|
|
|
if (fabs(angles[ROLL]) > 2)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (abs(angles[ROLL]) < 10)
|
2020-01-05 02:25:00 +00:00
|
|
|
self->spawnflags |= SF_PENDULUM_SLOW;
|
|
|
|
self->moveinfo.start_angles[ROLL] = angles[ROLL];
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
self->startframe = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self->spawnflags &= ~SF_PENDULUM_STARTON;
|
|
|
|
self->moveinfo.start_angles[ROLL] = 0;
|
|
|
|
VectorClear(self->s.angles);
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
}
|
|
|
|
gi.linkentity(self);
|
|
|
|
}
|
2021-01-26 05:08:08 +00:00
|
|
|
else if ((fabs(self->avelocity[ROLL]) < 10) && (fabs(self->s.angles[ROLL]) < 10))
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
self->spawnflags |= SF_PENDULUM_SLOW;
|
|
|
|
self->moveinfo.start_angles[ROLL] = angles[ROLL];
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
self->startframe = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
|
|
|
|
// if it's still there, nuke it
|
|
|
|
if (other)
|
|
|
|
BecomeExplosion1 (other);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pendulum_rotate (edict_t *self)
|
|
|
|
{
|
|
|
|
float this_angle;
|
|
|
|
float wave;
|
|
|
|
float sgor;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!(self->spawnflags & SF_PENDULUM_STARTON))
|
2020-01-05 02:25:00 +00:00
|
|
|
return;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->spawnflags & SF_PENDULUM_SLOW)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->startframe == 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
// Then we just started moving again after being blocked
|
|
|
|
self->avelocity[ROLL] = -self->s.angles[ROLL];
|
|
|
|
self->startframe = level.framenum;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float next_angle;
|
|
|
|
|
|
|
|
next_angle = self->s.angles[ROLL] + self->avelocity[ROLL]*FRAMETIME;
|
2021-01-26 05:08:08 +00:00
|
|
|
if ( (next_angle >= 0 && self->s.angles[ROLL] < 0) ||
|
2020-01-05 02:25:00 +00:00
|
|
|
(next_angle <= 0 && self->s.angles[ROLL] > 0) )
|
|
|
|
{
|
|
|
|
VectorClear(self->s.angles);
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
gi.linkentity(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self->nextthink = level.time + FRAMETIME;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float old_velocity = self->avelocity[ROLL];
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!self->startframe)
|
2020-01-05 02:25:00 +00:00
|
|
|
self->startframe = level.framenum;
|
|
|
|
|
|
|
|
sgor = sqrt( (float)sv_gravity->value / self->radius );
|
|
|
|
wave = sgor * (level.framenum - self->startframe) * 0.1;
|
|
|
|
this_angle = self->moveinfo.start_angles[ROLL] * cos(wave);
|
|
|
|
self->avelocity[ROLL] = -self->moveinfo.start_angles[ROLL] * sgor * sin(wave);
|
2021-01-26 05:08:08 +00:00
|
|
|
if ( (self->spawnflags & SF_PENDULUM_STOPPING) && (cos(wave) > 0.0))
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if ( ((old_velocity > 0) && (self->avelocity[ROLL] <= 0)) ||
|
2020-01-05 02:25:00 +00:00
|
|
|
((old_velocity < 0) && (self->avelocity[ROLL] >= 0)) )
|
|
|
|
{
|
|
|
|
self->spawnflags &= ~SF_PENDULUM_STARTON;
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
self->nextthink = 0;
|
|
|
|
gi.linkentity(self);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self->s.angles[ROLL] = this_angle;
|
|
|
|
self->nextthink = level.time + FRAMETIME;
|
|
|
|
}
|
|
|
|
gi.linkentity(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pendulum_use (edict_t *self, edict_t *other, edict_t *activator)
|
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->spawnflags & SF_PENDULUM_STARTON)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->spawnflags & SF_PENDULUM_STOP_AT_TOP)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
self->spawnflags |= SF_PENDULUM_STOPPING;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorClear(self->avelocity);
|
|
|
|
self->spawnflags &= ~SF_PENDULUM_STARTON;
|
|
|
|
gi.linkentity(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self->spawnflags |= SF_PENDULUM_STARTON;
|
|
|
|
self->spawnflags &= ~SF_PENDULUM_STOPPING;
|
|
|
|
self->think = pendulum_rotate;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->delay > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
float delay;
|
|
|
|
delay = self->delay * 2.0 * M_PI * sqrt(self->radius/(float)sv_gravity->value);
|
|
|
|
delay = 0.1 * (int)(10*delay);
|
|
|
|
self->nextthink = level.time + delay;
|
|
|
|
self->startframe = level.framenum + delay*10;
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!(self->spawnflags & SF_PENDULUM_STOP_AT_TOP))
|
2020-01-05 02:25:00 +00:00
|
|
|
self->delay = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-26 05:08:08 +00:00
|
|
|
if (self->s.angles[ROLL] == self->moveinfo.start_angles[ROLL])
|
2020-01-05 02:25:00 +00:00
|
|
|
self->startframe = level.framenum;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float t;
|
|
|
|
t = acos (self->s.angles[ROLL] / self->moveinfo.start_angles[ROLL]);
|
|
|
|
t /= sqrt((float)sv_gravity->value / self->radius);
|
|
|
|
self->startframe = level.framenum - t*10;
|
|
|
|
}
|
|
|
|
self->think(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pendulum_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
|
|
{
|
|
|
|
// Mostly copied from func_explosive_explode. We can't use that function because
|
|
|
|
// the origin is a bit different.
|
|
|
|
|
|
|
|
vec3_t origin;
|
|
|
|
vec3_t forward, left, up;
|
|
|
|
vec3_t chunkorigin;
|
|
|
|
vec3_t size;
|
|
|
|
int count;
|
|
|
|
int mass;
|
|
|
|
|
|
|
|
// Particles originate from business end of pendulum
|
|
|
|
AngleVectors(self->s.angles,forward,left,up);
|
|
|
|
VectorScale(forward,self->move_origin[0],forward);
|
|
|
|
VectorScale(left,-self->move_origin[1],left);
|
|
|
|
VectorScale(up,self->move_origin[2],up);
|
|
|
|
VectorAdd(self->s.origin,forward,origin);
|
|
|
|
VectorAdd(origin,left,origin);
|
|
|
|
VectorAdd(origin,up,origin);
|
|
|
|
|
|
|
|
self->mass *= 2;
|
|
|
|
self->takedamage = DAMAGE_NO;
|
|
|
|
|
|
|
|
VectorSubtract (origin, self->enemy->s.origin, self->velocity);
|
|
|
|
VectorNormalize (self->velocity);
|
|
|
|
VectorScale (self->velocity, 150, self->velocity);
|
|
|
|
|
|
|
|
// start chunks towards the center
|
|
|
|
VectorScale (size, 0.5, size);
|
|
|
|
|
|
|
|
mass = self->mass;
|
|
|
|
if (!mass)
|
|
|
|
mass = 75;
|
|
|
|
|
|
|
|
// big chunks
|
|
|
|
if (mass >= 100)
|
|
|
|
{
|
|
|
|
count = mass / 100;
|
|
|
|
if (count > 8)
|
|
|
|
count = 8;
|
2021-01-26 05:08:08 +00:00
|
|
|
while (count--)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
chunkorigin[0] = origin[0] + crandom() * size[0];
|
|
|
|
chunkorigin[1] = origin[1] + crandom() * size[1];
|
|
|
|
chunkorigin[2] = origin[2] + crandom() * size[2];
|
2021-01-26 05:08:08 +00:00
|
|
|
ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin, 0, 0, 0);
|
2020-01-05 02:25:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// small chunks
|
|
|
|
count = mass / 25;
|
|
|
|
if (count > 16)
|
|
|
|
count = 16;
|
2021-01-26 05:08:08 +00:00
|
|
|
while (count--)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
chunkorigin[0] = origin[0] + crandom() * size[0];
|
|
|
|
chunkorigin[1] = origin[1] + crandom() * size[1];
|
|
|
|
chunkorigin[2] = origin[2] + crandom() * size[2];
|
2021-01-26 05:08:08 +00:00
|
|
|
ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin, 0, 0, 0);
|
2020-01-05 02:25:00 +00:00
|
|
|
}
|
|
|
|
G_FreeEdict (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SP_func_pendulum (edict_t *ent)
|
|
|
|
{
|
|
|
|
float max_speed;
|
|
|
|
|
|
|
|
ent->class_id = ENTITY_FUNC_PENDULUM;
|
|
|
|
|
|
|
|
ent->solid = SOLID_BSP;
|
|
|
|
ent->movetype = MOVETYPE_PENDULUM;
|
|
|
|
if (ent->distance)
|
|
|
|
st.distance = ent->distance;
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!st.distance)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->moveinfo.distance = 90;
|
|
|
|
else
|
|
|
|
ent->moveinfo.distance = st.distance;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (st.noise)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->noise_index = gi.soundindex(st.noise);
|
|
|
|
else
|
|
|
|
ent->noise_index = gi.soundindex("world/land.wav");
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (ent->moveinfo.distance >= 360)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
gi.dprintf("func_pendulum distance must be < 360\n");
|
|
|
|
ent->moveinfo.distance = 359.;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ent->speed)
|
|
|
|
ent->speed = 100;
|
|
|
|
if (!ent->radius)
|
|
|
|
ent->radius = 100;
|
|
|
|
if (!ent->mass)
|
|
|
|
ent->mass = 200;
|
|
|
|
if (st.phase > 0)
|
|
|
|
ent->delay = st.phase;
|
|
|
|
else
|
|
|
|
ent->delay = 0;
|
2021-01-26 05:08:08 +00:00
|
|
|
if (ent->delay > 1.0)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->delay -= (int)(ent->delay);
|
|
|
|
|
|
|
|
// Coefficient of restitution - default = 0.5, must be <= 1.0
|
2021-01-26 05:08:08 +00:00
|
|
|
if (ent->attenuation == 0.0)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->attenuation = 0.5;
|
2021-01-26 05:08:08 +00:00
|
|
|
else if (ent->attenuation > 1.0)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->attenuation = 1.0;
|
|
|
|
|
|
|
|
if (!ent->dmg)
|
|
|
|
ent->dmg = 5;
|
|
|
|
// This is the damage delivered by the pendulum at max speed. Convert to
|
|
|
|
// a damage scale used in our damage equation.
|
|
|
|
max_speed = ent->moveinfo.distance/2 * M_PI / 180. * sqrt((float)sv_gravity->value * ent->radius );
|
2021-01-26 05:08:08 +00:00
|
|
|
if (max_speed <= 200.)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->dmg = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float dmg;
|
|
|
|
dmg = (float)(ent->dmg) * 100. / (max_speed - 200.);
|
|
|
|
ent->dmg = (int)(dmg - 0.5) + 1;
|
|
|
|
}
|
2021-01-26 05:08:08 +00:00
|
|
|
if (ent->health > 0)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
ent->die = pendulum_die;
|
|
|
|
ent->takedamage = DAMAGE_YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
ent->blocked = pendulum_blocked;
|
|
|
|
// ent->touch = pendulum_touch;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!ent->accel)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->accel = 1;
|
|
|
|
else if (ent->accel > ent->speed)
|
|
|
|
ent->accel = ent->speed;
|
|
|
|
|
2021-01-26 05:08:08 +00:00
|
|
|
if (!ent->decel)
|
2020-01-05 02:25:00 +00:00
|
|
|
ent->decel = 1;
|
|
|
|
else if (ent->decel > ent->speed)
|
|
|
|
ent->decel = ent->speed;
|
|
|
|
|
|
|
|
gi.setmodel (ent, ent->model);
|
|
|
|
|
|
|
|
ent->s.angles[ROLL] = ent->moveinfo.distance/2;
|
|
|
|
ent->moveinfo.start_angles[ROLL] = ent->s.angles[ROLL];
|
2021-01-26 05:08:08 +00:00
|
|
|
if (ent->spawnflags & SF_PENDULUM_STARTON)
|
2020-01-05 02:25:00 +00:00
|
|
|
{
|
|
|
|
ent->think = pendulum_rotate;
|
|
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ent->use = pendulum_use;
|
|
|
|
}
|
|
|
|
gi.linkentity (ent);
|
|
|
|
|
|
|
|
}
|
|
|
|
|