thirtyflightsofloving/missionpack/z_item.c
Knightmare66 3672ea6be8 Added cvars ui_new_textfield and ui_new_textbox to toggle drawing text fields and text boxes with new images and font-based tiles.
Improved Tactician Gunner prox mine detection in missionpack DLL.
Implemented Zaero modifications (can be pushed off ledge) to exploding barrels in missionpack DLL.
Changed incomplete vector parsing check in ED_ParseField() to a warning instead of an error in all game DLLs.
2021-08-21 14:27:04 -04:00

708 lines
16 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 barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
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);
//#ifdef USE_ZAERO_ITEMS_WEAPONS
void zCam_TrackEntity (struct edict_s *player, struct edict_s *track, qboolean playerVisiable, qboolean playerOffset);
void zCam_Stop (struct edict_s *player);
//#endif // USE_ZAERO_ITEMS_WEAPONS
void fire_empnuke(edict_t *ent, vec3_t center, int radius);
/*
======================================================================
misc_securitycamera
This is where the visor locates too...
======================================================================
*/
//#ifdef USE_ZAERO_ITEMS_WEAPONS
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 = 1000; // Knightmare- was 1
self->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
self->pain = camera_pain;
self->common_name = "Security Camera";
self->class_id = ENTITY_MISC_SECURITYCAMERA;
gi.linkentity(self);
}
char *camera_statusbar =
"xv 26 yb -75 string \"Tracking %s\" "
// timer
"if 24 " // was "if 20"
" xv 246 "
" num 3 25 " // was " num 3 21"
" xv 296 "
" pic 24 " // was " pic 20"
"endif "
;
void updateVisorHud (edict_t *ent)
{
static char buf[1024];
gi.WriteByte (svc_layout);
Com_sprintf(buf, sizeof(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
}
}
}
//#endif // USE_ZAERO_ITEMS_WEAPONS
/*
======================================================================
EMP Nuke
======================================================================
*/
void weapon_EMPNuke_fire (edict_t *ent)
{
fire_empnuke (ent, ent->s.origin, 1024);
// 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->moreflags |= FL2_DO_NOT_REFLECT; // Knightmare- do not reflect flag
empnuke->think = empBlastAnim;
empnuke->nextthink = level.time + FRAMETIME;
// empnuke->think = G_FreeEdict;
// empnuke->nextthink = level.time + 30;
empnuke->common_name = "EMP Nuke Center";
empnuke->class_id = ENTITY_Z_EMPNUKECENTER;
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
======================================================================
*/
//#ifdef USE_ZAERO_ITEMS_WEAPONS
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
PlasmaShield->common_name = "Plasma Shield";
PlasmaShield->class_id = ENTITY_Z_PLASMASHIELD;
gi.linkentity (PlasmaShield);
}
//#endif // USE_ZAERO_ITEMS_WEAPONS
/*
======================================================================
misc_crate
======================================================================
*/
void setupCrate (edict_t *self)
{
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_FALLFLOAT;
// self->movetype = MOVETYPE_STEP;
if (!self->mass)
self->mass = 400;
self->touch = barrel_touch;
self->think = M_droptofloor;
self->nextthink = level.time + 2 * FRAMETIME;
self->common_name = "Pushable Crate";
self->class_id = ENTITY_MISC_CRATE;
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);
}
/*
======================================================================
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);
self->common_name = "Ejection Seat";
self->class_id = ENTITY_MISC_SEAT;
}
/*
======================================================================
misc_commdish
======================================================================
*/
void Anim_CommDish (edict_t *self)
{
self->s.frame++;
if (self->s.frame >= 98)
{
self->s.frame = 98;
}
else
{
self->nextthink = level.time + FRAMETIME;
}
}
void Use_CommDish (edict_t *ent, edict_t *other, edict_t *activator)
{
ent->nextthink = level.time + FRAMETIME;
ent->think = Anim_CommDish;
ent->use = NULL;
gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/commdish.wav"), 1, ATTN_NORM, 0);
}
void SP_misc_commdish (edict_t *self)
{
if (deathmatch->value)
{ // auto-remove for deathmatch
G_FreeEdict (self);
return;
}
self->class_id = ENTITY_MISC_COMMDISH;
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_STEP;
self->model = "models/objects/satdish/tris.md2";
self->s.modelindex = gi.modelindex (self->model);
VectorSet (self->mins, -100, -100, 0);
VectorSet (self->maxs, 100, 100, 275);
self->monsterinfo.aiflags = AI_NOSTEP;
self->think = M_droptofloor;
self->nextthink = level.time + 2 * FRAMETIME;
self->use = Use_CommDish;
gi.linkentity (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->class_id = ENTITY_FUNC_BARRIER;
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 = 1000; // Knightmare- was 1
self->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
self->pain = barrier_pain;
gi.linkentity(self);
}