From 99477441143b90b85c8fc6cf9379fffc0f1f1195 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Fri, 3 Nov 2023 00:36:57 +0200 Subject: [PATCH] game: sync ctf g_{chase,func,items,misc,weapon} --- Makefile | 12 +- src/ctf/g_chase.c | 210 --- src/ctf/g_func.c | 2584 --------------------------------- src/ctf/g_items.c | 2911 -------------------------------------- src/ctf/g_misc.c | 2466 -------------------------------- src/ctf/g_monster.c | 1480 ------------------- src/ctf/g_phys.c | 485 +++++-- src/ctf/player/weapon.c | 2085 --------------------------- src/game/g_chase.c | 25 + src/game/g_combat.c | 14 + src/game/g_func.c | 7 +- src/game/g_items.c | 235 ++- src/game/g_misc.c | 34 + src/game/player/weapon.c | 54 +- 14 files changed, 751 insertions(+), 11851 deletions(-) delete mode 100644 src/ctf/g_chase.c delete mode 100644 src/ctf/g_func.c delete mode 100644 src/ctf/g_items.c delete mode 100644 src/ctf/g_misc.c delete mode 100644 src/ctf/g_monster.c delete mode 100644 src/ctf/player/weapon.c diff --git a/Makefile b/Makefile index 6920a133..ea05edb3 100644 --- a/Makefile +++ b/Makefile @@ -1481,12 +1481,12 @@ CTF_OBJS_ = \ src/common/shared/rand.o \ src/common/shared/shared.o \ src/ctf/g_ai.o \ - src/ctf/g_chase.o \ + src/game/g_chase.o \ src/ctf/g_cmds.o \ src/ctf/g_combat.o \ src/game/g_ctf.o \ - src/ctf/g_func.o \ - src/ctf/g_items.o \ + src/game/g_func.o \ + src/game/g_items.o \ src/game/g_newai.o \ src/game/g_newdm.o \ src/game/g_newfnc.o \ @@ -1494,8 +1494,8 @@ CTF_OBJS_ = \ src/game/g_newtrig.o \ src/game/g_newweap.o \ src/ctf/g_main.o \ - src/ctf/g_misc.o \ - src/ctf/g_monster.o \ + src/game/g_misc.o \ + src/game/g_monster.o \ src/ctf/g_phys.o \ src/ctf/g_save.o \ src/game/g_sphere.o \ @@ -1543,7 +1543,7 @@ CTF_OBJS_ = \ src/ctf/player/hud.o \ src/ctf/player/trail.o \ src/ctf/player/view.o \ - src/ctf/player/weapon.o + src/game/player/weapon.o # ---------- diff --git a/src/ctf/g_chase.c b/src/ctf/g_chase.c deleted file mode 100644 index d543483c..00000000 --- a/src/ctf/g_chase.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * ======================================================================= - * - * Chase cam. - * - * ======================================================================= - */ - -#include "header/local.h" - -void -UpdateChaseCam(edict_t *ent) -{ - vec3_t o, ownerv, goal; - edict_t *targ; - vec3_t forward, right; - trace_t trace; - int i; - vec3_t angles; - - /* is our chase target gone? */ - if (!ent->client->chase_target->inuse) - { - ent->client->chase_target = NULL; - return; - } - - targ = ent->client->chase_target; - - VectorCopy(targ->s.origin, ownerv); - ownerv[2] += targ->viewheight; - VectorCopy(targ->client->v_angle, angles); - - if (angles[PITCH] > 56) - { - angles[PITCH] = 56; - } - - AngleVectors(angles, forward, right, NULL); - VectorNormalize(forward); - VectorMA(ownerv, -30, forward, o); - - if (o[2] < targ->s.origin[2] + 20) - { - o[2] = targ->s.origin[2] + 20; - } - - /* jump animation lifts */ - if (!targ->groundentity) - { - o[2] += 16; - } - - trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID); - - VectorCopy(trace.endpos, goal); - - VectorMA(goal, 2, forward, goal); - - /* pad for floors and ceilings */ - VectorCopy(goal, o); - o[2] += 6; - trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); - - if (trace.fraction < 1) - { - VectorCopy(trace.endpos, goal); - goal[2] -= 6; - } - - VectorCopy(goal, o); - o[2] -= 6; - trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); - - if (trace.fraction < 1) - { - VectorCopy(trace.endpos, goal); - goal[2] += 6; - } - - ent->client->ps.pmove.pm_type = PM_FREEZE; - - VectorCopy(goal, ent->s.origin); - - for (i = 0; i < 3; i++) - { - ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT( - targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]); - } - - VectorCopy(targ->client->v_angle, ent->client->ps.viewangles); - VectorCopy(targ->client->v_angle, ent->client->v_angle); - - ent->viewheight = 0; - ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - gi.linkentity(ent); - - if ((!ent->client->showscores && !ent->client->menu && - !ent->client->showinventory && !ent->client->showhelp && - !(level.framenum & 31)) || ent->client->update_chase) - { - char s[1024]; - - ent->client->update_chase = false; - sprintf(s, "xv 0 yb -68 string2 \"Chasing %s\"", - targ->client->pers.netname); - gi.WriteByte(svc_layout); - gi.WriteString(s); - gi.unicast(ent, false); - } -} - -void -ChaseNext(edict_t *ent) -{ - int i; - edict_t *e; - - if (!ent->client->chase_target) - { - return; - } - - i = ent->client->chase_target - g_edicts; - - do - { - i++; - - if (i > maxclients->value) - { - i = 1; - } - - e = g_edicts + i; - - if (!e->inuse) - { - continue; - } - - if (e->solid != SOLID_NOT) - { - break; - } - } - while (e != ent->client->chase_target); - - ent->client->chase_target = e; - ent->client->update_chase = true; -} - -void -ChasePrev(edict_t *ent) -{ - int i; - edict_t *e; - - if (!ent->client->chase_target) - { - return; - } - - i = ent->client->chase_target - g_edicts; - - do - { - i--; - - if (i < 1) - { - i = maxclients->value; - } - - e = g_edicts + i; - - if (!e->inuse) - { - continue; - } - - if (e->solid != SOLID_NOT) - { - break; - } - } - while (e != ent->client->chase_target); - - ent->client->chase_target = e; - ent->client->update_chase = true; -} - diff --git a/src/ctf/g_func.c b/src/ctf/g_func.c deleted file mode 100644 index 26135e0a..00000000 --- a/src/ctf/g_func.c +++ /dev/null @@ -1,2584 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * ======================================================================= - * - * Level functions. Platforms, buttons, dooors and so on. - * - * ======================================================================= - */ - -#include "header/local.h" - -/* - * ========================================================= - * - * PLATS - * - * movement options: - * - * linear - * smooth start, hard stop - * smooth start, smooth stop - * - * start - * end - * acceleration - * speed - * deceleration - * begin sound - * end sound - * target fired when reaching end - * wait at end - * - * object characteristics that use move segments - * --------------------------------------------- - * movetype_push, or movetype_stop - * action when touched - * action when blocked - * action when used - * disabled? - * auto trigger spawning - * - * ========================================================= - */ - -#define PLAT_LOW_TRIGGER 1 - -#define STATE_TOP 0 -#define STATE_BOTTOM 1 -#define STATE_UP 2 -#define STATE_DOWN 3 - -#define DOOR_START_OPEN 1 -#define DOOR_REVERSE 2 -#define DOOR_CRUSHER 4 -#define DOOR_NOMONSTER 8 -#define DOOR_TOGGLE 32 -#define DOOR_X_AXIS 64 -#define DOOR_Y_AXIS 128 - -/* Support routines for movement (changes in origin using velocity) */ - -void -Move_Done(edict_t *ent) -{ - VectorClear(ent->velocity); - ent->moveinfo.endfunc(ent); -} - -void -Move_Final(edict_t *ent) -{ - if (ent->moveinfo.remaining_distance == 0) - { - Move_Done(ent); - return; - } - - VectorScale(ent->moveinfo.dir, - ent->moveinfo.remaining_distance / FRAMETIME, - ent->velocity); - - ent->think = Move_Done; - ent->nextthink = level.time + FRAMETIME; -} - -void -Move_Begin(edict_t *ent) -{ - float frames; - - if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance) - { - Move_Final(ent); - return; - } - - VectorScale(ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity); - frames = floor( - (ent->moveinfo.remaining_distance / - ent->moveinfo.speed) / FRAMETIME); - ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME; - ent->nextthink = level.time + (frames * FRAMETIME); - ent->think = Move_Final; -} - -void Think_AccelMove(edict_t *ent); - -void -Move_Calc(edict_t *ent, vec3_t dest, void (*func)(edict_t *)) -{ - VectorClear(ent->velocity); - VectorSubtract(dest, ent->s.origin, ent->moveinfo.dir); - ent->moveinfo.remaining_distance = VectorNormalize(ent->moveinfo.dir); - ent->moveinfo.endfunc = func; - - if ((ent->moveinfo.speed == ent->moveinfo.accel) && - (ent->moveinfo.speed == ent->moveinfo.decel)) - { - if (level.current_entity == - ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) - { - Move_Begin(ent); - } - else - { - ent->nextthink = level.time + FRAMETIME; - ent->think = Move_Begin; - } - } - else - { - /* accelerative */ - ent->moveinfo.current_speed = 0; - ent->think = Think_AccelMove; - ent->nextthink = level.time + FRAMETIME; - } -} - -/* Support routines for angular movement (changes in angle using avelocity) */ - -void -AngleMove_Done(edict_t *ent) -{ - VectorClear(ent->avelocity); - ent->moveinfo.endfunc(ent); -} - -void -AngleMove_Final(edict_t *ent) -{ - vec3_t move; - - if (ent->moveinfo.state == STATE_UP) - { - VectorSubtract(ent->moveinfo.end_angles, ent->s.angles, move); - } - else - { - VectorSubtract(ent->moveinfo.start_angles, ent->s.angles, move); - } - - if (VectorCompare(move, vec3_origin)) - { - AngleMove_Done(ent); - return; - } - - VectorScale(move, 1.0 / FRAMETIME, ent->avelocity); - - ent->think = AngleMove_Done; - ent->nextthink = level.time + FRAMETIME; -} - -void -AngleMove_Begin(edict_t *ent) -{ - vec3_t destdelta; - float len; - float traveltime; - float frames; - - /* set destdelta to the vector needed to move */ - if (ent->moveinfo.state == STATE_UP) - { - VectorSubtract(ent->moveinfo.end_angles, ent->s.angles, destdelta); - } - else - { - VectorSubtract(ent->moveinfo.start_angles, ent->s.angles, destdelta); - } - - /* calculate length of vector */ - len = VectorLength(destdelta); - - /* divide by speed to get time to reach dest */ - traveltime = len / ent->moveinfo.speed; - - if (traveltime < FRAMETIME) - { - AngleMove_Final(ent); - return; - } - - frames = floor(traveltime / FRAMETIME); - - /* scale the destdelta vector by the time spent traveling to get velocity */ - VectorScale(destdelta, 1.0 / traveltime, ent->avelocity); - - /* set nextthink to trigger a think when dest is reached */ - ent->nextthink = level.time + frames * FRAMETIME; - ent->think = AngleMove_Final; -} - -void -AngleMove_Calc(edict_t *ent, void (*func)(edict_t *)) -{ - VectorClear(ent->avelocity); - ent->moveinfo.endfunc = func; - - if (level.current_entity == - ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) - { - AngleMove_Begin(ent); - } - else - { - ent->nextthink = level.time + FRAMETIME; - ent->think = AngleMove_Begin; - } -} - -/* - * The team has completed a frame of movement, - * so change the speed for the next frame. - */ - -#define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2) - -void -plat_CalcAcceleratedMove(moveinfo_t *moveinfo) -{ - float accel_dist; - float decel_dist; - - moveinfo->move_speed = moveinfo->speed; - - if (moveinfo->remaining_distance < moveinfo->accel) - { - moveinfo->current_speed = moveinfo->remaining_distance; - return; - } - - accel_dist = AccelerationDistance(moveinfo->speed, moveinfo->accel); - decel_dist = AccelerationDistance(moveinfo->speed, moveinfo->decel); - - if ((moveinfo->remaining_distance - accel_dist - decel_dist) < 0) - { - float f; - - f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel); - moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f); - decel_dist = AccelerationDistance(moveinfo->move_speed, moveinfo->decel); - } - - moveinfo->decel_distance = decel_dist; -} - -void -plat_Accelerate(moveinfo_t *moveinfo) -{ - /* are we decelerating? */ - if (moveinfo->remaining_distance <= moveinfo->decel_distance) - { - if (moveinfo->remaining_distance < moveinfo->decel_distance) - { - if (moveinfo->next_speed) - { - moveinfo->current_speed = moveinfo->next_speed; - moveinfo->next_speed = 0; - return; - } - - if (moveinfo->current_speed > moveinfo->decel) - { - moveinfo->current_speed -= moveinfo->decel; - } - } - - return; - } - - /* are we at full speed and need to start decelerating during this move? */ - if (moveinfo->current_speed == moveinfo->move_speed) - { - if ((moveinfo->remaining_distance - moveinfo->current_speed) < - moveinfo->decel_distance) - { - float p1_distance; - float p2_distance; - float distance; - - p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance; - p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / moveinfo->move_speed)); - distance = p1_distance + p2_distance; - moveinfo->current_speed = moveinfo->move_speed; - moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance); - return; - } - } - - /* are we accelerating? */ - if (moveinfo->current_speed < moveinfo->speed) - { - float old_speed; - float p1_distance; - float p1_speed; - float p2_distance; - float distance; - - old_speed = moveinfo->current_speed; - - /* figure simple acceleration up to move_speed */ - moveinfo->current_speed += moveinfo->accel; - - if (moveinfo->current_speed > moveinfo->speed) - { - moveinfo->current_speed = moveinfo->speed; - } - - /* are we accelerating throughout this entire move? */ - if ((moveinfo->remaining_distance - moveinfo->current_speed) >= - moveinfo->decel_distance) - { - return; - } - - /* during this move we will accelrate from current_speed to move_speed - and cross over the decel_distance; figure the average speed for the - entire move */ - p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance; - p1_speed = (old_speed + moveinfo->move_speed) / 2.0; - p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed)); - distance = p1_distance + p2_distance; - moveinfo->current_speed = (p1_speed * (p1_distance / - distance)) + (moveinfo->move_speed * (p2_distance / distance)); - moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * - (p2_distance / distance); - return; - } - - /* we are at constant velocity (move_speed) */ - return; -} - -void -Think_AccelMove(edict_t *ent) -{ - ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed; - - if (ent->moveinfo.current_speed == 0) /* starting or blocked */ - { - plat_CalcAcceleratedMove(&ent->moveinfo); - } - - plat_Accelerate(&ent->moveinfo); - - /* will the entire move complete on next frame? */ - if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed) - { - Move_Final(ent); - return; - } - - VectorScale(ent->moveinfo.dir, ent->moveinfo.current_speed * 10, ent->velocity); - ent->nextthink = level.time + FRAMETIME; - ent->think = Think_AccelMove; -} - -void plat_go_down(edict_t *ent); - -void -plat_hit_top(edict_t *ent) -{ - if (!(ent->flags & FL_TEAMSLAVE)) - { - if (ent->moveinfo.sound_end) - { - gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_end, - 1, ATTN_STATIC, 0); - } - - ent->s.sound = 0; - } - - ent->moveinfo.state = STATE_TOP; - - ent->think = plat_go_down; - ent->nextthink = level.time + 3; -} - -void -plat_hit_bottom(edict_t *ent) -{ - if (!(ent->flags & FL_TEAMSLAVE)) - { - if (ent->moveinfo.sound_end) - { - gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, - ent->moveinfo.sound_end, 1, - ATTN_STATIC, 0); - } - - ent->s.sound = 0; - } - - ent->moveinfo.state = STATE_BOTTOM; -} - -void -plat_go_down(edict_t *ent) -{ - if (!(ent->flags & FL_TEAMSLAVE)) - { - if (ent->moveinfo.sound_start) - { - gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, - ent->moveinfo.sound_start, 1, - ATTN_STATIC, 0); - } - - ent->s.sound = ent->moveinfo.sound_middle; - } - - ent->moveinfo.state = STATE_DOWN; - Move_Calc(ent, ent->moveinfo.end_origin, plat_hit_bottom); -} - -void -plat_go_up(edict_t *ent) -{ - if (!(ent->flags & FL_TEAMSLAVE)) - { - if (ent->moveinfo.sound_start) - { - gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, - ent->moveinfo.sound_start, 1, - ATTN_STATIC, 0); - } - - ent->s.sound = ent->moveinfo.sound_middle; - } - - ent->moveinfo.state = STATE_UP; - Move_Calc(ent, ent->moveinfo.start_origin, plat_hit_top); -} - -void -plat_blocked(edict_t *self, edict_t *other) -{ - if (!(other->svflags & SVF_MONSTER) && (!other->client)) - { - /* give it a chance to go away on it's own terms (like gibs) */ - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100000, 1, 0, MOD_CRUSH); - - /* if it's still there, nuke it */ - if (other) - { - /* Hack for entity without it's origin near the model */ - VectorMA(other->absmin, 0.5, other->size, other->s.origin); - BecomeExplosion1(other); - } - - return; - } - - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); - - if (self->moveinfo.state == STATE_UP) - { - plat_go_down(self); - } - else if (self->moveinfo.state == STATE_DOWN) - { - plat_go_up(self); - } -} - -void -Use_Plat(edict_t *ent, edict_t *other, edict_t *activator) -{ - if (ent->think) - { - return; /* already down */ - } - - plat_go_down(ent); -} - -void -Touch_Plat_Center(edict_t *ent, edict_t *other, cplane_t *plane, - csurface_t *surf) -{ - if (!other->client) - { - return; - } - - if (other->health <= 0) - { - return; - } - - ent = ent->enemy; /* now point at the plat, not the trigger */ - - if (ent->moveinfo.state == STATE_BOTTOM) - { - plat_go_up(ent); - } - else if (ent->moveinfo.state == STATE_TOP) - { - ent->nextthink = level.time + 1; /* the player is still on the plat, so delay going down */ - } -} - -edict_t * -plat_spawn_inside_trigger(edict_t *ent) -{ - edict_t *trigger; - vec3_t tmin, tmax; - - /* middle trigger */ - trigger = G_Spawn(); - trigger->touch = Touch_Plat_Center; - trigger->movetype = MOVETYPE_NONE; - trigger->solid = SOLID_TRIGGER; - trigger->enemy = ent; - - tmin[0] = ent->mins[0] + 25; - tmin[1] = ent->mins[1] + 25; - tmin[2] = ent->mins[2]; - - tmax[0] = ent->maxs[0] - 25; - tmax[1] = ent->maxs[1] - 25; - tmax[2] = ent->maxs[2] + 8; - - tmin[2] = tmax[2] - (ent->pos1[2] - ent->pos2[2] + st.lip); - - if (ent->spawnflags & PLAT_LOW_TRIGGER) - { - tmax[2] = tmin[2] + 8; - } - - if (tmax[0] - tmin[0] <= 0) - { - tmin[0] = (ent->mins[0] + ent->maxs[0]) * 0.5; - tmax[0] = tmin[0] + 1; - } - - if (tmax[1] - tmin[1] <= 0) - { - tmin[1] = (ent->mins[1] + ent->maxs[1]) * 0.5; - tmax[1] = tmin[1] + 1; - } - - VectorCopy(tmin, trigger->mins); - VectorCopy(tmax, trigger->maxs); - - gi.linkentity(trigger); - - return NULL; -} - -/* - * QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER - * speed default 150 - * - * Plats are always drawn in the extended position, - * so they will light correctly. - * - * If the plat is the target of another trigger or button, - * it will start out disabled in the extended position until - * it is trigger, when it will lower and become a normal plat. - * - * "speed" overrides default 200. - * "accel" overrides default 500 - * "lip" overrides default 8 pixel lip - * - * If the "height" key is set, that will determine the amount - * the plat moves, instead of being implicitly determoveinfoned - * by the model's height. - * - * Set "sounds" to one of the following: - * 1) base fast - * 2) chain slow - */ -void -SP_func_plat(edict_t *ent) -{ - VectorClear(ent->s.angles); - ent->solid = SOLID_BSP; - ent->movetype = MOVETYPE_PUSH; - - gi.setmodel(ent, ent->model); - - ent->blocked = plat_blocked; - - if (!ent->speed) - { - ent->speed = 20; - } - else - { - ent->speed *= 0.1; - } - - if (!ent->accel) - { - ent->accel = 5; - } - else - { - ent->accel *= 0.1; - } - - if (!ent->decel) - { - ent->decel = 5; - } - else - { - ent->decel *= 0.1; - } - - if (!ent->dmg) - { - ent->dmg = 2; - } - - if (!st.lip) - { - st.lip = 8; - } - - /* pos1 is the top position, pos2 is the bottom */ - VectorCopy(ent->s.origin, ent->pos1); - VectorCopy(ent->s.origin, ent->pos2); - - if (st.height) - { - ent->pos2[2] -= st.height; - } - else - { - ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip; - } - - ent->use = Use_Plat; - - plat_spawn_inside_trigger(ent); /* the "start moving" trigger */ - - if (ent->targetname) - { - ent->moveinfo.state = STATE_UP; - } - else - { - VectorCopy(ent->pos2, ent->s.origin); - gi.linkentity(ent); - ent->moveinfo.state = STATE_BOTTOM; - } - - ent->moveinfo.speed = ent->speed; - ent->moveinfo.accel = ent->accel; - ent->moveinfo.decel = ent->decel; - ent->moveinfo.wait = ent->wait; - VectorCopy(ent->pos1, ent->moveinfo.start_origin); - VectorCopy(ent->s.angles, ent->moveinfo.start_angles); - VectorCopy(ent->pos2, ent->moveinfo.end_origin); - VectorCopy(ent->s.angles, ent->moveinfo.end_angles); - - ent->moveinfo.sound_start = gi.soundindex("plats/pt1_strt.wav"); - ent->moveinfo.sound_middle = gi.soundindex("plats/pt1_mid.wav"); - ent->moveinfo.sound_end = gi.soundindex("plats/pt1_end.wav"); -} - -/* ==================================================================== */ - -/* - * QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST - * - * You need to have an origin brush as part of this entity. The center of that brush will be - * the point around which it is rotated. It will rotate around the Z axis by default. You can - * check either the X_AXIS or Y_AXIS box to change that. - * - * "speed" determines how fast it moves; default value is 100. - * "dmg" damage to inflict when blocked (2 default) - * - * REVERSE will cause the it to rotate in the opposite direction. - * STOP mean it will stop moving instead of pushing entities - */ - -void -rotating_blocked(edict_t *self, edict_t *other) -{ - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); -} - -void -rotating_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (self->avelocity[0] || self->avelocity[1] || self->avelocity[2]) - { - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); - } -} - -void -rotating_use(edict_t *self, edict_t *other, edict_t *activator) -{ - if (!VectorCompare(self->avelocity, vec3_origin)) - { - self->s.sound = 0; - VectorClear(self->avelocity); - self->touch = NULL; - } - else - { - self->s.sound = self->moveinfo.sound_middle; - VectorScale(self->movedir, self->speed, self->avelocity); - - if (self->spawnflags & 16) - { - self->touch = rotating_touch; - } - } -} - -void -SP_func_rotating(edict_t *ent) -{ - ent->solid = SOLID_BSP; - - if (ent->spawnflags & 32) - { - ent->movetype = MOVETYPE_STOP; - } - else - { - ent->movetype = MOVETYPE_PUSH; - } - - /* set the axis of rotation */ - VectorClear(ent->movedir); - - if (ent->spawnflags & 4) - { - ent->movedir[2] = 1.0; - } - else if (ent->spawnflags & 8) - { - ent->movedir[0] = 1.0; - } - else /* Z_AXIS */ - { - ent->movedir[1] = 1.0; - } - - /* check for reverse rotation */ - if (ent->spawnflags & 2) - { - VectorNegate(ent->movedir, ent->movedir); - } - - if (!ent->speed) - { - ent->speed = 100; - } - - if (!ent->dmg) - { - ent->dmg = 2; - } - - ent->use = rotating_use; - - if (ent->dmg) - { - ent->blocked = rotating_blocked; - } - - if (ent->spawnflags & 1) - { - ent->use(ent, NULL, NULL); - } - - if (ent->spawnflags & 64) - { - ent->s.effects |= EF_ANIM_ALL; - } - - if (ent->spawnflags & 128) - { - ent->s.effects |= EF_ANIM_ALLFAST; - } - - gi.setmodel(ent, ent->model); - gi.linkentity(ent); -} - -/* - * ====================================================================== - * - * BUTTONS - * - * ====================================================================== - */ - -/* - * QUAKED func_button (0 .5 .8) ? - * When a button is touched, it moves some distance in the - * direction of it's angle, triggers all of it's targets, - * waits some time, then returns to it's original position - * where it can be triggered again. - * - * "angle" determines the opening direction - * "target" all entities with a matching targetname will be used - * "speed" override the default 40 speed - * "wait" override the default 1 second wait (-1 = never return) - * "lip" override the default 4 pixel lip remaining at end of move - * "health" if set, the button must be killed instead of touched - * "sounds" - * 1) silent - * 2) steam metal - * 3) wooden clunk - * 4) metallic click - * 5) in-out - */ - -void -button_done(edict_t *self) -{ - self->moveinfo.state = STATE_BOTTOM; - self->s.effects &= ~EF_ANIM23; - self->s.effects |= EF_ANIM01; -} - -void -button_return(edict_t *self) -{ - self->moveinfo.state = STATE_DOWN; - - Move_Calc(self, self->moveinfo.start_origin, button_done); - - self->s.frame = 0; - - if (self->health) - { - self->takedamage = DAMAGE_YES; - } -} - -void -button_wait(edict_t *self) -{ - self->moveinfo.state = STATE_TOP; - self->s.effects &= ~EF_ANIM01; - self->s.effects |= EF_ANIM23; - - G_UseTargets(self, self->activator); - self->s.frame = 1; - - if (self->moveinfo.wait >= 0) - { - self->nextthink = level.time + self->moveinfo.wait; - self->think = button_return; - } -} - -void -button_fire(edict_t *self) -{ - if ((self->moveinfo.state == STATE_UP) || - (self->moveinfo.state == STATE_TOP)) - { - return; - } - - self->moveinfo.state = STATE_UP; - - if (self->moveinfo.sound_start && !(self->flags & FL_TEAMSLAVE)) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_start, 1, - ATTN_STATIC, 0); - } - - Move_Calc(self, self->moveinfo.end_origin, button_wait); -} - -void -button_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->activator = activator; - button_fire(self); -} - -void -button_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (!other->client) - { - return; - } - - if (other->health <= 0) - { - return; - } - - self->activator = other; - button_fire(self); -} - -void -button_killed(edict_t *self, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - self->activator = attacker; - self->health = self->max_health; - self->takedamage = DAMAGE_NO; - button_fire(self); -} - -void -SP_func_button(edict_t *ent) -{ - vec3_t abs_movedir; - float dist; - - G_SetMovedir(ent->s.angles, ent->movedir); - ent->movetype = MOVETYPE_STOP; - ent->solid = SOLID_BSP; - gi.setmodel(ent, ent->model); - - if (ent->sounds != 1) - { - ent->moveinfo.sound_start = gi.soundindex("switches/butn2.wav"); - } - - if (!ent->speed) - { - ent->speed = 40; - } - - if (!ent->accel) - { - ent->accel = ent->speed; - } - - if (!ent->decel) - { - ent->decel = ent->speed; - } - - if (!ent->wait) - { - ent->wait = 3; - } - - if (!st.lip) - { - st.lip = 4; - } - - VectorCopy(ent->s.origin, ent->pos1); - abs_movedir[0] = fabs(ent->movedir[0]); - abs_movedir[1] = fabs(ent->movedir[1]); - abs_movedir[2] = fabs(ent->movedir[2]); - dist = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + - abs_movedir[2] * ent->size[2] - st.lip; - VectorMA(ent->pos1, dist, ent->movedir, ent->pos2); - - ent->use = button_use; - ent->s.effects |= EF_ANIM01; - - if (ent->health) - { - ent->max_health = ent->health; - ent->die = button_killed; - ent->takedamage = DAMAGE_YES; - } - else if (!ent->targetname) - { - ent->touch = button_touch; - } - - ent->moveinfo.state = STATE_BOTTOM; - - ent->moveinfo.speed = ent->speed; - ent->moveinfo.accel = ent->accel; - ent->moveinfo.decel = ent->decel; - ent->moveinfo.wait = ent->wait; - VectorCopy(ent->pos1, ent->moveinfo.start_origin); - VectorCopy(ent->s.angles, ent->moveinfo.start_angles); - VectorCopy(ent->pos2, ent->moveinfo.end_origin); - VectorCopy(ent->s.angles, ent->moveinfo.end_angles); - - gi.linkentity(ent); -} - -/* - * ====================================================================== - * - * DOORS - * - * spawn a trigger surrounding the entire team unless it is - * allready targeted by another - * - * ====================================================================== - */ - -/* - * QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED TOGGLE ANIMATED_FAST - * TOGGLE wait in both the start and end states for a trigger event. - * START_OPEN the door to moves to its destination when spawned, and operate in reverse. - * It is used to temporarily or permanently close off an area when triggered - * (not useful for touch or takedamage doors). - * NOMONSTER monsters will not trigger this door - * - * "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet - * "angle" determines the opening direction - * "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. - * "health" if set, door must be shot open - * "speed" movement speed (100 default) - * "wait" wait before returning (3 default, -1 = never return) - * "lip" lip remaining at end of move (8 default) - * "dmg" damage to inflict when blocked (2 default) - * "sounds" - * 1) silent - * 2) light - * 3) medium - * 4) heavy - */ - -void -door_use_areaportals(edict_t *self, qboolean open) -{ - edict_t *t = NULL; - - if (!self->target) - { - return; - } - - while ((t = G_Find(t, FOFS(targetname), self->target))) - { - if (Q_stricmp(t->classname, "func_areaportal") == 0) - { - gi.SetAreaPortalState(t->style, open); - } - } -} - -void door_go_down(edict_t *self); - -void -door_hit_top(edict_t *self) -{ - if (!(self->flags & FL_TEAMSLAVE)) - { - if (self->moveinfo.sound_end) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_end, 1, ATTN_STATIC, - 0); - } - - self->s.sound = 0; - } - - self->moveinfo.state = STATE_TOP; - - if (self->spawnflags & DOOR_TOGGLE) - { - return; - } - - if (self->moveinfo.wait >= 0) - { - self->think = door_go_down; - self->nextthink = level.time + self->moveinfo.wait; - } -} - -void -door_hit_bottom(edict_t *self) -{ - if (!(self->flags & FL_TEAMSLAVE)) - { - if (self->moveinfo.sound_end) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_end, 1, - ATTN_STATIC, 0); - } - - self->s.sound = 0; - } - - self->moveinfo.state = STATE_BOTTOM; - door_use_areaportals(self, false); -} - -void -door_go_down(edict_t *self) -{ - if (!(self->flags & FL_TEAMSLAVE)) - { - if (self->moveinfo.sound_start) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_start, 1, - ATTN_STATIC, 0); - } - - self->s.sound = self->moveinfo.sound_middle; - } - - if (self->max_health) - { - self->takedamage = DAMAGE_YES; - self->health = self->max_health; - } - - self->moveinfo.state = STATE_DOWN; - - if (strcmp(self->classname, "func_door") == 0) - { - Move_Calc(self, self->moveinfo.start_origin, door_hit_bottom); - } - else if (strcmp(self->classname, "func_door_rotating") == 0) - { - AngleMove_Calc(self, door_hit_bottom); - } -} - -void -door_go_up(edict_t *self, edict_t *activator) -{ - if (self->moveinfo.state == STATE_UP) - { - return; /* already going up */ - } - - if (self->moveinfo.state == STATE_TOP) - { - /* reset top wait time */ - if (self->moveinfo.wait >= 0) - { - self->nextthink = level.time + self->moveinfo.wait; - } - - return; - } - - if (!(self->flags & FL_TEAMSLAVE)) - { - if (self->moveinfo.sound_start) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_start, 1, - ATTN_STATIC, 0); - } - - self->s.sound = self->moveinfo.sound_middle; - } - - self->moveinfo.state = STATE_UP; - - if (strcmp(self->classname, "func_door") == 0) - { - Move_Calc(self, self->moveinfo.end_origin, door_hit_top); - } - else if (strcmp(self->classname, "func_door_rotating") == 0) - { - AngleMove_Calc(self, door_hit_top); - } - - G_UseTargets(self, activator); - door_use_areaportals(self, true); -} - -void -door_use(edict_t *self, edict_t *other, edict_t *activator) -{ - edict_t *ent; - - if (self->flags & FL_TEAMSLAVE) - { - return; - } - - if (self->spawnflags & DOOR_TOGGLE) - { - if ((self->moveinfo.state == STATE_UP) || - (self->moveinfo.state == STATE_TOP)) - { - /* trigger all paired doors */ - for (ent = self; ent; ent = ent->teamchain) - { - ent->message = NULL; - ent->touch = NULL; - door_go_down(ent); - } - - return; - } - } - - /* trigger all paired doors */ - for (ent = self; ent; ent = ent->teamchain) - { - ent->message = NULL; - ent->touch = NULL; - door_go_up(ent, activator); - } -} - -void -Touch_DoorTrigger(edict_t *self, edict_t *other, cplane_t *plane, - csurface_t *surf) -{ - if (other->health <= 0) - { - return; - } - - if (!(other->svflags & SVF_MONSTER) && (!other->client)) - { - return; - } - - if ((self->owner->spawnflags & DOOR_NOMONSTER) && - (other->svflags & SVF_MONSTER)) - { - return; - } - - if (level.time < self->touch_debounce_time) - { - return; - } - - self->touch_debounce_time = level.time + 1.0; - - door_use(self->owner, other, other); -} - -void -Think_CalcMoveSpeed(edict_t *self) -{ - edict_t *ent; - float min; - float time; - float newspeed; - float ratio; - float dist; - - if (self->flags & FL_TEAMSLAVE) - { - return; /* only the team master does this */ - } - - /* find the smallest distance any member of the team will be moving */ - min = fabs(self->moveinfo.distance); - - for (ent = self->teamchain; ent; ent = ent->teamchain) - { - dist = fabs(ent->moveinfo.distance); - - if (dist < min) - { - min = dist; - } - } - - time = min / self->moveinfo.speed; - - /* adjust speeds so they will all complete at the same time */ - for (ent = self; ent; ent = ent->teamchain) - { - newspeed = fabs(ent->moveinfo.distance) / time; - ratio = newspeed / ent->moveinfo.speed; - - if (ent->moveinfo.accel == ent->moveinfo.speed) - { - ent->moveinfo.accel = newspeed; - } - else - { - ent->moveinfo.accel *= ratio; - } - - if (ent->moveinfo.decel == ent->moveinfo.speed) - { - ent->moveinfo.decel = newspeed; - } - else - { - ent->moveinfo.decel *= ratio; - } - - ent->moveinfo.speed = newspeed; - } -} - -void -Think_SpawnDoorTrigger(edict_t *ent) -{ - edict_t *other; - vec3_t mins, maxs; - - if (ent->flags & FL_TEAMSLAVE) - { - return; /* only the team leader spawns a trigger */ - } - - VectorCopy(ent->absmin, mins); - VectorCopy(ent->absmax, maxs); - - for (other = ent->teamchain; other; other = other->teamchain) - { - AddPointToBounds(other->absmin, mins, maxs); - AddPointToBounds(other->absmax, mins, maxs); - } - - /* expand */ - mins[0] -= 60; - mins[1] -= 60; - maxs[0] += 60; - maxs[1] += 60; - - other = G_Spawn(); - VectorCopy(mins, other->mins); - VectorCopy(maxs, other->maxs); - other->owner = ent; - other->solid = SOLID_TRIGGER; - other->movetype = MOVETYPE_NONE; - other->touch = Touch_DoorTrigger; - gi.linkentity(other); - - if (ent->spawnflags & DOOR_START_OPEN) - { - door_use_areaportals(ent, true); - } - - Think_CalcMoveSpeed(ent); -} - -void -door_blocked(edict_t *self, edict_t *other) -{ - edict_t *ent; - - if (!(other->svflags & SVF_MONSTER) && (!other->client)) - { - /* give it a chance to go away on it's own terms (like gibs) */ - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100000, 1, 0, MOD_CRUSH); - - /* if it's still there, nuke it */ - if (other) - { - /* Hack for entitiy without their origin near the model */ - VectorMA(other->absmin, 0.5, other->size, other->s.origin); - BecomeExplosion1(other); - } - - return; - } - - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); - - if (self->spawnflags & DOOR_CRUSHER) - { - return; - } - - /* if a door has a negative wait, it would never come back if blocked, - so let it just squash the object to death real fast */ - if (self->moveinfo.wait >= 0) - { - if (self->moveinfo.state == STATE_DOWN) - { - for (ent = self->teammaster; ent; ent = ent->teamchain) - { - door_go_up(ent, ent->activator); - } - } - else - { - for (ent = self->teammaster; ent; ent = ent->teamchain) - { - door_go_down(ent); - } - } - } -} - -void -door_killed(edict_t *self, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - edict_t *ent; - - for (ent = self->teammaster; ent; ent = ent->teamchain) - { - ent->health = ent->max_health; - ent->takedamage = DAMAGE_NO; - } - - door_use(self->teammaster, attacker, attacker); -} - -void -door_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (!other->client) - { - return; - } - - if (level.time < self->touch_debounce_time) - { - return; - } - - self->touch_debounce_time = level.time + 5.0; - - gi.centerprintf(other, "%s", self->message); - gi.sound(other, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0); -} - -void -SP_func_door(edict_t *ent) -{ - vec3_t abs_movedir; - - if (ent->sounds != 1) - { - ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); - ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); - ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); - } - - G_SetMovedir(ent->s.angles, ent->movedir); - ent->movetype = MOVETYPE_PUSH; - ent->solid = SOLID_BSP; - gi.setmodel(ent, ent->model); - - ent->blocked = door_blocked; - ent->use = door_use; - - if (!ent->speed) - { - ent->speed = 100; - } - - if (deathmatch->value) - { - ent->speed *= 2; - } - - if (!ent->accel) - { - ent->accel = ent->speed; - } - - if (!ent->decel) - { - ent->decel = ent->speed; - } - - if (!ent->wait) - { - ent->wait = 3; - } - - if (!st.lip) - { - st.lip = 8; - } - - if (!ent->dmg) - { - ent->dmg = 2; - } - - /* calculate second position */ - VectorCopy(ent->s.origin, ent->pos1); - abs_movedir[0] = fabs(ent->movedir[0]); - abs_movedir[1] = fabs(ent->movedir[1]); - abs_movedir[2] = fabs(ent->movedir[2]); - ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * - ent->size[1] + abs_movedir[2] * ent->size[2] - - st.lip; - VectorMA(ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2); - - /* if it starts open, switch the positions */ - if (ent->spawnflags & DOOR_START_OPEN) - { - VectorCopy(ent->pos2, ent->s.origin); - VectorCopy(ent->pos1, ent->pos2); - VectorCopy(ent->s.origin, ent->pos1); - } - - ent->moveinfo.state = STATE_BOTTOM; - - if (ent->health) - { - ent->takedamage = DAMAGE_YES; - ent->die = door_killed; - ent->max_health = ent->health; - } - else if (ent->targetname && ent->message) - { - gi.soundindex("misc/talk.wav"); - ent->touch = door_touch; - } - - ent->moveinfo.speed = ent->speed; - ent->moveinfo.accel = ent->accel; - ent->moveinfo.decel = ent->decel; - ent->moveinfo.wait = ent->wait; - VectorCopy(ent->pos1, ent->moveinfo.start_origin); - VectorCopy(ent->s.angles, ent->moveinfo.start_angles); - VectorCopy(ent->pos2, ent->moveinfo.end_origin); - VectorCopy(ent->s.angles, ent->moveinfo.end_angles); - - if (ent->spawnflags & 16) - { - ent->s.effects |= EF_ANIM_ALL; - } - - if (ent->spawnflags & 64) - { - ent->s.effects |= EF_ANIM_ALLFAST; - } - - /* to simplify logic elsewhere, make non-teamed doors into a team of one */ - if (!ent->team) - { - ent->teammaster = ent; - } - - gi.linkentity(ent); - - ent->nextthink = level.time + FRAMETIME; - - if (ent->health || ent->targetname) - { - ent->think = Think_CalcMoveSpeed; - } - else - { - ent->think = Think_SpawnDoorTrigger; - } -} - -/* - * QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS - * TOGGLE causes the door to wait in both the start and end states for a trigger event. - * START_OPEN the door to moves to its destination when spawned, and operate in reverse. - * It is used to temporarily or permanently close off an area when triggered - * (not useful for touch or takedamage doors). - * NOMONSTER monsters will not trigger this door - * - * You need to have an origin brush as part of this entity. The center of that brush will be - * the point around which it is rotated. It will rotate around the Z axis by default. You can - * check either the X_AXIS or Y_AXIS box to change that. - * - * "distance" is how many degrees the door will be rotated. - * "speed" determines how fast the door moves; default value is 100. - * - * REVERSE will cause the door to rotate in the opposite direction. - * - * "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet - * "angle" determines the opening direction - * "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. - * "health" if set, door must be shot open - * "speed" movement speed (100 default) - * "wait" wait before returning (3 default, -1 = never return) - * "dmg" damage to inflict when blocked (2 default) - * "sounds" - * 1) silent - * 2) light - * 3) medium - * 4) heavy - */ - -void -SP_func_door_rotating(edict_t *ent) -{ - VectorClear(ent->s.angles); - - /* set the axis of rotation */ - VectorClear(ent->movedir); - - if (ent->spawnflags & DOOR_X_AXIS) - { - ent->movedir[2] = 1.0; - } - else if (ent->spawnflags & DOOR_Y_AXIS) - { - ent->movedir[0] = 1.0; - } - else /* Z_AXIS */ - { - ent->movedir[1] = 1.0; - } - - /* check for reverse rotation */ - if (ent->spawnflags & DOOR_REVERSE) - { - VectorNegate(ent->movedir, ent->movedir); - } - - if (!st.distance) - { - gi.dprintf("%s at %s with no distance set\n", ent->classname, - vtos(ent->s.origin)); - st.distance = 90; - } - - VectorCopy(ent->s.angles, ent->pos1); - VectorMA(ent->s.angles, st.distance, ent->movedir, ent->pos2); - ent->moveinfo.distance = st.distance; - - ent->movetype = MOVETYPE_PUSH; - ent->solid = SOLID_BSP; - gi.setmodel(ent, ent->model); - - ent->blocked = door_blocked; - ent->use = door_use; - - if (!ent->speed) - { - ent->speed = 100; - } - - if (!ent->accel) - { - ent->accel = ent->speed; - } - - if (!ent->decel) - { - ent->decel = ent->speed; - } - - if (!ent->wait) - { - ent->wait = 3; - } - - if (!ent->dmg) - { - ent->dmg = 2; - } - - if (ent->sounds != 1) - { - ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); - ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); - ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); - } - - /* if it starts open, switch the positions */ - if (ent->spawnflags & DOOR_START_OPEN) - { - VectorCopy(ent->pos2, ent->s.angles); - VectorCopy(ent->pos1, ent->pos2); - VectorCopy(ent->s.angles, ent->pos1); - VectorNegate(ent->movedir, ent->movedir); - } - - if (ent->health) - { - ent->takedamage = DAMAGE_YES; - ent->die = door_killed; - ent->max_health = ent->health; - } - - if (ent->targetname && ent->message) - { - gi.soundindex("misc/talk.wav"); - ent->touch = door_touch; - } - - ent->moveinfo.state = STATE_BOTTOM; - ent->moveinfo.speed = ent->speed; - ent->moveinfo.accel = ent->accel; - ent->moveinfo.decel = ent->decel; - ent->moveinfo.wait = ent->wait; - VectorCopy(ent->s.origin, ent->moveinfo.start_origin); - VectorCopy(ent->pos1, ent->moveinfo.start_angles); - VectorCopy(ent->s.origin, ent->moveinfo.end_origin); - VectorCopy(ent->pos2, ent->moveinfo.end_angles); - - if (ent->spawnflags & 16) - { - ent->s.effects |= EF_ANIM_ALL; - } - - /* to simplify logic elsewhere, make non-teamed doors into a team of one */ - if (!ent->team) - { - ent->teammaster = ent; - } - - gi.linkentity(ent); - - ent->nextthink = level.time + FRAMETIME; - - if (ent->health || ent->targetname) - { - ent->think = Think_CalcMoveSpeed; - } - else - { - ent->think = Think_SpawnDoorTrigger; - } -} - -/* - * QUAKED func_water (0 .5 .8) ? START_OPEN - * func_water is a moveable water brush. It must be targeted to operate. Use a non-water texture at your own risk. - * - * START_OPEN causes the water to move to its destination when spawned and operate in reverse. - * - * "angle" determines the opening direction (up or down only) - * "speed" movement speed (25 default) - * "wait" wait before returning (-1 default, -1 = TOGGLE) - * "lip" lip remaining at end of move (0 default) - * "sounds" (yes, these need to be changed) - * 0) no sound - * 1) water - * 2) lava - */ - -void -SP_func_water(edict_t *self) -{ - vec3_t abs_movedir; - - G_SetMovedir(self->s.angles, self->movedir); - self->movetype = MOVETYPE_PUSH; - self->solid = SOLID_BSP; - gi.setmodel(self, self->model); - - switch (self->sounds) - { - default: - break; - - case 1: /* water */ - self->moveinfo.sound_start = gi.soundindex("world/mov_watr.wav"); - self->moveinfo.sound_end = gi.soundindex("world/stp_watr.wav"); - break; - - case 2: /* lava */ - self->moveinfo.sound_start = gi.soundindex("world/mov_watr.wav"); - self->moveinfo.sound_end = gi.soundindex("world/stp_watr.wav"); - break; - } - - /* calculate second position */ - VectorCopy(self->s.origin, self->pos1); - abs_movedir[0] = fabs(self->movedir[0]); - abs_movedir[1] = fabs(self->movedir[1]); - abs_movedir[2] = fabs(self->movedir[2]); - self->moveinfo.distance = abs_movedir[0] * self->size[0] + abs_movedir[1] * - self->size[1] + abs_movedir[2] * self->size[2] - - st.lip; - VectorMA(self->pos1, self->moveinfo.distance, self->movedir, self->pos2); - - /* if it starts open, switch the positions */ - if (self->spawnflags & DOOR_START_OPEN) - { - VectorCopy(self->pos2, self->s.origin); - VectorCopy(self->pos1, self->pos2); - VectorCopy(self->s.origin, self->pos1); - } - - VectorCopy(self->pos1, self->moveinfo.start_origin); - VectorCopy(self->s.angles, self->moveinfo.start_angles); - VectorCopy(self->pos2, self->moveinfo.end_origin); - VectorCopy(self->s.angles, self->moveinfo.end_angles); - - self->moveinfo.state = STATE_BOTTOM; - - if (!self->speed) - { - self->speed = 25; - } - - self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed; - - if (!self->wait) - { - self->wait = -1; - } - - self->moveinfo.wait = self->wait; - - self->use = door_use; - - if (self->wait == -1) - { - self->spawnflags |= DOOR_TOGGLE; - } - - self->classname = "func_door"; - - gi.linkentity(self); -} - -#define TRAIN_START_ON 1 -#define TRAIN_TOGGLE 2 -#define TRAIN_BLOCK_STOPS 4 - -/* - * QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS - * Trains are moving platforms that players can ride. - * The targets origin specifies the min point of the train at each corner. - * The train spawns at the first target it is pointing at. - * If the train is the target of a button or trigger, it will not begin moving until activated. - * speed default 100 - * dmg default 2 - * noise looping sound to play when the train is in motion - * - */ -void train_next(edict_t *self); - -void -train_blocked(edict_t *self, edict_t *other) -{ - if (!(other->svflags & SVF_MONSTER) && (!other->client)) - { - /* give it a chance to go away on it's own terms (like gibs) */ - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100000, 1, 0, MOD_CRUSH); - - /* if it's still there, nuke it */ - if (other) - { - /* Hack for entity without an origin near the model */ - VectorMA(other->absmin, 0.5, other->size, other->s.origin); - BecomeExplosion1(other); - } - - return; - } - - if (level.time < self->touch_debounce_time) - { - return; - } - - if (!self->dmg) - { - return; - } - - self->touch_debounce_time = level.time + 0.5; - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); -} - -void -train_wait(edict_t *self) -{ - if (self->target_ent->pathtarget) - { - char *savetarget; - edict_t *ent; - - ent = self->target_ent; - savetarget = ent->target; - ent->target = ent->pathtarget; - G_UseTargets(ent, self->activator); - ent->target = savetarget; - - /* make sure we didn't get killed by a killtarget */ - if (!self->inuse) - { - return; - } - } - - if (self->moveinfo.wait) - { - if (self->moveinfo.wait > 0) - { - self->nextthink = level.time + self->moveinfo.wait; - self->think = train_next; - } - else if (self->spawnflags & TRAIN_TOGGLE) /* && wait < 0 */ - { - train_next(self); - self->spawnflags &= ~TRAIN_START_ON; - VectorClear(self->velocity); - self->nextthink = 0; - } - - if (!(self->flags & FL_TEAMSLAVE)) - { - if (self->moveinfo.sound_end) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_end, 1, - ATTN_STATIC, 0); - } - - self->s.sound = 0; - } - } - else - { - train_next(self); - } -} - -void -train_next(edict_t *self) -{ - edict_t *ent; - vec3_t dest; - qboolean first; - - first = true; -again: - - if (!self->target) - { - return; - } - - ent = G_PickTarget(self->target); - - if (!ent) - { - gi.dprintf("train_next: bad target %s\n", self->target); - return; - } - - self->target = ent->target; - - /* check for a teleport path_corner */ - if (ent->spawnflags & 1) - { - if (!first) - { - gi.dprintf("connected teleport path_corners, see %s at %s\n", - ent->classname, vtos(ent->s.origin)); - return; - } - - first = false; - VectorSubtract(ent->s.origin, self->mins, self->s.origin); - VectorCopy(self->s.origin, self->s.old_origin); - gi.linkentity(self); - goto again; - } - - self->moveinfo.wait = ent->wait; - self->target_ent = ent; - - if (!(self->flags & FL_TEAMSLAVE)) - { - if (self->moveinfo.sound_start) - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_start, 1, ATTN_STATIC, - 0); - } - - self->s.sound = self->moveinfo.sound_middle; - } - - VectorSubtract(ent->s.origin, self->mins, dest); - self->moveinfo.state = STATE_TOP; - VectorCopy(self->s.origin, self->moveinfo.start_origin); - VectorCopy(dest, self->moveinfo.end_origin); - Move_Calc(self, dest, train_wait); - self->spawnflags |= TRAIN_START_ON; -} - -void -train_resume(edict_t *self) -{ - edict_t *ent; - vec3_t dest; - - ent = self->target_ent; - - VectorSubtract(ent->s.origin, self->mins, dest); - self->moveinfo.state = STATE_TOP; - VectorCopy(self->s.origin, self->moveinfo.start_origin); - VectorCopy(dest, self->moveinfo.end_origin); - Move_Calc(self, dest, train_wait); - self->spawnflags |= TRAIN_START_ON; -} - -void -func_train_find(edict_t *self) -{ - edict_t *ent; - - if (!self->target) - { - gi.dprintf("train_find: no target\n"); - return; - } - - ent = G_PickTarget(self->target); - - if (!ent) - { - gi.dprintf("train_find: target %s not found\n", self->target); - return; - } - - self->target = ent->target; - - VectorSubtract(ent->s.origin, self->mins, self->s.origin); - gi.linkentity(self); - - /* if not triggered, start immediately */ - if (!self->targetname) - { - self->spawnflags |= TRAIN_START_ON; - } - - if (self->spawnflags & TRAIN_START_ON) - { - self->nextthink = level.time + FRAMETIME; - self->think = train_next; - self->activator = self; - } -} - -void -train_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->activator = activator; - - if (self->spawnflags & TRAIN_START_ON) - { - if (!(self->spawnflags & TRAIN_TOGGLE)) - { - return; - } - - self->spawnflags &= ~TRAIN_START_ON; - VectorClear(self->velocity); - self->nextthink = 0; - } - else - { - if (self->target_ent) - { - train_resume(self); - } - else - { - train_next(self); - } - } -} - -void -SP_func_train(edict_t *self) -{ - self->movetype = MOVETYPE_PUSH; - - VectorClear(self->s.angles); - self->blocked = train_blocked; - - if (self->spawnflags & TRAIN_BLOCK_STOPS) - { - self->dmg = 0; - } - else - { - if (!self->dmg) - { - self->dmg = 100; - } - } - - self->solid = SOLID_BSP; - gi.setmodel(self, self->model); - - if (st.noise) - { - self->moveinfo.sound_middle = gi.soundindex(st.noise); - } - - if (!self->speed) - { - self->speed = 100; - } - - self->moveinfo.speed = self->speed; - self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed; - - self->use = train_use; - - gi.linkentity(self); - - if (self->target) - { - /* start trains on the second frame, to make - sure their targets have had a chance to spawn */ - self->nextthink = level.time + FRAMETIME; - self->think = func_train_find; - } - else - { - gi.dprintf("func_train without a target at %s\n", vtos(self->absmin)); - } -} - -/* - * QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) - */ -void -trigger_elevator_use(edict_t *self, edict_t *other, edict_t *activator) -{ - edict_t *target; - - if (self->movetarget->nextthink) - { - return; - } - - if (!other->pathtarget) - { - gi.dprintf("elevator used with no pathtarget\n"); - return; - } - - target = G_PickTarget(other->pathtarget); - - if (!target) - { - gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget); - return; - } - - self->movetarget->target_ent = target; - train_resume(self->movetarget); -} - -void -trigger_elevator_init(edict_t *self) -{ - if (!self->target) - { - gi.dprintf("trigger_elevator has no target\n"); - return; - } - - self->movetarget = G_PickTarget(self->target); - - if (!self->movetarget) - { - gi.dprintf("trigger_elevator unable to find target %s\n", self->target); - return; - } - - if (strcmp(self->movetarget->classname, "func_train") != 0) - { - gi.dprintf("trigger_elevator target %s is not a train\n", self->target); - return; - } - - self->use = trigger_elevator_use; - self->svflags = SVF_NOCLIENT; -} - -void -SP_trigger_elevator(edict_t *self) -{ - self->think = trigger_elevator_init; - self->nextthink = level.time + FRAMETIME; -} - -/* - * QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON - * "wait" base time between triggering all targets, default is 1 - * "random" wait variance, default is 0 - * - * so, the basic time between firing is a random time between - * (wait - random) and (wait + random) - * - * "delay" delay before first firing when turned on, default is 0 - * - * "pausetime" additional delay used only the very first time - * and only if spawned with START_ON - * - * These can used but not touched. - */ -void -func_timer_think(edict_t *self) -{ - G_UseTargets(self, self->activator); - self->nextthink = level.time + self->wait + crandom() * self->random; -} - -void -func_timer_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->activator = activator; - - /* if on, turn it off */ - if (self->nextthink) - { - self->nextthink = 0; - return; - } - - /* turn it on */ - if (self->delay) - { - self->nextthink = level.time + self->delay; - } - else - { - func_timer_think(self); - } -} - -void -SP_func_timer(edict_t *self) -{ - if (!self->wait) - { - self->wait = 1.0; - } - - self->use = func_timer_use; - self->think = func_timer_think; - - if (self->random >= self->wait) - { - self->random = self->wait - FRAMETIME; - gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin)); - } - - if (self->spawnflags & 1) - { - self->nextthink = level.time + 1.0 + st.pausetime + self->delay + - self->wait + crandom() * self->random; - self->activator = self; - } - - self->svflags = SVF_NOCLIENT; -} - -/* - * QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE - * Conveyors are stationary brushes that move what's on them. - * The brush should be have a surface with at least one current content enabled. - * speed default 100 - */ - -void -func_conveyor_use(edict_t *self, edict_t *other, edict_t *activator) -{ - if (self->spawnflags & 1) - { - self->speed = 0; - self->spawnflags &= ~1; - } - else - { - self->speed = self->count; - self->spawnflags |= 1; - } - - if (!(self->spawnflags & 2)) - { - self->count = 0; - } -} - -void -SP_func_conveyor(edict_t *self) -{ - if (!self->speed) - { - self->speed = 100; - } - - if (!(self->spawnflags & 1)) - { - self->count = self->speed; - self->speed = 0; - } - - self->use = func_conveyor_use; - - gi.setmodel(self, self->model); - self->solid = SOLID_BSP; - gi.linkentity(self); -} - -/* - * QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down - * A secret door. Slide back and then to the side. - * - * open_once doors never closes - * 1st_left 1st move is left of arrow - * 1st_down 1st move is down from arrow - * always_shoot door is shootebale even if targeted - * - * "angle" determines the direction - * "dmg" damage to inflic when blocked (default 2) - * "wait" how long to hold in the open position (default 5, -1 means hold) - */ - -#define SECRET_ALWAYS_SHOOT 1 -#define SECRET_1ST_LEFT 2 -#define SECRET_1ST_DOWN 4 - -void door_secret_move1(edict_t *self); -void door_secret_move2(edict_t *self); -void door_secret_move3(edict_t *self); -void door_secret_move4(edict_t *self); -void door_secret_move5(edict_t *self); -void door_secret_move6(edict_t *self); -void door_secret_done(edict_t *self); - -void -door_secret_use(edict_t *self, edict_t *other, edict_t *activator) -{ - /* make sure we're not already moving */ - if (!VectorCompare(self->s.origin, vec3_origin)) - { - return; - } - - Move_Calc(self, self->pos1, door_secret_move1); - door_use_areaportals(self, true); -} - -void -door_secret_move1(edict_t *self) -{ - self->nextthink = level.time + 1.0; - self->think = door_secret_move2; -} - -void -door_secret_move2(edict_t *self) -{ - Move_Calc(self, self->pos2, door_secret_move3); -} - -void -door_secret_move3(edict_t *self) -{ - if (self->wait == -1) - { - return; - } - - self->nextthink = level.time + self->wait; - self->think = door_secret_move4; -} - -void -door_secret_move4(edict_t *self) -{ - Move_Calc(self, self->pos1, door_secret_move5); -} - -void -door_secret_move5(edict_t *self) -{ - self->nextthink = level.time + 1.0; - self->think = door_secret_move6; -} - -void -door_secret_move6(edict_t *self) -{ - Move_Calc(self, vec3_origin, door_secret_done); -} - -void -door_secret_done(edict_t *self) -{ - if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT)) - { - self->health = 0; - self->takedamage = DAMAGE_YES; - } - - door_use_areaportals(self, false); -} - -void -door_secret_blocked(edict_t *self, edict_t *other) -{ - if (!(other->svflags & SVF_MONSTER) && (!other->client)) - { - /* give it a chance to go away on it's own terms (like gibs) */ - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100000, 1, 0, MOD_CRUSH); - - /* if it's still there, nuke it */ - if (other) - { - /* Hack for entities without their origin near the model */ - VectorMA(other->absmin, 0.5, other->size, other->s.origin); - BecomeExplosion1(other); - } - - return; - } - - if (level.time < self->touch_debounce_time) - { - return; - } - - self->touch_debounce_time = level.time + 0.5; - - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); -} - -void -door_secret_die(edict_t *self, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - self->takedamage = DAMAGE_NO; - door_secret_use(self, attacker, attacker); -} - -void -SP_func_door_secret(edict_t *ent) -{ - vec3_t forward, right, up; - float side; - float width; - float length; - - ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); - ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); - ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); - - ent->movetype = MOVETYPE_PUSH; - ent->solid = SOLID_BSP; - gi.setmodel(ent, ent->model); - - ent->blocked = door_secret_blocked; - ent->use = door_secret_use; - - if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT)) - { - ent->health = 0; - ent->takedamage = DAMAGE_YES; - ent->die = door_secret_die; - } - - if (!ent->dmg) - { - ent->dmg = 2; - } - - if (!ent->wait) - { - ent->wait = 5; - } - - ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = 50; - - /* calculate positions */ - AngleVectors(ent->s.angles, forward, right, up); - VectorClear(ent->s.angles); - side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT); - - if (ent->spawnflags & SECRET_1ST_DOWN) - { - width = fabs(DotProduct(up, ent->size)); - } - else - { - width = fabs(DotProduct(right, ent->size)); - } - - length = fabs(DotProduct(forward, ent->size)); - - if (ent->spawnflags & SECRET_1ST_DOWN) - { - VectorMA(ent->s.origin, -1 * width, up, ent->pos1); - } - else - { - VectorMA(ent->s.origin, side * width, right, ent->pos1); - } - - VectorMA(ent->pos1, length, forward, ent->pos2); - - if (ent->health) - { - ent->takedamage = DAMAGE_YES; - ent->die = door_killed; - ent->max_health = ent->health; - } - else if (ent->targetname && ent->message) - { - gi.soundindex("misc/talk.wav"); - ent->touch = door_touch; - } - - ent->classname = "func_door"; - - gi.linkentity(ent); -} - -/* - * QUAKED func_killbox (1 0 0) ? - * Kills everything inside when fired, irrespective of protection. - */ -void -use_killbox(edict_t *self, edict_t *other, edict_t *activator) -{ - KillBox(self); - - /* Hack to make sure that really everything is killed */ - self->count--; - - if (!self->count) - { - self->think = G_FreeEdict; - self->nextthink = level.time + 1; - } -} - -void -SP_func_killbox(edict_t *ent) -{ - gi.setmodel(ent, ent->model); - ent->use = use_killbox; - ent->svflags = SVF_NOCLIENT; -} - diff --git a/src/ctf/g_items.c b/src/ctf/g_items.c deleted file mode 100644 index d44a0fac..00000000 --- a/src/ctf/g_items.c +++ /dev/null @@ -1,2911 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * Copyright (c) ZeniMax Media Inc. - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * ======================================================================= - * - * Item handling and item definitions. - * - * ======================================================================= - */ - -#include "header/local.h" - -#define HEALTH_IGNORE_MAX 1 -#define HEALTH_TIMED 2 - -qboolean Pickup_Weapon(edict_t *ent, edict_t *other); -void Use_Weapon(edict_t *ent, gitem_t *inv); -void Use_Weapon2(edict_t *ent, gitem_t *inv); -void Drop_Weapon(edict_t *ent, gitem_t *inv); - -void Weapon_Blaster(edict_t *ent); -void Weapon_Shotgun(edict_t *ent); -void Weapon_SuperShotgun(edict_t *ent); -void Weapon_Machinegun(edict_t *ent); -void Weapon_Chaingun(edict_t *ent); -void Weapon_HyperBlaster(edict_t *ent); -void Weapon_RocketLauncher(edict_t *ent); -void Weapon_Grenade(edict_t *ent); -void Weapon_GrenadeLauncher(edict_t *ent); -void Weapon_Railgun(edict_t *ent); -void Weapon_BFG(edict_t *ent); -void Weapon_ChainFist(edict_t *ent); -void Weapon_Disintegrator(edict_t *ent); -void Weapon_ETF_Rifle(edict_t *ent); -void Weapon_Heatbeam(edict_t *ent); -void Weapon_Prox(edict_t *ent); -void Weapon_Tesla(edict_t *ent); -void Weapon_ProxLauncher(edict_t *ent); - -void Weapon_Ionripper(edict_t *ent); -void Weapon_Phalanx(edict_t *ent); -void Weapon_Trap(edict_t *ent); - -static gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET}; -static gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT}; -static gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY}; - -static int jacket_armor_index; -static int combat_armor_index; -static int body_armor_index; -static int power_screen_index; -static int power_shield_index; - -#define HEALTH_IGNORE_MAX 1 -#define HEALTH_TIMED 2 - -void Use_Quad(edict_t *ent, gitem_t *item); -void Use_QuadFire(edict_t *ent, gitem_t *item); - -static int quad_drop_timeout_hack; - -/* ====================================================================== */ - -gitem_t * -GetItemByIndex(int index) -{ - if ((index == 0) || (index >= game.num_items)) - { - return NULL; - } - - return &itemlist[index]; -} - -gitem_t * -FindItemByClassname(char *classname) -{ - int i; - gitem_t *it; - - it = itemlist; - - for (i = 0; i < game.num_items; i++, it++) - { - if (!it->classname) - { - continue; - } - - if (!Q_stricmp(it->classname, classname)) - { - return it; - } - } - - return NULL; -} - -gitem_t * -FindItem(char *pickup_name) -{ - int i; - gitem_t *it; - - it = itemlist; - - for (i = 0; i < game.num_items; i++, it++) - { - if (!it->pickup_name) - { - continue; - } - - if (!Q_stricmp(it->pickup_name, pickup_name)) - { - return it; - } - } - - return NULL; -} - -/* ====================================================================== */ - -void -DoRespawn(edict_t *ent) -{ - if (ent->team) - { - edict_t *master; - int count; - int choice; - - master = ent->teammaster; - - /* in ctf, when we are weapons stay, only the master - of a team of weapons is spawned */ - if (ctf->value && - ((int)dmflags->value & DF_WEAPONS_STAY) && - master->item && (master->item->flags & IT_WEAPON)) - { - ent = master; - } - else - { - for (count = 0, ent = master; ent; ent = ent->chain, count++) - { - } - - choice = rand() % count; - - for (count = 0, ent = master; count < choice; ent = ent->chain, count++) - { - } - } - } - - ent->svflags &= ~SVF_NOCLIENT; - ent->solid = SOLID_TRIGGER; - gi.linkentity(ent); - - /* send an effect */ - ent->s.event = EV_ITEM_RESPAWN; -} - -void -SetRespawn(edict_t *ent, float delay) -{ - ent->flags |= FL_RESPAWN; - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - ent->nextthink = level.time + delay; - ent->think = DoRespawn; - gi.linkentity(ent); -} - -/* ====================================================================== */ - -qboolean -Pickup_Powerup(edict_t *ent, edict_t *other) -{ - int quantity; - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (((skill->value == 1) && (quantity >= 2)) || ((skill->value >= 2) && (quantity >= 1))) - { - return false; - } - - if ((coop->value) && (ent->item->flags & IT_STAY_COOP) && (quantity > 0)) - { - return false; - } - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - - if (deathmatch->value) - { - if (!(ent->spawnflags & DROPPED_ITEM)) - { - SetRespawn(ent, ent->item->quantity); - } - - if (((int)dmflags->value & DF_INSTANT_ITEMS) || - ((ent->item->use == Use_Quad) && - (ent->spawnflags & DROPPED_PLAYER_ITEM))) - { - if ((ent->item->use == Use_Quad) && - (ent->spawnflags & DROPPED_PLAYER_ITEM)) - { - quad_drop_timeout_hack = - (ent->nextthink - level.time) / FRAMETIME; - } - - ent->item->use(other, ent->item); - } - } - - return true; -} - -void -Drop_General(edict_t *ent, gitem_t *item) -{ - Drop_Item(ent, item); - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); -} - -/* ====================================================================== */ - -qboolean -Pickup_Adrenaline(edict_t *ent, edict_t *other) -{ - if (!deathmatch->value) - { - other->max_health += 1; - } - - if (other->health < other->max_health) - { - other->health = other->max_health; - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, ent->item->quantity); - } - - return true; -} - -qboolean -Pickup_AncientHead(edict_t *ent, edict_t *other) -{ - other->max_health += 2; - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, ent->item->quantity); - } - - return true; -} - -qboolean -Pickup_Bandolier(edict_t *ent, edict_t *other) -{ - gitem_t *item; - int index; - - if (other->client->pers.max_bullets < 250) - { - other->client->pers.max_bullets = 250; - } - - if (other->client->pers.max_shells < 150) - { - other->client->pers.max_shells = 150; - } - - if (other->client->pers.max_cells < 250) - { - other->client->pers.max_cells = 250; - } - - if (other->client->pers.max_slugs < 75) - { - other->client->pers.max_slugs = 75; - } - - item = FindItem("Bullets"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_bullets) - { - other->client->pers.inventory[index] = - other->client->pers.max_bullets; - } - } - - item = FindItem("Shells"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_shells) - { - other->client->pers.inventory[index] = - other->client->pers.max_shells; - } - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, ent->item->quantity); - } - - return true; -} - -qboolean -Pickup_Pack(edict_t *ent, edict_t *other) -{ - gitem_t *item; - int index; - - if (other->client->pers.max_bullets < 300) - { - other->client->pers.max_bullets = 300; - } - - if (other->client->pers.max_shells < 200) - { - other->client->pers.max_shells = 200; - } - - if (other->client->pers.max_rockets < 100) - { - other->client->pers.max_rockets = 100; - } - - if (other->client->pers.max_grenades < 100) - { - other->client->pers.max_grenades = 100; - } - - if (other->client->pers.max_cells < 300) - { - other->client->pers.max_cells = 300; - } - - if (other->client->pers.max_slugs < 100) - { - other->client->pers.max_slugs = 100; - } - - item = FindItem("Bullets"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_bullets) - { - other->client->pers.inventory[index] = - other->client->pers.max_bullets; - } - } - - item = FindItem("Shells"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_shells) - { - other->client->pers.inventory[index] = - other->client->pers.max_shells; - } - } - - item = FindItem("Cells"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_cells) - { - other->client->pers.inventory[index] = - other->client->pers.max_cells; - } - } - - item = FindItem("Grenades"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_grenades) - { - other->client->pers.inventory[index] = - other->client->pers.max_grenades; - } - } - - item = FindItem("Rockets"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_rockets) - { - other->client->pers.inventory[index] = - other->client->pers.max_rockets; - } - } - - item = FindItem("Slugs"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_slugs) - { - other->client->pers.inventory[index] = - other->client->pers.max_slugs; - } - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, ent->item->quantity); - } - - return true; -} - -/* ====================================================================== */ - -void -Use_Quad(edict_t *ent, gitem_t *item) -{ - int timeout; - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (quad_drop_timeout_hack) - { - timeout = quad_drop_timeout_hack; - quad_drop_timeout_hack = 0; - } - else - { - timeout = 300; - } - - if (ent->client->quad_framenum > level.framenum) - { - ent->client->quad_framenum += timeout; - } - else - { - ent->client->quad_framenum = level.framenum + timeout; - } - - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0); -} - -/* ====================================================================== */ - -void -Use_Breather(edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (ent->client->breather_framenum > level.framenum) - { - ent->client->breather_framenum += 300; - } - else - { - ent->client->breather_framenum = level.framenum + 300; - } -} - -/* ====================================================================== */ - -void -Use_Envirosuit(edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (ent->client->enviro_framenum > level.framenum) - { - ent->client->enviro_framenum += 300; - } - else - { - ent->client->enviro_framenum = level.framenum + 300; - } -} - -/* ====================================================================== */ - -void -Use_Invulnerability(edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (ent->client->invincible_framenum > level.framenum) - { - ent->client->invincible_framenum += 300; - } - else - { - ent->client->invincible_framenum = level.framenum + 300; - } - - gi.sound(ent, CHAN_ITEM, gi.soundindex( - "items/protect.wav"), 1, ATTN_NORM, 0); -} - -/* ====================================================================== */ - -void -Use_Silencer(edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - ent->client->silencer_shots += 30; -} - -/* ====================================================================== */ - -qboolean -Pickup_Key(edict_t *ent, edict_t *other) -{ - if (coop->value) - { - if (strcmp(ent->classname, "key_power_cube") == 0) - { - if (other->client->pers.power_cubes & - ((ent->spawnflags & 0x0000ff00) >> 8)) - { - return false; - } - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - other->client->pers.power_cubes |= - ((ent->spawnflags & 0x0000ff00) >> 8); - } - else - { - if (other->client->pers.inventory[ITEM_INDEX(ent->item)]) - { - return false; - } - - other->client->pers.inventory[ITEM_INDEX(ent->item)] = 1; - } - - return true; - } - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - return true; -} - -/* ====================================================================== */ - -qboolean -Add_Ammo(edict_t *ent, gitem_t *item, int count) -{ - int index; - int max; - - if (!ent->client) - { - return false; - } - - if (item->tag == AMMO_BULLETS) - { - max = ent->client->pers.max_bullets; - } - else if (item->tag == AMMO_SHELLS) - { - max = ent->client->pers.max_shells; - } - else if (item->tag == AMMO_ROCKETS) - { - max = ent->client->pers.max_rockets; - } - else if (item->tag == AMMO_GRENADES) - { - max = ent->client->pers.max_grenades; - } - else if (item->tag == AMMO_CELLS) - { - max = ent->client->pers.max_cells; - } - else if (item->tag == AMMO_SLUGS) - { - max = ent->client->pers.max_slugs; - } - else - { - return false; - } - - index = ITEM_INDEX(item); - - if (ent->client->pers.inventory[index] == max) - { - return false; - } - - ent->client->pers.inventory[index] += count; - - if (ent->client->pers.inventory[index] > max) - { - ent->client->pers.inventory[index] = max; - } - - return true; -} - -qboolean -Pickup_Ammo(edict_t *ent, edict_t *other) -{ - int oldcount; - int count; - qboolean weapon; - - weapon = (ent->item->flags & IT_WEAPON); - - if ((weapon) && ((int)dmflags->value & DF_INFINITE_AMMO)) - { - count = 1000; - } - else if (ent->count) - { - count = ent->count; - } - else - { - count = ent->item->quantity; - } - - oldcount = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (!Add_Ammo(other, ent->item, count)) - { - return false; - } - - if (weapon && !oldcount) - { - if ((other->client->pers.weapon != ent->item) && - (!deathmatch->value || - (other->client->pers.weapon == FindItem("blaster")))) - { - other->client->newweapon = ent->item; - } - } - - if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) && - (deathmatch->value)) - { - SetRespawn(ent, 30); - } - - return true; -} - -void -Drop_Ammo(edict_t *ent, gitem_t *item) -{ - edict_t *dropped; - int index; - - index = ITEM_INDEX(item); - dropped = Drop_Item(ent, item); - - if (ent->client->pers.inventory[index] >= item->quantity) - { - dropped->count = item->quantity; - } - else - { - dropped->count = ent->client->pers.inventory[index]; - } - - ent->client->pers.inventory[index] -= dropped->count; - ValidateSelectedItem(ent); -} - -/* ====================================================================== */ - -void -MegaHealth_think(edict_t *self) -{ - if ((self->owner->health > self->owner->max_health) - && !CTFHasRegeneration(self->owner)) - { - self->nextthink = level.time + 1; - self->owner->health -= 1; - return; - } - - if (!(self->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(self, 20); - } - else - { - G_FreeEdict(self); - } -} - -qboolean -Pickup_Health(edict_t *ent, edict_t *other) -{ - if (!(ent->style & HEALTH_IGNORE_MAX)) - { - if (other->health >= other->max_health) - { - return false; - } - } - - if ((other->health >= 250) && (ent->count > 25)) - { - return false; - } - - other->health += ent->count; - - if ((other->health > 250) && (ent->count > 25)) - { - other->health = 250; - } - - if (!(ent->style & HEALTH_IGNORE_MAX)) - { - if (other->health > other->max_health) - { - other->health = other->max_health; - } - } - - if ((ent->style & HEALTH_TIMED) && !CTFHasRegeneration(other)) - { - ent->think = MegaHealth_think; - ent->nextthink = level.time + 5; - ent->owner = other; - ent->flags |= FL_RESPAWN; - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - } - else - { - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, 30); - } - } - - return true; -} - -/* ====================================================================== */ - -int -ArmorIndex(edict_t *ent) -{ - if (!ent->client) - { - return 0; - } - - if (ent->client->pers.inventory[jacket_armor_index] > 0) - { - return jacket_armor_index; - } - - if (ent->client->pers.inventory[combat_armor_index] > 0) - { - return combat_armor_index; - } - - if (ent->client->pers.inventory[body_armor_index] > 0) - { - return body_armor_index; - } - - return 0; -} - -qboolean -Pickup_Armor(edict_t *ent, edict_t *other) -{ - int old_armor_index; - gitem_armor_t *oldinfo; - gitem_armor_t *newinfo; - int newcount; - float salvage; - int salvagecount; - - /* get info on new armor */ - newinfo = (gitem_armor_t *)ent->item->info; - - old_armor_index = ArmorIndex(other); - - /* handle armor shards specially */ - if (ent->item->tag == ARMOR_SHARD) - { - if (!old_armor_index) - { - other->client->pers.inventory[jacket_armor_index] = 2; - } - else - { - other->client->pers.inventory[old_armor_index] += 2; - } - } - - /* if player has no armor, just use it */ - else if (!old_armor_index) - { - other->client->pers.inventory[ITEM_INDEX(ent->item)] = - newinfo->base_count; - } - - /* use the better armor */ - else - { - /* get info on old armor */ - if (old_armor_index == jacket_armor_index) - { - oldinfo = &jacketarmor_info; - } - else if (old_armor_index == combat_armor_index) - { - oldinfo = &combatarmor_info; - } - else /* (old_armor_index == body_armor_index) */ - { - oldinfo = &bodyarmor_info; - } - - if (newinfo->normal_protection > oldinfo->normal_protection) - { - /* calc new armor values */ - salvage = oldinfo->normal_protection / newinfo->normal_protection; - salvagecount = salvage * - other->client->pers.inventory[old_armor_index]; - newcount = newinfo->base_count + salvagecount; - - if (newcount > newinfo->max_count) - { - newcount = newinfo->max_count; - } - - /* zero count of old armor so it goes away */ - other->client->pers.inventory[old_armor_index] = 0; - - /* change armor to new item with computed value */ - other->client->pers.inventory[ITEM_INDEX(ent->item)] = newcount; - } - else - { - /* calc new armor values */ - salvage = newinfo->normal_protection / oldinfo->normal_protection; - salvagecount = salvage * newinfo->base_count; - newcount = other->client->pers.inventory[old_armor_index] + salvagecount; - - if (newcount > oldinfo->max_count) - { - newcount = oldinfo->max_count; - } - - /* if we're already maxed out then we don't need the new armor */ - if (other->client->pers.inventory[old_armor_index] >= newcount) - { - return false; - } - - /* update current armor value */ - other->client->pers.inventory[old_armor_index] = newcount; - } - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, 20); - } - - return true; -} - -/* ====================================================================== */ - -int -PowerArmorType(edict_t *ent) -{ - if (!ent->client) - { - return POWER_ARMOR_NONE; - } - - if (!(ent->flags & FL_POWER_ARMOR)) - { - return POWER_ARMOR_NONE; - } - - if (ent->client->pers.inventory[power_shield_index] > 0) - { - return POWER_ARMOR_SHIELD; - } - - if (ent->client->pers.inventory[power_screen_index] > 0) - { - return POWER_ARMOR_SCREEN; - } - - return POWER_ARMOR_NONE; -} - -void -Use_PowerArmor(edict_t *ent, gitem_t *item) -{ - int index; - - if (ent->flags & FL_POWER_ARMOR) - { - ent->flags &= ~FL_POWER_ARMOR; - gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0); - } - else - { - index = ITEM_INDEX(FindItem("cells")); - - if (!ent->client->pers.inventory[index]) - { - gi.cprintf(ent, PRINT_HIGH, "No cells for power armor.\n"); - return; - } - - ent->flags |= FL_POWER_ARMOR; - gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power1.wav"), 1, ATTN_NORM, 0); - } -} - -qboolean -Pickup_PowerArmor(edict_t *ent, edict_t *other) -{ - int quantity; - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - - if (deathmatch->value) - { - if (!(ent->spawnflags & DROPPED_ITEM)) - { - SetRespawn(ent, ent->item->quantity); - } - - /* auto-use for DM only if we didn't already have one */ - if (!quantity) - { - ent->item->use(other, ent->item); - } - } - - return true; -} - -void -Drop_PowerArmor(edict_t *ent, gitem_t *item) -{ - if ((ent->flags & FL_POWER_ARMOR) && - (ent->client->pers.inventory[ITEM_INDEX(item)] == 1)) - { - Use_PowerArmor(ent, item); - } - - Drop_General(ent, item); -} - -/* ====================================================================== */ - -void -Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - qboolean taken; - - if (!other->client) - { - return; - } - - if (other->health < 1) - { - return; /* dead people can't pickup */ - } - - if (!ent->item->pickup) - { - return; /* not a grabbable item? */ - } - - if (CTFMatchSetup()) - { - return; /* can't pick stuff up right now */ - } - - taken = ent->item->pickup(ent, other); - - if (taken) - { - /* flash the screen */ - other->client->bonus_alpha = 0.25; - - /* show icon and name on status bar */ - other->client->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(ent->item->icon); - other->client->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS + ITEM_INDEX(ent->item); - other->client->pickup_msg_time = level.time + 3.0; - - /* change selected item */ - if (ent->item->use) - { - other->client->pers.selected_item = - other->client->ps.stats[STAT_SELECTED_ITEM] = ITEM_INDEX( - ent->item); - } - - if (ent->item->pickup == Pickup_Health) - { - if (ent->count == 2) - { - gi.sound(other, CHAN_ITEM, gi.soundindex( - "items/s_health.wav"), 1, ATTN_NORM, 0); - } - else if (ent->count == 10) - { - gi.sound(other, CHAN_ITEM, gi.soundindex( - "items/n_health.wav"), 1, ATTN_NORM, 0); - } - else if (ent->count == 25) - { - gi.sound(other, CHAN_ITEM, gi.soundindex( - "items/l_health.wav"), 1, ATTN_NORM, 0); - } - else - { - gi.sound(other, CHAN_ITEM, gi.soundindex( - "items/m_health.wav"), 1, ATTN_NORM, 0); - } - } - else if (ent->item->pickup_sound) - { - gi.sound(other, CHAN_ITEM, gi.soundindex( - ent->item->pickup_sound), 1, ATTN_NORM, 0); - } - } - - if (!(ent->spawnflags & ITEM_TARGETS_USED)) - { - G_UseTargets(ent, other); - ent->spawnflags |= ITEM_TARGETS_USED; - } - - if (!taken) - { - return; - } - - if (!((coop->value) && (ent->item->flags & IT_STAY_COOP)) || - (ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) - { - if (ent->flags & FL_RESPAWN) - { - ent->flags &= ~FL_RESPAWN; - } - else - { - G_FreeEdict(ent); - } - } -} - -/* ====================================================================== */ - -void -drop_temp_touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (other == ent->owner) - { - return; - } - - Touch_Item(ent, other, plane, surf); -} - -void -drop_make_touchable(edict_t *ent) -{ - ent->touch = Touch_Item; - - if (deathmatch->value) - { - ent->nextthink = level.time + 29; - ent->think = G_FreeEdict; - } -} - -edict_t * -Drop_Item(edict_t *ent, gitem_t *item) -{ - edict_t *dropped; - vec3_t forward, right; - vec3_t offset; - - dropped = G_Spawn(); - - dropped->classname = item->classname; - dropped->item = item; - dropped->spawnflags = DROPPED_ITEM; - dropped->s.effects = item->world_model_flags; - dropped->s.renderfx = RF_GLOW; - VectorSet(dropped->mins, -15, -15, -15); - VectorSet(dropped->maxs, 15, 15, 15); - gi.setmodel(dropped, dropped->item->world_model); - dropped->solid = SOLID_TRIGGER; - dropped->movetype = MOVETYPE_TOSS; - dropped->touch = drop_temp_touch; - dropped->owner = ent; - - if (ent->client) - { - trace_t trace; - - AngleVectors(ent->client->v_angle, forward, right, NULL); - VectorSet(offset, 24, 0, -16); - G_ProjectSource(ent->s.origin, offset, forward, right, dropped->s.origin); - trace = gi.trace(ent->s.origin, dropped->mins, dropped->maxs, - dropped->s.origin, ent, CONTENTS_SOLID); - VectorCopy(trace.endpos, dropped->s.origin); - } - else - { - AngleVectors(ent->s.angles, forward, right, NULL); - VectorCopy(ent->s.origin, dropped->s.origin); - } - - VectorScale(forward, 100, dropped->velocity); - dropped->velocity[2] = 300; - - dropped->think = drop_make_touchable; - dropped->nextthink = level.time + 1; - - gi.linkentity(dropped); - - return dropped; -} - -void -Use_Item(edict_t *ent, edict_t *other, edict_t *activator) -{ - ent->svflags &= ~SVF_NOCLIENT; - ent->use = NULL; - - if (ent->spawnflags & ITEM_NO_TOUCH) - { - ent->solid = SOLID_BBOX; - ent->touch = NULL; - } - else - { - ent->solid = SOLID_TRIGGER; - ent->touch = Touch_Item; - } - - gi.linkentity(ent); -} - -/* ====================================================================== */ - -void -droptofloor(edict_t *ent) -{ - trace_t tr; - vec3_t dest; - float *v; - - v = tv(-15, -15, -15); - VectorCopy(v, ent->mins); - v = tv(15, 15, 15); - VectorCopy(v, ent->maxs); - - if (ent->model) - { - gi.setmodel(ent, ent->model); - } - else - { - gi.setmodel(ent, ent->item->world_model); - } - - ent->solid = SOLID_TRIGGER; - ent->movetype = MOVETYPE_TOSS; - ent->touch = Touch_Item; - - v = tv(0, 0, -128); - VectorAdd(ent->s.origin, v, dest); - - tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID); - - if (tr.startsolid) - { - gi.dprintf("droptofloor: %s startsolid at %s\n", ent->classname, - vtos(ent->s.origin)); - G_FreeEdict(ent); - return; - } - - VectorCopy(tr.endpos, ent->s.origin); - - if (ent->team) - { - ent->flags &= ~FL_TEAMSLAVE; - ent->chain = ent->teamchain; - ent->teamchain = NULL; - - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - - if (ent == ent->teammaster) - { - ent->nextthink = level.time + FRAMETIME; - ent->think = DoRespawn; - } - } - - if (ent->spawnflags & ITEM_NO_TOUCH) - { - ent->solid = SOLID_BBOX; - ent->touch = NULL; - ent->s.effects &= ~EF_ROTATE; - ent->s.renderfx &= ~RF_GLOW; - } - - if (ent->spawnflags & ITEM_TRIGGER_SPAWN) - { - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - ent->use = Use_Item; - } - - gi.linkentity(ent); -} - -/* - * Precaches all data needed for a given item. - * This will be called for each item spawned in a level, - * and for each item in each client's inventory. - */ -void -PrecacheItem(gitem_t *it) -{ - char *s, *start; - char data[MAX_QPATH]; - int len; - gitem_t *ammo; - - if (!it) - { - return; - } - - if (it->pickup_sound) - { - gi.soundindex(it->pickup_sound); - } - - if (it->world_model) - { - gi.modelindex(it->world_model); - } - - if (it->view_model) - { - gi.modelindex(it->view_model); - } - - if (it->icon) - { - gi.imageindex(it->icon); - } - - /* parse everything for its ammo */ - if (it->ammo && it->ammo[0]) - { - ammo = FindItem(it->ammo); - - if (ammo != it) - { - PrecacheItem(ammo); - } - } - - /* parse the space seperated precache string for other items */ - s = it->precaches; - - if (!s || !s[0]) - { - return; - } - - while (*s) - { - start = s; - - while (*s && *s != ' ') - { - s++; - } - - len = s - start; - - if ((len >= MAX_QPATH) || (len < 5)) - { - gi.error("PrecacheItem: %s has bad precache string", it->classname); - } - - memcpy(data, start, len); - data[len] = 0; - - if (*s) - { - s++; - } - - /* determine type based on extension */ - if (!strcmp(data + len - 3, "md2")) - { - gi.modelindex(data); - } - else if (!strcmp(data + len - 3, "sp2")) - { - gi.modelindex(data); - } - else if (!strcmp(data + len - 3, "wav")) - { - gi.soundindex(data); - } - - if (!strcmp(data + len - 3, "pcx")) - { - gi.imageindex(data); - } - } -} - -/* - * Sets the clipping size and plants the object on the floor. - * - * Items can't be immediately dropped to floor, because they might - * be on an entity that hasn't spawned yet. - */ -void -SpawnItem(edict_t *ent, gitem_t *item) -{ - PrecacheItem(item); - - if (ent->spawnflags) - { - if (strcmp(ent->classname, "key_power_cube") != 0) - { - ent->spawnflags = 0; - gi.dprintf("%s at %s has invalid spawnflags set\n", - ent->classname, vtos(ent->s.origin)); - } - } - - /* some items will be prevented in deathmatch */ - if (deathmatch->value) - { - if ((int)dmflags->value & DF_NO_ARMOR) - { - if ((item->pickup == Pickup_Armor) || - (item->pickup == Pickup_PowerArmor)) - { - G_FreeEdict(ent); - return; - } - } - - if ((int)dmflags->value & DF_NO_ITEMS) - { - if (item->pickup == Pickup_Powerup) - { - G_FreeEdict(ent); - return; - } - } - - if ((int)dmflags->value & DF_NO_HEALTH) - { - if ((item->pickup == Pickup_Health) || - (item->pickup == Pickup_Adrenaline) || - (item->pickup == Pickup_AncientHead)) - { - G_FreeEdict(ent); - return; - } - } - - if ((int)dmflags->value & DF_INFINITE_AMMO) - { - if ((item->flags == IT_AMMO) || - (strcmp(ent->classname, "weapon_bfg") == 0)) - { - G_FreeEdict(ent); - return; - } - } - } - - if (coop->value && (strcmp(ent->classname, "key_power_cube") == 0)) - { - ent->spawnflags |= (1 << (8 + level.power_cubes)); - level.power_cubes++; - } - - /* don't let them drop items that stay in a coop game */ - if ((coop->value) && (item->flags & IT_STAY_COOP)) - { - item->drop = NULL; - } - - /* Don't spawn the flags unless enabled */ - if (!ctf->value && ((strcmp(ent->classname, "item_flag_team1") == 0) || - (strcmp(ent->classname, "item_flag_team2") == 0))) - { - G_FreeEdict(ent); - return; - } - - ent->item = item; - ent->nextthink = level.time + 2 * FRAMETIME; /* items start after other solids */ - ent->think = droptofloor; - ent->s.effects = item->world_model_flags; - ent->s.renderfx = RF_GLOW; - - if (ent->model) - { - gi.modelindex(ent->model); - } - - /* flags are server animated and have special handling */ - if ((strcmp(ent->classname, "item_flag_team1") == 0) || - (strcmp(ent->classname, "item_flag_team2") == 0)) - { - ent->think = CTFFlagSetup; - } -} - -/* ====================================================================== */ - -static const gitem_t gameitemlist[] = { - { - NULL - }, /* leave index 0 alone */ - - - /* - * QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - */ - { - "item_armor_body", - Pickup_Armor, - NULL, - NULL, - NULL, - "misc/ar1_pkup.wav", - "models/items/armor/body/tris.md2", EF_ROTATE, - NULL, - "i_bodyarmor", - "Body Armor", - 3, - 0, - NULL, - IT_ARMOR, - 0, - &bodyarmor_info, - ARMOR_BODY, - "" - }, - - /* - * QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_armor_combat", - Pickup_Armor, - NULL, - NULL, - NULL, - "misc/ar1_pkup.wav", - "models/items/armor/combat/tris.md2", EF_ROTATE, - NULL, - "i_combatarmor", - "Combat Armor", - 3, - 0, - NULL, - IT_ARMOR, - 0, - &combatarmor_info, - ARMOR_COMBAT, - "" - }, - - /* - * QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_armor_jacket", - Pickup_Armor, - NULL, - NULL, - NULL, - "misc/ar1_pkup.wav", - "models/items/armor/jacket/tris.md2", EF_ROTATE, - NULL, - "i_jacketarmor", - "Jacket Armor", - 3, - 0, - NULL, - IT_ARMOR, - 0, - &jacketarmor_info, - ARMOR_JACKET, - "" - }, - - /* - * QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_armor_shard", - Pickup_Armor, - NULL, - NULL, - NULL, - "misc/ar2_pkup.wav", - "models/items/armor/shard/tris.md2", EF_ROTATE, - NULL, - "i_jacketarmor", - "Armor Shard", - 3, - 0, - NULL, - IT_ARMOR, - 0, - NULL, - ARMOR_SHARD, - "" - }, - - /* - * QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_power_screen", - Pickup_PowerArmor, - Use_PowerArmor, - Drop_PowerArmor, - NULL, - "misc/ar3_pkup.wav", - "models/items/armor/screen/tris.md2", EF_ROTATE, - NULL, - "i_powerscreen", - "Power Screen", - 0, - 60, - NULL, - IT_ARMOR, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_power_shield", - Pickup_PowerArmor, - Use_PowerArmor, - Drop_PowerArmor, - NULL, - "misc/ar3_pkup.wav", - "models/items/armor/shield/tris.md2", EF_ROTATE, - NULL, - "i_powershield", - "Power Shield", - 0, - 60, - NULL, - IT_ARMOR, - 0, - NULL, - 0, - "misc/power2.wav misc/power1.wav" - }, - - /* - * weapon_grapple (.3 .3 1) (-16 -16 -16) (16 16 16) - * always owned, never in the world - */ - { - "weapon_grapple", - NULL, - Use_Weapon, - NULL, - CTFWeapon_Grapple, - "misc/w_pkup.wav", - NULL, 0, - "models/weapons/grapple/tris.md2", - "w_grapple", - "Grapple", - 0, - 0, - NULL, - IT_WEAPON, - WEAP_GRAPPLE, - NULL, - 0, - - "weapons/grapple/grfire.wav weapons/grapple/grpull.wav weapons/grapple/grhang.wav weapons/grapple/grreset.wav weapons/grapple/grhit.wav" - }, - - /* - * weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) - * always owned, never in the world - */ - { - "weapon_blaster", - NULL, - Use_Weapon, - NULL, - Weapon_Blaster, - "misc/w_pkup.wav", - NULL, 0, - "models/weapons/v_blast/tris.md2", - "w_blaster", - "Blaster", - 0, - 0, - NULL, - IT_WEAPON | IT_STAY_COOP, - WEAP_BLASTER, - NULL, - 0, - "weapons/blastf1a.wav misc/lasfly.wav" - }, - - /* - * QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_shotgun", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Shotgun, - "misc/w_pkup.wav", - "models/weapons/g_shotg/tris.md2", EF_ROTATE, - "models/weapons/v_shotg/tris.md2", - "w_shotgun", - "Shotgun", - 0, - 1, - "Shells", - IT_WEAPON | IT_STAY_COOP, - WEAP_SHOTGUN, - NULL, - 0, - "weapons/shotgf1b.wav weapons/shotgr1b.wav" - }, - - /* - * QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_supershotgun", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_SuperShotgun, - "misc/w_pkup.wav", - "models/weapons/g_shotg2/tris.md2", EF_ROTATE, - "models/weapons/v_shotg2/tris.md2", - "w_sshotgun", - "Super Shotgun", - 0, - 2, - "Shells", - IT_WEAPON | IT_STAY_COOP, - WEAP_SUPERSHOTGUN, - NULL, - 0, - "weapons/sshotf1b.wav" - }, - - /* - * QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_machinegun", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Machinegun, - "misc/w_pkup.wav", - "models/weapons/g_machn/tris.md2", EF_ROTATE, - "models/weapons/v_machn/tris.md2", - "w_machinegun", - "Machinegun", - 0, - 1, - "Bullets", - IT_WEAPON | IT_STAY_COOP, - WEAP_MACHINEGUN, - NULL, - 0, - - "weapons/machgf1b.wav weapons/machgf2b.wav weapons/machgf3b.wav weapons/machgf4b.wav weapons/machgf5b.wav" - }, - - /* - * QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_chaingun", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Chaingun, - "misc/w_pkup.wav", - "models/weapons/g_chain/tris.md2", EF_ROTATE, - "models/weapons/v_chain/tris.md2", - "w_chaingun", - "Chaingun", - 0, - 1, - "Bullets", - IT_WEAPON | IT_STAY_COOP, - WEAP_CHAINGUN, - NULL, - 0, - - "weapons/chngnu1a.wav weapons/chngnl1a.wav weapons/machgf3b.wav` weapons/chngnd1a.wav" - }, - - /* - * QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "ammo_grenades", - Pickup_Ammo, - Use_Weapon, - Drop_Ammo, - Weapon_Grenade, - "misc/am_pkup.wav", - "models/items/ammo/grenades/medium/tris.md2", 0, - "models/weapons/v_handgr/tris.md2", - "a_grenades", - "Grenades", - 3, - 5, - "grenades", - IT_AMMO | IT_WEAPON, - WEAP_GRENADES, - NULL, - AMMO_GRENADES, - - "weapons/hgrent1a.wav weapons/hgrena1b.wav weapons/hgrenc1b.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav " - }, - - /* - * QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_grenadelauncher", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_GrenadeLauncher, - "misc/w_pkup.wav", - "models/weapons/g_launch/tris.md2", EF_ROTATE, - "models/weapons/v_launch/tris.md2", - "w_glauncher", - "Grenade Launcher", - 0, - 1, - "Grenades", - IT_WEAPON | IT_STAY_COOP, - WEAP_GRENADELAUNCHER, - NULL, - 0, - - "models/objects/grenade/tris.md2 weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav" - }, - - /* - * QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_rocketlauncher", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_RocketLauncher, - "misc/w_pkup.wav", - "models/weapons/g_rocket/tris.md2", EF_ROTATE, - "models/weapons/v_rocket/tris.md2", - "w_rlauncher", - "Rocket Launcher", - 0, - 1, - "Rockets", - IT_WEAPON | IT_STAY_COOP, - WEAP_ROCKETLAUNCHER, - NULL, - 0, - - "models/objects/rocket/tris.md2 weapons/rockfly.wav weapons/rocklf1a.wav weapons/rocklr1b.wav models/objects/debris2/tris.md2" - }, - - /* - * QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_hyperblaster", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_HyperBlaster, - "misc/w_pkup.wav", - "models/weapons/g_hyperb/tris.md2", EF_ROTATE, - "models/weapons/v_hyperb/tris.md2", - "w_hyperblaster", - "HyperBlaster", - 0, - 1, - "Cells", - IT_WEAPON | IT_STAY_COOP, - WEAP_HYPERBLASTER, - NULL, - 0, - - "weapons/hyprbu1a.wav weapons/hyprbl1a.wav weapons/hyprbf1a.wav weapons/hyprbd1a.wav misc/lasfly.wav" - }, - - /* - * QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_railgun", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Railgun, - "misc/w_pkup.wav", - "models/weapons/g_rail/tris.md2", EF_ROTATE, - "models/weapons/v_rail/tris.md2", - "w_railgun", - "Railgun", - 0, - 1, - "Slugs", - IT_WEAPON | IT_STAY_COOP, - WEAP_RAILGUN, - NULL, - 0, - "weapons/rg_hum.wav" - }, - - /* - * QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "weapon_bfg", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_BFG, - "misc/w_pkup.wav", - "models/weapons/g_bfg/tris.md2", EF_ROTATE, - "models/weapons/v_bfg/tris.md2", - "w_bfg", - "BFG10K", - 0, - 50, - "Cells", - IT_WEAPON | IT_STAY_COOP, - WEAP_BFG, - NULL, - 0, - - "sprites/s_bfg1.sp2 sprites/s_bfg2.sp2 sprites/s_bfg3.sp2 weapons/bfg__f1y.wav weapons/bfg__l1a.wav weapons/bfg__x1b.wav weapons/bfg_hum.wav" - }, - - /* - * QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "ammo_shells", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/items/ammo/shells/medium/tris.md2", 0, - NULL, - "a_shells", - "Shells", - 3, - 10, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_SHELLS, - "" - }, - - /* - * QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "ammo_bullets", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/items/ammo/bullets/medium/tris.md2", 0, - NULL, - "a_bullets", - "Bullets", - 3, - 50, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_BULLETS, - "" - }, - - /* - * QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "ammo_cells", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/items/ammo/cells/medium/tris.md2", 0, - NULL, - "a_cells", - "Cells", - 3, - 50, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_CELLS, - "" - }, - - /* - * QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "ammo_rockets", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/items/ammo/rockets/medium/tris.md2", 0, - NULL, - "a_rockets", - "Rockets", - 3, - 5, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_ROCKETS, - "" - }, - - /* - * QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "ammo_slugs", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/items/ammo/slugs/medium/tris.md2", 0, - NULL, - "a_slugs", - "Slugs", - 3, - 10, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_SLUGS, - "" - }, - - /* - * QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_quad", - Pickup_Powerup, - Use_Quad, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/quaddama/tris.md2", EF_ROTATE, - NULL, - "p_quad", - "Quad Damage", - 2, - 60, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - "items/damage.wav items/damage2.wav items/damage3.wav" - }, - - /* - * QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_invulnerability", - Pickup_Powerup, - Use_Invulnerability, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/invulner/tris.md2", EF_ROTATE, - NULL, - "p_invulnerability", - "Invulnerability", - 2, - 300, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - "items/protect.wav items/protect2.wav items/protect4.wav" - }, - - /* - * QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_silencer", - Pickup_Powerup, - Use_Silencer, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/silencer/tris.md2", EF_ROTATE, - NULL, - "p_silencer", - "Silencer", - 2, - 60, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_breather", - Pickup_Powerup, - Use_Breather, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/breather/tris.md2", EF_ROTATE, - NULL, - "p_rebreather", - "Rebreather", - 2, - 60, - NULL, - IT_STAY_COOP | IT_POWERUP, - 0, - NULL, - 0, - "items/airout.wav" - }, - - /* - * QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_enviro", - Pickup_Powerup, - Use_Envirosuit, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/enviro/tris.md2", EF_ROTATE, - NULL, - "p_envirosuit", - "Environment Suit", - 2, - 60, - NULL, - IT_STAY_COOP | IT_POWERUP, - 0, - NULL, - 0, - "items/airout.wav" - }, - - /* - * QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16) - * Special item that gives +2 to maximum health - */ - { - "item_ancient_head", - Pickup_AncientHead, - NULL, - NULL, - NULL, - "items/pkup.wav", - "models/items/c_head/tris.md2", EF_ROTATE, - NULL, - "i_fixme", - "Ancient Head", - 2, - 60, - NULL, - 0, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16) - * gives +1 to maximum health - */ - { - "item_adrenaline", - Pickup_Adrenaline, - NULL, - NULL, - NULL, - "items/pkup.wav", - "models/items/adrenal/tris.md2", EF_ROTATE, - NULL, - "p_adrenaline", - "Adrenaline", - 2, - 60, - NULL, - 0, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_bandolier", - Pickup_Bandolier, - NULL, - NULL, - NULL, - "items/pkup.wav", - "models/items/band/tris.md2", EF_ROTATE, - NULL, - "p_bandolier", - "Bandolier", - 2, - 60, - NULL, - 0, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16) - */ - { - "item_pack", - Pickup_Pack, - NULL, - NULL, - NULL, - "items/pkup.wav", - "models/items/pack/tris.md2", EF_ROTATE, - NULL, - "i_pack", - "Ammo Pack", - 2, - 180, - NULL, - 0, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16) - * key for computer centers - */ - { - "key_data_cd", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/data_cd/tris.md2", EF_ROTATE, - NULL, - "k_datacd", - "Data CD", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_power_cube (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN NO_TOUCH - * warehouse circuits - */ - { - "key_power_cube", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/power/tris.md2", EF_ROTATE, - NULL, - "k_powercube", - "Power Cube", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16) - * key for the entrance of jail3 - */ - { - "key_pyramid", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/pyramid/tris.md2", EF_ROTATE, - NULL, - "k_pyramid", - "Pyramid Key", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16) - * key for the city computer - */ - { - "key_data_spinner", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/spinner/tris.md2", EF_ROTATE, - NULL, - "k_dataspin", - "Data Spinner", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16) - * security pass for the security level - */ - { - "key_pass", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/pass/tris.md2", EF_ROTATE, - NULL, - "k_security", - "Security Pass", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16) - * normal door key - blue - */ - { - "key_blue_key", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/key/tris.md2", EF_ROTATE, - NULL, - "k_bluekey", - "Blue Key", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16) - * normal door key - red - */ - { - "key_red_key", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/red_key/tris.md2", EF_ROTATE, - NULL, - "k_redkey", - "Red Key", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16) - * tank commander's head - */ - { - "key_commander_head", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/monsters/commandr/head/tris.md2", EF_GIB, - NULL, - "k_comhead", - "Commander's Head", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - /* - * QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16) - * tank commander's head - */ - { - "key_airstrike_target", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/keys/target/tris.md2", EF_ROTATE, - NULL, - "i_airstrike", - "Airstrike Marker", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - "" - }, - - { - NULL, - Pickup_Health, - NULL, - NULL, - NULL, - "items/pkup.wav", - NULL, 0, - NULL, - "i_health", - "Health", - 3, - 0, - NULL, - 0, - 0, - NULL, - 0, - - "items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav" - }, - - /* - * QUAKED item_flag_team1 (1 0.2 0) (-16 -16 -24) (16 16 32) - */ - { - "item_flag_team1", - CTFPickup_Flag, - NULL, - CTFDrop_Flag, - NULL, - "ctf/flagtk.wav", - "players/male/flag1.md2", EF_FLAG1, - NULL, - "i_ctf1", - "Red Flag", - 2, - 0, - NULL, - 0, - 0, - NULL, - 0, - "ctf/flagcap.wav" - }, - - /* - * QUAKED item_flag_team2 (1 0.2 0) (-16 -16 -24) (16 16 32) - */ - { - "item_flag_team2", - CTFPickup_Flag, - NULL, - CTFDrop_Flag, - NULL, - "ctf/flagtk.wav", - "players/male/flag2.md2", EF_FLAG2, - NULL, - "i_ctf2", - "Blue Flag", - 2, - 0, - NULL, - 0, - 0, - NULL, - 0, - "ctf/flagcap.wav" - }, - - /* Resistance Tech */ - { - "item_tech1", - CTFPickup_Tech, - NULL, - CTFDrop_Tech, - NULL, - "items/pkup.wav", - "models/ctf/resistance/tris.md2", EF_ROTATE, - NULL, - "tech1", - "Disruptor Shield", - 2, - 0, - NULL, - IT_TECH, - 0, - NULL, - 0, - "ctf/tech1.wav" - }, - - /* Strength Tech */ - { - "item_tech2", - CTFPickup_Tech, - NULL, - CTFDrop_Tech, - NULL, - "items/pkup.wav", - "models/ctf/strength/tris.md2", EF_ROTATE, - NULL, - "tech2", - "Power Amplifier", - 2, - 0, - NULL, - IT_TECH, - 0, - NULL, - 0, - "ctf/tech2.wav ctf/tech2x.wav" - }, - - /* Haste Tech */ - { - "item_tech3", - CTFPickup_Tech, - NULL, - CTFDrop_Tech, - NULL, - "items/pkup.wav", - "models/ctf/haste/tris.md2", EF_ROTATE, - NULL, - "tech3", - "Time Accel", - 2, - 0, - NULL, - IT_TECH, - 0, - NULL, - 0, - "ctf/tech3.wav" - }, - - /* Regeneration Tech */ - { - "item_tech4", - CTFPickup_Tech, - NULL, - CTFDrop_Tech, - NULL, - "items/pkup.wav", - "models/ctf/regeneration/tris.md2", EF_ROTATE, - NULL, - "tech4", - "AutoDoc", - 2, - 0, - NULL, - IT_TECH, - 0, - NULL, - 0, - "ctf/tech4.wav" - }, - - /* end of list marker */ - {NULL} -}; - -gitem_t itemlist[MAX_ITEMS]; - -/* - * QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - */ -void -SP_item_health(edict_t *self) -{ - if (!self) - { - return; - } - - if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) - { - G_FreeEdict(self); - return; - } - - self->model = "models/items/healing/medium/tris.md2"; - self->count = 10; - SpawnItem(self, FindItem("Health")); - gi.soundindex("items/n_health.wav"); -} - -/* - * QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - */ -void -SP_item_health_small(edict_t *self) -{ - if (!self) - { - return; - } - - if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) - { - G_FreeEdict(self); - return; - } - - self->model = "models/items/healing/stimpack/tris.md2"; - self->count = 2; - SpawnItem(self, FindItem("Health")); - self->style = HEALTH_IGNORE_MAX; - gi.soundindex("items/s_health.wav"); -} - -/* - * QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - */ -void -SP_item_health_large(edict_t *self) -{ - if (!self) - { - return; - } - - if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) - { - G_FreeEdict(self); - return; - } - - self->model = "models/items/healing/large/tris.md2"; - self->count = 25; - SpawnItem(self, FindItem("Health")); - gi.soundindex("items/l_health.wav"); -} - -/* - * QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - */ -void -SP_item_health_mega(edict_t *self) -{ - if (!self) - { - return; - } - - if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) - { - G_FreeEdict(self); - return; - } - - self->model = "models/items/mega_h/tris.md2"; - self->count = 100; - SpawnItem(self, FindItem("Health")); - gi.soundindex("items/m_health.wav"); - self->style = HEALTH_IGNORE_MAX | HEALTH_TIMED; -} - -void -SP_item_foodcube(edict_t *self) -{ - if (!self) - { - return; - } - - if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) - { - G_FreeEdict(self); - return; - } - - self->model = "models/objects/trapfx/tris.md2"; - SpawnItem(self, FindItem("Health")); - self->spawnflags |= DROPPED_ITEM; - self->style = HEALTH_IGNORE_MAX; - gi.soundindex("items/s_health.wav"); - self->classname = "foodcube"; -} - -void -InitItems(void) -{ - memset(itemlist, 0, sizeof(itemlist)); - memcpy(itemlist, gameitemlist, sizeof(gameitemlist)); - game.num_items = sizeof(gameitemlist) / sizeof(gameitemlist[0]) - 1; -} - -/* - * Called by worldspawn - */ -void -SetItemNames(void) -{ - int i; - gitem_t *it; - - for (i = 0; i < game.num_items; i++) - { - it = &itemlist[i]; - gi.configstring(CS_ITEMS + i, it->pickup_name); - } - - jacket_armor_index = ITEM_INDEX(FindItem("Jacket Armor")); - combat_armor_index = ITEM_INDEX(FindItem("Combat Armor")); - body_armor_index = ITEM_INDEX(FindItem("Body Armor")); - power_screen_index = ITEM_INDEX(FindItem("Power Screen")); - power_shield_index = ITEM_INDEX(FindItem("Power Shield")); -} - -void -SP_xatrix_item(edict_t *self) -{ - gitem_t *item; - int i; - char *spawnClass = NULL; - - if (!self) - { - return; - } - - if (!self->classname) - { - return; - } - - if (!strcmp(self->classname, "ammo_magslug")) - { - spawnClass = "ammo_flechettes"; - } - else if (!strcmp(self->classname, "ammo_trap")) - { - spawnClass = "weapon_proxlauncher"; - } - else if (!strcmp(self->classname, "item_quadfire")) - { - float chance; - - chance = random(); - - if (chance < 0.2) - { - spawnClass = "item_sphere_hunter"; - } - else if (chance < 0.6) - { - spawnClass = "item_sphere_vengeance"; - } - else - { - spawnClass = "item_sphere_defender"; - } - } - else if (!strcmp(self->classname, "weapon_boomer")) - { - spawnClass = "weapon_etf_rifle"; - } - else if (!strcmp(self->classname, "weapon_phalanx")) - { - spawnClass = "weapon_plasmabeam"; - } - - /* check item spawn functions */ - for (i = 0, item = itemlist; i < game.num_items; i++, item++) - { - if (!item->classname) - { - continue; - } - - if (!strcmp(item->classname, spawnClass)) - { - /* found it */ - SpawnItem(self, item); - return; - } - } -} diff --git a/src/ctf/g_misc.c b/src/ctf/g_misc.c deleted file mode 100644 index 744cbdb0..00000000 --- a/src/ctf/g_misc.c +++ /dev/null @@ -1,2466 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * Copyright (c) ZeniMax Media Inc. - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * ======================================================================= - * - * Miscellaneos entities, functs and functions. - * - * ======================================================================= - */ - -#include "header/local.h" - -int debristhisframe; -int gibsthisframe; - -extern void M_WorldEffects(edict_t *ent); - -/* - * QUAKED func_group (0 0 0) ? - * Used to group brushes together just for editor convenience. - */ - -/* ===================================================== */ - -void -Use_Areaportal(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) -{ - if (!ent) - { - return; - } - - ent->count ^= 1; /* toggle state */ - gi.SetAreaPortalState(ent->style, ent->count); -} - -/* - * QUAKED func_areaportal (0 0 0) ? - * - * This is a non-visible object that divides the world into - * areas that are seperated when this portal is not activated. - * Usually enclosed in the middle of a door. - */ -void -SP_func_areaportal(edict_t *ent) -{ - if (!ent) - { - return; - } - - ent->use = Use_Areaportal; - ent->count = 0; /* always start closed; */ -} - -/* ===================================================== */ - -void -VelocityForDamage(int damage, vec3_t v) -{ - v[0] = 100.0 * crandom(); - v[1] = 100.0 * crandom(); - v[2] = 200.0 + 100.0 * random(); - - if (damage < 50) - { - VectorScale(v, 0.7, v); - } - else - { - VectorScale(v, 1.2, v); - } -} - -void -ClipGibVelocity(edict_t *ent) -{ - if (!ent) - { - return; - } - - if (ent->velocity[0] < -300) - { - ent->velocity[0] = -300; - } - else if (ent->velocity[0] > 300) - { - ent->velocity[0] = 300; - } - - if (ent->velocity[1] < -300) - { - ent->velocity[1] = -300; - } - else if (ent->velocity[1] > 300) - { - ent->velocity[1] = 300; - } - - if (ent->velocity[2] < 200) - { - ent->velocity[2] = 200; /* always some upwards */ - } - else if (ent->velocity[2] > 500) - { - ent->velocity[2] = 500; - } -} - -/* ===================================================== */ - -void -gib_think(edict_t *self) -{ - if (!self) - { - return; - } - - self->s.frame++; - self->nextthink = level.time + FRAMETIME; - - if (self->s.frame == 10) - { - self->think = G_FreeEdict; - self->nextthink = level.time + 8 + random() * 10; - } -} - -void -gib_touch(edict_t *self, edict_t *other /* unused */, cplane_t *plane, csurface_t *surf /* unused */) -{ - vec3_t normal_angles, right; - - if (!self) - { - return; - } - - if (!self->groundentity) - { - return; - } - - self->touch = NULL; - - if (plane) - { - gi.sound(self, CHAN_VOICE, gi.soundindex( - "misc/fhit3.wav"), 1, ATTN_NORM, 0); - - vectoangles(plane->normal, normal_angles); - AngleVectors(normal_angles, NULL, right, NULL); - vectoangles(right, self->s.angles); - - if (self->s.modelindex == sm_meat_index) - { - self->s.frame++; - self->think = gib_think; - self->nextthink = level.time + FRAMETIME; - } - } -} - -void -gib_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, - int damage /* unused */, vec3_t point /* unused */) -{ - if (!self) - { - return; - } - - G_FreeEdict(self); -} - -void -ThrowGib(edict_t *self, char *gibname, int damage, int type) -{ - edict_t *gib; - vec3_t vd; - vec3_t origin; - vec3_t size; - float vscale; - - gibsthisframe++; - - if (gibsthisframe > MAX_GIBS) - { - return; - } - - gib = G_Spawn(); - - VectorScale(self->size, 0.5, size); - VectorAdd(self->absmin, size, origin); - gib->s.origin[0] = origin[0] + crandom() * size[0]; - gib->s.origin[1] = origin[1] + crandom() * size[1]; - gib->s.origin[2] = origin[2] + crandom() * size[2]; - - gi.setmodel(gib, gibname); - gib->solid = SOLID_BBOX; - gib->s.effects |= EF_GIB; - gib->flags |= FL_NO_KNOCKBACK; - gib->takedamage = DAMAGE_YES; - gib->die = gib_die; - gib->health = 250; - - if (type == GIB_ORGANIC) - { - gib->movetype = MOVETYPE_TOSS; - gib->touch = gib_touch; - vscale = 0.5; - } - else - { - gib->movetype = MOVETYPE_BOUNCE; - vscale = 1.0; - } - - VelocityForDamage(damage, vd); - VectorMA(self->velocity, vscale, vd, gib->velocity); - ClipGibVelocity(gib); - gib->avelocity[0] = random() * 600; - gib->avelocity[1] = random() * 600; - gib->avelocity[2] = random() * 600; - - gib->think = G_FreeEdict; - gib->nextthink = level.time + 10 + random() * 10; - - gi.linkentity(gib); -} - -void -ThrowHead(edict_t *self, char *gibname, int damage, int type) -{ - vec3_t vd; - float vscale; - - self->s.skinnum = 0; - self->s.frame = 0; - VectorClear(self->mins); - VectorClear(self->maxs); - - self->s.modelindex2 = 0; - gi.setmodel(self, gibname); - self->solid = SOLID_BBOX; - self->s.effects |= EF_GIB; - self->s.effects &= ~EF_FLIES; - self->s.sound = 0; - self->flags |= FL_NO_KNOCKBACK; - self->svflags &= ~SVF_MONSTER; - self->takedamage = DAMAGE_YES; - self->die = gib_die; - - if (type == GIB_ORGANIC) - { - self->movetype = MOVETYPE_TOSS; - self->touch = gib_touch; - vscale = 0.5; - } - else - { - self->movetype = MOVETYPE_BOUNCE; - vscale = 1.0; - } - - VelocityForDamage(damage, vd); - VectorMA(self->velocity, vscale, vd, self->velocity); - ClipGibVelocity(self); - - self->avelocity[YAW] = crandom() * 600; - - self->think = G_FreeEdict; - self->nextthink = level.time + 10 + random() * 10; - - gi.linkentity(self); -} - -void -ThrowGibACID(edict_t *self, char *gibname, int damage, int type) -{ - edict_t *gib; - vec3_t vd; - vec3_t origin; - vec3_t size; - float vscale; - - if (!self || !gibname) - { - return; - } - - gibsthisframe++; - - if (gibsthisframe > MAX_GIBS) - { - return; - } - - gib = G_Spawn(); - - VectorScale(self->size, 0.5, size); - VectorAdd(self->absmin, size, origin); - gib->s.origin[0] = origin[0] + crandom() * size[0]; - gib->s.origin[1] = origin[1] + crandom() * size[1]; - gib->s.origin[2] = origin[2] + crandom() * size[2]; - - /* gi.setmodel (gib, gibname); */ - gib->s.modelindex = gi.modelindex(gibname); - - gib->clipmask = MASK_SHOT; - gib->solid = SOLID_BBOX; - - gib->s.effects |= EF_GREENGIB; - /* note to self check this */ - gib->s.renderfx |= RF_FULLBRIGHT; - gib->flags |= FL_NO_KNOCKBACK; - gib->takedamage = DAMAGE_YES; - gib->die = gib_die; - gib->dmg = 2; - gib->health = 250; - - if (type == GIB_ORGANIC) - { - gib->movetype = MOVETYPE_TOSS; - vscale = 3.0; - } - else - { - gib->movetype = MOVETYPE_BOUNCE; - vscale = 1.0; - } - - VelocityForDamage(damage, vd); - VectorMA(self->velocity, vscale, vd, gib->velocity); - ClipGibVelocity(gib); - gib->avelocity[0] = random() * 600; - gib->avelocity[1] = random() * 600; - gib->avelocity[2] = random() * 600; - - gib->think = G_FreeEdict; - gib->nextthink = level.time + 10 + random() * 10; - - gi.linkentity(gib); -} - -void -ThrowHeadACID(edict_t *self, char *gibname, int damage, int type) -{ - vec3_t vd; - float vscale; - - if (!self || !gibname) - { - return; - } - - self->s.skinnum = 0; - self->s.frame = 0; - VectorClear(self->mins); - VectorClear(self->maxs); - - self->s.modelindex2 = 0; - gi.setmodel(self, gibname); - - self->clipmask = MASK_SHOT; - self->solid = SOLID_BBOX; - - self->s.effects |= EF_GREENGIB; - self->s.effects &= ~EF_FLIES; - self->s.effects |= RF_FULLBRIGHT; - self->s.sound = 0; - self->flags |= FL_NO_KNOCKBACK; - self->svflags &= ~SVF_MONSTER; - self->takedamage = DAMAGE_YES; - self->die = gib_die; - self->dmg = 2; - - if (type == GIB_ORGANIC) - { - self->movetype = MOVETYPE_TOSS; - vscale = 0.5; - } - else - { - self->movetype = MOVETYPE_BOUNCE; - vscale = 1.0; - } - - VelocityForDamage(damage, vd); - VectorMA(self->velocity, vscale, vd, self->velocity); - ClipGibVelocity(self); - - self->avelocity[YAW] = crandom() * 600; - - self->think = G_FreeEdict; - self->nextthink = level.time + 10 + random() * 10; - - gi.linkentity(self); -} - -void -ThrowClientHead(edict_t *self, int damage) -{ - vec3_t vd; - char *gibname; - - if (!self) - { - return; - } - - if (randk() & 1) - { - gibname = "models/objects/gibs/head2/tris.md2"; - self->s.skinnum = 1; /* second skin is player */ - } - else - { - gibname = "models/objects/gibs/skull/tris.md2"; - self->s.skinnum = 0; - } - - self->s.origin[2] += 32; - self->s.frame = 0; - gi.setmodel(self, gibname); - VectorSet(self->mins, -16, -16, 0); - VectorSet(self->maxs, 16, 16, 16); - - self->takedamage = DAMAGE_NO; - self->solid = SOLID_BBOX; - self->s.effects = EF_GIB; - self->s.sound = 0; - self->flags |= FL_NO_KNOCKBACK; - - self->movetype = MOVETYPE_BOUNCE; - VelocityForDamage(damage, vd); - VectorAdd(self->velocity, vd, self->velocity); - - if (self->client) /* bodies in the queue don't have a client anymore */ - { - self->client->anim_priority = ANIM_DEATH; - self->client->anim_end = self->s.frame; - } - else - { - self->think = NULL; - self->nextthink = 0; - } - - gi.linkentity(self); -} - -/* ===================================================== */ - -void -debris_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, - int damage /* unused */, vec3_t point /* unused */) -{ - if (!self) - { - return; - } - - G_FreeEdict(self); -} - -void -ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin) -{ - edict_t *chunk; - vec3_t v; - - debristhisframe++; - - if (debristhisframe > MAX_DEBRIS) - { - return; - } - - chunk = G_Spawn(); - VectorCopy(origin, chunk->s.origin); - gi.setmodel(chunk, modelname); - v[0] = 100 * crandom(); - v[1] = 100 * crandom(); - v[2] = 100 + 100 * crandom(); - VectorMA(self->velocity, speed, v, chunk->velocity); - chunk->movetype = MOVETYPE_BOUNCE; - chunk->solid = SOLID_BBOX; - chunk->avelocity[0] = random() * 600; - chunk->avelocity[1] = random() * 600; - chunk->avelocity[2] = random() * 600; - chunk->think = G_FreeEdict; - chunk->nextthink = level.time + 5 + random() * 5; - chunk->s.frame = 0; - chunk->flags = 0; - chunk->classname = "debris"; - chunk->takedamage = DAMAGE_YES; - chunk->die = debris_die; - chunk->health = 250; - gi.linkentity(chunk); -} - -void -BecomeExplosion1(edict_t *self) -{ - if (!self) - { - return; - } - - /* flags are important */ - if (strcmp(self->classname, "item_flag_team1") == 0) - { - CTFResetFlag(CTF_TEAM1); /* this will free self! */ - gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n", - CTFTeamName(CTF_TEAM1)); - return; - } - - if (strcmp(self->classname, "item_flag_team2") == 0) - { - CTFResetFlag(CTF_TEAM2); /* this will free self! */ - gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n", - CTFTeamName(CTF_TEAM2)); - return; - } - - /* techs are important too */ - if (self->item && (self->item->flags & IT_TECH)) - { - CTFRespawnTech(self); /* this frees self! */ - return; - } - - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_EXPLOSION1); - gi.WritePosition(self->s.origin); - gi.multicast(self->s.origin, MULTICAST_PVS); - - G_FreeEdict(self); -} - -void -BecomeExplosion2(edict_t *self) -{ - if (!self) - { - return; - } - - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_EXPLOSION2); - gi.WritePosition(self->s.origin); - gi.multicast(self->s.origin, MULTICAST_PVS); - - G_FreeEdict(self); -} -/* ===================================================== */ - -/* - * QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT - * - * Target: next path corner - * Pathtarget: gets used when an entity that has - * this path_corner targeted touches it - */ -void -path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, - csurface_t *surf /* unused */) -{ - vec3_t v; - edict_t *next; - - if (!self || !other) - { - return; - } - - if (other->movetarget != self) - { - return; - } - - if (other->enemy) - { - return; - } - - if (self->pathtarget) - { - char *savetarget; - - savetarget = self->target; - self->target = self->pathtarget; - G_UseTargets(self, other); - self->target = savetarget; - } - - if (self->target) - { - next = G_PickTarget(self->target); - } - else - { - next = NULL; - } - - if ((next) && (next->spawnflags & 1)) - { - VectorCopy(next->s.origin, v); - v[2] += next->mins[2]; - v[2] -= other->mins[2]; - VectorCopy(v, other->s.origin); - next = G_PickTarget(next->target); - other->s.event = EV_OTHER_TELEPORT; - } - - other->goalentity = other->movetarget = next; - - if (self->wait) - { - other->monsterinfo.pausetime = level.time + self->wait; - other->monsterinfo.stand(other); - return; - } - - if (!other->movetarget) - { - other->monsterinfo.pausetime = level.time + 100000000; - other->monsterinfo.stand(other); - } - else - { - VectorSubtract(other->goalentity->s.origin, other->s.origin, v); - other->ideal_yaw = vectoyaw(v); - } -} - -void -SP_path_corner(edict_t *self) -{ - if (!self) - { - return; - } - - if (!self->targetname) - { - gi.dprintf("path_corner with no targetname at %s\n", - vtos(self->s.origin)); - G_FreeEdict(self); - return; - } - - self->solid = SOLID_TRIGGER; - self->touch = path_corner_touch; - VectorSet(self->mins, -8, -8, -8); - VectorSet(self->maxs, 8, 8, 8); - self->svflags |= SVF_NOCLIENT; - gi.linkentity(self); -} - -/* ===================================================== */ - -/* - * QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold - * - * Makes this the target of a monster and it will head here - * when first activated before going after the activator. If - * hold is selected, it will stay here. - */ -void -point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, - csurface_t *surf /* unused */) -{ - edict_t *activator; - - if (!self || !other) - { - return; - } - - if (other->movetarget != self) - { - return; - } - - if (self->target) - { - other->target = self->target; - other->goalentity = other->movetarget = G_PickTarget(other->target); - - if (!other->goalentity) - { - gi.dprintf("%s at %s target %s does not exist\n", - self->classname, - vtos(self->s.origin), - self->target); - other->movetarget = self; - } - - self->target = NULL; - } - else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM | FL_FLY))) - { - other->monsterinfo.pausetime = level.time + 100000000; - other->monsterinfo.aiflags |= AI_STAND_GROUND; - other->monsterinfo.stand(other); - } - - if (other->movetarget == self) - { - other->target = NULL; - other->movetarget = NULL; - other->goalentity = other->enemy; - other->monsterinfo.aiflags &= ~AI_COMBAT_POINT; - } - - if (self->pathtarget) - { - char *savetarget; - - savetarget = self->target; - self->target = self->pathtarget; - - if (other->enemy && other->enemy->client) - { - activator = other->enemy; - } - else if (other->oldenemy && other->oldenemy->client) - { - activator = other->oldenemy; - } - else if (other->activator && other->activator->client) - { - activator = other->activator; - } - else - { - activator = other; - } - - G_UseTargets(self, activator); - self->target = savetarget; - } -} - -void -SP_point_combat(edict_t *self) -{ - if (!self) - { - return; - } - - if (deathmatch->value) - { - G_FreeEdict(self); - return; - } - - self->solid = SOLID_TRIGGER; - self->touch = point_combat_touch; - VectorSet(self->mins, -8, -8, -16); - VectorSet(self->maxs, 8, 8, 16); - self->svflags = SVF_NOCLIENT; - gi.linkentity(self); -} - -/* ===================================================== */ - -/* - * QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) - * - * Just for the debugging level. Don't use - */ -static int robotron[4]; - -void -TH_viewthing(edict_t *ent) -{ - ent->s.frame = (ent->s.frame + 1) % 7; - ent->nextthink = level.time + FRAMETIME; - - if (ent->spawnflags) - { - if (ent->s.frame == 0) - { - ent->spawnflags = (ent->spawnflags + 1) % 4 + 1; - ent->s.modelindex = robotron[ent->spawnflags - 1]; - } - } -} - -void -SP_viewthing(edict_t *ent) -{ - if (!ent) - { - return; - } - - gi.dprintf("viewthing spawned\n"); - - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.renderfx = RF_FRAMELERP; - VectorSet(ent->mins, -16, -16, -24); - VectorSet(ent->maxs, 16, 16, 32); - ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2"); - gi.linkentity(ent); - ent->nextthink = level.time + 0.5; - ent->think = TH_viewthing; - return; -} - -/* ===================================================== */ - -/* - * QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) - * - * Used as a positional target for spotlights, etc. - */ -void -SP_info_null(edict_t *self) -{ - if (!self) - { - return; - } - - G_FreeEdict(self); -} - -/* - * QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) - * - * Used as a positional target for lighting. - */ -void -SP_info_notnull(edict_t *self) -{ - if (!self) - { - return; - } - - VectorCopy(self->s.origin, self->absmin); - VectorCopy(self->s.origin, self->absmax); -} - -#define START_OFF 1 - -/* - * QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF - * - * Non-displayed light. - * Default light value is 300. - * Default style is 0. - * If targeted, will toggle between on and off. - * Default _cone value is 10 (used to set size of light for spotlights) - */ -void -light_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) -{ - if (!self) - { - return; - } - - if (self->spawnflags & START_OFF) - { - gi.configstring(CS_LIGHTS + self->style, "m"); - self->spawnflags &= ~START_OFF; - } - else - { - gi.configstring(CS_LIGHTS + self->style, "a"); - self->spawnflags |= START_OFF; - } -} - -void -SP_light(edict_t *self) -{ - if (!self) - { - return; - } - - /* no targeted lights in deathmatch, because they cause global messages */ - if (!self->targetname || deathmatch->value) - { - G_FreeEdict(self); - return; - } - - if (self->style >= 32) - { - self->use = light_use; - - if (self->spawnflags & START_OFF) - { - gi.configstring(CS_LIGHTS + self->style, "a"); - } - else - { - gi.configstring(CS_LIGHTS + self->style, "m"); - } - } -} - -/* ===================================================== */ - -/* - * QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST - * This is just a solid wall if not inhibited - * - * TRIGGER_SPAWN the wall will not be present until triggered - * it will then blink in to existance; it will - * kill anything that was in it's way - * - * TOGGLE only valid for TRIGGER_SPAWN walls - * this allows the wall to be turned on and off - * - * START_ON only valid for TRIGGER_SPAWN walls - * the wall will initially be present - */ -void -func_wall_use(edict_t *self, edict_t *other, edict_t *activator) -{ - if (self->solid == SOLID_NOT) - { - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - KillBox(self); - } - else - { - self->solid = SOLID_NOT; - self->svflags |= SVF_NOCLIENT; - } - - gi.linkentity(self); - - if (!(self->spawnflags & 2)) - { - self->use = NULL; - } -} - -void -SP_func_wall(edict_t *self) -{ - self->movetype = MOVETYPE_PUSH; - gi.setmodel(self, self->model); - - if (self->spawnflags & 8) - { - self->s.effects |= EF_ANIM_ALL; - } - - if (self->spawnflags & 16) - { - self->s.effects |= EF_ANIM_ALLFAST; - } - - /* just a wall */ - if ((self->spawnflags & 7) == 0) - { - self->solid = SOLID_BSP; - gi.linkentity(self); - return; - } - - /* it must be TRIGGER_SPAWN */ - if (!(self->spawnflags & 1)) - { - self->spawnflags |= 1; - } - - /* yell if the spawnflags are odd */ - if (self->spawnflags & 4) - { - if (!(self->spawnflags & 2)) - { - gi.dprintf("func_wall START_ON without TOGGLE\n"); - self->spawnflags |= 2; - } - } - - self->use = func_wall_use; - - if (self->spawnflags & 4) - { - self->solid = SOLID_BSP; - } - else - { - self->solid = SOLID_NOT; - self->svflags |= SVF_NOCLIENT; - } - - gi.linkentity(self); -} - -/* - * QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST - * This is solid bmodel that will fall if it's support it removed. - */ - -void -func_object_touch(edict_t *self, edict_t *other, cplane_t *plane, - csurface_t *surf) -{ - /* only squash thing we fall on top of */ - if (!plane) - { - return; - } - - if (plane->normal[2] < 1.0) - { - return; - } - - if (other->takedamage == DAMAGE_NO) - { - return; - } - - T_Damage(other, self, self, vec3_origin, self->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); -} - -void -func_object_release(edict_t *self) -{ - self->movetype = MOVETYPE_TOSS; - self->touch = func_object_touch; -} - -void -func_object_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - self->use = NULL; - KillBox(self); - func_object_release(self); -} - -void -SP_func_object(edict_t *self) -{ - gi.setmodel(self, self->model); - - self->mins[0] += 1; - self->mins[1] += 1; - self->mins[2] += 1; - self->maxs[0] -= 1; - self->maxs[1] -= 1; - self->maxs[2] -= 1; - - if (!self->dmg) - { - self->dmg = 100; - } - - if (self->spawnflags == 0) - { - self->solid = SOLID_BSP; - self->movetype = MOVETYPE_PUSH; - self->think = func_object_release; - self->nextthink = level.time + 2 * FRAMETIME; - } - else - { - self->solid = SOLID_NOT; - self->movetype = MOVETYPE_PUSH; - self->use = func_object_use; - self->svflags |= SVF_NOCLIENT; - } - - if (self->spawnflags & 2) - { - self->s.effects |= EF_ANIM_ALL; - } - - if (self->spawnflags & 4) - { - self->s.effects |= EF_ANIM_ALLFAST; - } - - self->clipmask = MASK_MONSTERSOLID; - - gi.linkentity(self); -} - -/* - * QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST - * Any brush that you want to explode or break apart. If you want an - * explosion, set dmg and it will do a radius explosion of that amount - * at the center of the bursh. - * - * If targeted it will not be shootable. - * - * health defaults to 100. - * - * mass defaults to 75. This determines how much debris is emitted when - * it explodes. You get one large chunk per 100 of mass (up to 8) and - * one small chunk per 25 of mass (up to 16). So 800 gives the most. - */ -void -func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - vec3_t origin; - vec3_t chunkorigin; - vec3_t size; - int count; - int mass; - - /* bmodel origins are (0 0 0), we need to adjust that here */ - VectorScale(self->size, 0.5, size); - VectorAdd(self->absmin, size, origin); - VectorCopy(origin, self->s.origin); - - self->takedamage = DAMAGE_NO; - - if (self->dmg) - { - T_RadiusDamage(self, attacker, self->dmg, NULL, - self->dmg + 40, MOD_EXPLOSIVE); - } - - VectorSubtract(self->s.origin, inflictor->s.origin, self->velocity); - VectorNormalize(self->velocity); - VectorScale(self->velocity, 150, self->velocity); - - /* start chunks towards the center */ - VectorScale(size, 0.5, size); - - mass = self->mass; - - if (!mass) - { - mass = 75; - } - - /* big chunks */ - if (mass >= 100) - { - count = mass / 100; - - if (count > 8) - { - count = 8; - } - - while (count--) - { - chunkorigin[0] = origin[0] + crandom() * size[0]; - chunkorigin[1] = origin[1] + crandom() * size[1]; - chunkorigin[2] = origin[2] + crandom() * size[2]; - ThrowDebris(self, "models/objects/debris1/tris.md2", 1, chunkorigin); - } - } - - /* small chunks */ - count = mass / 25; - - if (count > 16) - { - count = 16; - } - - while (count--) - { - chunkorigin[0] = origin[0] + crandom() * size[0]; - chunkorigin[1] = origin[1] + crandom() * size[1]; - chunkorigin[2] = origin[2] + crandom() * size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", 2, chunkorigin); - } - - G_UseTargets(self, attacker); - - if (self->dmg) - { - BecomeExplosion1(self); - } - else - { - G_FreeEdict(self); - } -} - -void -func_explosive_use(edict_t *self, edict_t *other, edict_t *activator) -{ - func_explosive_explode(self, self, other, self->health, vec3_origin); -} - -void -func_explosive_spawn(edict_t *self, edict_t *other, edict_t *activator) -{ - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - self->use = NULL; - KillBox(self); - gi.linkentity(self); -} - -void -SP_func_explosive(edict_t *self) -{ - if (deathmatch->value) - { - /* auto-remove for deathmatch */ - G_FreeEdict(self); - return; - } - - self->movetype = MOVETYPE_PUSH; - - gi.modelindex("models/objects/debris1/tris.md2"); - gi.modelindex("models/objects/debris2/tris.md2"); - - gi.setmodel(self, self->model); - - if (self->spawnflags & 1) - { - self->svflags |= SVF_NOCLIENT; - self->solid = SOLID_NOT; - self->use = func_explosive_spawn; - } - else - { - self->solid = SOLID_BSP; - - if (self->targetname) - { - self->use = func_explosive_use; - } - } - - if (self->spawnflags & 2) - { - self->s.effects |= EF_ANIM_ALL; - } - - if (self->spawnflags & 4) - { - self->s.effects |= EF_ANIM_ALLFAST; - } - - if (self->use != func_explosive_use) - { - if (!self->health) - { - self->health = 100; - } - - self->die = func_explosive_explode; - self->takedamage = DAMAGE_YES; - } - - gi.linkentity(self); -} - -/* - * QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40) - * Large exploding box. You can override its mass (100), - * health (80), and dmg (150). - */ -void -barrel_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) - -{ - float ratio; - vec3_t v; - - if ((!other->groundentity) || (other->groundentity == self)) - { - return; - } - - ratio = (float)other->mass / (float)self->mass; - VectorSubtract(self->s.origin, other->s.origin, v); - M_walkmove(self, vectoyaw(v), 20 * ratio * FRAMETIME); -} - -void -barrel_explode(edict_t *self) -{ - vec3_t org; - float spd; - vec3_t save; - - T_RadiusDamage(self, self->activator, self->dmg, NULL, - self->dmg + 40, MOD_BARREL); - - VectorCopy(self->s.origin, save); - VectorMA(self->absmin, 0.5, self->size, self->s.origin); - - /* a few big chunks */ - spd = 1.5 * (float)self->dmg / 200.0; - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org); - - /* bottom corners */ - spd = 1.75 * (float)self->dmg / 200.0; - VectorCopy(self->absmin, org); - ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); - VectorCopy(self->absmin, org); - org[0] += self->size[0]; - ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); - VectorCopy(self->absmin, org); - org[1] += self->size[1]; - ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); - VectorCopy(self->absmin, org); - org[0] += self->size[0]; - org[1] += self->size[1]; - ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); - - /* a bunch of little chunks */ - spd = 2 * self->dmg / 200; - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - org[0] = self->s.origin[0] + crandom() * self->size[0]; - org[1] = self->s.origin[1] + crandom() * self->size[1]; - org[2] = self->s.origin[2] + crandom() * self->size[2]; - ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); - - VectorCopy(save, self->s.origin); - - if (self->groundentity) - { - BecomeExplosion2(self); - } - else - { - BecomeExplosion1(self); - } -} - -void -barrel_delay(edict_t *self, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - self->takedamage = DAMAGE_NO; - self->nextthink = level.time + 2 * FRAMETIME; - self->think = barrel_explode; - self->activator = attacker; -} - -void -SP_misc_explobox(edict_t *self) -{ - if (deathmatch->value) - { - /* auto-remove for deathmatch */ - G_FreeEdict(self); - return; - } - - gi.modelindex("models/objects/debris1/tris.md2"); - gi.modelindex("models/objects/debris2/tris.md2"); - gi.modelindex("models/objects/debris3/tris.md2"); - - self->solid = SOLID_BBOX; - self->movetype = MOVETYPE_STEP; - - self->model = "models/objects/barrels/tris.md2"; - self->s.modelindex = gi.modelindex(self->model); - VectorSet(self->mins, -16, -16, 0); - VectorSet(self->maxs, 16, 16, 40); - - if (!self->mass) - { - self->mass = 400; - } - - if (!self->health) - { - self->health = 10; - } - - if (!self->dmg) - { - self->dmg = 150; - } - - self->die = barrel_delay; - self->takedamage = DAMAGE_YES; - self->monsterinfo.aiflags = AI_NOSTEP; - - self->touch = barrel_touch; - - self->think = M_droptofloor; - self->nextthink = level.time + 2 * FRAMETIME; - - gi.linkentity(self); -} - -/* - * QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8) - */ -void -misc_blackhole_use(edict_t *ent, edict_t *other, edict_t *activator) -{ - G_FreeEdict(ent); -} - -void -misc_blackhole_think(edict_t *self) -{ - if (++self->s.frame < 19) - { - self->nextthink = level.time + FRAMETIME; - } - else - { - self->s.frame = 0; - self->nextthink = level.time + FRAMETIME; - } -} - -void -SP_misc_blackhole(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_NOT; - VectorSet(ent->mins, -64, -64, 0); - VectorSet(ent->maxs, 64, 64, 8); - ent->s.modelindex = gi.modelindex("models/objects/black/tris.md2"); - ent->s.renderfx = RF_TRANSLUCENT; - ent->use = misc_blackhole_use; - ent->think = misc_blackhole_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity(ent); -} - -/* - * QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32) - */ -void -misc_eastertank_think(edict_t *self) -{ - if (++self->s.frame < 293) - { - self->nextthink = level.time + FRAMETIME; - } - else - { - self->s.frame = 254; - self->nextthink = level.time + FRAMETIME; - } -} - -void -SP_misc_eastertank(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - VectorSet(ent->mins, -32, -32, -16); - VectorSet(ent->maxs, 32, 32, 32); - ent->s.modelindex = gi.modelindex("models/monsters/tank/tris.md2"); - ent->s.frame = 254; - ent->think = misc_eastertank_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity(ent); -} - -/* - * QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32) - */ -void -misc_easterchick_think(edict_t *self) -{ - if (++self->s.frame < 247) - { - self->nextthink = level.time + FRAMETIME; - } - else - { - self->s.frame = 208; - self->nextthink = level.time + FRAMETIME; - } -} - -void -SP_misc_easterchick(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - VectorSet(ent->mins, -32, -32, 0); - VectorSet(ent->maxs, 32, 32, 32); - ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2"); - ent->s.frame = 208; - ent->think = misc_easterchick_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity(ent); -} - -/* - * QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32) - */ -void -misc_easterchick2_think(edict_t *self) -{ - if (++self->s.frame < 287) - { - self->nextthink = level.time + FRAMETIME; - } - else - { - self->s.frame = 248; - self->nextthink = level.time + FRAMETIME; - } -} - -void -SP_misc_easterchick2(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - VectorSet(ent->mins, -32, -32, 0); - VectorSet(ent->maxs, 32, 32, 32); - ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2"); - ent->s.frame = 248; - ent->think = misc_easterchick2_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity(ent); -} - -/* - * QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48) - * Not really a monster, this is the Tank Commander's decapitated body. - * There should be a item_commander_head that has this as it's target. - */ -void -commander_body_think(edict_t *self) -{ - if (++self->s.frame < 24) - { - self->nextthink = level.time + FRAMETIME; - } - else - { - self->nextthink = 0; - } - - if (self->s.frame == 22) - { - gi.sound(self, CHAN_BODY, gi.soundindex("tank/thud.wav"), 1, ATTN_NORM, 0); - } -} - -void -commander_body_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->think = commander_body_think; - self->nextthink = level.time + FRAMETIME; - gi.sound(self, CHAN_BODY, gi.soundindex("tank/pain.wav"), 1, ATTN_NORM, 0); -} - -void -commander_body_drop(edict_t *self) -{ - self->movetype = MOVETYPE_TOSS; - self->s.origin[2] += 2; -} - -void -SP_monster_commander_body(edict_t *self) -{ - self->movetype = MOVETYPE_NONE; - self->solid = SOLID_BBOX; - self->model = "models/monsters/commandr/tris.md2"; - self->s.modelindex = gi.modelindex(self->model); - VectorSet(self->mins, -32, -32, 0); - VectorSet(self->maxs, 32, 32, 48); - self->use = commander_body_use; - self->takedamage = DAMAGE_YES; - self->flags = FL_GODMODE; - self->s.renderfx |= RF_FRAMELERP; - gi.linkentity(self); - - gi.soundindex("tank/thud.wav"); - gi.soundindex("tank/pain.wav"); - - self->think = commander_body_drop; - self->nextthink = level.time + 5 * FRAMETIME; -} - -/* - * QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4) - * The origin is the bottom of the banner. - * The banner is 128 tall. - */ -void -misc_banner_think(edict_t *ent) -{ - ent->s.frame = (ent->s.frame + 1) % 16; - ent->nextthink = level.time + FRAMETIME; -} - -void -SP_misc_banner(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_NOT; - ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2"); - ent->s.frame = rand() % 16; - gi.linkentity(ent); - - ent->think = misc_banner_think; - ent->nextthink = level.time + FRAMETIME; -} - -/* - * QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED - * This is the dead player model. Comes in 6 exciting different poses! - */ -void -misc_deadsoldier_die(edict_t *self, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - int n; - - if (self->health > -80) - { - return; - } - - gi.sound(self, CHAN_BODY, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); - - for (n = 0; n < 4; n++) - { - ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", - damage, GIB_ORGANIC); - } - - ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); -} - -void -SP_misc_deadsoldier(edict_t *ent) -{ - if (deathmatch->value) - { - /* auto-remove for deathmatch */ - G_FreeEdict(ent); - return; - } - - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.modelindex = gi.modelindex("models/deadbods/dude/tris.md2"); - - /* Defaults to frame 0 */ - if (ent->spawnflags & 2) - { - ent->s.frame = 1; - } - else if (ent->spawnflags & 4) - { - ent->s.frame = 2; - } - else if (ent->spawnflags & 8) - { - ent->s.frame = 3; - } - else if (ent->spawnflags & 16) - { - ent->s.frame = 4; - } - else if (ent->spawnflags & 32) - { - ent->s.frame = 5; - } - else - { - ent->s.frame = 0; - } - - VectorSet(ent->mins, -16, -16, 0); - VectorSet(ent->maxs, 16, 16, 16); - ent->deadflag = DEAD_DEAD; - ent->takedamage = DAMAGE_YES; - ent->svflags |= SVF_MONSTER | SVF_DEADMONSTER; - ent->die = misc_deadsoldier_die; - ent->monsterinfo.aiflags |= AI_GOOD_GUY; - - gi.linkentity(ent); -} - -/* - * QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32) - * This is the Viper for the flyby bombing. - * It is trigger_spawned, so you must have something use it for it to show up. - * There must be a path for it to follow once it is activated. - * - * "speed" How fast the Viper should fly - */ -extern void train_use(edict_t *self, edict_t *other, edict_t *activator); -extern void func_train_find(edict_t *self); - -void -misc_viper_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->svflags &= ~SVF_NOCLIENT; - self->use = train_use; - train_use(self, other, activator); -} - -void -SP_misc_viper(edict_t *ent) -{ - if (!ent->target) - { - gi.dprintf("misc_viper without a target at %s\n", vtos(ent->absmin)); - G_FreeEdict(ent); - return; - } - - if (!ent->speed) - { - ent->speed = 300; - } - - ent->movetype = MOVETYPE_PUSH; - ent->solid = SOLID_NOT; - ent->s.modelindex = gi.modelindex("models/ships/viper/tris.md2"); - VectorSet(ent->mins, -16, -16, 0); - VectorSet(ent->maxs, 16, 16, 32); - - ent->think = func_train_find; - ent->nextthink = level.time + FRAMETIME; - ent->use = misc_viper_use; - ent->svflags |= SVF_NOCLIENT; - ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed; - - gi.linkentity(ent); -} - -/* - * QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72) - * This is a large stationary viper as seen in Paul's intro - */ -void -SP_misc_bigviper(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - VectorSet(ent->mins, -176, -120, -24); - VectorSet(ent->maxs, 176, 120, 72); - ent->s.modelindex = gi.modelindex("models/ships/bigviper/tris.md2"); - gi.linkentity(ent); -} - -/* - * QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8) - * "dmg" how much boom should the bomb make? - */ -void -misc_viper_bomb_touch(edict_t *self, edict_t *other, cplane_t *plane, - csurface_t *surf) -{ - G_UseTargets(self, self->activator); - - self->s.origin[2] = self->absmin[2] + 1; - T_RadiusDamage(self, self, self->dmg, NULL, self->dmg + 40, MOD_BOMB); - BecomeExplosion2(self); -} - -void -misc_viper_bomb_prethink(edict_t *self) -{ - vec3_t v; - float diff; - - self->groundentity = NULL; - - diff = self->timestamp - level.time; - - if (diff < -1.0) - { - diff = -1.0; - } - - VectorScale(self->moveinfo.dir, 1.0 + diff, v); - v[2] = diff; - - diff = self->s.angles[2]; - vectoangles(v, self->s.angles); - self->s.angles[2] = diff + 10; -} - -void -misc_viper_bomb_use(edict_t *self, edict_t *other, edict_t *activator) -{ - edict_t *viper; - - self->solid = SOLID_BBOX; - self->svflags &= ~SVF_NOCLIENT; - self->s.effects |= EF_ROCKET; - self->use = NULL; - self->movetype = MOVETYPE_TOSS; - self->prethink = misc_viper_bomb_prethink; - self->touch = misc_viper_bomb_touch; - self->activator = activator; - - viper = G_Find(NULL, FOFS(classname), "misc_viper"); - VectorScale(viper->moveinfo.dir, viper->moveinfo.speed, self->velocity); - - self->timestamp = level.time; - VectorCopy(viper->moveinfo.dir, self->moveinfo.dir); -} - -void -SP_misc_viper_bomb(edict_t *self) -{ - self->movetype = MOVETYPE_NONE; - self->solid = SOLID_NOT; - VectorSet(self->mins, -8, -8, -8); - VectorSet(self->maxs, 8, 8, 8); - - self->s.modelindex = gi.modelindex("models/objects/bomb/tris.md2"); - - if (!self->dmg) - { - self->dmg = 1000; - } - - self->use = misc_viper_bomb_use; - self->svflags |= SVF_NOCLIENT; - - gi.linkentity(self); -} - -/* - * QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32) - * This is a Storgg ship for the flybys. - * It is trigger_spawned, so you must have something use it for it to show up. - * There must be a path for it to follow once it is activated. - * - * "speed" How fast it should fly - */ -extern void train_use(edict_t *self, edict_t *other, edict_t *activator); -extern void func_train_find(edict_t *self); - -void -misc_strogg_ship_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->svflags &= ~SVF_NOCLIENT; - self->use = train_use; - train_use(self, other, activator); -} - -void -SP_misc_strogg_ship(edict_t *ent) -{ - if (!ent->target) - { - gi.dprintf("%s without a target at %s\n", ent->classname, vtos(ent->absmin)); - G_FreeEdict(ent); - return; - } - - if (!ent->speed) - { - ent->speed = 300; - } - - ent->movetype = MOVETYPE_PUSH; - ent->solid = SOLID_NOT; - ent->s.modelindex = gi.modelindex("models/ships/strogg1/tris.md2"); - VectorSet(ent->mins, -16, -16, 0); - VectorSet(ent->maxs, 16, 16, 32); - - ent->think = func_train_find; - ent->nextthink = level.time + FRAMETIME; - ent->use = misc_strogg_ship_use; - ent->svflags |= SVF_NOCLIENT; - ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed; - - gi.linkentity(ent); -} - -/* - * QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128) - */ -void -misc_satellite_dish_think(edict_t *self) -{ - self->s.frame++; - - if (self->s.frame < 38) - { - self->nextthink = level.time + FRAMETIME; - } -} - -void -misc_satellite_dish_use(edict_t *self, edict_t *other, edict_t *activator) -{ - self->s.frame = 0; - self->think = misc_satellite_dish_think; - self->nextthink = level.time + FRAMETIME; -} - -void -SP_misc_satellite_dish(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - VectorSet(ent->mins, -64, -64, 0); - VectorSet(ent->maxs, 64, 64, 128); - ent->s.modelindex = gi.modelindex("models/objects/satellite/tris.md2"); - ent->use = misc_satellite_dish_use; - gi.linkentity(ent); -} - -/* - * QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12) - */ -void -SP_light_mine1(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.modelindex = gi.modelindex("models/objects/minelite/light1/tris.md2"); - gi.linkentity(ent); -} - -/* - * QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12) - */ -void -SP_light_mine2(edict_t *ent) -{ - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.modelindex = gi.modelindex("models/objects/minelite/light2/tris.md2"); - gi.linkentity(ent); -} - -/* - * QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8) - * Intended for use with the target_spawner - */ -void -SP_misc_gib_arm(edict_t *ent) -{ - gi.setmodel(ent, "models/objects/gibs/arm/tris.md2"); - ent->solid = SOLID_BBOX; - ent->s.effects |= EF_GIB; - ent->takedamage = DAMAGE_YES; - ent->die = gib_die; - ent->movetype = MOVETYPE_TOSS; - ent->svflags |= SVF_MONSTER; - ent->deadflag = DEAD_DEAD; - ent->avelocity[0] = random() * 200; - ent->avelocity[1] = random() * 200; - ent->avelocity[2] = random() * 200; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 30; - gi.linkentity(ent); -} - -/* - * QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8) - * Intended for use with the target_spawner - */ -void -SP_misc_gib_leg(edict_t *ent) -{ - gi.setmodel(ent, "models/objects/gibs/leg/tris.md2"); - ent->solid = SOLID_BBOX; - ent->s.effects |= EF_GIB; - ent->takedamage = DAMAGE_YES; - ent->die = gib_die; - ent->movetype = MOVETYPE_TOSS; - ent->svflags |= SVF_MONSTER; - ent->deadflag = DEAD_DEAD; - ent->avelocity[0] = random() * 200; - ent->avelocity[1] = random() * 200; - ent->avelocity[2] = random() * 200; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 30; - gi.linkentity(ent); -} - -/* - * QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8) - * Intended for use with the target_spawner - */ -void -SP_misc_gib_head(edict_t *ent) -{ - gi.setmodel(ent, "models/objects/gibs/head/tris.md2"); - ent->solid = SOLID_BBOX; - ent->s.effects |= EF_GIB; - ent->takedamage = DAMAGE_YES; - ent->die = gib_die; - ent->movetype = MOVETYPE_TOSS; - ent->svflags |= SVF_MONSTER; - ent->deadflag = DEAD_DEAD; - ent->avelocity[0] = random() * 200; - ent->avelocity[1] = random() * 200; - ent->avelocity[2] = random() * 200; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 30; - gi.linkentity(ent); -} - -/* ===================================================== */ - -/* - * QUAKED target_character (0 0 1) ? - * used with target_string (must be on same "team") - * "count" is position in the string (starts at 1) - */ - -void -SP_target_character(edict_t *self) -{ - self->movetype = MOVETYPE_PUSH; - gi.setmodel(self, self->model); - self->solid = SOLID_BSP; - self->s.frame = 12; - gi.linkentity(self); - return; -} - -/* - * QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8) - */ -void -target_string_use(edict_t *self, edict_t *other, edict_t *activator) -{ - edict_t *e; - int n, l; - char c; - - l = strlen(self->message); - - for (e = self->teammaster; e; e = e->teamchain) - { - if (!e->count) - { - continue; - } - - n = e->count - 1; - - if (n > l) - { - e->s.frame = 12; - continue; - } - - c = self->message[n]; - - if ((c >= '0') && (c <= '9')) - { - e->s.frame = c - '0'; - } - else if (c == '-') - { - e->s.frame = 10; - } - else if (c == ':') - { - e->s.frame = 11; - } - else - { - e->s.frame = 12; - } - } -} - -void -SP_target_string(edict_t *self) -{ - if (!self->message) - { - self->message = ""; - } - - self->use = target_string_use; -} - -/* - * QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE - * target a target_string with this - * - * The default is to be a time of day clock - * - * TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget" - * If START_OFF, this entity must be used before it starts - * - * "style" 0 "xx" - * 1 "xx:xx" - * 2 "xx:xx:xx" - */ - -#define CLOCK_MESSAGE_SIZE 16 - -void -func_clock_reset(edict_t *self) -{ - self->activator = NULL; - - if (self->spawnflags & 1) - { - self->health = 0; - self->wait = self->count; - } - else if (self->spawnflags & 2) - { - self->health = self->count; - self->wait = 0; - } -} - -void -func_clock_format_countdown(edict_t *self) -{ - if (self->style == 0) - { - Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health); - return; - } - - if (self->style == 1) - { - Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", - self->health / 60, self->health % 60); - - if (self->message[3] == ' ') - { - self->message[3] = '0'; - } - - return; - } - - if (self->style == 2) - { - Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", - self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, - self->health % 60); - - if (self->message[3] == ' ') - { - self->message[3] = '0'; - } - - if (self->message[6] == ' ') - { - self->message[6] = '0'; - } - - return; - } -} - -void -func_clock_think(edict_t *self) -{ - if (!self->enemy) - { - self->enemy = G_Find(NULL, FOFS(targetname), self->target); - - if (!self->enemy) - { - return; - } - } - - if (self->spawnflags & 1) - { - func_clock_format_countdown(self); - self->health++; - } - else if (self->spawnflags & 2) - { - func_clock_format_countdown(self); - self->health--; - } - else - { - struct tm *ltime; - time_t gmtime; - - time(&gmtime); - ltime = localtime(&gmtime); - Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", - ltime->tm_hour, ltime->tm_min, ltime->tm_sec); - - if (self->message[3] == ' ') - { - self->message[3] = '0'; - } - - if (self->message[6] == ' ') - { - self->message[6] = '0'; - } - } - - self->enemy->message = self->message; - self->enemy->use(self->enemy, self, self); - - if (((self->spawnflags & 1) && (self->health > self->wait)) || - ((self->spawnflags & 2) && (self->health < self->wait))) - { - if (self->pathtarget) - { - char *savetarget; - char *savemessage; - - savetarget = self->target; - savemessage = self->message; - self->target = self->pathtarget; - self->message = NULL; - G_UseTargets(self, self->activator); - self->target = savetarget; - self->message = savemessage; - } - - if (!(self->spawnflags & 8)) - { - return; - } - - func_clock_reset(self); - - if (self->spawnflags & 4) - { - return; - } - } - - self->nextthink = level.time + 1; -} - -void -func_clock_use(edict_t *self, edict_t *other, edict_t *activator) -{ - if (!(self->spawnflags & 8)) - { - self->use = NULL; - } - - if (self->activator) - { - return; - } - - self->activator = activator; - self->think(self); -} - -void -SP_func_clock(edict_t *self) -{ - if (!self->target) - { - gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); - G_FreeEdict(self); - return; - } - - if ((self->spawnflags & 2) && (!self->count)) - { - gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin)); - G_FreeEdict(self); - return; - } - - if ((self->spawnflags & 1) && (!self->count)) - { - self->count = 60 * 60; - } - - func_clock_reset(self); - - self->message = gi.TagMalloc(CLOCK_MESSAGE_SIZE, TAG_LEVEL); - - self->think = func_clock_think; - - if (self->spawnflags & 4) - { - self->use = func_clock_use; - } - else - { - self->nextthink = level.time + 1; - } -} - -/* ================================================================================= */ - -void -teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane, - csurface_t *surf) -{ - edict_t *dest; - int i; - - if (!other->client) - { - return; - } - - dest = G_Find(NULL, FOFS(targetname), self->target); - - if (!dest) - { - gi.dprintf("Couldn't find destination\n"); - return; - } - - CTFPlayerResetGrapple(other); - - /* unlink to make sure it can't possibly interfere with KillBox */ - gi.unlinkentity(other); - - VectorCopy(dest->s.origin, other->s.origin); - VectorCopy(dest->s.origin, other->s.old_origin); - other->s.origin[2] += 10; - - /* clear the velocity and hold them in place briefly */ - VectorClear(other->velocity); - other->client->ps.pmove.pm_time = 160 >> 3; /* hold time */ - other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; - - /* draw the teleport splash at source and on the player */ - self->owner->s.event = EV_PLAYER_TELEPORT; - other->s.event = EV_PLAYER_TELEPORT; - - /* set angles */ - for (i = 0; i < 3; i++) - { - other->client->ps.pmove.delta_angles[i] = - ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); - } - - VectorClear(other->s.angles); - VectorClear(other->client->ps.viewangles); - VectorClear(other->client->v_angle); - - /* kill anything at the destination */ - KillBox(other); - - gi.linkentity(other); -} - -/* - * QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16) - * Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object. - */ -void -SP_misc_teleporter(edict_t *ent) -{ - edict_t *trig; - - if (!ent->target) - { - gi.dprintf("teleporter without a target.\n"); - G_FreeEdict(ent); - return; - } - - gi.setmodel(ent, "models/objects/dmspot/tris.md2"); - ent->s.skinnum = 1; - ent->s.effects = EF_TELEPORTER; - ent->s.sound = gi.soundindex("world/amb10.wav"); - ent->solid = SOLID_BBOX; - - VectorSet(ent->mins, -32, -32, -24); - VectorSet(ent->maxs, 32, 32, -16); - gi.linkentity(ent); - - trig = G_Spawn(); - trig->touch = teleporter_touch; - trig->solid = SOLID_TRIGGER; - trig->target = ent->target; - trig->owner = ent; - VectorCopy(ent->s.origin, trig->s.origin); - VectorSet(trig->mins, -8, -8, 8); - VectorSet(trig->maxs, 8, 8, 24); - gi.linkentity(trig); -} - -/* - * QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) - * Point teleporters at these. - */ -void -SP_misc_teleporter_dest(edict_t *ent) -{ - gi.setmodel(ent, "models/objects/dmspot/tris.md2"); - ent->s.skinnum = 0; - ent->solid = SOLID_BBOX; - VectorSet(ent->mins, -32, -32, -24); - VectorSet(ent->maxs, 32, 32, -16); - gi.linkentity(ent); -} diff --git a/src/ctf/g_monster.c b/src/ctf/g_monster.c deleted file mode 100644 index 23511b3b..00000000 --- a/src/ctf/g_monster.c +++ /dev/null @@ -1,1480 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * Copyright (c) ZeniMax Media Inc. - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * ======================================================================= - * - * Monster utility functions. While unused by the CTF code they must - * persist here since most of the other game codes has ties to it. - * - * ======================================================================= - */ - -#include "header/local.h" - -void monster_start_go(edict_t *self); - -/* Monster weapons */ - -void -monster_fire_bullet(edict_t *self, vec3_t start, vec3_t dir, int damage, - int kick, int hspread, int vspread, int flashtype) -{ - if (!self) - { - return; - } - - fire_bullet(self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, - int kick, int hspread, int vspread, int count, int flashtype) -{ - if (!self) - { - return; - } - - fire_shotgun(self, start, aimdir, damage, kick, hspread, - vspread, count, MOD_UNKNOWN); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, - int speed, int flashtype, int effect) -{ - if (!self) - { - return; - } - - fire_blaster(self, start, dir, damage, speed, effect, false); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_blueblaster(edict_t *self, vec3_t start, vec3_t dir, int damage, - int speed, int flashtype, int effect) -{ - if (!self) - { - return; - } - - fire_blueblaster(self, start, dir, damage, speed, effect); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(MZ_BLUEHYPERBLASTER); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_blaster2(edict_t *self, vec3_t start, vec3_t dir, int damage, - int speed, int flashtype, int effect) -{ - if (!self) - { - return; - } - - fire_blaster2(self, start, dir, damage, speed, effect, false); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_ionripper(edict_t *self, vec3_t start, vec3_t dir, int damage, - int speed, int flashtype, int effect) -{ - if (!self) - { - return; - } - - fire_ionripper(self, start, dir, damage, speed, effect); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_heat(edict_t *self, vec3_t start, vec3_t dir, int damage, - int speed, int flashtype) -{ - if (!self) - { - return; - } - - fire_heat(self, start, dir, damage, speed, damage, damage); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_tracker(edict_t *self, vec3_t start, vec3_t dir, int damage, - int speed, edict_t *enemy, int flashtype) -{ - if (!self || !enemy) - { - return; - } - - fire_tracker(self, start, dir, damage, speed, enemy); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_heatbeam(edict_t *self, vec3_t start, vec3_t dir, vec3_t offset, - int damage, int kick, int flashtype) -{ - if (!self) - { - return; - } - - fire_heatbeam(self, start, dir, offset, damage, kick, true); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -dabeam_hit(edict_t *self) -{ - edict_t *ignore; - vec3_t start; - vec3_t end; - trace_t tr; - - if (!self) - { - return; - } - - ignore = self; - VectorCopy(self->s.origin, start); - VectorMA(start, 2048, self->movedir, end); - - while (1) - { - tr = gi.trace(start, NULL, NULL, end, ignore, - CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER); - - if (!tr.ent) - { - break; - } - - /* hurt it if we can */ - if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && - (tr.ent != self->owner)) - { - T_Damage(tr.ent, self, self->owner, self->movedir, tr.endpos, - vec3_origin, self->dmg, skill->value, DAMAGE_ENERGY, - MOD_TARGET_LASER); - } - - if (self->dmg < 0) /* healer ray */ - { - /* when player is at 100 health - just undo health fix */ - if (tr.ent->client && (tr.ent->health > 100)) - { - tr.ent->health += self->dmg; - } - } - - /* if we hit something that's not a monster or - player or is immune to lasers, we're done */ - if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) - { - if (self->spawnflags & 0x80000000) - { - self->spawnflags &= ~0x80000000; - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_LASER_SPARKS); - gi.WriteByte(10); - gi.WritePosition(tr.endpos); - gi.WriteDir(tr.plane.normal); - gi.WriteByte(self->s.skinnum); - gi.multicast(tr.endpos, MULTICAST_PVS); - } - - break; - } - - ignore = tr.ent; - VectorCopy(tr.endpos, start); - } - - VectorCopy(tr.endpos, self->s.old_origin); - self->nextthink = level.time + 0.1; - self->think = G_FreeEdict; -} - -void -monster_dabeam(edict_t *self) -{ - vec3_t last_movedir; - vec3_t point; - - if (!self) - { - return; - } - - self->movetype = MOVETYPE_NONE; - self->solid = SOLID_NOT; - self->s.renderfx |= RF_BEAM | RF_TRANSLUCENT; - self->s.modelindex = 1; - - self->s.frame = 2; - - if (self->owner->monsterinfo.aiflags & AI_MEDIC) - { - self->s.skinnum = 0xf3f3f1f1; - } - else - { - self->s.skinnum = 0xf2f2f0f0; - } - - if (self->enemy) - { - VectorCopy(self->movedir, last_movedir); - VectorMA(self->enemy->absmin, 0.5, self->enemy->size, point); - - if (self->owner->monsterinfo.aiflags & AI_MEDIC) - { - point[0] += sin(level.time) * 8; - } - - VectorSubtract(point, self->s.origin, self->movedir); - VectorNormalize(self->movedir); - - if (!VectorCompare(self->movedir, last_movedir)) - { - self->spawnflags |= 0x80000000; - } - } - else - { - G_SetMovedir(self->s.angles, self->movedir); - } - - self->think = dabeam_hit; - self->nextthink = level.time + 0.1; - VectorSet(self->mins, -8, -8, -8); - VectorSet(self->maxs, 8, 8, 8); - gi.linkentity(self); - - self->spawnflags |= 0x80000001; - self->svflags &= ~SVF_NOCLIENT; -} - -void -monster_fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, - int damage, int speed, int flashtype) -{ - if (!self) - { - return; - } - - fire_grenade(self, start, aimdir, damage, speed, 2.5, damage + 40); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_rocket(edict_t *self, vec3_t start, vec3_t dir, - int damage, int speed, int flashtype) -{ - if (!self) - { - return; - } - - fire_rocket(self, start, dir, damage, speed, damage + 20, damage); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_railgun(edict_t *self, vec3_t start, vec3_t aimdir, - int damage, int kick, int flashtype) -{ - if (!self) - { - return; - } - - if (!(gi.pointcontents(start) & MASK_SOLID)) - { - fire_rail(self, start, aimdir, damage, kick); - } - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -void -monster_fire_bfg(edict_t *self, vec3_t start, vec3_t aimdir, - int damage, int speed, int kick /* unused */, float damage_radius, - int flashtype) -{ - if (!self) - { - return; - } - - fire_bfg(self, start, aimdir, damage, speed, damage_radius); - - gi.WriteByte(svc_muzzleflash2); - gi.WriteShort(self - g_edicts); - gi.WriteByte(flashtype); - gi.multicast(start, MULTICAST_PVS); -} - -/* ================================================================== */ - -/* Monster utility functions */ - -void -M_FliesOff(edict_t *self) -{ - if (!self) - { - return; - } - - self->s.effects &= ~EF_FLIES; - self->s.sound = 0; -} - -void -M_FliesOn(edict_t *self) -{ - if (!self) - { - return; - } - - if (self->waterlevel) - { - return; - } - - self->s.effects |= EF_FLIES; - self->s.sound = gi.soundindex("infantry/inflies1.wav"); - self->think = M_FliesOff; - self->nextthink = level.time + 60; -} - -void -M_FlyCheck(edict_t *self) -{ - if (!self) - { - return; - } - - if (self->waterlevel) - { - return; - } - - if (random() > 0.5) - { - return; - } - - self->think = M_FliesOn; - self->nextthink = level.time + 5 + 10 * random(); -} - -void -AttackFinished(edict_t *self, float time) -{ - if (!self) - { - return; - } - - self->monsterinfo.attack_finished = level.time + time; -} - -void -M_CheckGround(edict_t *ent) -{ - vec3_t point; - trace_t trace; - - if (!ent) - { - return; - } - - if (ent->flags & (FL_SWIM | FL_FLY)) - { - return; - } - - if ((ent->velocity[2] * ent->gravityVector[2]) < -100) - { - ent->groundentity = NULL; - return; - } - - /* if the hull point one-quarter unit down - is solid the entity is on ground */ - point[0] = ent->s.origin[0]; - point[1] = ent->s.origin[1]; - point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]); - - trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, point, - ent, MASK_MONSTERSOLID); - - /* check steepness */ - if ((trace.plane.normal[2] < 0.7) && !trace.startsolid) - { - ent->groundentity = NULL; - return; - } - - if (!trace.startsolid && !trace.allsolid) - { - VectorCopy(trace.endpos, ent->s.origin); - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; - ent->velocity[2] = 0; - } -} - -void -M_CatagorizePosition(edict_t *ent) -{ - vec3_t point; - int cont; - - if (!ent) - { - return; - } - - /* get waterlevel */ - point[0] = (ent->absmax[0] + ent->absmin[0])/2; - point[1] = (ent->absmax[1] + ent->absmin[1])/2; - point[2] = ent->absmin[2] + 2; - cont = gi.pointcontents(point); - - if (!(cont & MASK_WATER)) - { - ent->waterlevel = 0; - ent->watertype = 0; - return; - } - - ent->watertype = cont; - ent->waterlevel = 1; - point[2] += 26; - cont = gi.pointcontents(point); - - if (!(cont & MASK_WATER)) - { - return; - } - - ent->waterlevel = 2; - point[2] += 22; - cont = gi.pointcontents(point); - - if (cont & MASK_WATER) - { - ent->waterlevel = 3; - } -} - -void -M_WorldEffects(edict_t *ent) -{ - int dmg; - - if (!ent) - { - return; - } - - if (ent->health > 0) - { - if (!(ent->flags & FL_SWIM)) - { - if (ent->waterlevel < 3) - { - ent->air_finished = level.time + 12; - } - else if (ent->air_finished < level.time) - { - /* drown! */ - if (ent->pain_debounce_time < level.time) - { - dmg = 2 + 2 * floor(level.time - ent->air_finished); - - if (dmg > 15) - { - dmg = 15; - } - - T_Damage(ent, world, world, vec3_origin, ent->s.origin, - vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); - ent->pain_debounce_time = level.time + 1; - } - } - } - else - { - if (ent->waterlevel > 0) - { - ent->air_finished = level.time + 9; - } - else if (ent->air_finished < level.time) - { - /* suffocate! */ - if (ent->pain_debounce_time < level.time) - { - dmg = 2 + 2 * floor(level.time - ent->air_finished); - - if (dmg > 15) - { - dmg = 15; - } - - T_Damage(ent, world, world, vec3_origin, ent->s.origin, - vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); - ent->pain_debounce_time = level.time + 1; - } - } - } - } - - if (ent->waterlevel == 0) - { - if (ent->flags & FL_INWATER) - { - gi.sound(ent, CHAN_BODY, gi.soundindex( - "player/watr_out.wav"), 1, ATTN_NORM, 0); - ent->flags &= ~FL_INWATER; - } - - return; - } - - if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA)) - { - if (ent->damage_debounce_time < level.time) - { - ent->damage_debounce_time = level.time + 0.2; - T_Damage(ent, world, world, vec3_origin, ent->s.origin, - vec3_origin, 10 * ent->waterlevel, 0, 0, MOD_LAVA); - } - } - - if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME) && !(ent->svflags & SVF_DEADMONSTER)) - { - if (ent->damage_debounce_time < level.time) - { - ent->damage_debounce_time = level.time + 1; - T_Damage(ent, world, world, vec3_origin, ent->s.origin, - vec3_origin, 4 * ent->waterlevel, 0, 0, MOD_SLIME); - } - } - - if (!(ent->flags & FL_INWATER)) - { - if (!(ent->svflags & SVF_DEADMONSTER)) - { - if (ent->watertype & CONTENTS_LAVA) - { - if (random() <= 0.5) - { - gi.sound(ent, CHAN_BODY, gi.soundindex( - "player/lava1.wav"), 1, ATTN_NORM, 0); - } - else - { - gi.sound(ent, CHAN_BODY, gi.soundindex( - "player/lava2.wav"), 1, ATTN_NORM, 0); - } - } - else if (ent->watertype & CONTENTS_SLIME) - { - gi.sound(ent, CHAN_BODY, gi.soundindex( - "player/watr_in.wav"), 1, ATTN_NORM, 0); - } - else if (ent->watertype & CONTENTS_WATER) - { - gi.sound(ent, CHAN_BODY, gi.soundindex( - "player/watr_in.wav"), 1, ATTN_NORM, 0); - } - } - - ent->flags |= FL_INWATER; - ent->damage_debounce_time = 0; - } -} - -void -M_droptofloor(edict_t *ent) -{ - vec3_t end; - trace_t trace; - - if (!ent) - { - return; - } - - ent->s.origin[2] += 1; - VectorCopy(ent->s.origin, end); - end[2] -= 256; - - trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end, - ent, MASK_MONSTERSOLID); - - if ((trace.fraction == 1) || trace.allsolid) - { - return; - } - - VectorCopy(trace.endpos, ent->s.origin); - - gi.linkentity(ent); - M_CheckGround(ent); - M_CatagorizePosition(ent); -} - -void -M_SetEffects(edict_t *ent) -{ - int remaining; - - if (!ent) - { - return; - } - - ent->s.effects &= ~(EF_COLOR_SHELL | EF_POWERSCREEN | EF_DOUBLE | EF_QUAD | EF_PENT); - ent->s.renderfx &= ~(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE); - - if (ent->monsterinfo.aiflags & AI_RESURRECTING) - { - ent->s.effects |= EF_COLOR_SHELL; - ent->s.renderfx |= RF_SHELL_RED; - } - - if (ent->health <= 0) - { - return; - } - - if (ent->powerarmor_time > level.time) - { - if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN) - { - ent->s.effects |= EF_POWERSCREEN; - } - else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD) - { - ent->s.effects |= EF_COLOR_SHELL; - ent->s.renderfx |= RF_SHELL_GREEN; - } - } - - if (ent->monsterinfo.quad_framenum > level.framenum) - { - remaining = ent->monsterinfo.quad_framenum - level.framenum; - - if ((remaining > 30) || (remaining & 4)) - { - ent->s.effects |= EF_QUAD; - } - } - else - { - ent->s.effects &= ~EF_QUAD; - } - - if (ent->monsterinfo.double_framenum > level.framenum) - { - remaining = ent->monsterinfo.double_framenum - level.framenum; - - if ((remaining > 30) || (remaining & 4)) - { - ent->s.effects |= EF_DOUBLE; - } - } - else - { - ent->s.effects &= ~EF_DOUBLE; - } - - if (ent->monsterinfo.invincible_framenum > level.framenum) - { - remaining = ent->monsterinfo.invincible_framenum - level.framenum; - - if ((remaining > 30) || (remaining & 4)) - { - ent->s.effects |= EF_PENT; - } - } - else - { - ent->s.effects &= ~EF_PENT; - } -} - -void -M_MoveFrame(edict_t *self) -{ - mmove_t *move; - int index; - - if (!self) - { - return; - } - - move = self->monsterinfo.currentmove; - self->nextthink = level.time + FRAMETIME; - - if ((self->monsterinfo.nextframe) && - (self->monsterinfo.nextframe >= move->firstframe) && - (self->monsterinfo.nextframe <= move->lastframe)) - { - self->s.frame = self->monsterinfo.nextframe; - self->monsterinfo.nextframe = 0; - } - else - { - /* prevent nextframe from leaking into a future move */ - self->monsterinfo.nextframe = 0; - - if (self->s.frame == move->lastframe) - { - if (move->endfunc) - { - move->endfunc(self); - - /* regrab move, endfunc is very likely to change it */ - move = self->monsterinfo.currentmove; - - /* check for death */ - if (self->svflags & SVF_DEADMONSTER) - { - return; - } - } - } - - if ((self->s.frame < move->firstframe) || - (self->s.frame > move->lastframe)) - { - self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; - self->s.frame = move->firstframe; - } - else - { - if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) - { - self->s.frame++; - - if (self->s.frame > move->lastframe) - { - self->s.frame = move->firstframe; - } - } - } - } - - index = self->s.frame - move->firstframe; - - if (move->frame[index].aifunc) - { - if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) - { - move->frame[index].aifunc(self, - move->frame[index].dist * self->monsterinfo.scale); - } - else - { - move->frame[index].aifunc(self, 0); - } - } - - if (move->frame[index].thinkfunc) - { - move->frame[index].thinkfunc(self); - } -} - -void -monster_think(edict_t *self) -{ - if (!self) - { - return; - } - - M_MoveFrame(self); - - if (self->linkcount != self->monsterinfo.linkcount) - { - self->monsterinfo.linkcount = self->linkcount; - M_CheckGround(self); - } - - M_CatagorizePosition(self); - M_WorldEffects(self); - M_SetEffects(self); -} - -/* - * Using a monster makes it angry - * at the current activator - */ -void -monster_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) -{ - if (!self || !activator) - { - return; - } - - if (self->enemy) - { - return; - } - - if (self->health <= 0) - { - return; - } - - if (activator->flags & FL_NOTARGET) - { - return; - } - - if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY)) - { - return; - } - - /* delay reaction so if the monster is teleported, its sound is still heard */ - self->enemy = activator; - FoundTarget(self); -} - -void -monster_triggered_spawn(edict_t *self) -{ - if (!self) - { - return; - } - - self->s.origin[2] += 1; - KillBox(self); - - self->solid = SOLID_BBOX; - self->movetype = MOVETYPE_STEP; - self->svflags &= ~SVF_NOCLIENT; - self->air_finished = level.time + 12; - gi.linkentity(self); - - monster_start_go(self); - - if (self->enemy && !(self->spawnflags & 1) && - !(self->enemy->flags & FL_NOTARGET)) - { - FoundTarget(self); - } - else - { - self->enemy = NULL; - } -} - -void -monster_triggered_spawn_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) -{ - if (!self || !activator) - { - return; - } - - /* we have a one frame delay here so we - don't telefrag the guy who activated us */ - self->think = monster_triggered_spawn; - self->nextthink = level.time + FRAMETIME; - - if (activator->client) - { - self->enemy = activator; - } - - self->use = monster_use; -} - -void -monster_triggered_start(edict_t *self) -{ - if (!self) - { - return; - } - - self->solid = SOLID_NOT; - self->movetype = MOVETYPE_NONE; - self->svflags |= SVF_NOCLIENT; - self->nextthink = 0; - self->use = monster_triggered_spawn_use; -} - -/* - * When a monster dies, it fires all of its targets - * with the current enemy as activator. - */ -void -monster_death_use(edict_t *self) -{ - if (!self) - { - return; - } - - self->flags &= ~(FL_FLY | FL_SWIM); - self->monsterinfo.aiflags &= AI_GOOD_GUY; - - if (self->item) - { - Drop_Item(self, self->item); - self->item = NULL; - } - - if (self->deathtarget) - { - self->target = self->deathtarget; - } - - if (!self->target) - { - return; - } - - G_UseTargets(self, self->enemy); -} - -/* ================================================================== */ - -qboolean -monster_start(edict_t *self) -{ - if (!self) - { - return false; - } - - if (deathmatch->value) - { - G_FreeEdict(self); - return false; - } - - if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY)) - { - self->spawnflags &= ~4; - self->spawnflags |= 1; - } - - if (!(self->monsterinfo.aiflags & AI_GOOD_GUY)) - { - level.total_monsters++; - } - - self->nextthink = level.time + FRAMETIME; - self->svflags |= SVF_MONSTER; - self->s.renderfx |= RF_FRAMELERP; - self->takedamage = DAMAGE_AIM; - self->air_finished = level.time + 12; - self->use = monster_use; - - if(!self->max_health) - { - self->max_health = self->health; - } - - self->clipmask = MASK_MONSTERSOLID; - - self->s.skinnum = 0; - self->deadflag = DEAD_NO; - self->svflags &= ~SVF_DEADMONSTER; - - if (!self->monsterinfo.checkattack) - { - self->monsterinfo.checkattack = M_CheckAttack; - } - - VectorCopy(self->s.origin, self->s.old_origin); - - if (st.item) - { - self->item = FindItemByClassname(st.item); - - if (!self->item) - { - gi.dprintf("%s at %s has bad item: %s\n", self->classname, - vtos(self->s.origin), st.item); - } - } - - /* randomize what frame they start on */ - if (self->monsterinfo.currentmove) - { - self->s.frame = self->monsterinfo.currentmove->firstframe + - (randk() % (self->monsterinfo.currentmove->lastframe - - self->monsterinfo.currentmove->firstframe + 1)); - } - - self->monsterinfo.base_height = self->maxs[2]; - self->monsterinfo.quad_framenum = 0; - self->monsterinfo.double_framenum = 0; - self->monsterinfo.invincible_framenum = 0; - - return true; -} - -void -monster_start_go(edict_t *self) -{ - vec3_t v; - - if (!self) - { - return; - } - - if (self->health <= 0) - { - return; - } - - /* check for target to combat_point and change to combattarget */ - if (self->target) - { - qboolean notcombat; - qboolean fixup; - edict_t *target; - - target = NULL; - notcombat = false; - fixup = false; - - while ((target = G_Find(target, FOFS(targetname), self->target)) != NULL) - { - if (strcmp(target->classname, "point_combat") == 0) - { - self->combattarget = self->target; - fixup = true; - } - else - { - notcombat = true; - } - } - - if (notcombat && self->combattarget) - { - gi.dprintf("%s at %s has target with mixed types\n", - self->classname, vtos(self->s.origin)); - } - - if (fixup) - { - self->target = NULL; - } - } - - /* validate combattarget */ - if (self->combattarget) - { - edict_t *target; - - target = NULL; - - while ((target = G_Find(target, FOFS(targetname), - self->combattarget)) != NULL) - { - if (strcmp(target->classname, "point_combat") != 0) - { - gi.dprintf( "%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", - self->classname, (int)self->s.origin[0], (int)self->s.origin[1], - (int)self->s.origin[2], self->combattarget, target->classname, - (int)target->s.origin[0], (int)target->s.origin[1], - (int)target->s.origin[2]); - } - } - } - - if (self->target) - { - self->goalentity = self->movetarget = G_PickTarget(self->target); - - if (!self->movetarget) - { - gi.dprintf("%s can't find target %s at %s\n", self->classname, - self->target, vtos(self->s.origin)); - self->target = NULL; - self->monsterinfo.pausetime = 100000000; - self->monsterinfo.stand(self); - } - else if (strcmp(self->movetarget->classname, "path_corner") == 0) - { - VectorSubtract(self->goalentity->s.origin, self->s.origin, v); - self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); - self->monsterinfo.walk(self); - self->target = NULL; - } - else - { - self->goalentity = self->movetarget = NULL; - self->monsterinfo.pausetime = 100000000; - self->monsterinfo.stand(self); - } - } - else - { - self->monsterinfo.pausetime = 100000000; - self->monsterinfo.stand(self); - } - - self->think = monster_think; - self->nextthink = level.time + FRAMETIME; -} - -void -walkmonster_start_go(edict_t *self) -{ - if (!self) - { - return; - } - - if (!(self->spawnflags & 2) && (level.time < 1)) - { - M_droptofloor(self); - - if (self->groundentity) - { - if (!M_walkmove(self, 0, 0)) - { - gi.dprintf("%s in solid at %s\n", self->classname, - vtos(self->s.origin)); - } - } - } - - if (!self->yaw_speed) - { - self->yaw_speed = 20; - } - - if (!self->viewheight) - { - self->viewheight = 25; - } - - if (self->spawnflags & 2) - { - monster_triggered_start(self); - } - else - { - monster_start_go(self); - } -} - -void -walkmonster_start(edict_t *self) -{ - if (!self) - { - return; - } - - self->think = walkmonster_start_go; - monster_start(self); -} - -void -flymonster_start_go(edict_t *self) -{ - if (!self) - { - return; - } - - if (!M_walkmove(self, 0, 0)) - { - gi.dprintf("%s in solid at %s\n", self->classname, vtos(self->s.origin)); - } - - if (!self->yaw_speed) - { - self->yaw_speed = 10; - } - - if (!self->viewheight) - { - self->viewheight = 25; - } - - if (self->spawnflags & 2) - { - monster_triggered_start(self); - } - else - { - monster_start_go(self); - } -} - -void -flymonster_start(edict_t *self) -{ - if (!self) - { - return; - } - - self->flags |= FL_FLY; - self->think = flymonster_start_go; - monster_start(self); -} - -void -swimmonster_start_go(edict_t *self) -{ - if (!self) - { - return; - } - - if (!self->yaw_speed) - { - self->yaw_speed = 10; - } - - if (!self->viewheight) - { - self->viewheight = 10; - } - - if (self->spawnflags & 2) - { - monster_triggered_start(self); - } - else - { - monster_start_go(self); - } -} - -void -swimmonster_start(edict_t *self) -{ - if (!self) - { - return; - } - - self->flags |= FL_SWIM; - self->think = swimmonster_start_go; - monster_start(self); -} - -void stationarymonster_start_go(edict_t *self); - -void -stationarymonster_triggered_spawn(edict_t *self) -{ - if (!self) - { - return; - } - - KillBox(self); - - self->solid = SOLID_BBOX; - self->movetype = MOVETYPE_NONE; - self->svflags &= ~SVF_NOCLIENT; - self->air_finished = level.time + 12; - gi.linkentity(self); - - monster_start_go(self); - - if (self->enemy && !(self->spawnflags & 1) && - !(self->enemy->flags & FL_NOTARGET)) - { - if (!(self->enemy->flags & FL_DISGUISED)) - { - FoundTarget(self); - } - else - { - self->enemy = NULL; - } - } - else - { - self->enemy = NULL; - } -} - -void -stationarymonster_triggered_spawn_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) -{ - if (!self || !activator) - { - return; - } - - /* we have a one frame delay here so we don't telefrag the guy who activated us */ - self->think = stationarymonster_triggered_spawn; - self->nextthink = level.time + FRAMETIME; - - if (activator->client) - { - self->enemy = activator; - } - - self->use = monster_use; -} - -void -stationarymonster_triggered_start(edict_t *self) -{ - if (!self) - { - return; - } - - self->solid = SOLID_NOT; - self->movetype = MOVETYPE_NONE; - self->svflags |= SVF_NOCLIENT; - self->nextthink = 0; - self->use = stationarymonster_triggered_spawn_use; -} - -void -stationarymonster_start_go(edict_t *self) -{ - - if (!self) - { - return; - } - - if (!self->yaw_speed) - { - self->yaw_speed = 20; - } - - if (self->spawnflags & 2) - { - stationarymonster_triggered_start(self); - } - else - { - monster_start_go(self); - } -} - -void -stationarymonster_start(edict_t *self) -{ - if (!self) - { - return; - } - - self->think = stationarymonster_start_go; - monster_start(self); -} - -void -monster_done_dodge(edict_t *self) -{ - if (!self) - { - return; - } - - self->monsterinfo.aiflags &= ~AI_DODGING; -} diff --git a/src/ctf/g_phys.c b/src/ctf/g_phys.c index b46a3e20..867ee0d5 100644 --- a/src/ctf/g_phys.c +++ b/src/ctf/g_phys.c @@ -27,6 +27,24 @@ #include "header/local.h" +#define STOP_EPSILON 0.1 +#define MAX_CLIP_PLANES 5 +#define FRICTION 6 +#define WATERFRICTION 1 + +void SV_Physics_NewToss(edict_t *ent); + +typedef struct +{ + edict_t *ent; + vec3_t origin; + vec3_t angles; + float deltayaw; +} pushed_t; + +static pushed_t pushed[MAX_EDICTS], *pushed_p; +static edict_t *obstacle; + /* * pushmove objects do not obey gravity, and do not interact with each other or * trigger fields, but block normal movement and push normal objects when they move. @@ -164,11 +182,10 @@ SV_Impact(edict_t *e1, trace_t *trace) /* * Slide off of the impacting object - * returns the blocked flags (1 = floor, - * 2 = step / wall) + * returns the blocked flags: + * 1 = floor + * 2 = step / wall */ -#define STOP_EPSILON 0.1 - int ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { @@ -205,13 +222,15 @@ ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) } /* - * The basic solid body movement clip that slides along multiple planes - * Returns the clipflags if the velocity was modified (hit something solid) + * The basic solid body movement clip + * that slides along multiple planes + * Returns the clipflags if the velocity + * was modified (hit something solid) + * * 1 = floor * 2 = wall / step * 4 = dead stop */ -#define MAX_CLIP_PLANES 5 int SV_FlyMove(edict_t *ent, float time, int mask) { @@ -228,6 +247,11 @@ SV_FlyMove(edict_t *ent, float time, int mask) float time_left; int blocked; + if (!ent) + { + return 0; + } + numbumps = 4; blocked = 0; @@ -307,14 +331,15 @@ SV_FlyMove(edict_t *ent, float time, int mask) VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; - /* modify original_velocity so it parallels all of the clip planes */ + /* modify original_velocity so it + parallels all of the clip planes */ for (i = 0; i < numplanes; i++) { ClipVelocity(original_velocity, planes[i], new_velocity, 1); for (j = 0; j < numplanes; j++) { - if (j != i) + if ((j != i) && !VectorCompare(planes[i], planes[j])) { if (DotProduct(new_velocity, planes[j]) < 0) { @@ -336,7 +361,6 @@ SV_FlyMove(edict_t *ent, float time, int mask) } else { - /* go along the crease */ if (numplanes != 2) { @@ -349,8 +373,9 @@ SV_FlyMove(edict_t *ent, float time, int mask) VectorScale(dir, d, ent->velocity); } - /* if original velocity is against the original velocity, - stop dead to avoid tiny occilations in sloping corners */ + /* if original velocity is against the original + velocity, stop dead to avoid tiny occilations + in sloping corners */ if (DotProduct(ent->velocity, primal_velocity) <= 0) { VectorCopy(vec3_origin, ent->velocity); @@ -364,7 +389,20 @@ SV_FlyMove(edict_t *ent, float time, int mask) void SV_AddGravity(edict_t *ent) { - ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME; + if (!ent) + { + return; + } + + if (ent->gravityVector[2] > 0) + { + VectorMA(ent->velocity, ent->gravity * sv_gravity->value * FRAMETIME, + ent->gravityVector, ent->velocity); + } + else + { + ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME; + } } /* @@ -384,6 +422,11 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) vec3_t p[8]; int i, j, k, j2, k4; + if (!ent) + { + return; + } + for (k = 0; k < 2; k++) { k4 = k * 4; @@ -479,13 +522,9 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) } } -/* - * =============================================================================== - * - * PUSHMOVE - * - * =============================================================================== - */ +/* ================================================================== */ + +/* PUSHMOVE */ /* * Does not change the entities velocity at all @@ -514,6 +553,17 @@ retry: trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask); + /* startsolid treats different-content volumes + as continuous, like the bbox of a monster/player + and the floor of an elevator. So do another trace + that only collides with BSP so that we make a best + effort to keep these entities inside non-solid space + */ + if (trace.startsolid && (mask & ~MASK_SOLID)) + { + trace = gi.trace (start, ent->mins, ent->maxs, end, ent, MASK_SOLID); + } + VectorCopy(trace.endpos, ent->s.origin); gi.linkentity(ent); @@ -537,7 +587,8 @@ retry: { SV_Impact(ent, &trace); - /* if the pushed entity went away and the pusher is still there */ + /* if the pushed entity went away + and the pusher is still there */ if (!trace.ent->inuse && ent->inuse) { /* move the pusher back and try again */ @@ -547,6 +598,8 @@ retry: } } + ent->gravity = 1.0; + if (ent->inuse) { G_TouchTriggers(ent); @@ -555,17 +608,6 @@ retry: return trace; } -typedef struct -{ - edict_t *ent; - vec3_t origin; - vec3_t angles; - float deltayaw; -} pushed_t; - -pushed_t pushed[MAX_EDICTS], *pushed_p; -edict_t *obstacle; - /* * Objects need to be moved back on a failed push, * otherwise riders would continue to slide. @@ -579,6 +621,11 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) vec3_t org, org2, move2, forward, right, up; vec3_t realmins, realmaxs; + if (!pusher) + { + return false; + } + /* clamp the move to 1/8 units, so the position will be accurate for client side prediction */ for (i = 0; i < 3; i++) @@ -623,7 +670,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) rotating brush models. */ RealBoundingBox(pusher, realmins, realmaxs); - /* see if any solid entities are inside the final position */ + /* see if any solid entities + are inside the final position */ check = g_edicts + 1; for (e = 1; e < globals.num_edicts; e++, check++) @@ -646,7 +694,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) continue; /* not linked in anywhere */ } - /* if the entity is standing on the pusher, it will definitely be moved */ + /* if the entity is standing on the pusher, + it will definitely be moved */ if (check->groundentity != pusher) { /* see if the ent needs to be tested */ @@ -660,7 +709,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) continue; } - /* see if the ent's bbox is inside the pusher's final position */ + /* see if the ent's bbox is inside + the pusher's final position */ if (!SV_TestEntityPosition(check)) { continue; @@ -702,13 +752,16 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) if (!block) { - /* pushed ok */ + /* pushed ok */ gi.linkentity(check); + + /* impact? */ continue; } /* if it is ok to leave in the old position, do it - this is only relevent for riding entities, not pushed */ + this is only relevent for riding entities, not + pushed */ VectorSubtract(check->s.origin, move, check->s.origin); block = SV_TestEntityPosition(check); @@ -719,11 +772,12 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) } } - /* save off the obstacle so we can call the block function */ + /* save off the obstacle so we can + call the block function */ obstacle = check; /* move back any entities we already moved - go backwards, so if the same entity was pushed/ + go backwards, so if the same entity was pushed twice, it goes back to the original position */ for (p = pushed_p - 1; p >= pushed; p--) { @@ -760,15 +814,21 @@ SV_Physics_Pusher(edict_t *ent) vec3_t move, amove; edict_t *part, *mv; - /* if not a team captain, so movement will be handled elsewhere */ + if (!ent) + { + return; + } + + /* if not a team captain, so movement + will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; } - /* make sure all team slaves can move before commiting any moves - or calling any think functions if the move is blocked, all - moved objects will be backed out */ + /* make sure all team slaves can move before commiting + any moves or calling any think functions if the move + is blocked, all moved objects will be backed out */ pushed_p = pushed; for (part = ent; part; part = part->teamchain) @@ -787,14 +847,15 @@ SV_Physics_Pusher(edict_t *ent) } } - if (pushed_p > &pushed[MAX_EDICTS-1]) + if (pushed_p > &pushed[MAX_EDICTS - 1]) { - gi.error("pushed_p > &pushed[MAX_EDICTS-1], memory corrupted"); + gi.error("pushed_p > &pushed[MAX_EDICTS - 1], memory corrupted"); } if (part) { - /* the move failed, bump all nextthink times and back out moves */ + /* the move failed, bump all nextthink + times and back out moves */ for (mv = ent; mv; mv = mv->teamchain) { if (mv->nextthink > 0) @@ -816,7 +877,11 @@ SV_Physics_Pusher(edict_t *ent) /* the move succeeded, so call all think functions */ for (part = ent; part; part = part->teamchain) { - SV_RunThink(part); + /* prevent entities that are on trains that have gone away from thinking! */ + if (part->inuse) + { + SV_RunThink(part); + } } } } @@ -829,6 +894,11 @@ SV_Physics_Pusher(edict_t *ent) void SV_Physics_None(edict_t *ent) { + if (!ent) + { + return; + } + /* regular thinking */ SV_RunThink(ent); } @@ -839,6 +909,11 @@ SV_Physics_None(edict_t *ent) void SV_Physics_Noclip(edict_t *ent) { + if (!ent) + { + return; + } + /* regular thinking */ if (!SV_RunThink(ent)) { @@ -851,16 +926,13 @@ SV_Physics_Noclip(edict_t *ent) gi.linkentity(ent); } -/* - * ============================================================================== - * - * TOSS / BOUNCE - * - * ============================================================================== - */ +/* ================================================================== */ + +/* TOSS / BOUNCE */ /* - * Toss, bounce, and fly movement. When onground, do nothing. + * Toss, bounce, and fly movement. + * When onground, do nothing. */ void SV_Physics_Toss(edict_t *ent) @@ -873,10 +945,22 @@ SV_Physics_Toss(edict_t *ent) qboolean isinwater; vec3_t old_origin; + if (!ent) + { + return; + } + /* regular thinking */ SV_RunThink(ent); - /* if not a team captain, so movement will be handled elsewhere */ + /* entities are very often freed during thinking */ + if (!ent->inuse) + { + return; + } + + /* if not a team captain, so movement + will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; @@ -897,7 +981,7 @@ SV_Physics_Toss(edict_t *ent) } /* if onground, return without moving */ - if (ent->groundentity) + if (ent->groundentity && (ent->gravity > 0.0)) { return; } @@ -908,7 +992,8 @@ SV_Physics_Toss(edict_t *ent) /* add gravity */ if ((ent->movetype != MOVETYPE_FLY) && - (ent->movetype != MOVETYPE_FLYMISSILE)) + (ent->movetype != MOVETYPE_FLYMISSILE) + && (ent->movetype != MOVETYPE_WALLBOUNCE)) { SV_AddGravity(ent); } @@ -927,7 +1012,11 @@ SV_Physics_Toss(edict_t *ent) if (trace.fraction < 1) { - if (ent->movetype == MOVETYPE_BOUNCE) + if (ent->movetype == MOVETYPE_WALLBOUNCE) + { + backoff = 2.0; + } + else if (ent->movetype == MOVETYPE_BOUNCE) { backoff = 1.5; } @@ -938,8 +1027,14 @@ SV_Physics_Toss(edict_t *ent) ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff); + if (ent->movetype == MOVETYPE_WALLBOUNCE) + { + vectoangles(ent->velocity, ent->s.angles); + } + /* stop if on ground */ - if (trace.plane.normal[2] > 0.7) + if ((trace.plane.normal[2] > 0.7) && + (ent->movetype != MOVETYPE_WALLBOUNCE)) { if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE)) { @@ -967,8 +1062,12 @@ SV_Physics_Toss(edict_t *ent) if (!wasinwater && isinwater) { - gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, - gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); + /* don't play splash sound for entities already in water on level start */ + if (level.framenum > 3) + { + gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, + gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); + } } else if (wasinwater && !isinwater) { @@ -984,34 +1083,32 @@ SV_Physics_Toss(edict_t *ent) } } -/* - * =============================================================================== - * - * STEPPING MOVEMENT - * - * =============================================================================== - */ +/* ================================================================== */ + +/* STEPPING MOVEMENT */ /* - * Monsters freefall when they don't have a ground entity, otherwise - * all movement is done with discrete steps. + * Monsters freefall when they don't have a ground + * entity, otherwise all movement is done with + * discrete steps. * - * This is also used for objects that have become still on the ground, but - * will fall if the floor is pulled out from under them. + * This is also used for objects that have become + * still on the ground, but will fall if the floor + * is pulled out from under them. */ - -#define sv_stopspeed 100 -#define sv_friction 6 -#define sv_waterfriction 1 - void SV_AddRotationalFriction(edict_t *ent) { int n; float adjustment; + if (!ent) + { + return; + } + VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - adjustment = FRAMETIME * sv_stopspeed * sv_friction; + adjustment = FRAMETIME * sv_stopspeed->value * FRICTION; for (n = 0; n < 3; n++) { @@ -1046,6 +1143,13 @@ SV_Physics_Step(edict_t *ent) float friction; edict_t *groundentity; int mask; + vec3_t oldorig; + trace_t tr; + + if (!ent) + { + return; + } /* airborn monsters should always check for ground */ if (!ent->groundentity) @@ -1072,8 +1176,8 @@ SV_Physics_Step(edict_t *ent) } /* add gravity except: - flying monsters - swimming monsters who are in the water */ + - flying monsters + - swimming monsters who are in the water */ if (!wasonground) { if (!(ent->flags & FL_FLY)) @@ -1097,8 +1201,8 @@ SV_Physics_Step(edict_t *ent) if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); - control = speed < sv_stopspeed ? sv_stopspeed : speed; - friction = sv_friction / 3; + control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed; + friction = FRICTION / 3; newspeed = speed - (FRAMETIME * control * friction); if (newspeed < 0) @@ -1114,9 +1218,8 @@ SV_Physics_Step(edict_t *ent) if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); - control = speed < sv_stopspeed ? sv_stopspeed : speed; - newspeed = speed - - (FRAMETIME * control * sv_waterfriction * ent->waterlevel); + control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed; + newspeed = speed - (FRAMETIME * control * WATERFRICTION * ent->waterlevel); if (newspeed < 0) { @@ -1129,7 +1232,7 @@ SV_Physics_Step(edict_t *ent) if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) { - /* apply friction let dead monsters who + /* apply friction: let dead monsters who aren't completely onground slide */ if ((wasonground) || (ent->flags & (FL_SWIM | FL_FLY))) { @@ -1140,9 +1243,9 @@ SV_Physics_Step(edict_t *ent) if (speed) { - friction = sv_friction; + friction = FRICTION; - control = speed < sv_stopspeed ? sv_stopspeed : speed; + control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed; newspeed = speed - FRAMETIME * control * friction; if (newspeed < 0) @@ -1167,11 +1270,33 @@ SV_Physics_Step(edict_t *ent) mask = MASK_SOLID; } + VectorCopy(ent->s.origin, oldorig); SV_FlyMove(ent, FRAMETIME, mask); + /* Evil hack to work around dead parasites (and maybe other monster) + falling through the worldmodel into the void. We copy the current + origin (see above) and after the SV_FlyMove() was performend we + checl if we're stuck in the world model. If yes we're undoing the + move. */ + if (!VectorCompare(ent->s.origin, oldorig)) + { + tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); + + if (tr.startsolid) + { + VectorCopy(oldorig, ent->s.origin); + } + } + gi.linkentity(ent); + ent->gravity = 1.0; G_TouchTriggers(ent); + if (!ent->inuse) + { + return; + } + if (ent->groundentity) { if (!wasonground) @@ -1184,15 +1309,39 @@ SV_Physics_Step(edict_t *ent) } } + if (!ent->inuse) /* g_touchtrigger free problem */ + { + return; + } + /* regular thinking */ SV_RunThink(ent); } -/* ============================================================================ */ +/* ================================================================== */ void G_RunEntity(edict_t *ent) { + trace_t trace; + vec3_t previous_origin; + qboolean saved_origin; + + if (!ent) + { + return; + } + + if (ent->movetype == MOVETYPE_STEP) + { + VectorCopy(ent->s.origin, previous_origin); + saved_origin = true; + } + else + { + saved_origin = false; + } + if (ent->prethink) { ent->prethink(ent); @@ -1217,10 +1366,166 @@ G_RunEntity(edict_t *ent) case MOVETYPE_BOUNCE: case MOVETYPE_FLY: case MOVETYPE_FLYMISSILE: + case MOVETYPE_WALLBOUNCE: SV_Physics_Toss(ent); break; + case MOVETYPE_NEWTOSS: + SV_Physics_NewToss(ent); + break; default: gi.error("SV_Physics: bad movetype %i", (int)ent->movetype); } + + /* if we moved, check and fix origin if needed */ + /* also check inuse since entities are very often freed while thinking */ + if (saved_origin && ent->inuse && !VectorCompare(ent->s.origin, previous_origin)) + { + trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, + previous_origin, ent, MASK_MONSTERSOLID); + + if (trace.allsolid || trace.startsolid) + { + VectorCopy(previous_origin, ent->s.origin); + } + } } +/* + * Toss, bounce, and fly movement. When on ground and + * no velocity, do nothing. With velocity, slide. + */ +void +SV_Physics_NewToss(edict_t *ent) +{ + trace_t trace; + vec3_t move; + edict_t *slave; + qboolean wasinwater; + qboolean isinwater; + float speed, newspeed; + vec3_t old_origin; + + if (!ent) + { + return; + } + + /* regular thinking */ + SV_RunThink(ent); + + /* if not a team captain, so movement will be handled elsewhere */ + if (ent->flags & FL_TEAMSLAVE) + { + return; + } + + /* find out what we're sitting on. */ + VectorCopy(ent->s.origin, move); + move[2] -= 0.25; + trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, + move, ent, ent->clipmask); + + if (ent->groundentity && ent->groundentity->inuse) + { + ent->groundentity = trace.ent; + } + else + { + ent->groundentity = NULL; + } + + /* if we're sitting on something flat and have no velocity of our own, return. */ + if (ent->groundentity && (trace.plane.normal[2] == 1.0) && + !ent->velocity[0] && !ent->velocity[1] && !ent->velocity[2]) + { + return; + } + + /* store the old origin */ + VectorCopy(ent->s.origin, old_origin); + + SV_CheckVelocity(ent); + + /* add gravity */ + SV_AddGravity(ent); + + if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) + { + SV_AddRotationalFriction(ent); + } + + /* add friction */ + speed = VectorLength(ent->velocity); + + if (ent->waterlevel) /* friction for water movement */ + { + newspeed = speed - (WATERFRICTION * 6 * ent->waterlevel); + + if (newspeed < 0) + { + newspeed = 0; + } + + newspeed /= speed; + VectorScale(ent->velocity, newspeed, ent->velocity); + } + else if (!ent->groundentity) /* friction for air movement */ + { + newspeed = speed - ((FRICTION)); + + if (newspeed < 0) + { + newspeed = 0; + } + + newspeed /= speed; + VectorScale(ent->velocity, newspeed, ent->velocity); + } + else /* use ground friction */ + { + newspeed = speed - (FRICTION * 6); + + if (newspeed < 0) + { + newspeed = 0; + } + + newspeed /= speed; + VectorScale(ent->velocity, newspeed, ent->velocity); + } + + SV_FlyMove(ent, FRAMETIME, ent->clipmask); + gi.linkentity(ent); + + G_TouchTriggers(ent); + + /* check for water transition */ + wasinwater = (ent->watertype & MASK_WATER); + ent->watertype = gi.pointcontents(ent->s.origin); + isinwater = ent->watertype & MASK_WATER; + + if (isinwater) + { + ent->waterlevel = 1; + } + else + { + ent->waterlevel = 0; + } + + if (!wasinwater && isinwater) + { + gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); + } + else if (wasinwater && !isinwater) + { + gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); + } + + /* move teamslaves */ + for (slave = ent->teamchain; slave; slave = slave->teamchain) + { + VectorCopy(ent->s.origin, slave->s.origin); + gi.linkentity(slave); + } +} diff --git a/src/ctf/player/weapon.c b/src/ctf/player/weapon.c deleted file mode 100644 index 08684439..00000000 --- a/src/ctf/player/weapon.c +++ /dev/null @@ -1,2085 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * Copyright (c) ZeniMax Media Inc. - * - * This program 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. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * ======================================================================= - * - * Player weapons. - * - * ======================================================================= - */ - -#include "../header/local.h" -#include "../monster/misc/player.h" -#include - -#define PLAYER_NOISE_SELF 0 -#define PLAYER_NOISE_IMPACT 1 - -#define FRAME_FIRE_FIRST (FRAME_ACTIVATE_LAST + 1) -#define FRAME_IDLE_FIRST (FRAME_FIRE_LAST + 1) -#define FRAME_DEACTIVATE_FIRST (FRAME_IDLE_LAST + 1) - -#define GRENADE_TIMER 3.0 -#define GRENADE_MINSPEED 400 -#define GRENADE_MAXSPEED 800 - -#define CHAINFIST_REACH 64 - -#define HEATBEAM_DM_DMG 15 -#define HEATBEAM_SP_DMG 15 - -#define TRAP_TIMER 5.0 -#define TRAP_MINSPEED 300 -#define TRAP_MAXSPEED 700 - -static qboolean is_quad; -static qboolean is_quadfire; -static byte damage_multiplier; -static byte is_silenced; - -void weapon_grenade_fire(edict_t *ent, qboolean held); -void weapon_trap_fire(edict_t *ent, qboolean held); - -byte -P_DamageModifier(edict_t *ent) -{ - is_quad = 0; - damage_multiplier = 1; - - if (!ent) - { - return 0; - } - - if (ent->client->quad_framenum > level.framenum) - { - damage_multiplier *= 4; - is_quad = 1; - - /* if we're quad and DF_NO_STACK_DOUBLE is on, return now. */ - if (((int)(dmflags->value) & DF_NO_STACK_DOUBLE)) - { - return damage_multiplier; - } - } - - if (ent->client->double_framenum > level.framenum) - { - if ((deathmatch->value) || (damage_multiplier == 1)) - { - damage_multiplier *= 2; - is_quad = 1; - } - } - - if (ent->client->quadfire_framenum > level.framenum) - { - if ((deathmatch->value) || (damage_multiplier == 1)) - { - damage_multiplier *= 2; - is_quadfire = 1; - } - } - - return damage_multiplier; -} - -void -P_ProjectSource(edict_t *ent, vec3_t distance, - vec3_t forward, vec3_t right, vec3_t result) -{ - gclient_t *client = ent->client; - float *point = ent->s.origin; - vec3_t _distance; - - if (!client) - { - return; - } - - VectorCopy(distance, _distance); - - if (client->pers.hand == LEFT_HANDED) - { - _distance[1] *= -1; - } - else if (client->pers.hand == CENTER_HANDED) - { - _distance[1] = 0; - } - - G_ProjectSource(point, _distance, forward, right, result); - - // Berserker: fix - now the projectile hits exactly where the scope is pointing. - if (aimfix->value) - { - vec3_t start, end; - VectorSet(start, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] + ent->viewheight); - VectorMA(start, 8192, forward, end); - - trace_t tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); - if (tr.fraction < 1) - { - VectorSubtract(tr.endpos, result, forward); - VectorNormalize(forward); - } - } -} - -void -P_ProjectSource2(edict_t *ent, vec3_t point, vec3_t distance, vec3_t forward, - vec3_t right, vec3_t up, vec3_t result) -{ - gclient_t *client = ent->client; - vec3_t _distance; - - if (!client) - { - return; - } - - VectorCopy(distance, _distance); - - if (client->pers.hand == LEFT_HANDED) - { - _distance[1] *= -1; - } - else if (client->pers.hand == CENTER_HANDED) - { - _distance[1] = 0; - } - - G_ProjectSource2(point, _distance, forward, right, up, result); - - // Berserker: fix - now the projectile hits exactly where the scope is pointing. - if (aimfix->value) - { - vec3_t start, end; - VectorSet(start, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] + ent->viewheight); - VectorMA(start, 8192, forward, end); - - trace_t tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); - if (tr.fraction < 1) - { - VectorSubtract(tr.endpos, result, forward); - VectorNormalize(forward); - } - } -} - -/* - * Each player can have two noise objects associated with it: - * a personal noise (jumping, pain, weapon firing), and a weapon - * target noise (bullet wall impacts) - * - * Monsters that don't directly see the player can move - * to a noise in hopes of seeing the player from there. - */ -static edict_t * -PlayerNoise_Spawn(edict_t *who, int type) -{ - edict_t *noise; - - if (!who) - { - return NULL; - } - - noise = G_SpawnOptional(); - if (!noise) - { - return NULL; - } - - noise->classname = "player_noise"; - noise->spawnflags = type; - VectorSet (noise->mins, -8, -8, -8); - VectorSet (noise->maxs, 8, 8, 8); - noise->owner = who; - noise->svflags = SVF_NOCLIENT; - - return noise; -} - -static void -PlayerNoise_Verify(edict_t *who) -{ - edict_t *e; - edict_t *n1; - edict_t *n2; - - if (!who) - { - return; - } - - n1 = who->mynoise; - n2 = who->mynoise2; - - if (n1 && !n1->inuse) - { - n1 = NULL; - } - - if (n2 && !n2->inuse) - { - n2 = NULL; - } - - if (n1 && n2) - { - return; - } - - for (e = g_edicts + 1 + game.maxclients; e < &g_edicts[globals.num_edicts]; e++) - { - if (!e->inuse || strcmp(e->classname, "player_noise") != 0) - { - continue; - } - - if (e->owner && e->owner != who) - { - continue; - } - - e->owner = who; - - if (!n2 && (e->spawnflags == PLAYER_NOISE_IMPACT || n1)) - { - n2 = e; - } - else - { - n1 = e; - } - - if (n1 && n2) - { - break; - } - } - - if (!n1) - { - n1 = PlayerNoise_Spawn(who, PLAYER_NOISE_SELF); - } - - if (!n2) - { - n2 = PlayerNoise_Spawn(who, PLAYER_NOISE_IMPACT); - } - - who->mynoise = n1; - who->mynoise2 = n2; -} - -void -PlayerNoise(edict_t *who, vec3_t where, int type) -{ - edict_t *noise; - - if (!who || !who->client) - { - return; - } - - if (type == PNOISE_WEAPON) - { - if (who->client->silencer_shots) - { - who->client->silencer_shots--; - return; - } - } - - if (deathmatch->value) - { - return; - } - - if (who->flags & FL_NOTARGET) - { - return; - } - - if (!who->mynoise) - { - noise = G_Spawn(); - noise->classname = "player_noise"; - VectorSet(noise->mins, -8, -8, -8); - VectorSet(noise->maxs, 8, 8, 8); - noise->owner = who; - noise->svflags = SVF_NOCLIENT; - who->mynoise = noise; - - noise = G_Spawn(); - noise->classname = "player_noise"; - VectorSet(noise->mins, -8, -8, -8); - VectorSet(noise->maxs, 8, 8, 8); - noise->owner = who; - noise->svflags = SVF_NOCLIENT; - who->mynoise2 = noise; - } - - if ((type == PNOISE_SELF) || (type == PNOISE_WEAPON)) - { - noise = who->mynoise; - level.sound_entity = noise; - level.sound_entity_framenum = level.framenum; - } - else - { - noise = who->mynoise2; - level.sound2_entity = noise; - level.sound2_entity_framenum = level.framenum; - } - - VectorCopy(where, noise->s.origin); - VectorSubtract(where, noise->maxs, noise->absmin); - VectorAdd(where, noise->maxs, noise->absmax); - noise->last_sound_time = level.time; - gi.linkentity(noise); -} - -qboolean -Pickup_Weapon(edict_t *ent, edict_t *other) -{ - int index; - gitem_t *ammo; - - if (!ent || !other) - { - return false; - } - - index = ITEM_INDEX(ent->item); - - if ((((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value) && - other->client->pers.inventory[index]) - { - if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) - { - return false; /* leave the weapon for others to pickup */ - } - } - - other->client->pers.inventory[index]++; - - if (!(ent->spawnflags & DROPPED_ITEM)) - { - /* give them some ammo with it */ - ammo = FindItem(ent->item->ammo); - - if ((int)dmflags->value & DF_INFINITE_AMMO) - { - Add_Ammo(other, ammo, 1000); - } - else - { - Add_Ammo(other, ammo, ammo->quantity); - } - - if (!(ent->spawnflags & DROPPED_PLAYER_ITEM)) - { - if (deathmatch->value) - { - if ((int)(dmflags->value) & DF_WEAPONS_STAY) - { - ent->flags |= FL_RESPAWN; - } - else - { - SetRespawn(ent, 30); - } - } - - if (coop->value) - { - ent->flags |= FL_RESPAWN; - ent->flags |= FL_COOP_TAKEN; - } - } - } - - if ((other->client->pers.weapon != ent->item) && - (other->client->pers.inventory[index] == 1) && - (!deathmatch->value || - (other->client->pers.weapon == FindItem("blaster")))) - { - other->client->newweapon = ent->item; - } - - return true; -} - -/* - * The old weapon has been dropped all the - * way, so make the new one current - */ -void -ChangeWeapon(edict_t *ent) -{ - int i; - - if (!ent) - { - return; - } - - if (ent->client->grenade_time) - { - ent->client->grenade_time = level.time; - ent->client->weapon_sound = 0; - weapon_grenade_fire(ent, false); - ent->client->grenade_time = 0; - } - - ent->client->pers.lastweapon = ent->client->pers.weapon; - ent->client->pers.weapon = ent->client->newweapon; - ent->client->newweapon = NULL; - ent->client->machinegun_shots = 0; - - /* set visible model */ - if (ent->s.modelindex == 255) - { - if (ent->client->pers.weapon) - { - i = ((ent->client->pers.weapon->weapmodel & 0xff) << 8); - } - else - { - i = 0; - } - - ent->s.skinnum = (ent - g_edicts - 1) | i; - } - - if (ent->client->pers.weapon && ent->client->pers.weapon->ammo) - { - ent->client->ammo_index = - ITEM_INDEX(FindItem(ent->client->pers.weapon->ammo)); - } - else - { - ent->client->ammo_index = 0; - } - - if (!ent->client->pers.weapon) - { - /* dead */ - ent->client->ps.gunindex = 0; - return; - } - - ent->client->weaponstate = WEAPON_ACTIVATING; - ent->client->ps.gunframe = 0; - ent->client->ps.gunindex = gi.modelindex( - ent->client->pers.weapon->view_model); - - ent->client->anim_priority = ANIM_PAIN; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain1; - ent->client->anim_end = FRAME_crpain4; - } - else - { - ent->s.frame = FRAME_pain301; - ent->client->anim_end = FRAME_pain304; - } -} - -void -NoAmmoWeaponChange(edict_t *ent) -{ - if (!ent) - { - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))] && - ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))]) - { - ent->client->newweapon = FindItem("railgun"); - return; - } - - if ((ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] >= 2) && - ent->client->pers.inventory[ITEM_INDEX(FindItem("Plasma Beam"))]) - { - ent->client->newweapon = FindItem("Plasma Beam"); - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("flechettes"))] && - ent->client->pers.inventory[ITEM_INDEX(FindItem("etf rifle"))]) - { - ent->client->newweapon = FindItem("etf rifle"); - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] > 1 && - ent->client->pers.inventory[ITEM_INDEX(FindItem("ionripper"))]) - { - ent->client->newweapon = FindItem("ionripper"); - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] && - ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))]) - { - ent->client->newweapon = FindItem("hyperblaster"); - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))] && - ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))]) - { - ent->client->newweapon = FindItem("chaingun"); - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))] && - ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))]) - { - ent->client->newweapon = FindItem("machinegun"); - return; - } - - if ((ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] > 1) && - ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))]) - { - ent->client->newweapon = FindItem("super shotgun"); - return; - } - - if (ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] && - ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))]) - { - ent->client->newweapon = FindItem("shotgun"); - return; - } - - ent->client->newweapon = FindItem("blaster"); -} - -/* - * Called by ClientBeginServerFrame and ClientThink - */ -void -Think_Weapon(edict_t *ent) -{ - if (!ent) - { - return; - } - - /* if just died, put the weapon away */ - if (ent->health < 1) - { - ent->client->newweapon = NULL; - ChangeWeapon(ent); - } - - /* call active weapon think routine */ - if (ent->client->pers.weapon && ent->client->pers.weapon->weaponthink) - { - is_quad = (ent->client->quad_framenum > level.framenum); - - if (ent->client->silencer_shots) - { - is_silenced = MZ_SILENCED; - } - else - { - is_silenced = 0; - } - - ent->client->pers.weapon->weaponthink(ent); - } -} - -/* - * Make the weapon ready if there is ammo - */ -void -Use_Weapon(edict_t *ent, gitem_t *item) -{ - int ammo_index; - gitem_t *ammo_item; - - if (!ent || !item) - { - return; - } - - /* see if we're already using it */ - if (item == ent->client->pers.weapon) - { - return; - } - - if (item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO)) - { - ammo_item = FindItem(item->ammo); - ammo_index = ITEM_INDEX(ammo_item); - - if (!ent->client->pers.inventory[ammo_index]) - { - gi.cprintf(ent, PRINT_HIGH, "No %s for %s.\n", - ammo_item->pickup_name, item->pickup_name); - return; - } - - if (ent->client->pers.inventory[ammo_index] < item->quantity) - { - gi.cprintf(ent, PRINT_HIGH, "Not enough %s for %s.\n", - ammo_item->pickup_name, item->pickup_name); - return; - } - } - - /* change to this weapon when down */ - ent->client->newweapon = item; -} - -void -Use_Weapon2(edict_t *ent, gitem_t *item) -{ - int ammo_index; - gitem_t *ammo_item; - gitem_t *nextitem; - int index; - - if (!ent || !item) - { - return; - } - - if (strcmp(item->pickup_name, "HyperBlaster") == 0) - { - if (item == ent->client->pers.weapon) - { - item = FindItem("Ionripper"); - index = ITEM_INDEX(item); - - if (!ent->client->pers.inventory[index]) - { - item = FindItem("HyperBlaster"); - } - } - } - - else if (strcmp(item->pickup_name, "Railgun") == 0) - { - ammo_item = FindItem(item->ammo); - ammo_index = ITEM_INDEX(ammo_item); - - if (!ent->client->pers.inventory[ammo_index]) - { - nextitem = FindItem("Phalanx"); - ammo_item = FindItem(nextitem->ammo); - ammo_index = ITEM_INDEX(ammo_item); - - if (ent->client->pers.inventory[ammo_index]) - { - item = FindItem("Phalanx"); - index = ITEM_INDEX(item); - - if (!ent->client->pers.inventory[index]) - { - item = FindItem("Railgun"); - } - } - } - else if (item == ent->client->pers.weapon) - { - item = FindItem("Phalanx"); - index = ITEM_INDEX(item); - - if (!ent->client->pers.inventory[index]) - { - item = FindItem("Railgun"); - } - } - } - - /* see if we're already using it */ - if (item == ent->client->pers.weapon) - { - return; - } - - if (item->ammo) - { - ammo_item = FindItem(item->ammo); - ammo_index = ITEM_INDEX(ammo_item); - - if (!ent->client->pers.inventory[ammo_index] && !g_select_empty->value) - { - gi.cprintf(ent, PRINT_HIGH, "No %s for %s.\n", - ammo_item->pickup_name, item->pickup_name); - return; - } - } - - /* change to this weapon when down */ - ent->client->newweapon = item; -} - -void -Drop_Weapon(edict_t *ent, gitem_t *item) -{ - int index; - - if (!ent || !item) - { - return; - } - - if ((int)(dmflags->value) & DF_WEAPONS_STAY) - { - return; - } - - index = ITEM_INDEX(item); - - /* see if we're already using it */ - if (((item == ent->client->pers.weapon) || - (item == ent->client->newweapon)) && - (ent->client->pers.inventory[index] == 1)) - { - gi.cprintf(ent, PRINT_HIGH, "Can't drop current weapon\n"); - return; - } - - Drop_Item(ent, item); - ent->client->pers.inventory[index]--; -} - -/* - * A generic function to handle the basics of weapon thinking - */ -#define FRAME_FIRE_FIRST (FRAME_ACTIVATE_LAST + 1) -#define FRAME_IDLE_FIRST (FRAME_FIRE_LAST + 1) -#define FRAME_DEACTIVATE_FIRST (FRAME_IDLE_LAST + 1) - -static void -Weapon_Generic2(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)) -{ - int n; - - if (ent->deadflag || (ent->s.modelindex != 255)) /* VWep animations screw up corpses */ - { - return; - } - - if (ent->client->weaponstate == WEAPON_DROPPING) - { - if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST) - { - ChangeWeapon(ent); - return; - } - else if ((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4) - { - ent->client->anim_priority = ANIM_REVERSE; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain4 + 1; - ent->client->anim_end = FRAME_crpain1; - } - else - { - ent->s.frame = FRAME_pain304 + 1; - ent->client->anim_end = FRAME_pain301; - } - } - - ent->client->ps.gunframe++; - return; - } - - if (ent->client->weaponstate == WEAPON_ACTIVATING) - { - if ((ent->client->ps.gunframe == FRAME_ACTIVATE_LAST) || - instantweap->value) - { - ent->client->weaponstate = WEAPON_READY; - ent->client->ps.gunframe = FRAME_IDLE_FIRST; - return; - } - - ent->client->ps.gunframe++; - return; - } - - if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING)) - { - ent->client->weaponstate = WEAPON_DROPPING; - - if (instantweap->value) - { - ChangeWeapon(ent); - return; - } - else - { - ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST; - } - - if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4) - { - ent->client->anim_priority = ANIM_REVERSE; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain4 + 1; - ent->client->anim_end = FRAME_crpain1; - } - else - { - ent->s.frame = FRAME_pain304 + 1; - ent->client->anim_end = FRAME_pain301; - } - } - - return; - } - - if (ent->client->weaponstate == WEAPON_READY) - { - if (((ent->client->latched_buttons | - ent->client->buttons) & BUTTON_ATTACK)) - { - ent->client->latched_buttons &= ~BUTTON_ATTACK; - - if ((!ent->client->ammo_index) || - (ent->client->pers.inventory[ent->client->ammo_index] >= - ent->client->pers.weapon->quantity)) - { - ent->client->ps.gunframe = FRAME_FIRE_FIRST; - ent->client->weaponstate = WEAPON_FIRING; - - /* start the animation */ - ent->client->anim_priority = ANIM_ATTACK; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crattak1 - 1; - ent->client->anim_end = FRAME_crattak9; - } - else - { - ent->s.frame = FRAME_attack1 - 1; - ent->client->anim_end = FRAME_attack8; - } - } - else - { - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, - gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - - NoAmmoWeaponChange(ent); - } - } - else - { - if (ent->client->ps.gunframe == FRAME_IDLE_LAST) - { - ent->client->ps.gunframe = FRAME_IDLE_FIRST; - return; - } - - if (pause_frames) - { - for (n = 0; pause_frames[n]; n++) - { - if (ent->client->ps.gunframe == pause_frames[n]) - { - if (rand() & 15) - { - return; - } - } - } - } - - ent->client->ps.gunframe++; - return; - } - } - - if (ent->client->weaponstate == WEAPON_FIRING) - { - for (n = 0; fire_frames[n]; n++) - { - if (ent->client->ps.gunframe == fire_frames[n]) - { - if (!CTFApplyStrengthSound(ent)) - { - if (ent->client->quad_framenum > level.framenum) - { - gi.sound(ent, CHAN_ITEM, - gi.soundindex( - "items/damage3.wav"), 1, ATTN_NORM, 0); - } - } - - CTFApplyHasteSound(ent); - - fire(ent); - break; - } - } - - if (!fire_frames[n]) - { - ent->client->ps.gunframe++; - } - - if (ent->client->ps.gunframe == FRAME_IDLE_FIRST + 1) - { - ent->client->weaponstate = WEAPON_READY; - } - } -} - -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)) -{ - int oldstate = ent->client->weaponstate; - - Weapon_Generic2(ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, - FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, - fire_frames, fire); - - /* run the weapon frame again if hasted */ - if ((Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0) && - (ent->client->weaponstate == WEAPON_FIRING)) - { - return; - } - - if ((CTFApplyHaste(ent) || - ((Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0) && - (ent->client->weaponstate != WEAPON_FIRING))) && - (oldstate == ent->client->weaponstate)) - { - Weapon_Generic2(ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, - FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, - fire_frames, fire); - } -} - -/* - * ====================================================================== - * - * GRENADE - * - * ====================================================================== - */ - -#define GRENADE_TIMER 3.0 -#define GRENADE_MINSPEED 400 -#define GRENADE_MAXSPEED 800 - -void -weapon_grenade_fire(edict_t *ent, qboolean held) -{ - vec3_t offset; - vec3_t forward, right; - vec3_t start; - int damage = 125; - float timer; - int speed; - float radius; - - radius = damage + 40; - - if (is_quad) - { - damage *= 4; - } - - VectorSet(offset, 8, 8, ent->viewheight - 8); - AngleVectors(ent->client->v_angle, forward, right, NULL); - P_ProjectSource(ent, offset, forward, right, start); - - timer = ent->client->grenade_time - level.time; - speed = GRENADE_MINSPEED + (GRENADE_TIMER - - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER); - fire_grenade2(ent, start, forward, damage, speed, timer, radius, held); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } - - ent->client->grenade_time = level.time + 1.0; - - if (ent->deadflag || (ent->s.modelindex != 255)) /* VWep animations screw up corpses */ - { - return; - } - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->client->anim_priority = ANIM_ATTACK; - ent->s.frame = FRAME_crattak1 - 1; - ent->client->anim_end = FRAME_crattak3; - } - else - { - ent->client->anim_priority = ANIM_REVERSE; - ent->s.frame = FRAME_wave08; - ent->client->anim_end = FRAME_wave01; - } -} - -void -Weapon_Grenade(edict_t *ent) -{ - if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY)) - { - ChangeWeapon(ent); - return; - } - - if (ent->client->weaponstate == WEAPON_ACTIVATING) - { - ent->client->weaponstate = WEAPON_READY; - ent->client->ps.gunframe = 16; - return; - } - - if (ent->client->weaponstate == WEAPON_READY) - { - if (((ent->client->latched_buttons | - ent->client->buttons) & BUTTON_ATTACK)) - { - ent->client->latched_buttons &= ~BUTTON_ATTACK; - - if (ent->client->pers.inventory[ent->client->ammo_index]) - { - ent->client->ps.gunframe = 1; - ent->client->weaponstate = WEAPON_FIRING; - ent->client->grenade_time = 0; - } - else - { - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, - gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - - NoAmmoWeaponChange(ent); - } - - return; - } - - if ((ent->client->ps.gunframe == 29) || - (ent->client->ps.gunframe == 34) || - (ent->client->ps.gunframe == 39) || - (ent->client->ps.gunframe == 48)) - { - if (rand() & 15) - { - return; - } - } - - if (++ent->client->ps.gunframe > 48) - { - ent->client->ps.gunframe = 16; - } - - return; - } - - if (ent->client->weaponstate == WEAPON_FIRING) - { - if (ent->client->ps.gunframe == 5) - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex( - "weapons/hgrena1b.wav"), 1, ATTN_NORM, 0); - } - - if (ent->client->ps.gunframe == 11) - { - if (!ent->client->grenade_time) - { - ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2; - ent->client->weapon_sound = gi.soundindex( - "weapons/hgrenc1b.wav"); - } - - /* they waited too long, detonate it in their hand */ - if (!ent->client->grenade_blew_up && - (level.time >= ent->client->grenade_time)) - { - ent->client->weapon_sound = 0; - weapon_grenade_fire(ent, true); - ent->client->grenade_blew_up = true; - } - - if (ent->client->buttons & BUTTON_ATTACK) - { - return; - } - - if (ent->client->grenade_blew_up) - { - if (level.time >= ent->client->grenade_time) - { - ent->client->ps.gunframe = 15; - ent->client->grenade_blew_up = false; - } - else - { - return; - } - } - } - - if (ent->client->ps.gunframe == 12) - { - ent->client->weapon_sound = 0; - weapon_grenade_fire(ent, false); - } - - if ((ent->client->ps.gunframe == 15) && - (level.time < ent->client->grenade_time)) - { - return; - } - - ent->client->ps.gunframe++; - - if (ent->client->ps.gunframe == 16) - { - ent->client->grenade_time = 0; - ent->client->weaponstate = WEAPON_READY; - } - } -} - -/* - * ====================================================================== - * - * GRENADE LAUNCHER - * - * ====================================================================== - */ - -void -weapon_grenadelauncher_fire(edict_t *ent) -{ - vec3_t offset; - vec3_t forward, right; - vec3_t start; - int damage = 120; - float radius; - - radius = damage + 40; - - if (is_quad) - { - damage *= 4; - } - - VectorSet(offset, 8, 8, ent->viewheight - 8); - AngleVectors(ent->client->v_angle, forward, right, NULL); - P_ProjectSource(ent, offset, forward, right, start); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - - fire_grenade(ent, start, forward, damage, 600, 2.5, radius); - - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_GRENADE | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - ent->client->ps.gunframe++; - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } -} - -void -Weapon_GrenadeLauncher(edict_t *ent) -{ - static int pause_frames[] = {34, 51, 59, 0}; - static int fire_frames[] = {6, 0}; - - Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, - fire_frames, weapon_grenadelauncher_fire); -} - -/* - * ====================================================================== - * - * ROCKET - * - * ====================================================================== - */ - -void -Weapon_RocketLauncher_Fire(edict_t *ent) -{ - vec3_t offset, start; - vec3_t forward, right; - int damage; - float damage_radius; - int radius_damage; - - damage = 100 + (int)(random() * 20.0); - radius_damage = 120; - damage_radius = 120; - - if (is_quad) - { - damage *= 4; - radius_damage *= 4; - } - - AngleVectors(ent->client->v_angle, forward, right, NULL); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - - VectorSet(offset, 8, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - fire_rocket(ent, start, forward, damage, 650, damage_radius, radius_damage); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_ROCKET | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - ent->client->ps.gunframe++; - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } -} - -void -Weapon_RocketLauncher(edict_t *ent) -{ - static int pause_frames[] = {25, 33, 42, 50, 0}; - static int fire_frames[] = {5, 0}; - - Weapon_Generic(ent, 4, 12, 50, 54, pause_frames, - fire_frames, Weapon_RocketLauncher_Fire); -} - -/* - * ====================================================================== - * - * BLASTER / HYPERBLASTER - * - * ====================================================================== - */ - -void -Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage, - qboolean hyper, int effect) -{ - vec3_t forward, right; - vec3_t start; - vec3_t offset; - - if (is_quad) - { - damage *= 4; - } - - AngleVectors(ent->client->v_angle, forward, right, NULL); - VectorSet(offset, 24, 8, ent->viewheight - 8); - VectorAdd(offset, g_offset, offset); - P_ProjectSource(ent, offset, forward, right, start); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - - fire_blaster(ent, start, forward, damage, 1000, effect, hyper); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - - if (hyper) - { - gi.WriteByte(MZ_HYPERBLASTER | is_silenced); - } - else - { - gi.WriteByte(MZ_BLASTER | is_silenced); - } - - gi.multicast(ent->s.origin, MULTICAST_PVS); - - PlayerNoise(ent, start, PNOISE_WEAPON); -} - -void -Weapon_Blaster_Fire(edict_t *ent) -{ - int damage; - - if (deathmatch->value) - { - damage = 15; - } - else - { - damage = 10; - } - - Blaster_Fire(ent, vec3_origin, damage, false, EF_BLASTER); - ent->client->ps.gunframe++; -} - -void -Weapon_Blaster(edict_t *ent) -{ - static int pause_frames[] = {19, 32, 0}; - static int fire_frames[] = {5, 0}; - - Weapon_Generic(ent, 4, 8, 52, 55, pause_frames, - fire_frames, Weapon_Blaster_Fire); -} - -void -Weapon_HyperBlaster_Fire(edict_t *ent) -{ - float rotation; - vec3_t offset; - int effect; - int damage; - - ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav"); - - if (!(ent->client->buttons & BUTTON_ATTACK)) - { - ent->client->ps.gunframe++; - } - else - { - if (!ent->client->pers.inventory[ent->client->ammo_index]) - { - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex( - "weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - - NoAmmoWeaponChange(ent); - } - else - { - rotation = (ent->client->ps.gunframe - 5) * 2 * M_PI / 6; - offset[0] = -4 * sin(rotation); - offset[1] = 0; - offset[2] = 4 * cos(rotation); - - if ((ent->client->ps.gunframe == 6) || - (ent->client->ps.gunframe == 9)) - { - effect = EF_HYPERBLASTER; - } - else - { - effect = 0; - } - - if (deathmatch->value) - { - damage = 15; - } - else - { - damage = 20; - } - - Blaster_Fire(ent, offset, damage, true, effect); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } - - ent->client->anim_priority = ANIM_ATTACK; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crattak1 - 1; - ent->client->anim_end = FRAME_crattak9; - } - else - { - ent->s.frame = FRAME_attack1 - 1; - ent->client->anim_end = FRAME_attack8; - } - } - - ent->client->ps.gunframe++; - - if ((ent->client->ps.gunframe == 12) && - ent->client->pers.inventory[ent->client->ammo_index]) - { - ent->client->ps.gunframe = 6; - } - } - - if (ent->client->ps.gunframe == 12) - { - gi.sound(ent, CHAN_AUTO, gi.soundindex( - "weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0); - ent->client->weapon_sound = 0; - } -} - -void -Weapon_HyperBlaster(edict_t *ent) -{ - static int pause_frames[] = {0}; - static int fire_frames[] = {6, 7, 8, 9, 10, 11, 0}; - - Weapon_Generic(ent, 5, 20, 49, 53, pause_frames, - fire_frames, Weapon_HyperBlaster_Fire); -} - -/* - * ====================================================================== - * - * MACHINEGUN / CHAINGUN - * - * ====================================================================== - */ - -void -Machinegun_Fire(edict_t *ent) -{ - int i; - vec3_t start; - vec3_t forward, right; - vec3_t angles; - int damage = 8; - int kick = 2; - vec3_t offset; - - if (!(ent->client->buttons & BUTTON_ATTACK)) - { - ent->client->machinegun_shots = 0; - ent->client->ps.gunframe++; - return; - } - - if (ent->client->ps.gunframe == 5) - { - ent->client->ps.gunframe = 4; - } - else - { - ent->client->ps.gunframe = 5; - } - - if (ent->client->pers.inventory[ent->client->ammo_index] < 1) - { - ent->client->ps.gunframe = 6; - - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex( - "weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - - NoAmmoWeaponChange(ent); - return; - } - - if (is_quad) - { - damage *= 4; - kick *= 4; - } - - for (i = 1; i < 3; i++) - { - ent->client->kick_origin[i] = crandom() * 0.35; - ent->client->kick_angles[i] = crandom() * 0.7; - } - - ent->client->kick_origin[0] = crandom() * 0.35; - ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5; - - /* raise the gun as it is firing */ - if (!deathmatch->value) - { - ent->client->machinegun_shots++; - - if (ent->client->machinegun_shots > 9) - { - ent->client->machinegun_shots = 9; - } - } - - /* get start / end positions */ - VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles); - AngleVectors(angles, forward, right, NULL); - VectorSet(offset, 0, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - fire_bullet(ent, start, forward, damage, kick, - DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, - MOD_MACHINEGUN); - - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_MACHINEGUN | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } - - ent->client->anim_priority = ANIM_ATTACK; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crattak1 - (int)(random() + 0.25); - ent->client->anim_end = FRAME_crattak9; - } - else - { - ent->s.frame = FRAME_attack1 - (int)(random() + 0.25); - ent->client->anim_end = FRAME_attack8; - } -} - -void -Weapon_Machinegun(edict_t *ent) -{ - static int pause_frames[] = {23, 45, 0}; - static int fire_frames[] = {4, 5, 0}; - - Weapon_Generic(ent, 3, 5, 45, 49, pause_frames, - fire_frames, Machinegun_Fire); -} - -void -Chaingun_Fire(edict_t *ent) -{ - int i; - int shots; - vec3_t start; - vec3_t forward, right, up; - float r, u; - vec3_t offset; - int damage; - int kick = 2; - - if (deathmatch->value) - { - damage = 6; - } - else - { - damage = 8; - } - - if (ent->client->ps.gunframe == 5) - { - gi.sound(ent, CHAN_AUTO, gi.soundindex( - "weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0); - } - - if ((ent->client->ps.gunframe == 14) && - !(ent->client->buttons & BUTTON_ATTACK)) - { - ent->client->ps.gunframe = 32; - ent->client->weapon_sound = 0; - return; - } - else if ((ent->client->ps.gunframe == 21) && - (ent->client->buttons & BUTTON_ATTACK) && - ent->client->pers.inventory[ent->client->ammo_index]) - { - ent->client->ps.gunframe = 15; - } - else - { - ent->client->ps.gunframe++; - } - - if (ent->client->ps.gunframe == 22) - { - ent->client->weapon_sound = 0; - gi.sound(ent, CHAN_AUTO, gi.soundindex( - "weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0); - } - else - { - ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav"); - } - - ent->client->anim_priority = ANIM_ATTACK; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1); - ent->client->anim_end = FRAME_crattak9; - } - else - { - ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1); - ent->client->anim_end = FRAME_attack8; - } - - if (ent->client->ps.gunframe <= 9) - { - shots = 1; - } - else if (ent->client->ps.gunframe <= 14) - { - if (ent->client->buttons & BUTTON_ATTACK) - { - shots = 2; - } - else - { - shots = 1; - } - } - else - { - shots = 3; - } - - if (ent->client->pers.inventory[ent->client->ammo_index] < shots) - { - shots = ent->client->pers.inventory[ent->client->ammo_index]; - } - - if (!shots) - { - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex( - "weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - - NoAmmoWeaponChange(ent); - return; - } - - if (is_quad) - { - damage *= 4; - kick *= 4; - } - - for (i = 0; i < 3; i++) - { - ent->client->kick_origin[i] = crandom() * 0.35; - ent->client->kick_angles[i] = crandom() * 0.7; - } - - for (i = 0; i < shots; i++) - { - /* get start / end positions */ - AngleVectors(ent->client->v_angle, forward, right, up); - r = 7 + crandom() * 4; - u = crandom() * 4; - VectorSet(offset, 0, r, u + ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - - fire_bullet(ent, start, forward, damage, kick, - DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, - MOD_CHAINGUN); - } - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte((MZ_CHAINGUN1 + shots - 1) | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index] -= shots; - } -} - -void -Weapon_Chaingun(edict_t *ent) -{ - static int pause_frames[] = {38, 43, 51, 61, 0}; - static int fire_frames[] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0}; - - Weapon_Generic(ent, 4, 31, 61, 64, pause_frames, fire_frames, Chaingun_Fire); -} - -/* - * ====================================================================== - * - * SHOTGUN / SUPERSHOTGUN - * - * ====================================================================== - */ - -void -weapon_shotgun_fire(edict_t *ent) -{ - vec3_t start; - vec3_t forward, right; - vec3_t offset; - int damage = 4; - int kick = 8; - - if (ent->client->ps.gunframe == 9) - { - ent->client->ps.gunframe++; - return; - } - - AngleVectors(ent->client->v_angle, forward, right, NULL); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -2; - - VectorSet(offset, 0, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - - if (is_quad) - { - damage *= 4; - kick *= 4; - } - - if (deathmatch->value) - { - fire_shotgun(ent, start, forward, damage, kick, 500, 500, - DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN); - } - else - { - fire_shotgun(ent, start, forward, damage, kick, 500, - 500, DEFAULT_SHOTGUN_COUNT, MOD_SHOTGUN); - } - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_SHOTGUN | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - ent->client->ps.gunframe++; - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } -} - -void -Weapon_Shotgun(edict_t *ent) -{ - static int pause_frames[] = {22, 28, 34, 0}; - static int fire_frames[] = {8, 9, 0}; - - Weapon_Generic(ent, 7, 18, 36, 39, pause_frames, fire_frames, - weapon_shotgun_fire); -} - -void -weapon_supershotgun_fire(edict_t *ent) -{ - vec3_t start; - vec3_t forward, right; - vec3_t offset; - vec3_t v; - int damage = 6; - int kick = 12; - - AngleVectors(ent->client->v_angle, forward, right, NULL); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -2; - - VectorSet(offset, 0, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - - if (is_quad) - { - damage *= 4; - kick *= 4; - } - - v[PITCH] = ent->client->v_angle[PITCH]; - v[YAW] = ent->client->v_angle[YAW] - 5; - v[ROLL] = ent->client->v_angle[ROLL]; - AngleVectors(v, forward, NULL, NULL); - - if (aimfix->value) - { - AngleVectors(v, forward, right, NULL); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -2; - - VectorSet(offset, 0, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - } - - fire_shotgun(ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, - DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN); - - v[YAW] = ent->client->v_angle[YAW] + 5; - AngleVectors(v, forward, NULL, NULL); - - if (aimfix->value) - { - AngleVectors(v, forward, right, NULL); - - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -2; - - VectorSet(offset, 0, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - } - - fire_shotgun(ent, start, forward, damage, kick, - DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, - DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_SSHOTGUN | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - ent->client->ps.gunframe++; - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index] -= 2; - } -} - -void -Weapon_SuperShotgun(edict_t *ent) -{ - static int pause_frames[] = {29, 42, 57, 0}; - static int fire_frames[] = {7, 0}; - - Weapon_Generic(ent, 6, 17, 57, 61, pause_frames, - fire_frames, weapon_supershotgun_fire); -} - -/* - * ====================================================================== - * - * RAILGUN - * - * ====================================================================== - */ - -void -weapon_railgun_fire(edict_t *ent) -{ - vec3_t start; - vec3_t forward, right; - vec3_t offset; - int damage; - int kick; - - if (deathmatch->value) - { - /* normal damage is too extreme in dm */ - damage = 100; - kick = 200; - } - else - { - damage = 150; - kick = 250; - } - - if (is_quad) - { - damage *= 4; - kick *= 4; - } - - AngleVectors(ent->client->v_angle, forward, right, NULL); - - VectorScale(forward, -3, ent->client->kick_origin); - ent->client->kick_angles[0] = -3; - - VectorSet(offset, 0, 7, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - fire_rail(ent, start, forward, damage, kick); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_RAILGUN | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - ent->client->ps.gunframe++; - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index]--; - } -} - -void -Weapon_Railgun(edict_t *ent) -{ - static int pause_frames[] = {56, 0}; - static int fire_frames[] = {4, 0}; - - Weapon_Generic(ent, 3, 18, 56, 61, pause_frames, - fire_frames, weapon_railgun_fire); -} - -/* - * ====================================================================== - * - * BFG10K - * - * ====================================================================== - */ - -void -weapon_bfg_fire(edict_t *ent) -{ - vec3_t offset, start; - vec3_t forward, right; - int damage; - float damage_radius = 1000; - - if (deathmatch->value) - { - damage = 200; - } - else - { - damage = 500; - } - - if (ent->client->ps.gunframe == 9) - { - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_BFG | is_silenced); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - ent->client->ps.gunframe++; - - PlayerNoise(ent, start, PNOISE_WEAPON); - return; - } - - /* cells can go down during windup (from power armor hits), so - check again and abort firing if we don't have enough now */ - if (ent->client->pers.inventory[ent->client->ammo_index] < 50) - { - ent->client->ps.gunframe++; - return; - } - - if (is_quad) - { - damage *= 4; - } - - AngleVectors(ent->client->v_angle, forward, right, NULL); - - VectorScale(forward, -2, ent->client->kick_origin); - - /* make a big pitch kick with an inverse fall */ - ent->client->v_dmg_pitch = -40; - ent->client->v_dmg_roll = crandom() * 8; - ent->client->v_dmg_time = level.time + DAMAGE_TIME; - - VectorSet(offset, 8, 8, ent->viewheight - 8); - P_ProjectSource(ent, offset, forward, right, start); - fire_bfg(ent, start, forward, damage, 400, damage_radius); - - ent->client->ps.gunframe++; - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index] -= 50; - } -} - -void -Weapon_BFG(edict_t *ent) -{ - static int pause_frames[] = {39, 45, 50, 55, 0}; - static int fire_frames[] = {9, 17, 0}; - - Weapon_Generic(ent, 8, 32, 55, 58, pause_frames, - fire_frames, weapon_bfg_fire); -} - diff --git a/src/game/g_chase.c b/src/game/g_chase.c index 4b9a60b7..c6bbb235 100644 --- a/src/game/g_chase.c +++ b/src/game/g_chase.c @@ -143,6 +143,20 @@ UpdateChaseCam(edict_t *ent) ent->viewheight = 0; ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; gi.linkentity(ent); + + if ((!ent->client->showscores && !ent->client->menu && + !ent->client->showinventory && !ent->client->showhelp && + !(level.framenum & 31)) || ent->client->update_chase) + { + char s[1024]; + + ent->client->update_chase = false; + sprintf(s, "xv 0 yb -68 string2 \"Chasing %s\"", + targ->client->pers.netname); + gi.WriteByte(svc_layout); + gi.WriteString(s); + gi.unicast(ent, false); + } } void @@ -179,6 +193,11 @@ ChaseNext(edict_t *ent) continue; } + if (e->solid != SOLID_NOT) + { + break; + } + if (!e->client->resp.spectator) { break; @@ -224,11 +243,17 @@ ChasePrev(edict_t *ent) continue; } + if (e->solid != SOLID_NOT) + { + break; + } + if (!e->client->resp.spectator) { break; } } + while (e != ent->client->chase_target); ent->client->chase_target = e; diff --git a/src/game/g_combat.c b/src/game/g_combat.c index acd0617b..c348003d 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -147,6 +147,11 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, return; } + if (targ->health < -999) + { + targ->health = -999; + } + /* Reset AI flag for being ducked. This fixes a corner case were the monster is ressurected by a medic and get's stuck in the next frame for mmove_t not matching the AI state. */ @@ -660,6 +665,15 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) qboolean CheckTeamDamage(edict_t *targ, edict_t *attacker) { + if (ctf->value && targ->client && attacker->client) + { + if ((targ->client->resp.ctf_team == attacker->client->resp.ctf_team) && + (targ != attacker)) + { + return true; + } + } + return false; } diff --git a/src/game/g_func.c b/src/game/g_func.c index 2f38888c..c4627d69 100644 --- a/src/game/g_func.c +++ b/src/game/g_func.c @@ -786,6 +786,7 @@ plat_spawn_inside_trigger(edict_t *ent) tmin[0] = ent->mins[0] + 25; tmin[1] = ent->mins[1] + 25; + tmin[2] = ent->mins[2]; tmax[0] = ent->maxs[0] - 25; tmax[1] = ent->maxs[1] - 25; @@ -1701,7 +1702,11 @@ SP_func_rotating(edict_t *ent) } ent->use = rotating_use; - ent->blocked = rotating_blocked; + + if (ent->dmg) + { + ent->blocked = rotating_blocked; + } if (ent->spawnflags & 1) { diff --git a/src/game/g_items.c b/src/game/g_items.c index 9a1900bb..0ec90919 100644 --- a/src/game/g_items.c +++ b/src/game/g_items.c @@ -163,14 +163,25 @@ DoRespawn(edict_t *ent) master = ent->teammaster; - for (count = 0, ent = master; ent; ent = ent->chain, count++) + /* in ctf, when we are weapons stay, only the master + of a team of weapons is spawned */ + if (ctf->value && + ((int)dmflags->value & DF_WEAPONS_STAY) && + master->item && (master->item->flags & IT_WEAPON)) { + ent = master; } - - choice = count ? randk() % count : 0; - - for (count = 0, ent = master; count < choice; ent = ent->chain, count++) + else { + for (count = 0, ent = master; ent; ent = ent->chain, count++) + { + } + + choice = count ? randk() % count : 0; + + for (count = 0, ent = master; count < choice; ent = ent->chain, count++) + { + } } } @@ -246,6 +257,20 @@ Pickup_Powerup(edict_t *ent, edict_t *other) { SetRespawn(ent, ent->item->quantity); } + + if (((int)dmflags->value & DF_INSTANT_ITEMS) || + ((ent->item->use == Use_Quad) && + (ent->spawnflags & DROPPED_PLAYER_ITEM))) + { + if ((ent->item->use == Use_Quad) && + (ent->spawnflags & DROPPED_PLAYER_ITEM)) + { + quad_drop_timeout_hack = + (ent->nextthink - level.time) / FRAMETIME; + } + + ent->item->use(other, ent->item); + } } return true; @@ -1030,7 +1055,8 @@ Use_Invulnerability(edict_t *ent, gitem_t *item) ent->client->invincible_framenum = level.framenum + 300; } - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect.wav"), 1, ATTN_NORM, 0); + gi.sound(ent, CHAN_ITEM, gi.soundindex( + "items/protect.wav"), 1, ATTN_NORM, 0); } /* ====================================================================== */ @@ -1279,7 +1305,8 @@ MegaHealth_think(edict_t *self) return; } - if (self->owner->health > self->owner->max_health) + if ((self->owner->health > self->owner->max_health) + && !CTFHasRegeneration(self->owner)) { self->nextthink = level.time + 1; self->owner->health -= 1; @@ -1312,8 +1339,18 @@ Pickup_Health(edict_t *ent, edict_t *other) } } + if ((other->health >= 250) && (ent->count > 25)) + { + return false; + } + other->health += ent->count; + if ((other->health > 250) && (ent->count > 25)) + { + other->health = 250; + } + if (!(ent->style & HEALTH_IGNORE_MAX)) { if (other->health > other->max_health) @@ -1322,7 +1359,7 @@ Pickup_Health(edict_t *ent, edict_t *other) } } - if (ent->style & HEALTH_TIMED) + if ((ent->style & HEALTH_TIMED) && !CTFHasRegeneration(other)) { ent->think = MegaHealth_think; ent->nextthink = level.time + 5; @@ -1623,6 +1660,11 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_ return; /* not a grabbable item? */ } + if (CTFMatchSetup()) + { + return; /* can't pick stuff up right now */ + } + taken = ent->item->pickup(ent, other); if (taken) @@ -2251,6 +2293,14 @@ SpawnItem(edict_t *ent, gitem_t *item) item->drop = NULL; } + /* Don't spawn the flags unless enabled */ + if (!ctf->value && ((strcmp(ent->classname, "item_flag_team1") == 0) || + (strcmp(ent->classname, "item_flag_team2") == 0))) + { + G_FreeEdict(ent); + return; + } + ent->item = item; ent->nextthink = level.time + 2 * FRAMETIME; /* items start after other solids */ ent->think = droptofloor; @@ -2262,6 +2312,13 @@ SpawnItem(edict_t *ent, gitem_t *item) gi.modelindex(ent->model); } + /* flags are server animated and have special handling */ + if ((strcmp(ent->classname, "item_flag_team1") == 0) || + (strcmp(ent->classname, "item_flag_team2") == 0)) + { + ent->think = CTFFlagSetup; + } + if (ent->spawnflags & 1) { SetTriggeredSpawn(ent); @@ -2420,6 +2477,32 @@ static const gitem_t gameitemlist[] = { "misc/power2.wav misc/power1.wav" }, + /* + * weapon_grapple (.3 .3 1) (-16 -16 -16) (16 16 16) + * always owned, never in the world + */ + { + "weapon_grapple", + NULL, + Use_Weapon, + NULL, + CTFWeapon_Grapple, + "misc/w_pkup.wav", + NULL, 0, + "models/weapons/grapple/tris.md2", + "w_grapple", + "Grapple", + 0, + 0, + NULL, + IT_WEAPON, + WEAP_GRAPPLE, + NULL, + 0, + + "weapons/grapple/grfire.wav weapons/grapple/grpull.wav weapons/grapple/grhang.wav weapons/grapple/grreset.wav weapons/grapple/grhit.wav" + }, + /* * weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) * always owned, never in the world @@ -3903,6 +3986,142 @@ static const gitem_t gameitemlist[] = { "items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav" }, + /* + * QUAKED item_flag_team1 (1 0.2 0) (-16 -16 -24) (16 16 32) + */ + { + "item_flag_team1", + CTFPickup_Flag, + NULL, + CTFDrop_Flag, + NULL, + "ctf/flagtk.wav", + "players/male/flag1.md2", EF_FLAG1, + NULL, + "i_ctf1", + "Red Flag", + 2, + 0, + NULL, + 0, + 0, + NULL, + 0, + "ctf/flagcap.wav" + }, + + /* + * QUAKED item_flag_team2 (1 0.2 0) (-16 -16 -24) (16 16 32) + */ + { + "item_flag_team2", + CTFPickup_Flag, + NULL, + CTFDrop_Flag, + NULL, + "ctf/flagtk.wav", + "players/male/flag2.md2", EF_FLAG2, + NULL, + "i_ctf2", + "Blue Flag", + 2, + 0, + NULL, + 0, + 0, + NULL, + 0, + "ctf/flagcap.wav" + }, + + /* Resistance Tech */ + { + "item_tech1", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/resistance/tris.md2", EF_ROTATE, + NULL, + "tech1", + "Disruptor Shield", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech1.wav" + }, + + /* Strength Tech */ + { + "item_tech2", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/strength/tris.md2", EF_ROTATE, + NULL, + "tech2", + "Power Amplifier", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech2.wav ctf/tech2x.wav" + }, + + /* Haste Tech */ + { + "item_tech3", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/haste/tris.md2", EF_ROTATE, + NULL, + "tech3", + "Time Accel", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech3.wav" + }, + + /* Regeneration Tech */ + { + "item_tech4", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/regeneration/tris.md2", EF_ROTATE, + NULL, + "tech4", + "AutoDoc", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech4.wav" + }, + /* end of list marker */ {NULL} }; diff --git a/src/game/g_misc.c b/src/game/g_misc.c index ba6152c5..7d0be413 100644 --- a/src/game/g_misc.c +++ b/src/game/g_misc.c @@ -567,6 +567,30 @@ BecomeExplosion1(edict_t *self) return; } + /* flags are important */ + if (strcmp(self->classname, "item_flag_team1") == 0) + { + CTFResetFlag(CTF_TEAM1); /* this will free self! */ + gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n", + CTFTeamName(CTF_TEAM1)); + return; + } + + if (strcmp(self->classname, "item_flag_team2") == 0) + { + CTFResetFlag(CTF_TEAM2); /* this will free self! */ + gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n", + CTFTeamName(CTF_TEAM2)); + return; + } + + /* techs are important too */ + if (self->item && (self->item->flags & IT_TECH)) + { + CTFRespawnTech(self); /* this frees self! */ + return; + } + gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_EXPLOSION1); gi.WritePosition(self->s.origin); @@ -819,6 +843,14 @@ TH_viewthing(edict_t *ent) ent->s.frame = (ent->s.frame + 1) % 7; ent->nextthink = level.time + FRAMETIME; + + if (ent->spawnflags) + { + if (ent->s.frame == 0) + { + ent->spawnflags = (ent->spawnflags + 1) % 4 + 1; + } + } } void @@ -3029,6 +3061,8 @@ teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, return; } + CTFPlayerResetGrapple(other); + /* unlink to make sure it can't possibly interfere with KillBox */ gi.unlinkentity(other); diff --git a/src/game/player/weapon.c b/src/game/player/weapon.c index a198470b..c37b9f48 100644 --- a/src/game/player/weapon.c +++ b/src/game/player/weapon.c @@ -824,8 +824,8 @@ Change_Weap_Animation(edict_t *ent) * A generic function to handle * the basics of weapon thinking */ -void -Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, +static void +Weapon_Generic2(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)) { @@ -965,17 +965,22 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, { if (ent->client->ps.gunframe == fire_frames[n]) { - if (ent->client->quad_framenum > level.framenum) + if (!CTFApplyStrengthSound(ent)) { - gi.sound(ent, CHAN_ITEM, gi.soundindex( - "items/damage3.wav"), 1, ATTN_NORM, 0); - } - else if (ent->client->double_framenum > level.framenum) - { - gi.sound(ent, CHAN_ITEM, gi.soundindex( - "misc/ddamage3.wav"), 1, ATTN_NORM, 0); + if (ent->client->quad_framenum > level.framenum) + { + gi.sound(ent, CHAN_ITEM, gi.soundindex( + "items/damage3.wav"), 1, ATTN_NORM, 0); + } + else if (ent->client->double_framenum > level.framenum) + { + gi.sound(ent, CHAN_ITEM, gi.soundindex( + "misc/ddamage3.wav"), 1, ATTN_NORM, 0); + } } + CTFApplyHasteSound(ent); + fire(ent); break; } @@ -993,6 +998,35 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, } } +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)) +{ + int oldstate = ent->client->weaponstate; + + Weapon_Generic2(ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, + FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, + fire_frames, fire); + + /* run the weapon frame again if hasted */ + if ((Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0) && + (ent->client->weaponstate == WEAPON_FIRING)) + { + return; + } + + if ((CTFApplyHaste(ent) || + ((Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0) && + (ent->client->weaponstate != WEAPON_FIRING))) && + (oldstate == ent->client->weaponstate)) + { + Weapon_Generic2(ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, + FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, + fire_frames, fire); + } +} + /* * ====================================================================== *