mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
eb97af1598
Added GetFileList() to game function imports in misison pack DLL game.h.
611 lines
No EOL
14 KiB
C
611 lines
No EOL
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;
|
|
}
|
|
|
|
if (ent->client->pers.visorFrames == 0)
|
|
ent->client->pers.visorFrames = (sk_visor_time->value * 10);
|
|
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, sk_empnuke_radius->value);
|
|
|
|
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};
|
|
|
|
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
|
|
if (deathmatch->value)
|
|
#endif
|
|
{
|
|
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)
|
|
{
|
|
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
|
|
if (deathmatch->value)
|
|
#endif
|
|
{
|
|
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]--;
|
|
|
|
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
|
|
if (deathmatch->value)
|
|
#endif
|
|
{
|
|
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);
|
|
|
|
// fudge 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 = sk_plasmashield_health->value; // was 4000
|
|
PlasmaShield->die = PlasmaShield_killed;
|
|
PlasmaShield->takedamage = DAMAGE_YES;
|
|
|
|
PlasmaShield->think = PlasmaShield_die;
|
|
PlasmaShield->nextthink = level.time + sk_plasmashield_life->value; // was 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;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
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);
|
|
} |