thirtyflightsofloving/missionpack/g_vehicle.c
Knightmare66 7b6f4281ad Added POSTTHINK_CHILD_MOVEMENT macro to new movewith host entities in g_spawn.c->SpawnEntities() in default Lazarus DLL.
Added Zaero flare gun to no-autoswitch in p_weapon.c->Pickup_Weapon() in missionpack DLL.
Added LM plasma rifle to p_weapon.c->NoAmmoWeaponChange() in missionpack DLL.
Added output of modelindex5&6, alpha, and attenuation to properties command  in missionpack DLL.
Added nogib and environment spawnflags to trigger_hurt and trigger_hurt_boox in missionpack DLL.
Added MONSTER_KNOWS_MIRRORS flag to berserker, barracuda shark, and mutant in missionpack DLL.
Added entity class IDs to misc_actor and target_actor in missionpack DLL.
Added syntax whitespacing to some files in missionpack DLL.
Added backpack drop to certain monsters in missionpack DLL.
Changed some sound paths for new monsters and weapons in missionpack DLL.
2020-10-29 13:03:20 -04:00

485 lines
14 KiB
C

#include "g_local.h"
#define RFAST -3
#define RMEDIUM -2
#define RSLOW -1
#define STOP 0
#define SLOW 1
#define MEDIUM 2
#define FAST 3
#define VEHICLE_BLOCK_STOPS 4
void func_vehicle_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
vec3_t origin;
vec3_t chunkorigin;
vec3_t size;
int count;
int mass;
if(self->deathtarget)
{
self->target = self->deathtarget;
G_UseTargets (self, attacker);
}
// bmodel origins are (0 0 0), we need to adjust that here
VectorScale (self->size, 0.5, size);
VectorAdd (self->absmin, size, origin);
VectorCopy (origin, self->s.origin);
self->takedamage = DAMAGE_NO;
if (self->dmg)
T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
VectorSubtract (self->s.origin, inflictor->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;
// big chunks
if (mass >= 100)
{
count = mass / 100;
if (count > 8)
count = 8;
while(count--)
{
chunkorigin[0] = origin[0] + crandom() * size[0];
chunkorigin[1] = origin[1] + crandom() * size[1];
chunkorigin[2] = origin[2] + crandom() * size[2];
ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin, 0, 0);
}
}
// small chunks
count = mass / 25;
if (count > 16)
count = 16;
while(count--)
{
chunkorigin[0] = origin[0] + crandom() * size[0];
chunkorigin[1] = origin[1] + crandom() * size[1];
chunkorigin[2] = origin[2] + crandom() * size[2];
ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin, 0, 0);
}
if (self->dmg)
BecomeExplosion1 (self);
else
G_FreeEdict (self);
}
void vehicle_blocked (edict_t *self, edict_t *other)
{
edict_t *attacker;
if((self->spawnflags & VEHICLE_BLOCK_STOPS) || (other == world))
{
VectorClear(self->velocity);
VectorClear(self->avelocity);
self->moveinfo.current_speed = 0;
gi.linkentity(self);
return;
}
if (other->takedamage)
{
if (self->teammaster->owner)
attacker = self->teammaster->owner;
else
attacker = self->owner;
T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
}
else
{
VectorClear(self->velocity);
VectorClear(self->avelocity);
self->moveinfo.current_speed = 0;
self->moveinfo.state = STOP;
gi.linkentity(self);
}
if (!(other->svflags & SVF_MONSTER) && (!other->client))
{
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
if (other)
BecomeExplosion1 (other);
return;
}
}
// Not needed, because collisions are effectively prevented by the vehicle physics in
// g_phys.c.
void vehicle_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
vec3_t dir, v;
vec3_t new_origin, new_velocity;
vec_t points;
vec_t vspeed, mspeed;
vec_t knockback;
vec3_t end;
trace_t tr;
if (other == world || (self->spawnflags & VEHICLE_BLOCK_STOPS) )
{
VectorClear(self->velocity);
VectorClear(self->avelocity);
self->moveinfo.current_speed = 0;
gi.linkentity(self);
}
if (!self->owner) return; // if vehicle isn't being driven, it can't hurt anybody
if (other == self->owner) return; // can't hurt the driver
if (other->takedamage == DAMAGE_NO) return;
// we damage func_explosives elsewhere. About all that's left to hurt are
// players and monsters
if (!other->client && !(other->svflags & SVF_MONSTER)) return;
vspeed = VectorLength(self->velocity);
if(!vspeed) return;
VectorSubtract(other->s.origin,self->s.origin,dir);
dir[2] = 0;
VectorNormalize(dir);
VectorCopy(self->velocity,v);
VectorNormalize(v);
// damage and knockback are proportional to square of velocity * mass of vehicle.
// Lessee... with a mass=2000 vehicle traveling 400 units/sec, give 100 points
// damage and a velocity of 160 to a 200 mass monster.
vspeed *= DotProduct(dir,v);
mspeed = VectorLength(other->velocity) * DotProduct(dir,v);
vspeed -= mspeed;
if(vspeed <= 0.) return;
// for speed < 200, don't do damage but move monster
if(vspeed < 200) {
if(other->mass > self->mass) vspeed *= (float)self->mass/other->mass;
VectorMA(other->velocity,vspeed,dir,new_velocity);
VectorMA(other->s.origin,FRAMETIME,new_velocity,new_origin);
new_origin[2] += 2;
// if the move would place the monster in a solid, make him go splat
VectorCopy(new_origin,end);
end[2] -= 1;
tr = gi.trace(new_origin,other->mins,other->maxs,end,self,CONTENTS_SOLID);
if(tr.startsolid)
// splat
T_Damage (other, self, self->owner, dir, self->s.origin, vec3_origin,
other->health - other->gib_health + 1, 0, 0, MOD_VEHICLE);
else
{
// go ahead and move the bastard
VectorCopy(new_velocity,other->velocity);
VectorCopy(new_origin,other->s.origin);
gi.linkentity(other);
}
return;
}
if (other->damage_debounce_time > level.time) return;
other->damage_debounce_time = level.time + 0.2;
points = 100. * (self->mass/2000.0f * vspeed*vspeed/160000);
// knockback takes too long to take effect. If we can move him w/o throwing him
// into a solid, do so NOW
dir[2] = 0.2; // make knockback slightly upward
VectorMA(other->velocity,vspeed,dir,new_velocity);
VectorMA(other->s.origin,FRAMETIME,new_velocity,new_origin);
if(gi.pointcontents(new_origin) & CONTENTS_SOLID)
knockback = (160.0f/500.0f) * 200.0f * (self->mass/2000.0f * vspeed*vspeed/160000);
else {
knockback = 0;
VectorCopy(new_velocity,other->velocity);
VectorCopy(new_origin, other->s.origin);
}
T_Damage (other, self, self->owner, dir, self->s.origin, vec3_origin,
(int)points, (int)knockback, 0, MOD_VEHICLE);
gi.linkentity(other);
}
void vehicle_disengage (edict_t *vehicle)
{
edict_t *driver;
vec3_t forward, left, f1, l1;
driver = vehicle->owner;
if(!driver) return;
AngleVectors(vehicle->s.angles, forward, left, NULL);
VectorCopy(vehicle->velocity,driver->velocity);
VectorScale(forward,vehicle->move_origin[0],f1);
VectorScale(left,-vehicle->move_origin[1],l1);
VectorAdd(vehicle->s.origin,f1,driver->s.origin);
VectorAdd(driver->s.origin,l1,driver->s.origin);
driver->s.origin[2] += vehicle->move_origin[2];
driver->vehicle = NULL;
driver->client->vehicle_framenum = level.framenum;
driver->movetype = MOVETYPE_WALK;
driver->gravity = 1;
// turn ON client side prediction for this player
driver->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
vehicle->s.sound = 0;
gi.linkentity (driver);
vehicle->owner = NULL;
}
void vehicle_think (edict_t *self)
{
float aspeed, speed, newspeed;
vec3_t forward, left, f1, l1, v;
self->nextthink = level.time + FRAMETIME;
VectorCopy(self->oldvelocity,v);
v[2] = 0;
speed = VectorLength(v);
if(speed > 0)
self->s.effects |= EF_ANIM_ALL;
else
self->s.effects &= ~EF_ANIM_ALL;
AngleVectors(self->s.angles, forward, left, NULL);
if(DotProduct(forward,self->oldvelocity) < 0) speed = -speed;
self->moveinfo.current_speed = speed;
if (self->owner)
{
// ... then we have a driver
if (self->owner->health <= 0)
{
vehicle_disengage(self);
return;
}
if (self->owner->client->use)
{
// if he's pressing the use key, and he didn't just
// get on or off, disengage
if(level.framenum - self->owner->client->vehicle_framenum > 2)
{
VectorCopy(self->velocity,self->oldvelocity);
vehicle_disengage(self);
return;
}
}
if (self->owner->client->ucmd.forwardmove != 0 && level.time > self->moveinfo.wait)
{
if(self->owner->client->ucmd.forwardmove > 0)
{
if(self->moveinfo.state < FAST)
{
self->moveinfo.state++;
self->moveinfo.next_speed = self->moveinfo.state * self->speed/3;
self->moveinfo.wait = level.time + FRAMETIME;
}
}
else
{
if(self->moveinfo.state > RFAST)
{
self->moveinfo.state--;
self->moveinfo.next_speed = self->moveinfo.state * self->speed/3;
self->moveinfo.wait = level.time + FRAMETIME;
}
}
}
if(self->moveinfo.current_speed < self->moveinfo.next_speed)
{
speed = self->moveinfo.current_speed + self->accel/10;
if(speed > self->moveinfo.next_speed) speed = self->moveinfo.next_speed;
}
else if(self->moveinfo.current_speed > self->moveinfo.next_speed)
{
speed = self->moveinfo.current_speed - self->decel/10;
if(speed < self->moveinfo.next_speed) speed = self->moveinfo.next_speed;
}
VectorScale(forward,speed,self->velocity);
if (self->owner->client->ucmd.sidemove != 0 && speed != 0 )
{
aspeed = 180.*speed/(M_PI*self->radius);
if(self->owner->client->ucmd.sidemove > 0) aspeed = -aspeed;
self->avelocity[1] = aspeed;
}
else
self->avelocity[1] = 0;
if(speed != 0)
self->s.sound = self->noise_index;
else
self->s.sound = self->noise_index2;
#ifdef LOOP_SOUND_ATTENUATION
self->s.attenuation = self->attenuation;
#endif
gi.linkentity(self);
// Copy velocities and set position of driver
VectorCopy(self->velocity,self->owner->velocity);
VectorScale(forward,self->move_origin[0],f1);
VectorScale(left,-self->move_origin[1],l1);
VectorAdd(self->s.origin,f1,self->owner->s.origin);
VectorAdd(self->owner->s.origin,l1,self->owner->s.origin);
self->owner->s.origin[2] += self->move_origin[2];
// If moving, turn driver
if(speed != 0)
{
float yaw;
yaw = self->avelocity[1]*FRAMETIME;
self->owner->s.angles[YAW] += yaw;
self->owner->client->ps.pmove.delta_angles[YAW] += ANGLE2SHORT(yaw);
self->owner->client->ps.pmove.pm_type = PM_FREEZE;
}
VectorCopy(self->velocity,self->oldvelocity);
gi.linkentity(self->owner);
}
else
{
int i;
edict_t *ent;
vec3_t dir, drive;
//
// No driver
//
// if vehicle has stopped, drop it to ground.
// otherwise slow it down
if(speed==0)
{
if(!self->groundentity)
SV_AddGravity (self);
}
else
{
// no driver... slow to an eventual stop in no more than 5 sec.
self->moveinfo.next_speed = 0;
self->moveinfo.state = STOP;
if(speed > 0)
newspeed = max(0.,speed - self->speed/50);
else
newspeed = min(0.,speed + self->speed/50);
VectorScale(forward,newspeed,self->velocity);
VectorScale(self->avelocity,newspeed/speed,self->avelocity);
VectorCopy(self->velocity,self->oldvelocity);
gi.linkentity(self);
}
// check if a player has mounted the vehicle
// first get driving position
VectorScale(forward,self->move_origin[0],f1);
VectorScale(left,-self->move_origin[1],l1);
VectorAdd(self->s.origin,f1,drive);
VectorAdd(drive,l1,drive);
drive[2] += self->move_origin[2];
// find a player
for (i=1, ent=&g_edicts[1] ; i<=maxclients->value ; i++, ent++) {
if (!ent->inuse) continue;
if (ent->movetype == MOVETYPE_NOCLIP) continue;
if (!ent->client->use) continue;
if (level.framenum - ent->client->vehicle_framenum <= 2) continue;
// determine distance from vehicle "move_origin"
VectorSubtract(drive,ent->s.origin,dir);
if (fabs(dir[2]) < 64)
dir[2] = 0;
if (VectorLength(dir) < 16) {
ent->client->vehicle_framenum = level.framenum;
// player has taken control of vehicle
// move vehicle up slightly to avoid roundoff collisions
self->s.origin[2] += 1;
gi.linkentity(self);
if(self->message)
gi.centerprintf(ent,self->message);
self->owner = ent;
ent->movetype = MOVETYPE_PUSH;
ent->gravity = 0;
ent->vehicle = self;
// turn off client side prediction for this player
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
// force a good driving position
VectorCopy(drive,self->owner->s.origin);
gi.linkentity(ent);
// vehicle idle noise
self->s.sound = self->noise_index2;
#ifdef LOOP_SOUND_ATTENUATION
self->s.attenuation = self->attenuation;
#endif
// reset wait time so we can start accelerating
self->moveinfo.wait = 0;
}
}
}
// if(self->movewith_next && (self->movewith_next->movewith_ent == self))
// set_child_movement (self);
}
void turn_vehicle (edict_t *self)
{
self->s.angles[YAW] = self->ideal_yaw;
gi.linkentity(self);
self->prethink = NULL;
}
void SP_func_vehicle (edict_t *self)
{
self->class_id = ENTITY_FUNC_VEHICLE;
self->ideal_yaw = self->s.angles[YAW];
VectorClear (self->s.angles);
self->solid = SOLID_BSP;
gi.setmodel (self, self->model);
// usermodel (for alias model vehicles) goes on modelindex2
if (self->usermodel && strlen(self->usermodel))
{
char modelname[256];
// check for "models/" already in path
if ( !strncmp(self->usermodel, "models/", 7) )
Com_sprintf(modelname, sizeof(modelname), "%s", self->usermodel);
else
Com_sprintf(modelname, sizeof(modelname), "models/%s", self->usermodel);
self->s.modelindex2 = gi.modelindex (modelname);
}
self->movetype = MOVETYPE_VEHICLE;
if (!self->speed)
self->speed = 200;
if (!self->accel)
self->accel = self->speed; // accelerates to full speed in 1 second (approximate).
if (!self->decel)
self->decel = self->accel;
if (!self->mass)
self->mass = 2000;
if (!self->radius)
self->radius = 256;
self->blocked = vehicle_blocked;
self->touch = vehicle_touch;
self->think = vehicle_think;
self->nextthink = level.time + FRAMETIME;
self->noise_index = gi.soundindex("engine/engine.wav");
self->noise_index2 = gi.soundindex("engine/idle.wav");
#ifdef LOOP_SOUND_ATTENUATION
if (self->attenuation <= 0)
self->attenuation = ATTN_IDLE;
#endif
VectorClear(self->velocity);
VectorClear(self->avelocity);
self->moveinfo.current_speed = 0;
self->moveinfo.state = STOP;
gi.linkentity (self);
VectorCopy(self->size,self->org_size);
self->postthink = train_move_children; // Knightmare- supports movewith
if (self->ideal_yaw != 0)
self->prethink = turn_vehicle;
if (self->health) {
self->die = func_vehicle_explode;
self->takedamage = DAMAGE_YES;
}
else
self->takedamage = DAMAGE_NO;
}