quake2-zaero/z_item.c

606 lines
14 KiB
C

#include "g_local.h"
extern qboolean is_quad;
extern byte is_silenced;
void playQuadSound(edict_t *ent);
void Weapon_Generic (edict_t *ent,
int FRAME_ACTIVATE_LAST,
int FRAME_FIRE_LAST,
int FRAME_IDLE_LAST,
int FRAME_DEACTIVATE_LAST,
int *pause_frames,
int *fire_frames,
void (*fire)(edict_t *ent));
void NoAmmoWeaponChange (edict_t *ent);
void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
void Grenade_Explode(edict_t *ent);
void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
void zCam_TrackEntity(struct edict_s *player, struct edict_s *track, qboolean playerVisiable, qboolean playerOffset);
void zCam_Stop(struct edict_s *player);
void fire_empnuke(edict_t *ent, vec3_t center, int radius);
/*
misc_securitycamera
This is where the visor locates too...
*/
void use_securitycamera (edict_t *self, edict_t *other, edict_t *activator)
{
self->active = !self->active;
}
#define CAMERA_FRAME_FIRST 0
#define CAMERA_FRAME_LAST 59
void securitycamera_think(edict_t *self)
{
if (self->active)
{
self->s.frame++;
if (self->s.frame > CAMERA_FRAME_LAST)
self->s.frame = CAMERA_FRAME_FIRST;
}
if (self->timeout > level.time)
{
self->s.effects |= EF_COLOR_SHELL;
self->s.renderfx |= RF_SHELL_GREEN;
}
else
{
self->s.effects &= ~EF_COLOR_SHELL;
self->s.renderfx &= ~RF_SHELL_GREEN;
}
self->nextthink = level.time + FRAMETIME;
}
void camera_pain(edict_t *self, edict_t *other, float kick, int damage)
{
self->timeout = level.time + FRAMETIME * 2;
}
void SP_misc_securitycamera(edict_t *self)
{
vec3_t offset, forward, up;
// no message? error
if (!self->message)
{
gi.error("misc_securitycamera w/o message");
G_FreeEdict(self);
return;
}
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_NONE;
self->s.modelindex = gi.modelindex("models/objects/camera/tris.md2");
// set the bounding box
VectorSet(self->mins, -16, -16, -32);
VectorSet(self->maxs, 16, 16, 0);
// set the angle of direction
VectorCopy(self->mangle, self->move_angles);
VectorSet(self->s.angles, 0, self->mangle[YAW], 0);
// get an offset
AngleVectors(self->s.angles, forward, NULL, up);
VectorSet(offset, 0, 0, 0);
VectorMA(offset, 8, forward, offset);
VectorMA(offset, -32, up, offset);
VectorAdd(self->s.origin, offset, self->move_origin);
if (self->targetname)
{
self->use = use_securitycamera;
self->active = false;
}
else
{
self->active = true;
}
self->think = securitycamera_think;
self->nextthink = level.time + FRAMETIME;
self->health = 1;
self->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
self->pain = camera_pain;
gi.linkentity(self);
}
char *camera_statusbar =
"xv 26 yb -75 string \"Tracking %s\" "
// timer
"if 20 "
" xv 246 "
" num 3 21 "
" xv 296 "
" pic 20 "
"endif "
;
void updateVisorHud(edict_t *ent)
{
static char buf[1024];
gi.WriteByte (svc_layout);
sprintf(buf, camera_statusbar, ent->client->zCameraTrack->message);
gi.WriteString(buf);
}
void startVisorStatic(edict_t *ent)
{
ent->client->zCameraStaticFramenum = level.time + FRAMETIME * 2;
}
void startVisor(edict_t *ent, edict_t *e)
{
// don't do anything if we're already at the destination camera
if (e == ent->client->zCameraTrack)
return;
// no more time?
if (ent->client->pers.visorFrames <= 0)
{
gi.cprintf(ent, PRINT_HIGH, "No time left for visor\n");
return;
}
// look thru the camera
zCam_TrackEntity(ent, e, true, true);
startVisorStatic(ent);
updateVisorHud(ent);
gi.unicast(ent, true); // reliably send to ent
ent->client->showscores = true;
// play activation sound
gi.sound(ent, CHAN_AUTO, gi.soundindex("items/visor/act.wav"), 1, ATTN_NORM, 0);
}
void stopCamera(edict_t *self)
{
zCam_Stop(self);
self->client->showscores = false;
gi.sound(self, CHAN_AUTO, gi.soundindex("items/visor/deact.wav"), 1, ATTN_NORM, 0);
}
edict_t *findNextCamera(edict_t *old)
{
edict_t *e = NULL;
// first of all, are there *any* cameras?
e = G_Find(NULL, FOFS(classname), "misc_securitycamera");
if (e == NULL)
return NULL;
// start with the current and try to find another good camera
e = old;
while(1)
{
e = G_Find(e, FOFS(classname), "misc_securitycamera");
if (e == NULL)
continue; // loop back around
if (e == old)
return e;
if (!e->active)
continue;
return e;
}
return NULL;
}
void Use_Visor (edict_t *ent, gitem_t *item)
{
if (ent->client->zCameraTrack == NULL)
{
edict_t *e = findNextCamera(NULL);
if (e == NULL)
{
gi.cprintf(ent, PRINT_HIGH, "No cameras are available\n");
return;
}
startVisor(ent, e);
}
else
{
edict_t *e = findNextCamera(ent->client->zCameraTrack);
if (e != NULL &&
e != ent->client->zCameraTrack)
{
ent->client->zCameraTrack = e;
// play sound
gi.sound(ent, CHAN_AUTO, gi.soundindex("items/visor/act.wav"), 1, ATTN_NORM, 0);
startVisorStatic(ent);
updateVisorHud(ent);
gi.unicast(ent, true); // reliably send to ent
}
}
}
/*
=================
EMP Nuke
=================
*/
void weapon_EMPNuke_fire (edict_t *ent)
{
fire_empnuke(ent, ent->s.origin, 1024);
ent->client->pers.inventory[ent->client->ammo_index]--;
if(ent->client->pers.inventory[ent->client->ammo_index])
{
ent->client->weaponstate = WEAPON_ACTIVATING;
ent->client->ps.gunframe = 0;
}
else
{
NoAmmoWeaponChange (ent);
ChangeWeapon(ent);
}
}
void Weapon_EMPNuke (edict_t *ent)
{
static int pause_frames[] = {25, 34, 43, 0};
static int fire_frames[] = {16, 0};
if (deathmatch->value)
{
if (ent->client->ps.gunframe == 0)
{
gi.sound(ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_act.wav"), 1, ATTN_NORM, 0);
}
else if (ent->client->ps.gunframe == 11)
{
gi.sound(ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_spin.wav"), 1, ATTN_NORM, 0);
}
else if (ent->client->ps.gunframe == 35)
{
gi.sound(ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_idle.wav"), 1, ATTN_NORM, 0);
}
}
Weapon_Generic (ent, 9, 16, 43, 47, pause_frames, fire_frames, weapon_EMPNuke_fire);
}
void empnukeFinish(edict_t *ent)
{
// gi.sound(ent, CHAN_VOICE, gi.soundindex("items/empnuke/empdie.wav"), 1, ATTN_NORM, 0);
G_FreeEdict(ent);
}
void empBlastAnim(edict_t *ent)
{
ent->s.frame++;
ent->s.skinnum++;
if(ent->s.frame > 5)
{
ent->svflags |= SVF_NOCLIENT;
ent->s.modelindex = 0;
ent->s.frame = 0;
ent->s.skinnum = 0;
// ent->s.sound = gi.soundindex ("items/empnuke/empactive.wav");
ent->think = empnukeFinish;
ent->nextthink = level.time + 30;
}
else
{
ent->nextthink = level.time + FRAMETIME;
}
}
void fire_empnuke(edict_t *ent, vec3_t center, int radius)
{
edict_t *empnuke;
// gi.sound(ent, CHAN_VOICE, gi.soundindex("items/empnuke/empfire.wav"), 1, ATTN_NORM, 0);
gi.sound(ent, CHAN_VOICE, gi.soundindex("items/empnuke/emp_trg.wav"), 1, ATTN_NORM, 0);
empnuke = G_Spawn();
empnuke->owner = ent;
empnuke->dmg = radius;
VectorCopy(center, empnuke->s.origin);
empnuke->classname = "EMPNukeCenter";
/// empnuke->svflags |= SVF_NOCLIENT;
empnuke->movetype = MOVETYPE_NONE;
empnuke->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2");
empnuke->s.skinnum = 0;
// empnuke->s.renderfx = RF_TRANSLUCENT | RF_FULLBRIGHT;
// empnuke->s.renderfx = RF_TRANSLUCENT;
empnuke->think = empBlastAnim;
empnuke->nextthink = level.time + FRAMETIME;
// empnuke->think = G_FreeEdict;
// empnuke->nextthink = level.time + 30;
gi.linkentity (empnuke);
// gi.sound(empnuke, CHAN_VOICE, gi.soundindex("items/empnuke/emp_exp.wav"), 1, ATTN_NORM, 0);
}
qboolean EMPNukeCheck(edict_t *ent, vec3_t pos)
{
edict_t *check = NULL;
while ((check = G_Find (check, FOFS(classname), "EMPNukeCenter")) != NULL)
{
vec3_t v;
if(check->owner != ent)
{
VectorSubtract (check->s.origin, pos, v);
if(VectorLength(v) <= check->dmg)
{
return true;
}
}
}
return false;
}
/*
=================
Plasma Shield
=================
*/
void PlasmaShield_die (edict_t *self)
{
if (deathmatch->value)
{
gi.sound(self, CHAN_VOICE, gi.soundindex("items/plasmashield/psdie.wav"), 1, ATTN_NORM, 0);
}
G_FreeEdict(self);
}
void PlasmaShield_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
PlasmaShield_die(self);
}
void Use_PlasmaShield (edict_t *ent, gitem_t *item)
{
int ammoIdx = ITEM_INDEX(item);
edict_t *PlasmaShield;
vec3_t forward, right, up, frontbottomleft, backtopright;
if(!ent->client->pers.inventory[ammoIdx])
{
return;
}
if(EMPNukeCheck(ent, ent->s.origin))
{
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
return;
}
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
ent->client->pers.inventory[ammoIdx]--;
if (deathmatch->value)
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("items/plasmashield/psfire.wav"), 1, ATTN_NORM, 0);
}
PlasmaShield = G_Spawn();
// PlasmaShield->owner = ent;
PlasmaShield->classname = "PlasmaShield";
PlasmaShield->movetype = MOVETYPE_PUSH;
PlasmaShield->solid = SOLID_BBOX;
PlasmaShield->s.modelindex = gi.modelindex("sprites/plasmashield.sp2");
PlasmaShield->s.effects |= EF_POWERSCREEN;
PlasmaShield->s.sound = gi.soundindex ("items/plasmashield/psactive.wav");
AngleVectors (ent->client->v_angle, forward, right, up);
vectoangles (forward, PlasmaShield->s.angles);
VectorMA (ent->s.origin, 50, forward, PlasmaShield->s.origin);
// fuge the bbox
VectorScale(forward, 10, frontbottomleft);
VectorMA(frontbottomleft, -30, right, frontbottomleft);
VectorMA(frontbottomleft, -30, up, frontbottomleft);
VectorScale(forward, 5, backtopright);
VectorMA(backtopright, 30, right, backtopright);
VectorMA(backtopright, 50, up, backtopright);
ClearBounds (PlasmaShield->mins, PlasmaShield->maxs);
AddPointToBounds (frontbottomleft, PlasmaShield->mins, PlasmaShield->maxs);
AddPointToBounds (backtopright, PlasmaShield->mins, PlasmaShield->maxs);
PlasmaShield->health = PlasmaShield->max_health = 4000;
PlasmaShield->die = PlasmaShield_killed;
PlasmaShield->takedamage = DAMAGE_YES;
PlasmaShield->think = PlasmaShield_die;
PlasmaShield->nextthink = level.time + 10;
gi.linkentity (PlasmaShield);
}
/*
misc_crate
*/
void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
void setupCrate(edict_t *self)
{
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_FALLFLOAT;
if (!self->mass)
self->mass = 400;
self->touch = barrel_touch;
self->think = M_droptofloor;
self->nextthink = level.time + 2 * FRAMETIME;
gi.linkentity(self);
}
void SP_misc_crate(edict_t *self)
{
// setup specific to this size
self->s.modelindex = gi.modelindex("models/objects/crate/crate64.md2");
VectorSet (self->mins, -32, -32, 0);
VectorSet (self->maxs, 32, 32, 64);
// generic crate setup
setupCrate(self);
}
void SP_misc_crate_medium(edict_t *self)
{
// setup specific to this size
self->s.modelindex = gi.modelindex("models/objects/crate/crate48.md2");
VectorSet (self->mins, -24, -24, 0);
VectorSet (self->maxs, 24, 24, 48);
// generic crate setup
setupCrate(self);
}
void SP_misc_crate_small(edict_t *self)
{
// setup specific to this size
self->s.modelindex = gi.modelindex("models/objects/crate/crate32.md2");
VectorSet (self->mins, -16, -16, 0);
VectorSet (self->maxs, 16, 16, 32);
// generic crate setup
setupCrate(self);
}
/*******************************************
func_barrier
*/
qboolean thruBarrier(edict_t *targ, edict_t *inflictor)
{
trace_t tr;
edict_t *e = inflictor;
while(e)
{
tr = gi.trace(e->s.origin, NULL, NULL, targ->s.origin, e, MASK_SHOT);
if (!tr.ent || tr.fraction >= 1.0)
return false;
if (tr.ent == targ)
return false;
if (tr.ent->classname && Q_stricmp(tr.ent->classname, "func_barrier") == 0)
return true;
if(e == tr.ent)
break;
e = tr.ent;
}
}
void barrier_think(edict_t *self)
{
if (self->timeout > level.time)
{
self->svflags &= ~SVF_NOCLIENT;
}
else
{
self->svflags |= SVF_NOCLIENT;
}
self->nextthink = level.time + FRAMETIME;
}
void barrier_pain(edict_t *self, edict_t *other, float kick, int damage)
{
self->timeout = level.time + FRAMETIME * 2;
if (self->damage_debounce_time < level.time)
{
gi.sound (self, CHAN_AUTO, gi.soundindex("weapons/lashit.wav"), 1, ATTN_NORM, 0);
self->damage_debounce_time = level.time + FRAMETIME * 2;
}
}
void barrier_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == world)
return;
self->timeout = level.time + FRAMETIME * 2;
if (self->touch_debounce_time < level.time)
{
gi.sound (self, CHAN_AUTO, gi.soundindex("weapons/lashit.wav"), 1, ATTN_NORM, 0);
self->touch_debounce_time = level.time + FRAMETIME * 2;
}
}
void SP_func_barrier(edict_t *self)
{
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_NONE;
self->s.modelindex = gi.modelindex("models/objects/wall/tris.md2");
self->svflags = SVF_NOCLIENT;
self->s.effects = EF_BFG;
self->think = barrier_think;
self->nextthink = level.time + FRAMETIME;
self->touch = barrier_touch;
self->health = 1;
self->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
self->pain = barrier_pain;
gi.linkentity(self);
}
/*********************************************
misc_seat
*/
void SP_misc_seat(edict_t *self)
{
self->s.modelindex = gi.modelindex("models/objects/seat/tris.md2");
VectorSet(self->mins, -16, -16, 0);
VectorSet(self->maxs, 16, 16, 40);
// make this pushable
setupCrate(self);
}