thirtyflightsofloving/game/m_actor_weap.c
Knightmare66 054d9522ec More formatting changes to misc_actor code in default missionpack and Lazarus DLLs.
Added support for LMSP plasma rifle to misc_actor in missionpack DLL.
2021-11-03 18:02:54 -04:00

823 lines
23 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
This file is part of Lazarus Quake 2 Mod source code.
Lazarus Quake 2 Mod source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Lazarus Quake 2 Mod source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Lazarus Quake 2 Mod source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "g_local.h"
#include "m_actor.h"
void muzzleflash_think (edict_t *flash)
{
if (level.time >= flash->wait)
{
flash->svflags |= SVF_NOCLIENT;
flash->s.effects &= ~EF_HYPERBLASTER;
}
else
{
flash->svflags &= ~SVF_NOCLIENT;
if (flash->s.frame ^= 1)
flash->s.effects |= EF_HYPERBLASTER;
else
flash->s.effects &= ~EF_HYPERBLASTER;
flash->nextthink = level.time + FRAMETIME;
}
gi.linkentity(flash);
}
void TraceAimPoint (vec3_t start, vec3_t target)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_DEBUGTRAIL);
gi.WritePosition (start);
gi.WritePosition (target);
gi.multicast (start, MULTICAST_ALL);
}
void ActorTarget (edict_t *self, vec3_t target)
{
float accuracy;
float dist;
float tf;
vec3_t v;
if (!self->enemy) {
VectorClear (target);
return;
}
if (self->monsterinfo.aiflags & AI_GOOD_GUY)
accuracy = 5.0 - skill->value;
else
accuracy = skill->value + 2.0;
if (self->enemy->health > 0)
{
int weapon;
trace_t tr;
vec3_t start;
qboolean can_see=false;
VectorCopy(self->s.origin,start);
start[2] += self->viewheight;
VectorMA (self->enemy->s.origin, -0.2, self->enemy->velocity, target);
weapon = self->actor_weapon[self->actor_current_weapon];
if (weapon == 7 && (rand() & 1))
{
// Fire rockets at feet half the time
target[2] += self->enemy->mins[2] + 1;
tr = gi.trace(start, NULL, NULL, target, self, MASK_SHOT);
if (tr.ent == self->enemy)
can_see=true;
else
target[2] -= self->enemy->mins[2] + 1;
}
if (!can_see)
{
// Fire at origin if origin can be seen
tr = gi.trace(start, NULL, NULL, target, self, MASK_SHOT);
if (tr.ent == self->enemy)
can_see = true;
}
// Otherwise fire at eyeballs
if (!can_see)
target[2] += self->enemy->viewheight;
}
else
{
// For dead targets, fire at center of bounding box (point entities)
// or origin (brush models)
if (self->enemy->solid == SOLID_BBOX)
VectorMA (self->enemy->absmin, 0.5, self->enemy->size, target);
else
VectorAdd (self->enemy->s.origin, self->enemy->origin_offset, target);
}
if (accuracy == 5.0)
return;
VectorSubtract (target, self->s.origin, v);
dist = VectorLength(v);
tf = (dist < 256) ? dist/2 : 256;
tf *= (float) (5.0 - accuracy) / 25.0;
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
}
// Blaster
void actorBlaster (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
int damage;
int effect, color;
if (!self->enemy || !self->enemy->inuse)
return;
// Knightmare- select color and effect
if (sk_blaster_color->value == 2) { // green
color = BLASTER_GREEN;
effect = (EF_BLASTER|EF_TRACKER);
}
else if (sk_blaster_color->value == 3) { // blue
color = BLASTER_BLUE;
#ifdef KMQUAKE2_ENGINE_MOD
effect = EF_BLASTER|EF_BLUEHYPERBLASTER;
#else
effect = EF_BLUEHYPERBLASTER;
#endif
}
#ifdef KMQUAKE2_ENGINE_MOD
else if ((int)sk_blaster_color->value == 4) { // red
color = BLASTER_RED;
effect = EF_BLASTER|EF_IONRIPPER;
}
#endif
else { // standard yellow
color = BLASTER_ORANGE;
effect = EF_BLASTER;
}
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
damage = 5;
else
damage = 10;
monster_fire_blaster (self, start, forward, damage, 600, MZ2_SOLDIER_BLASTER_2, effect, color);
if (developer->value)
TraceAimPoint (start, target);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
monster_fire_blaster (self, start, forward, damage, 600, MZ2_SOLDIER_BLASTER_2, effect, color);
}
}
// Shotgun
void actorShotgun (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
if (!self->enemy || !self->enemy->inuse)
return;
AngleVectors (self->s.angles, forward, right, up);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
if (self->framenumbers % 2)
G_ProjectSource2(self->s.origin, self->muzzle2, forward, right, up, start);
else
G_ProjectSource2(self->s.origin, self->muzzle, forward, right, up, start);
self->framenumbers++;
}
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
fire_shotgun (self, start, forward, 4, 8, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, MOD_SHOTGUN);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_CHAINFIST_SMOKE);
gi.WritePosition(start);
gi.multicast(start, MULTICAST_PVS);
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/shotgf1b.wav"), 1, ATTN_NORM, 0);
if (self->flash)
{
VectorCopy(start,self->flash->s.origin);
self->flash->s.frame = 0;
self->flash->think = muzzleflash_think;
self->flash->wait = level.time + FRAMETIME;
self->flash->think(self->flash);
}
if (developer->value)
{
if (!(self->monsterinfo.aiflags & AI_TWO_GUNS) || (self->framenumbers % 2))
TraceAimPoint (start, target);
}
}
// Super Shotgun
void actorSuperShotgun (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
vec3_t angles;
if (!self->enemy || !self->enemy->inuse)
return;
AngleVectors (self->s.angles, forward, right, up);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
if (self->framenumbers % 2)
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
self->framenumbers++;
}
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
vectoangles (forward, angles);
angles[YAW] -= 5;
AngleVectors(angles,forward,NULL,NULL);
fire_shotgun (self, start, forward, 6, 12, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);
angles[YAW] += 10;
AngleVectors(angles,forward,NULL,NULL);
fire_shotgun (self, start, forward, 6, 12, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_CHAINFIST_SMOKE);
gi.WritePosition(start);
gi.multicast(start, MULTICAST_PVS);
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/sshotf1b.wav"), 1, ATTN_NORM, 0);
if (self->flash)
{
VectorCopy(start,self->flash->s.origin);
self->flash->s.frame = 0;
self->flash->think = muzzleflash_think;
self->flash->wait = level.time + FRAMETIME;
self->flash->think(self->flash);
}
if (developer->value)
{
if (!(self->monsterinfo.aiflags & AI_TWO_GUNS) || (self->framenumbers % 2))
TraceAimPoint (start, target);
}
}
// Machinegun
void actorMachineGun (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
int damage;
if (!self->enemy || !self->enemy->inuse) {
self->monsterinfo.pausetime = 0;
return;
}
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
damage = 2;
else
damage = 4;
fire_bullet (self, start, forward, damage, 2, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_CHAINFIST_SMOKE);
gi.WritePosition(start);
gi.multicast(start, MULTICAST_PVS);
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex(va("weapons/machgf%db.wav", self->actor_gunframe % 5 + 1)), 1, ATTN_NORM, 0);
if (self->flash)
{
VectorCopy (start, self->flash->s.origin);
self->flash->think = muzzleflash_think;
self->flash->wait = level.time + FRAMETIME;
self->flash->think(self->flash);
}
if (developer->value)
TraceAimPoint (start, target);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
fire_bullet (self, start, forward, damage, 2, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_CHAINFIST_SMOKE);
gi.WritePosition(start);
gi.multicast(start, MULTICAST_PVS);
}
}
// Chaingun
void actorChaingun (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
int i;
int shots;
int damage;
if (!self->enemy || !self->enemy->inuse)
self->monsterinfo.pausetime = 0;
if (level.time >= self->monsterinfo.pausetime) {
self->s.sound = 0;
gi.sound(self,CHAN_AUTO,gi.soundindex("weapons/chngnd1a.wav"),1,ATTN_IDLE,0);
return;
}
if (self->actor_gunframe == 0)
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0);
if (self->actor_gunframe == 21 && level.time < self->monsterinfo.pausetime)
self->actor_gunframe = 15;
else
self->actor_gunframe++;
self->s.sound = gi.soundindex("weapons/chngnl1a.wav");
#ifdef LOOP_SOUND_ATTENUATION
self->s.attenuation = ATTN_IDLE;
#endif
if (self->actor_gunframe <= 9)
shots = 1;
else if (self->actor_gunframe <= 14)
shots = 2;
else
shots = 3;
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
damage = 2;
else
damage = 4;
for (i=0; i<shots; i++)
fire_bullet (self, start, forward, damage, 2, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_CHAINFIST_SMOKE);
gi.WritePosition(start);
gi.multicast(start, MULTICAST_PVS);
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex(va("weapons/machgf%db.wav",self->actor_gunframe % 5 + 1)), 1, ATTN_NORM, 0);
if (self->flash)
{
VectorCopy(start,self->flash->s.origin);
self->flash->think = muzzleflash_think;
self->flash->wait = level.time + FRAMETIME;
self->flash->think(self->flash);
}
if (developer->value)
TraceAimPoint (start, target);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
for (i=0; i<shots; i++)
fire_bullet (self, start, forward, damage, 2, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_CHAINFIST_SMOKE);
gi.WritePosition(start);
gi.multicast(start, MULTICAST_PVS);
}
}
// Grenade Launcher
// DWH: Pretty much a straight copy from Lazarus gunner code
#define GRENADE_VELOCITY 632.4555320337
#define GRENADE_VELOCITY_SQUARED 400000
void actorGrenadeLauncher (edict_t *self)
{
vec3_t start,target;
vec3_t forward, right, up;
vec3_t aim;
vec3_t dist;
vec_t monster_speed;
if (!self->enemy || !self->enemy->inuse)
return;
AngleVectors (self->s.angles, forward, right, up);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
if (self->framenumbers % 2)
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
self->framenumbers++;
}
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
if (self->enemy->absmin[2] <= self->absmax[2])
target[2] += self->enemy->mins[2] - self->enemy->viewheight;
VectorSubtract (target, start, aim);
// lead target... 20, 35, 50, 65 chance of leading
if ( random() < (0.2 + skill->value * 0.15) )
{
float dist;
float time;
dist = VectorLength (aim);
time = dist/GRENADE_VELOCITY; // Not correct, but better than nothin'
VectorMA(target, time, self->enemy->velocity, target);
VectorSubtract (target, start, aim);
}
VectorCopy (aim, forward);
VectorNormalize (aim);
if (aim[2] < 1.0)
{
float cosa, t, x, vx, y;
float drop;
float last_error, last_up, v_error;
int i;
VectorCopy (forward, target); // save target point
// horizontal distance to target
x = sqrt( forward[0] * forward[0] + forward[1] * forward[1]);
cosa = sqrt(aim[0] * aim[0] + aim[1] * aim[1]);
// constant horizontal velocity (since grenades don't have drag)
vx = GRENADE_VELOCITY * cosa;
// time to reach target x
t = x / vx;
// in that time, grenade will drop this much:
drop = 0.5 * sv_gravity->value * t * (t + FRAMETIME);
forward[2] = target[2] + drop;
// this is a good first cut, but incorrect since angle now changes, so
// horizontal speed changes
VectorCopy (forward, aim);
VectorNormalize (aim);
cosa = sqrt(aim[0] * aim[0] + aim[1] * aim[1]);
vx = GRENADE_VELOCITY * cosa;
t = x / vx;
y = GRENADE_VELOCITY * aim[2] * t - 0.5 * sv_gravity->value * t * (t + FRAMETIME);
v_error = target[2] - y;
last_error = 2 * v_error;
for (i=0; i<10 && fabs(v_error) > 4 && fabs(v_error) < fabs(last_error); i++)
{
drop = 0.5 * sv_gravity->value * t * (t + FRAMETIME);
forward[2] = target[2] + drop;
VectorCopy (forward, aim);
VectorNormalize (aim);
cosa = sqrt(aim[0] * aim[0] + aim[1] * aim[1]);
vx = GRENADE_VELOCITY * cosa;
t = x / vx;
y = GRENADE_VELOCITY * aim[2] * t - 0.5 * sv_gravity->value * t * (t + FRAMETIME);
v_error = target[2]-y;
// If error is increasing... we can't get there from here and
// probably shouldn't be here in the first place. Too late now...
// use last aim vector and shoot.
if (fabs(v_error) < fabs(last_error))
last_up = forward[2];
}
if (fabs(v_error) > fabs(last_error)) {
forward[2] = last_up;
VectorCopy (forward, aim);
VectorNormalize (aim);
}
// Sanity check... if gunner is at the same elevation or a bit above the
// target entity, check to make sure he won't bounce grenades off the
// top of a doorway. If he WOULD do that, then figure out the max elevation
// angle that will get the grenade through the door, and hope we get a
// good bounce.
if ( (self->s.origin[2] - self->enemy->s.origin[2] < 160) &&
(self->s.origin[2] - self->enemy->s.origin[2] > -16) ) {
trace_t tr;
VectorAdd (start, forward, target);
tr = gi.trace(start, vec3_origin, vec3_origin, target, self, MASK_SOLID);
if (tr.fraction < 1.0) {
// OK... the aim vector hit a solid, but would the grenade actually hit?
int contents;
cosa = sqrt(aim[0] * aim[0] + aim[1] * aim[1]);
vx = GRENADE_VELOCITY * cosa;
VectorSubtract (tr.endpos, start, dist);
dist[2] = 0;
x = VectorLength(dist);
t = x / vx;
drop = 0.5 * sv_gravity->value * t * (t + FRAMETIME);
tr.endpos[2] -= drop;
// move just a bit in the aim direction
tr.endpos[0] += aim[0];
tr.endpos[1] += aim[1];
contents = gi.pointcontents(tr.endpos);
while ((contents & MASK_SOLID) && (target[2] > self->enemy->s.origin[2]))
{
target[2] -= 8.0;
VectorSubtract (target, start, forward);
VectorCopy (forward, aim);
VectorNormalize (aim);
tr = gi.trace(start, vec3_origin, vec3_origin, target, self, MASK_SOLID);
if (tr.fraction < 1.0)
{
cosa = sqrt(aim[0] * aim[0] + aim[1] * aim[1]);
vx = GRENADE_VELOCITY * cosa;
VectorSubtract (tr.endpos, start, dist);
dist[2] = 0;
x = VectorLength(dist);
t = x / vx;
drop = 0.5 * sv_gravity->value * t * (t + FRAMETIME);
tr.endpos[2] -= drop;
tr.endpos[0] += aim[0];
tr.endpos[1] += aim[1];
contents = gi.pointcontents(tr.endpos);
}
// drop aim point another bit for insurance
target[2] -= 8;
VectorSubtract (target, start, forward);
VectorCopy (forward, aim);
VectorNormalize (aim);
}
}
}
}
// DWH - take into account (sort of) Lazarus feature of adding shooter's velocity to
// grenade velocity
monster_speed = VectorLength(self->velocity);
if (monster_speed > 0) {
vec3_t v1;
vec_t delta;
VectorCopy (self->velocity, v1);
VectorNormalize (v1);
delta = -monster_speed / GRENADE_VELOCITY;
VectorMA (aim, delta, v1, aim);
VectorNormalize (aim);
}
fire_grenade (self, start, aim, 50, GRENADE_VELOCITY, 2.5, 90, false);
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
if (developer->value)
{
if (!(self->monsterinfo.aiflags & AI_TWO_GUNS) || (self->framenumbers % 2))
TraceAimPoint (start, target);
}
}
// Rocket Launcher
void actorRocket (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
int damage = 80;
if (!self->enemy || !self->enemy->inuse)
return;
AngleVectors (self->s.angles, forward, right, up);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
if (self->framenumbers % 2)
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
self->framenumbers++;
}
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
fire_rocket (self, start, forward, damage, 550, damage+20, damage,
( (self->spawnflags & SF_MONSTER_SPECIAL) ? self->enemy : NULL) );
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
if (developer->value)
{
if (!(self->monsterinfo.aiflags & AI_TWO_GUNS) || (self->framenumbers % 2))
TraceAimPoint (start, target);
}
}
void actorHyperblaster (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
int damage;
int effect;
int color;
if (!self->enemy || !self->enemy->inuse) {
self->monsterinfo.pausetime = 0;
self->s.sound = 0;
return;
}
self->s.sound = gi.soundindex("weapons/hyprbl1a.wav");
#ifdef LOOP_SOUND_ATTENUATION
self->s.attenuation = ATTN_IDLE;
#endif
if (level.time >= self->monsterinfo.pausetime)
{
self->actor_gunframe++;
}
else
{
// Knightmare- select color
if ((int)sk_hyperblaster_color->value == 2) // green
color = BLASTER_GREEN;
else if ((int)sk_hyperblaster_color->value == 3) // blue
color = BLASTER_BLUE;
#ifdef KMQUAKE2_ENGINE_MOD
else if ((int)sk_hyperblaster_color->value == 4) // red
color = BLASTER_RED;
#endif
else // standard yellow
color = BLASTER_ORANGE;
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
if ((random() * 3) < 1)
{
if ((int)sk_hyperblaster_color->value == 2) // green
effect = (EF_HYPERBLASTER|EF_TRACKER);
else if ((int)sk_hyperblaster_color->value == 3) // blue
effect = EF_BLUEHYPERBLASTER;
#ifdef KMQUAKE2_ENGINE_MOD
else if ((int)sk_hyperblaster_color->value == 4) // red
effect = EF_HYPERBLASTER|EF_IONRIPPER;
#endif
else // standard yellow
effect = EF_HYPERBLASTER;
}
else
effect = 0;
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/hyprbf1a.wav"), 1, ATTN_NORM, 0);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
damage = 8;
else
damage = 15;
fire_blaster (self, start, forward, damage, 1000, effect, true, color);
if (developer->value)
TraceAimPoint (start, target);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
if ((random() * 3) < 1)
{
if (sk_hyperblaster_color->value == 2) // green
effect = (EF_HYPERBLASTER|EF_TRACKER);
else if (sk_hyperblaster_color->value == 3) // blue
effect = EF_BLUEHYPERBLASTER;
#ifdef KMQUAKE2_ENGINE_MOD
else if (sk_hyperblaster_color->value == 4) // red
effect = EF_HYPERBLASTER|EF_IONRIPPER;
#endif
else // standard yellow
effect = EF_HYPERBLASTER;
}
else
effect = 0;
fire_blaster (self, start, forward, damage, 1000, effect, true, color);
}
self->actor_gunframe++;
if (self->actor_gunframe == 12)
self->actor_gunframe = 6;
}
if (self->actor_gunframe == 12)
{
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
self->s.sound = 0;
}
}
// Railgun
void actorRailGun (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
if (!self->enemy || !self->enemy->inuse)
return;
AngleVectors (self->s.angles, forward, right, up);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
if (self->framenumbers % 2)
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
self->framenumbers++;
}
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
fire_rail (self, start, forward, 80, 100, false, 0, 0, 0); // Do slightly less damage
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/railgf1a.wav"), 1, ATTN_NORM, 0);
if (developer->value)
{
if (!(self->monsterinfo.aiflags & AI_TWO_GUNS) || (self->framenumbers % 2))
TraceAimPoint (start, target);
}
}
void actorBFG (edict_t *self)
{
vec3_t start, target;
vec3_t forward, right, up;
if (!self->enemy || !self->enemy->inuse) {
self->monsterinfo.pausetime = 0;
return;
}
if (self->actor_gunframe == 0)
gi.positioned_sound(self->s.origin, self, CHAN_WEAPON, gi.soundindex("weapons/bfg__f1y.wav"), 1, ATTN_NORM, 0);
if (self->actor_gunframe == 10)
{
AngleVectors (self->s.angles, forward, right, up);
if (self->monsterinfo.aiflags & AI_TWO_GUNS)
{
if (self->framenumbers % 2)
G_ProjectSource2 (self->s.origin, self->muzzle2, forward, right, up, start);
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
self->framenumbers++;
}
else
G_ProjectSource2 (self->s.origin, self->muzzle, forward, right, up, start);
ActorTarget (self, target);
VectorSubtract (target, start, forward);
VectorNormalize (forward);
fire_bfg (self, start, forward, 500, 300, 1000);
self->endtime = level.time + 1;
if (developer->value)
{
if (!(self->monsterinfo.aiflags & AI_TWO_GUNS) || (self->framenumbers % 2))
TraceAimPoint (start, target);
}
}
self->actor_gunframe++;
}