quake2-rerelease-dll/rerelease/xatrix/g_xatrix_weapon.cpp
2023-08-07 14:48:30 -05:00

605 lines
14 KiB
C++

// Copyright (c) ZeniMax Media Inc.
// Licensed under the GNU General Public License 2.0.
#include "../g_local.h"
// RAFAEL
void fire_blueblaster(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, effects_t effect)
{
edict_t *bolt;
trace_t tr;
bolt = G_Spawn();
bolt->s.origin = start;
bolt->s.old_origin = start;
bolt->s.angles = vectoangles(dir);
bolt->velocity = dir * speed;
bolt->svflags |= SVF_PROJECTILE;
bolt->movetype = MOVETYPE_FLYMISSILE;
bolt->flags |= FL_DODGE;
bolt->clipmask = MASK_PROJECTILE;
bolt->solid = SOLID_BBOX;
bolt->s.effects |= effect;
bolt->s.modelindex = gi.modelindex("models/objects/laser/tris.md2");
bolt->s.skinnum = 1;
bolt->s.sound = gi.soundindex("misc/lasfly.wav");
bolt->owner = self;
bolt->touch = blaster_touch;
bolt->nextthink = level.time + 2_sec;
bolt->think = G_FreeEdict;
bolt->dmg = damage;
bolt->classname = "bolt";
bolt->style = MOD_BLUEBLASTER;
gi.linkentity(bolt);
tr = gi.traceline(self->s.origin, bolt->s.origin, bolt, bolt->clipmask);
if (tr.fraction < 1.0f)
{
bolt->s.origin = tr.endpos + (tr.plane.normal * 1.f);
bolt->touch(bolt, tr.ent, tr, false);
}
}
// RAFAEL
/*
fire_ionripper
*/
THINK(ionripper_sparks) (edict_t *self) -> void
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_WELDING_SPARKS);
gi.WriteByte(0);
gi.WritePosition(self->s.origin);
gi.WriteDir(vec3_origin);
gi.WriteByte(irandom(0xe4, 0xe8));
gi.multicast(self->s.origin, MULTICAST_PVS, false);
G_FreeEdict(self);
}
// RAFAEL
TOUCH(ionripper_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
{
if (other == self->owner)
return;
if (tr.surface && (tr.surface->flags & SURF_SKY))
{
G_FreeEdict(self);
return;
}
if (self->owner->client)
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
if (other->takedamage)
{
T_Damage(other, self, self->owner, self->velocity, self->s.origin, tr.plane.normal, self->dmg, 1, DAMAGE_ENERGY, MOD_RIPPER);
}
else
{
return;
}
G_FreeEdict(self);
}
// RAFAEL
void fire_ionripper(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, effects_t effect)
{
edict_t *ion;
trace_t tr;
ion = G_Spawn();
ion->s.origin = start;
ion->s.old_origin = start;
ion->s.angles = vectoangles(dir);
ion->velocity = dir * speed;
ion->movetype = MOVETYPE_WALLBOUNCE;
ion->clipmask = MASK_PROJECTILE;
// [Paril-KEX]
if (self->client && !G_ShouldPlayersCollide(true))
ion->clipmask &= ~CONTENTS_PLAYER;
ion->solid = SOLID_BBOX;
ion->s.effects |= effect;
ion->svflags |= SVF_PROJECTILE;
ion->flags |= FL_DODGE;
ion->s.renderfx |= RF_FULLBRIGHT;
ion->s.modelindex = gi.modelindex("models/objects/boomrang/tris.md2");
ion->s.sound = gi.soundindex("misc/lasfly.wav");
ion->owner = self;
ion->touch = ionripper_touch;
ion->nextthink = level.time + 3_sec;
ion->think = ionripper_sparks;
ion->dmg = damage;
ion->dmg_radius = 100;
gi.linkentity(ion);
tr = gi.traceline(self->s.origin, ion->s.origin, ion, ion->clipmask);
if (tr.fraction < 1.0f)
{
ion->s.origin = tr.endpos + (tr.plane.normal * 1.f);
ion->touch(ion, tr.ent, tr, false);
}
}
// RAFAEL
/*
fire_heat
*/
THINK(heat_think) (edict_t *self) -> void
{
edict_t *target = nullptr;
edict_t *acquire = nullptr;
vec3_t vec;
vec3_t oldang;
float len;
float oldlen = 0;
float dot, olddot = 1;
vec3_t fwd = AngleVectors(self->s.angles).forward;
// acquire new target
while ((target = findradius(target, self->s.origin, 1024)) != nullptr)
{
if (self->owner == target)
continue;
if (!target->client)
continue;
if (target->health <= 0)
continue;
if (!visible(self, target))
continue;
//if (!infront(self, target))
// continue;
vec = self->s.origin - target->s.origin;
len = vec.length();
dot = vec.normalized().dot(fwd);
// targets that require us to turn less are preferred
if (dot >= olddot)
continue;
if (acquire == nullptr || dot < olddot || len < oldlen)
{
acquire = target;
oldlen = len;
olddot = dot;
}
}
if (acquire != nullptr)
{
oldang = self->s.angles;
vec = (acquire->s.origin - self->s.origin).normalized();
float t = self->accel;
float d = self->movedir.dot(vec);
if (d < 0.45f && d > -0.45f)
vec = -vec;
self->movedir = slerp(self->movedir, vec, t).normalized();
self->s.angles = vectoangles(self->movedir);
if (!self->enemy)
{
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/railgr1a.wav"), 1.f, 0.25f, 0);
self->enemy = acquire;
}
}
else
self->enemy = nullptr;
self->velocity = self->movedir * self->speed;
self->nextthink = level.time + FRAME_TIME_MS;
}
// RAFAEL
void fire_heat(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, float damage_radius, int radius_damage, float turn_fraction)
{
edict_t *heat;
heat = G_Spawn();
heat->s.origin = start;
heat->movedir = dir;
heat->s.angles = vectoangles(dir);
heat->velocity = dir * speed;
heat->flags |= FL_DODGE;
heat->movetype = MOVETYPE_FLYMISSILE;
heat->svflags |= SVF_PROJECTILE;
heat->clipmask = MASK_PROJECTILE;
heat->solid = SOLID_BBOX;
heat->s.effects |= EF_ROCKET;
heat->s.modelindex = gi.modelindex("models/objects/rocket/tris.md2");
heat->owner = self;
heat->touch = rocket_touch;
heat->speed = speed;
heat->accel = turn_fraction;
heat->nextthink = level.time + FRAME_TIME_MS;
heat->think = heat_think;
heat->dmg = damage;
heat->radius_dmg = radius_damage;
heat->dmg_radius = damage_radius;
heat->s.sound = gi.soundindex("weapons/rockfly.wav");
gi.linkentity(heat);
}
// RAFAEL
/*
fire_plasma
*/
TOUCH(plasma_touch) (edict_t *ent, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
{
vec3_t origin;
if (other == ent->owner)
return;
if (tr.surface && (tr.surface->flags & SURF_SKY))
{
G_FreeEdict(ent);
return;
}
if (ent->owner->client)
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
// calculate position for the explosion entity
origin = ent->s.origin + (ent->velocity * -0.02f);
if (other->takedamage)
{
T_Damage(other, ent, ent->owner, ent->velocity, ent->s.origin, tr.plane.normal, ent->dmg, 0, DAMAGE_ENERGY, MOD_PHALANX);
}
T_RadiusDamage(ent, ent->owner, (float) ent->radius_dmg, other, ent->dmg_radius, DAMAGE_ENERGY, MOD_PHALANX);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_PLASMA_EXPLOSION);
gi.WritePosition(origin);
gi.multicast(ent->s.origin, MULTICAST_PHS, false);
G_FreeEdict(ent);
}
// RAFAEL
void fire_plasma(edict_t *self, const vec3_t &start, const vec3_t &dir, int damage, int speed, float damage_radius, int radius_damage)
{
edict_t *plasma;
plasma = G_Spawn();
plasma->s.origin = start;
plasma->movedir = dir;
plasma->s.angles = vectoangles(dir);
plasma->velocity = dir * speed;
plasma->movetype = MOVETYPE_FLYMISSILE;
plasma->clipmask = MASK_PROJECTILE;
// [Paril-KEX]
if (self->client && !G_ShouldPlayersCollide(true))
plasma->clipmask &= ~CONTENTS_PLAYER;
plasma->solid = SOLID_BBOX;
plasma->svflags |= SVF_PROJECTILE;
plasma->flags |= FL_DODGE;
plasma->owner = self;
plasma->touch = plasma_touch;
plasma->nextthink = level.time + gtime_t::from_sec(8000.f / speed);
plasma->think = G_FreeEdict;
plasma->dmg = damage;
plasma->radius_dmg = radius_damage;
plasma->dmg_radius = damage_radius;
plasma->s.sound = gi.soundindex("weapons/rockfly.wav");
plasma->s.modelindex = gi.modelindex("sprites/s_photon.sp2");
plasma->s.effects |= EF_PLASMA | EF_ANIM_ALLFAST;
gi.linkentity(plasma);
}
THINK(Trap_Gib_Think) (edict_t *ent) -> void
{
if (ent->owner->s.frame != 5)
{
G_FreeEdict(ent);
return;
}
vec3_t forward, right, up;
vec3_t vec;
AngleVectors(ent->owner->s.angles, forward, right, up);
// rotate us around the center
float degrees = (150.f * gi.frame_time_s) + ent->owner->delay;
vec3_t diff = ent->owner->s.origin - ent->s.origin;
vec = RotatePointAroundVector(up, diff, degrees);
ent->s.angles[1] += degrees;
vec3_t new_origin = ent->owner->s.origin - vec;
trace_t tr = gi.traceline(ent->s.origin, new_origin, ent, MASK_SOLID);
ent->s.origin = tr.endpos;
// pull us towards the trap's center
diff.normalize();
ent->s.origin += diff * (15.0f * gi.frame_time_s);
ent->watertype = gi.pointcontents(ent->s.origin);
if (ent->watertype & MASK_WATER)
ent->waterlevel = WATER_FEET;
ent->nextthink = level.time + FRAME_TIME_S;
gi.linkentity(ent);
}
DIE(trap_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
{
BecomeExplosion1(self);
}
// RAFAEL
void SP_item_foodcube(edict_t *best);
void SpawnDamage(int type, const vec3_t &origin, const vec3_t &normal, int damage);
// RAFAEL
THINK(Trap_Think) (edict_t *ent) -> void
{
edict_t *target = nullptr;
edict_t *best = nullptr;
vec3_t vec;
float len;
float oldlen = 8000;
if (ent->timestamp < level.time)
{
BecomeExplosion1(ent);
// note to self
// cause explosion damage???
return;
}
ent->nextthink = level.time + 10_hz;
if (!ent->groundentity)
return;
// ok lets do the blood effect
if (ent->s.frame > 4)
{
if (ent->s.frame == 5)
{
bool spawn = ent->wait == 64;
ent->wait -= 2;
if (spawn)
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/trapdown.wav"), 1, ATTN_IDLE, 0);
ent->delay += 2.f;
if (ent->wait < 19)
ent->s.frame++;
return;
}
ent->s.frame++;
if (ent->s.frame == 8)
{
ent->nextthink = level.time + 1_sec;
ent->think = G_FreeEdict;
ent->s.effects &= ~EF_TRAP;
best = G_Spawn();
best->count = ent->mass;
best->s.scale = 1.f + ((ent->accel - 100.f) / 300.f) * 1.0f;
SP_item_foodcube(best);
best->s.origin = ent->s.origin;
best->s.origin[2] += 24 * best->s.scale;
best->s.angles[YAW] = frandom() * 360;
best->velocity[2] = 400;
best->think(best);
best->nextthink = 0_ms;
best->s.old_origin = best->s.origin;
gi.linkentity(best);
gi.sound(best, CHAN_AUTO, gi.soundindex("misc/fhit3.wav"), 1.f, ATTN_NORM, 0.f);
return;
}
return;
}
ent->s.effects &= ~EF_TRAP;
if (ent->s.frame >= 4)
{
ent->s.effects |= EF_TRAP;
// clear the owner if in deathmatch
if (deathmatch->integer)
ent->owner = nullptr;
}
if (ent->s.frame < 4)
{
ent->s.frame++;
return;
}
while ((target = findradius(target, ent->s.origin, 256)) != nullptr)
{
if (target == ent)
continue;
// [Paril-KEX] don't allow traps to be placed near flags or teleporters
// if it's a monster or player with health > 0
// or it's a player start point
// and we can see it
// blow up
if (target->classname && ((deathmatch->integer &&
((!strncmp(target->classname, "info_player_", 12)) ||
(!strcmp(target->classname, "misc_teleporter_dest")) ||
(!strncmp(target->classname, "item_flag_", 10))))) &&
(visible(target, ent)))
{
BecomeExplosion1(ent);
return;
}
if (!(target->svflags & SVF_MONSTER) && !target->client)
continue;
if (target != ent->teammaster && CheckTeamDamage(target, ent->teammaster))
continue;
// [Paril-KEX]
if (!deathmatch->integer && target->client)
continue;
if (target->health <= 0)
continue;
if (!visible(ent, target))
continue;
vec = ent->s.origin - target->s.origin;
len = vec.length();
if (!best)
{
best = target;
oldlen = len;
continue;
}
if (len < oldlen)
{
oldlen = len;
best = target;
}
}
// pull the enemy in
if (best)
{
if (best->groundentity)
{
best->s.origin[2] += 1;
best->groundentity = nullptr;
}
vec = ent->s.origin - best->s.origin;
len = vec.normalize();
float max_speed = best->client ? 290.f : 150.f;
best->velocity += (vec * clamp(max_speed - len, 64.f, max_speed));
ent->s.sound = gi.soundindex("weapons/trapsuck.wav");
if (len < 48)
{
if (best->mass < 400)
{
ent->takedamage = false;
ent->solid = SOLID_NOT;
ent->die = nullptr;
T_Damage(best, ent, ent->teammaster, vec3_origin, best->s.origin, vec3_origin, 100000, 1, DAMAGE_NONE, MOD_TRAP);
if (best->svflags & SVF_MONSTER)
M_ProcessPain(best);
ent->enemy = best;
ent->wait = 64;
ent->s.old_origin = ent->s.origin;
ent->timestamp = level.time + 30_sec;
ent->accel = best->mass;
if (deathmatch->integer)
ent->mass = best->mass / 4;
else
ent->mass = best->mass / 10;
// ok spawn the food cube
ent->s.frame = 5;
// link up any gibs that this monster may have spawned
for (uint32_t i = 0; i < globals.num_edicts; i++)
{
edict_t *e = &g_edicts[i];
if (!e->inuse)
continue;
else if (strcmp(e->classname, "gib"))
continue;
else if ((e->s.origin - ent->s.origin).length() > 128.f)
continue;
e->movetype = MOVETYPE_NONE;
e->nextthink = level.time + FRAME_TIME_S;
e->think = Trap_Gib_Think;
e->owner = ent;
Trap_Gib_Think(e);
}
}
else
{
BecomeExplosion1(ent);
// note to self
// cause explosion damage???
return;
}
}
}
}
// RAFAEL
void fire_trap(edict_t *self, const vec3_t &start, const vec3_t &aimdir, int speed)
{
edict_t *trap;
vec3_t dir;
vec3_t forward, right, up;
dir = vectoangles(aimdir);
AngleVectors(dir, forward, right, up);
trap = G_Spawn();
trap->s.origin = start;
trap->velocity = aimdir * speed;
float gravityAdjustment = level.gravity / 800.f;
trap->velocity += up * (200 + crandom() * 10.0f) * gravityAdjustment;
trap->velocity += right * (crandom() * 10.0f);
trap->avelocity = { 0, 300, 0 };
trap->movetype = MOVETYPE_BOUNCE;
trap->solid = SOLID_BBOX;
trap->takedamage = true;
trap->mins = { -4, -4, 0 };
trap->maxs = { 4, 4, 8 };
trap->die = trap_die;
trap->health = 20;
trap->s.modelindex = gi.modelindex("models/weapons/z_trap/tris.md2");
trap->owner = trap->teammaster = self;
trap->nextthink = level.time + 1_sec;
trap->think = Trap_Think;
trap->classname = "food_cube_trap";
// RAFAEL 16-APR-98
trap->s.sound = gi.soundindex("weapons/traploop.wav");
// END 16-APR-98
trap->flags |= ( FL_DAMAGEABLE | FL_MECHANICAL | FL_TRAP );
trap->clipmask = MASK_PROJECTILE & ~CONTENTS_DEADMONSTER;
// [Paril-KEX]
if (self->client && !G_ShouldPlayersCollide(true))
trap->clipmask &= ~CONTENTS_PLAYER;
gi.linkentity(trap);
trap->timestamp = level.time + 30_sec;
}