thirtyflightsofloving/missionpack/g_weapon_q1.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

1024 lines
26 KiB
C

// g_weapon_q1.c
#include "g_local.h"
void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
/*
=================
q1_fire_nail
Fires a Nail, Used by Q1 NG and SNG
=================
*/
void q1_nail_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
int mod;
if (other == self->owner)
return;
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (self);
return;
}
if (self->owner->client)
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
if (other->takedamage)
{
if (self->spawnflags & 1)
mod = MOD_Q1_SNG;
else
mod = MOD_Q1_NG;
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 0, DAMAGE_BULLET, mod);
}
else
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_SHOTGUN);
gi.WritePosition (self->s.origin);
if (!plane)
gi.WriteDir (vec3_origin);
else
gi.WriteDir (plane->normal);
gi.multicast (self->s.origin, MULTICAST_PVS);
//not if online, too laggy
if(!deathmatch->value)
{
float sound= random();
if (sound < 0.3)
{
if (sound < 0.1)
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/ric3.wav"), 1, ATTN_STATIC, 0);
else if (sound < 0.2)
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/ric2.wav"), 1, ATTN_STATIC, 0);
else
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/ric1.wav"), 1, ATTN_STATIC, 0);
}
else if (sound < 0.5)
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1weapons/tink1.wav"), 1, ATTN_STATIC, 0);
}
}
G_FreeEdict (self);
}
void q1_fire_nail (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, qboolean sng)
{
edict_t *nail;
trace_t tr;
VectorNormalize (dir);
nail = G_Spawn();
VectorCopy (start, nail->s.origin);
VectorCopy (start, nail->s.old_origin);
vectoangles (dir, nail->s.angles);
VectorScale (dir, speed, nail->velocity);
nail->movetype = MOVETYPE_FLYMISSILE;
nail->clipmask = MASK_SHOT;
nail->solid = SOLID_BBOX;
nail->svflags = SVF_DEADMONSTER;
nail->s.renderfx = RF_FULLBRIGHT;
VectorClear (nail->mins);
VectorClear (nail->maxs);
nail->s.modelindex = gi.modelindex ("models/objects/q1nail/tris.md2");
nail->owner = self;
nail->touch = q1_nail_touch;
nail->nextthink = level.time + 8000/speed;
nail->think = G_FreeEdict;
nail->dmg = damage;
nail->classname = "nail";
if (sng)
nail->spawnflags = 1;
nail->common_name = "Nail";
nail->class_id = ENTITY_Q1_NAIL;
gi.linkentity (nail);
if (self->client)
check_dodge (self, nail->s.origin, dir, speed);
tr = gi.trace (self->s.origin, NULL, NULL, nail->s.origin, nail, MASK_SHOT);
if (tr.fraction < 1.0)
{
VectorMA (nail->s.origin, -10, dir, nail->s.origin);
nail->touch (nail, tr.ent, NULL, NULL);
}
}
/*
=================
q1_fire_laser
Fires a laser bolt, used by Q1 enforcer and laser trap.
=================
*/
void q1_laser_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == self->owner)
return;
if (surf && (surf->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, plane->normal, self->dmg, 0, DAMAGE_ENERGY, MOD_Q1_LASER);
}
else
{
#if 1 // From Decino's Q2Infighter mod
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_WELDING_SPARKS);
gi.WriteByte (15);
gi.WritePosition (self->s.origin);
gi.WriteDir ((!plane) ? vec3_origin : plane->normal);
gi.WriteByte (226);
gi.multicast (self->s.origin, MULTICAST_PVS);
#else
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_SHOTGUN);
gi.WritePosition (self->s.origin);
if (!plane)
gi.WriteDir (vec3_origin);
else
gi.WriteDir (plane->normal);
gi.multicast (self->s.origin, MULTICAST_PVS);
#endif
// not if online, too laggy
if (!deathmatch->value)
{
gi.sound (self, CHAN_WEAPON, gi.soundindex ("q1enforcer/enfstop.wav"), 1, ATTN_STATIC, 0);
}
}
G_FreeEdict (self);
}
void q1_fire_laser (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed)
{
edict_t *laser;
trace_t tr;
VectorNormalize (dir);
laser = G_Spawn();
VectorCopy (start, laser->s.origin);
VectorCopy (start, laser->s.old_origin);
vectoangles (dir, laser->s.angles);
VectorScale (dir, speed, laser->velocity);
laser->movetype = MOVETYPE_FLYMISSILE;
laser->clipmask = MASK_SHOT;
laser->solid = SOLID_BBOX;
laser->svflags = SVF_DEADMONSTER;
laser->s.renderfx = RF_FULLBRIGHT;
VectorClear (laser->mins);
VectorClear (laser->maxs);
laser->s.modelindex = gi.modelindex ("models/monsters/q1enforcer/laser/tris.md2");
laser->owner = self;
laser->touch = q1_laser_touch;
laser->nextthink = level.time + 8000/speed;
laser->think = G_FreeEdict;
laser->dmg = damage;
laser->classname = "laser";
laser->common_name = "Laser";
laser->class_id = ENTITY_Q1_LASER;
gi.linkentity (laser);
if (self->client)
check_dodge (self, laser->s.origin, dir, speed);
tr = gi.trace (self->s.origin, NULL, NULL, laser->s.origin, laser, MASK_SHOT);
if (tr.fraction < 1.0)
{
VectorMA (laser->s.origin, -10, dir, laser->s.origin);
laser->touch (laser, tr.ent, NULL, NULL);
}
}
/*
=================
q1_fire_flame
Fires a flame bolt. Used by the Hell Knight.
=================
*/
void q1_flame_splash (int type, int count, int color, vec3_t start, vec3_t movdir, vec3_t origin)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (type);
gi.WriteByte (count);
gi.WritePosition (start);
gi.WriteDir (movdir);
gi.WriteByte (color);
gi.multicast (origin, MULTICAST_PVS);
}
void q1_flame_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == self->owner)
return;
if (surf && (surf->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, plane->normal, self->dmg, 1, DAMAGE_ENERGY, MOD_Q1_FLAMEBOLT);
}
else
{
#if 1 // From Decino's Q2Infighter mod
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_WELDING_SPARKS);
gi.WriteByte (15);
gi.WritePosition (self->s.origin);
gi.WriteDir ((!plane) ? vec3_origin : plane->normal);
// gi.WriteByte (66);
gi.WriteByte (228);
gi.multicast (self->s.origin, MULTICAST_PVS);
#else
q1_flame_splash (TE_TUNNEL_SPARKS, 16, 71, self->s.origin, plane->normal, self->s.origin);
#endif
}
gi.sound (self, CHAN_WEAPON, gi.soundindex("q1hknight/hit.wav"), 1, ATTN_NORM, 0);
G_FreeEdict (self);
}
void q1_fire_flame (edict_t *self, vec3_t start, vec3_t dir, float leftrightoff)
{
edict_t *bolt;
trace_t tr;
int damage, speed;
// vec3_t up;
damage = 9;
speed = 300;
VectorNormalize (dir);
bolt = G_Spawn();
bolt->svflags |= SVF_DEADMONSTER;
// yes, I know it looks weird that projectiles are deadmonsters
// what this means is that when prediction is used against the object
// (blaster/hyperblaster shots), the player won't be solid clipped against
// the object. Right now trying to run into a firing hyperblaster
// is very jerky since you are predicted 'against' the shots.
VectorCopy (start, bolt->s.origin);
VectorCopy (start, bolt->s.old_origin);
vectoangles (dir, bolt->s.angles);
VectorScale (dir, speed, bolt->velocity);
bolt->movetype = MOVETYPE_FLYMISSILE;
bolt->clipmask = MASK_SHOT;
bolt->solid = SOLID_BBOX;
bolt->s.effects |= EF_IONRIPPER;
VectorClear (bolt->mins);
VectorClear (bolt->maxs);
bolt->s.modelindex = gi.modelindex ("models/monsters/q1hknight/k_spike/tris.md2");
bolt->owner = self;
bolt->touch = q1_flame_touch;
bolt->nextthink = level.time + 2;
bolt->think = G_FreeEdict;
bolt->dmg = damage;
bolt->classname = "bolt";
bolt->common_name = "Hell Knight Flame";
bolt->class_id = ENTITY_Q1_HKNIGHT_FLAME;
gi.linkentity (bolt);
tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
if (tr.fraction < 1.0)
{
VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
bolt->touch (bolt, tr.ent, NULL, NULL);
}
}
/*
=================
q1_fire_grenade
Fires a grenade. Used by the Ogre.
=================
*/
void q1_explode (edict_t *self)
{
if (self->s.frame == 5)
{
G_FreeEdict (self);
return;
}
self->nextthink = level.time + FRAMETIME;
self->s.frame++;
}
void q1_grenade_explode (edict_t *ent)
{
vec3_t origin;
if (ent->owner->client)
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
// FIXME: if we are onground then raise our Z just a bit since we are a point?
if (ent->enemy)
{
float points;
vec3_t v;
vec3_t dir;
VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
VectorMA (ent->enemy->s.origin, 0.5, v, v);
VectorSubtract (ent->s.origin, v, v);
points = ent->dmg - 0.5 * VectorLength (v);
VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, MOD_Q1_GL);
}
T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, MOD_Q1_GL_SPLASH);
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
gi.sound (ent, CHAN_AUTO, gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (origin);
gi.multicast (ent->s.origin, MULTICAST_PVS);
// explosion sprite
gi.unlinkentity (ent);
ent->solid = SOLID_NOT;
ent->touch = NULL;
VectorCopy( origin, ent->s.origin);
VectorCopy( origin, ent->s.old_origin);
VectorClear (ent->velocity);
ent->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
ent->s.frame = 0;
ent->s.sound = 0;
ent->s.effects &= ~EF_ANIM_ALLFAST;
ent->think = q1_explode;
ent->nextthink = level.time + FRAMETIME;
gi.linkentity (ent);
}
void q1_grenade_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (ent);
return;
}
if (!other->takedamage ||
(other->solid == SOLID_BSP))
{
gi.sound (ent, CHAN_RELIABLE|CHAN_WEAPON, gi.soundindex ("q1weapons/bounce.wav"), 1, ATTN_NORM, 0);
return;
}
ent->enemy = other;
q1_grenade_explode (ent);
}
void q1_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
{
edict_t *grenade;
vec3_t dir;
vec3_t forward, right, up;
vectoangles (aimdir, dir);
AngleVectors (dir, forward, right, up);
grenade = G_Spawn();
VectorCopy (start, grenade->s.origin);
VectorScale (aimdir, speed, grenade->velocity);
VectorMA (grenade->velocity, 200 + (random() - 0.5) * 20.0, up, grenade->velocity);
VectorMA (grenade->velocity, (random() - 0.5) * 20.0, right, grenade->velocity);
VectorSet (grenade->avelocity, 300, 300, 300);
grenade->movetype = MOVETYPE_BOUNCE;
grenade->clipmask = MASK_SHOT;
grenade->solid = SOLID_BBOX;
grenade->s.effects |= EF_GRENADE;
VectorClear (grenade->mins);
VectorClear (grenade->maxs);
grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
grenade->owner = self;
grenade->touch = q1_grenade_touch;
grenade->nextthink = level.time + timer;
grenade->think = q1_grenade_explode;
grenade->dmg = damage;
grenade->dmg_radius = damage_radius;
grenade->classname = "grenade";
grenade->common_name = "Quake Grenade";
grenade->class_id = ENTITY_Q1_GRENADE;
gi.linkentity (grenade);
}
/*
=================
q1_fire_rocket
Fires a q1 rocket. Used by ?
=================
*/
void q1_rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
vec3_t origin;
if (other == ent->owner)
return;
if (surf && (surf->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
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
if (other->takedamage)
{
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_Q1_RL);
}
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_Q1_RL_SPLASH);
gi.sound (ent,CHAN_AUTO , gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (origin);
gi.multicast (ent->s.origin, MULTICAST_PVS);
// if (ent->target_ent)
// G_FreeEdict(ent->target_ent);
// explosion sprite
gi.unlinkentity (ent);
ent->solid = SOLID_NOT;
ent->touch = NULL;
VectorCopy( origin, ent->s.origin);
VectorCopy( origin, ent->s.old_origin);
VectorClear (ent->velocity);
ent->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
ent->s.frame = 0;
ent->s.sound = 0;
ent->s.effects &= ~EF_ANIM_ALLFAST;
ent->think = q1_explode;
ent->nextthink = level.time + FRAMETIME;
gi.linkentity (ent);
}
void q1rocketTrail_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
G_FreeEdict (ent);
}
// Rocket Trails
// needed to give the yellow part of the Rocket trail
void q1rocket_trail (edict_t *self, vec3_t start, vec3_t dir)
{
edict_t *bolt;
VectorNormalize (dir);
bolt = G_Spawn();
VectorCopy (start, bolt->s.origin);
VectorCopy (start, bolt->s.old_origin);
vectoangles (dir, bolt->s.angles);
VectorScale (dir, 950, bolt->velocity);
bolt->movetype = MOVETYPE_FLYMISSILE;
bolt->clipmask = MASK_SHOT;
bolt->solid = SOLID_BBOX;
bolt->s.effects = EF_BLASTER;
bolt->svflags |= SVF_DEADMONSTER;
VectorClear (bolt->mins);
VectorClear (bolt->maxs);
bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
bolt->owner = self;
bolt->touch = q1rocketTrail_touch;
bolt->nextthink = level.time + 8000/950;
bolt->think = G_FreeEdict;
bolt->classname = "bolt";
gi.linkentity (bolt);
}
void q1_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
{
edict_t *rocket;
rocket = G_Spawn();
VectorCopy (start, rocket->s.origin);
VectorCopy (dir, rocket->movedir);
vectoangles (dir, rocket->s.angles);
VectorScale (dir, speed, rocket->velocity);
rocket->movetype = MOVETYPE_FLYMISSILE;
rocket->clipmask = MASK_SHOT;
rocket->solid = SOLID_BBOX;
rocket->s.effects |= EF_GRENADE;
VectorClear (rocket->mins);
VectorClear (rocket->maxs);
rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
rocket->owner = self;
rocket->touch = q1_rocket_touch;
rocket->nextthink = level.time + 8000/speed;
rocket->think = G_FreeEdict;
rocket->dmg = damage;
rocket->radius_dmg = radius_damage;
rocket->dmg_radius = damage_radius;
rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
rocket->classname = "rocket";
q1rocket_trail (rocket, start, dir);
rocket->common_name = "Quake Rocket";
rocket->class_id = ENTITY_Q1_ROCKET;
gi.linkentity (rocket);
}
/*
=================
q1_fire_lightning
Fires a lighting bolt. Used by ?
=================
*/
void q1_fire_lightning (edict_t *self, vec3_t start, vec3_t dir, int damage)
{
vec3_t end;
trace_t tr;
VectorNormalize(dir);
VectorMA(start,600,dir,end);
// Initial Trace - damage if close enough
tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
if (tr.fraction < 1.0)
{
if(tr.ent && (tr.ent !=self) && (tr.ent->takedamage))
T_Damage (tr.ent, self, self, dir, tr.endpos, tr.plane.normal, damage, 0, DAMAGE_ENERGY, MOD_Q1_LG);
return;
}
else
{
// trace 2
tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
if (self->client->chasetoggle)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
// gi.WriteByte (TE_HEATBEAM);
gi.WriteShort (self->client->oldplayer - g_edicts);
gi.WritePosition (start);
gi.WritePosition (tr.endpos);
gi.multicast (self->client->oldplayer->s.origin, MULTICAST_PVS);
// gi.multicast (self->client->oldplayer->s.origin, MULTICAST_ALL);
}
else
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
// gi.WriteByte (TE_HEATBEAM);
gi.WriteShort (self - g_edicts);
gi.WritePosition (start);
gi.WritePosition (tr.endpos);
gi.multicast (self->s.origin, MULTICAST_PVS);
// gi.multicast (self->s.origin, MULTICAST_ALL);
}
if ((tr.ent != self) && (tr.ent->takedamage))
T_Damage (tr.ent, self, self, dir, tr.endpos, tr.plane.normal, damage, 0, DAMAGE_ENERGY, MOD_Q1_LG);
}
}
/*
=================
q1_fire_firepod
Fires a homing fire pod. Used by the Vore (shalrath).
=================
*/
void q1_firepod_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
// int mod;
vec3_t origin;
if (other == self->owner)
return;
if ( (Q_stricmp(other->classname, "q1_monster_zombie") == 0) || (Q_stricmp(other->classname, "monster_q1_zombie") == 0) )
T_Damage (other, self, self->owner, vec3_origin, other->s.origin, vec3_origin, 200, 0, 0, 0);
T_RadiusDamage (self, self->owner, 40, NULL, 40, MOD_Q1_FIREPOD);
gi.sound (self,CHAN_AUTO , gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
VectorMA (self->s.origin, -0.02, self->velocity, origin);
// gi.WriteByte (svc_temp_entity);
// gi.WriteByte (TE_Q1_EXPLOSION);
// FX Paril
// gi.WriteByte (TE_ROCKET_EXPLOSION);
// gi.WritePosition (origin);
// gi.multicast (self->s.origin, MULTICAST_PVS);
gi.unlinkentity (self);
self->solid = SOLID_NOT;
self->touch = NULL;
VectorCopy (origin, self->s.origin);
VectorCopy (origin, self->s.old_origin);
VectorClear (self->velocity);
self->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
self->s.frame = 0;
self->s.sound = 0;
self->s.effects &= ~EF_ANIM_ALLFAST;
self->think = q1_explode;
self->nextthink = level.time + FRAMETIME;
gi.linkentity (self);
// q1_explo_particles (self, origin);
// if (Q_stricmp(self->classname, "freed"))
// G_FreeEdict (self);
}
void q1_firepod_home (edict_t *self)
{
vec3_t dir, vtemp;
VectorClear (self->velocity);
VectorCopy (self->enemy->s.origin, vtemp);
VectorSet (dir, 0, 0, 10);
VectorAdd (vtemp, dir, vtemp);
VectorClear (dir);
if (self->enemy->health < 1)
{
G_FreeEdict(self);
return;
}
if (self->enemy->client && (self->enemy->client->ps.pmove.pm_flags & PMF_DUCKED))
vtemp[2] -= 10;
VectorSubtract (vtemp, self->s.origin, dir);
VectorNormalize (dir);
if (skill->value >= 2)
VectorMA (self->velocity, 350, dir, self->velocity);
else
VectorMA (self->velocity, 250, dir, self->velocity);
self->nextthink = level.time + 0.2;
}
void q1_fire_firepod (edict_t *self, vec3_t dir)
{
edict_t *pod;
trace_t tr;
vec3_t start, temp;
VectorCopy (self->s.origin, start);
VectorSet (temp, 0, 0, 10);
VectorAdd (start, temp, start);
VectorNormalize (dir);
pod = G_Spawn();
pod->svflags |= SVF_DEADMONSTER;
// yes, I know it looks weird that projectiles are deadmonsters
// what this means is that when prediction is used against the object
// (blaster/hyperblaster shots), the player won't be solid clipped against
// the object. Right now trying to run into a firing hyperblaster
// is very jerky since you are predicted 'against' the shots.
VectorCopy (start, pod->s.origin);
VectorCopy (start, pod->s.old_origin);
vectoangles (dir, pod->s.angles);
VectorScale (dir, 400, pod->velocity);
pod->movetype = MOVETYPE_FLYMISSILE;
pod->clipmask = MASK_SHOT;
pod->solid = SOLID_BBOX;
VectorSet (pod->avelocity, 300, 300, 300);
#ifdef KMQUAKE2_ENGINE_MOD
pod->s.effects |= EF_FLAG1|EF_BLUEHYPERBLASTER;
#else
pod->s.effects |= EF_FLAG1|EF_FLAG2;
#endif
VectorClear (pod->mins);
VectorClear (pod->maxs);
pod->s.modelindex = gi.modelindex ("models/monsters/q1shalrath/v_spike/tris.md2");
// pod->s.sound = gi.soundindex ("misc/lasfly.wav");
pod->touch = q1_firepod_touch;
pod->nextthink = level.time +.1;
pod->think = q1_firepod_home;
pod->classname = "firepod";
pod->enemy = self->enemy;
pod->owner = self;
pod->common_name = "Firepod";
pod->class_id = ENTITY_Q1_FIREPOD;
gi.linkentity (pod);
// if (self->client)
// check_dodge (self, pod->s.origin, dir, speed);
tr = gi.trace (self->s.origin, NULL, NULL, pod->s.origin, pod, MASK_SHOT);
if (tr.fraction < 1.0)
{
VectorMA (pod->s.origin, -10, dir, pod->s.origin);
pod->touch (pod, tr.ent, NULL, NULL);
}
}
/*
=================
q1_fire_lavaball
Fires a lavaball. Used by Chthon.
=================
*/
/*static*/ void q1_lavaball_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
vec3_t origin;
if (other == ent->owner)
return;
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (ent);
return;
}
// calculate position for the explosion entity
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
if (other->takedamage)
{
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg,(ent->dmg*2), 0, MOD_Q1_RL);
}
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_Q1_RL_SPLASH);
gi.sound (ent, CHAN_WEAPON, gi.soundindex ("q1weapons/r_exp3.wav"), 1.0, ATTN_NORM, 0);
//
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (origin);
gi.multicast (ent->s.origin, MULTICAST_PVS);
//
// explosion sprite
gi.unlinkentity(ent);
ent->solid = SOLID_NOT;
ent->touch = NULL;
VectorCopy(ent->s.origin, origin);
VectorCopy(ent->s.origin, ent->s.old_origin);
VectorClear (ent->velocity);
ent->s.modelindex = gi.modelindex ("sprites/s_explod.sp2");
ent->s.frame = 0;
ent->s.sound = 0;
ent->s.effects &= ~EF_ANIM_ALLFAST;
ent->think = q1_explode;
ent->nextthink = level.time + FRAMETIME;
ent->enemy = other;
gi.linkentity (ent);
}
void q1_fire_lavaball (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
{
edict_t *lavaball;
lavaball = G_Spawn();
VectorCopy (start, lavaball->s.origin);
VectorCopy (dir, lavaball->movedir);
vectoangles (dir, lavaball->s.angles);
VectorScale (dir, speed, lavaball->velocity);
lavaball->movetype = MOVETYPE_FLYMISSILE;
lavaball->clipmask = MASK_SHOT;
lavaball->solid = SOLID_BBOX;
lavaball->s.effects |= EF_ROCKET; //EF_FLAG1
lavaball->s.renderfx |= RF_GLOW;
VectorClear (lavaball->mins);
VectorClear (lavaball->maxs);
lavaball->s.modelindex = gi.modelindex ("models/monsters/q1chthon/lavaball/tris.md2");
lavaball->owner = self;
lavaball->touch = q1_lavaball_touch;
lavaball->nextthink = level.time + 8000/speed;
lavaball->think = G_FreeEdict;
lavaball->dmg = damage;
lavaball->radius_dmg = radius_damage;
lavaball->dmg_radius = damage_radius;
lavaball->s.sound = gi.soundindex ("weapons/rockfly.wav");
lavaball->classname = "chthon_lavaball";
lavaball->common_name = "Chthon Lavaball";
lavaball->class_id = ENTITY_Q1_LAVABALL;
gi.linkentity (lavaball);
}
/*
=================
q1_acidbolt_touch
Fires a bolt of acid spit. Used by the Scrag.
=================
*/
/*static*/ void q1_acidbolt_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (ent);
return;
}
if (other->takedamage)
{
// FIX MOD
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_Q1_RL);
}
else
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_WELDING_SPARKS);
gi.WriteByte (15);
gi.WritePosition (ent->s.origin);
gi.WriteDir ((!plane) ? vec3_origin : plane->normal);
// gi.WriteByte (209);
gi.WriteByte (222);
gi.multicast (ent->s.origin, MULTICAST_PVS);
gi.sound (ent, CHAN_VOICE, gi.soundindex ("q1scrag/hit.wav"), 1, ATTN_NORM, 0);
}
G_FreeEdict (ent);
}
/*
void G_Spawn_Trails(int type, vec3_t start, vec3_t endpos, vec3_t origin ) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(type);
gi.WritePosition(start);
gi.WritePosition(endpos);
gi.multicast(origin, MULTICAST_PVS);
}
*/
void q1_fire_acidspit (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed)
{
edict_t *acidbolt;
acidbolt = G_Spawn();
VectorCopy (start, acidbolt->s.origin);
VectorCopy (dir, acidbolt->movedir);
vectoangles (dir, acidbolt->s.angles);
VectorScale (dir, speed, acidbolt->velocity);
acidbolt->movetype = MOVETYPE_FLYMISSILE;
acidbolt->clipmask = MASK_SHOT;
acidbolt->solid = SOLID_BBOX;
acidbolt->s.effects |= EF_GREENGIB; // EF_HYPERBLASTER EF_BFG EF_GREENTRAIL
acidbolt->s.renderfx |= RF_TRANSLUCENT; // FULLBRIGHT
VectorClear (acidbolt->mins);
VectorClear (acidbolt->maxs);
acidbolt->s.modelindex = gi.modelindex ("models/monsters/q1scrag/w_spike/tris.md2");
acidbolt->owner = self;
acidbolt->touch = q1_acidbolt_touch;
acidbolt->nextthink = level.time + 8000/speed;
acidbolt->think = G_FreeEdict;
acidbolt->dmg = damage;
// acidbolt->s.sound = gi.soundindex ("weapons/rockfly.wav");
acidbolt->classname = "acidbolt";
acidbolt->common_name = "Acid Bolt";
acidbolt->class_id = ENTITY_Q1_ACIDBOLT;
gi.linkentity (acidbolt);
}
/*
=================
q1_fire_gib
Fires a gib projectile. Used by the Zombie.
=================
*/
/*static*/ void q1_zombiegib_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (ent);
return;
}
if (other->takedamage)
{
// FIX MOD
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_Q1_RL);
gi.sound (ent, CHAN_RELIABLE|CHAN_WEAPON, gi.soundindex ("q1zombie/z_hit.wav"), 1.0, ATTN_NORM, 0);
}
else
{
gi.sound (ent, CHAN_RELIABLE|CHAN_WEAPON, gi.soundindex ("q1zombie/z_miss.wav"), 1.0, ATTN_NORM, 0);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_BLOOD);
gi.WritePosition (ent->s.origin);
gi.WriteDir (plane->normal);
gi.multicast (ent->s.origin, MULTICAST_PVS);
}
// no more touches
ent->touch = NULL;
ent->nextthink = level.time + 3;
ent->think = G_FreeEdict;
// G_FreeEdict (ent);
}
void q1_fire_gib (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)
{
edict_t *gib;
vec3_t dir;
vec3_t up; //forward, right,
vectoangles (aimdir, dir);
AngleVectors (dir, NULL, NULL, up);
gib = G_Spawn();
VectorCopy (start, gib->s.origin);
VectorScale (aimdir, speed, gib->velocity);
VectorMA (gib->velocity, 200, up, gib->velocity);
// VectorMA (gib->velocity, 20, right, gib->velocity);
VectorSet (gib->avelocity, 3000, 1000, 2000);
gib->movetype = MOVETYPE_BOUNCE;
gib->clipmask = MASK_SHOT;
gib->solid = SOLID_BBOX;
gib->s.effects |= EF_GIB;
VectorClear (gib->mins);
VectorClear (gib->maxs);
gib->s.modelindex = gi.modelindex ("models/monsters/q1zombie/gib/tris.md2");
gib->owner = self;
gib->touch = q1_zombiegib_touch;
gib->nextthink = level.time + 2.5;
// gib->think = G_FreeEdict;
gib->think = gib_fade; // use gib_fade() instead of directly removing
gib->dmg = damage;
gib->classname = "gib";
gib->common_name = "Gib Projectile";
gib->class_id = ENTITY_Q1_ZOMBIE_GIB;
gi.linkentity (gib);
}