#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); }