From 5103bd6f7288a67680da35926f1bd6897f896efe Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sun, 22 Oct 2023 12:19:30 +0300 Subject: [PATCH] game: sync rogue g_* files --- Makefile | 10 +- src/game/g_ai.c | 549 ++- src/game/g_chase.c | 1 - src/game/g_combat.c | 290 +- src/game/g_items.c | 590 ++- src/game/player/weapon.c | 336 +- src/game/savegame/tables/gamefunc_decs.h | 2 +- src/rogue/g_ai.c | 1751 --------- src/rogue/g_chase.c | 249 -- src/rogue/g_combat.c | 1177 ------ src/rogue/g_func.c | 4249 --------------------- src/rogue/g_items.c | 3845 ------------------- src/rogue/player/weapon.c | 2649 ------------- src/rogue/savegame/tables/gamefunc_decs.h | 2 +- 14 files changed, 1573 insertions(+), 14127 deletions(-) delete mode 100644 src/rogue/g_ai.c delete mode 100644 src/rogue/g_chase.c delete mode 100644 src/rogue/g_combat.c delete mode 100644 src/rogue/g_func.c delete mode 100644 src/rogue/g_items.c delete mode 100644 src/rogue/player/weapon.c diff --git a/Makefile b/Makefile index 1394b3a0..893f5cc2 100644 --- a/Makefile +++ b/Makefile @@ -1465,12 +1465,12 @@ ROGUE_OBJS_ = \ src/common/shared/flash.o \ src/common/shared/rand.o \ src/common/shared/shared.o \ - src/rogue/g_ai.o \ - src/rogue/g_chase.o \ + src/game/g_ai.o \ + src/game/g_chase.o \ src/game/g_cmds.o \ - src/rogue/g_combat.o \ + src/game/g_combat.o \ src/game/g_func.o \ - src/rogue/g_items.o \ + src/game/g_items.o \ src/game/g_main.o \ src/game/g_misc.o \ src/game/g_monster.o \ @@ -1526,7 +1526,7 @@ ROGUE_OBJS_ = \ src/rogue/player/hud.o \ src/rogue/player/trail.o \ src/rogue/player/view.o \ - src/rogue/player/weapon.o \ + src/game/player/weapon.o \ src/rogue/savegame/savegame.o # ---------- diff --git a/src/game/g_ai.c b/src/game/g_ai.c index 2299677e..0ef6f1ea 100644 --- a/src/game/g_ai.c +++ b/src/game/g_ai.c @@ -37,6 +37,8 @@ static float enemy_yaw; qboolean FindTarget(edict_t *self); qboolean ai_checkattack(edict_t *self); +/* ========================================================================== */ + /* * Called once each frame to set level.sight_client * to the player to be checked for in findtarget. @@ -73,9 +75,8 @@ AI_SetSightClient(void) ent = &g_edicts[check]; - if (ent->inuse && - (ent->health > 0) && - !(ent->flags & FL_NOTARGET)) + if (ent->inuse && (ent->health > 0) && + !(ent->flags & (FL_NOTARGET | FL_DISGUISED))) { level.sight_client = ent; return; /* got one */ @@ -118,6 +119,7 @@ void ai_stand(edict_t *self, float dist) { vec3_t v; + qboolean retval; if (!self) { @@ -144,8 +146,30 @@ ai_stand(edict_t *self, float dist) self->monsterinfo.run(self); } - M_ChangeYaw(self); - ai_checkattack(self); + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } + + /* find out if we're going to be shooting */ + retval = ai_checkattack(self); + + /* record sightings of player */ + if ((self->enemy) && (self->enemy->inuse) && + (visible(self, self->enemy))) + { + self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; + VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); + VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); + self->monsterinfo.trail_time = level.time; + self->monsterinfo.blind_fire_delay = 0; + } + /* check retval to make sure we're not blindfiring */ + else if (!retval) + { + FindTarget(self); + return; + } } else { @@ -200,7 +224,8 @@ ai_walk(edict_t *self, float dist) return; } - if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time)) + if ((self->monsterinfo.search) && + (level.time > self->monsterinfo.idle_time)) { if (self->monsterinfo.idle_time) { @@ -223,23 +248,69 @@ void ai_charge(edict_t *self, float dist) { vec3_t v; + float ofs; if (!self) { return; } - if(self->enemy) + if (!self->enemy || !self->enemy->inuse) { - VectorSubtract(self->enemy->s.origin, self->s.origin, v); + return; + } + + if (visible(self, self->enemy)) + { + VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); + } + + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + VectorSubtract(self->enemy->s.origin, self->s.origin, v); + self->ideal_yaw = vectoyaw(v); } - self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); if (dist) { - M_walkmove(self, self->s.angles[YAW], dist); + if (self->monsterinfo.aiflags & AI_CHARGING) + { + M_MoveToGoal(self, dist); + return; + } + + /* circle strafe support */ + if (self->monsterinfo.attack_state == AS_SLIDING) + { + /* if we're fighting a tesla, NEVER circle strafe */ + if ((self->enemy) && (self->enemy->classname) && + (!strcmp(self->enemy->classname, "tesla"))) + { + ofs = 0; + } + else if (self->monsterinfo.lefty) + { + ofs = 90; + } + else + { + ofs = -90; + } + + if (M_walkmove(self, self->ideal_yaw + ofs, dist)) + { + return; + } + + self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; + M_walkmove(self, self->ideal_yaw - ofs, dist); + } + else + { + M_walkmove(self, self->s.angles[YAW], dist); + } } } @@ -267,7 +338,10 @@ ai_turn(edict_t *self, float dist) return; } - M_ChangeYaw(self); + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } } /* ============================================================================ */ @@ -360,7 +434,7 @@ visible(edict_t *self, edict_t *other) spot2[2] += other->viewheight; trace = gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); - if (trace.fraction == 1.0) + if ((trace.fraction == 1.0) || (trace.ent == other)) { return true; } @@ -417,6 +491,7 @@ HuntTarget(edict_t *self) self->monsterinfo.stand(self); } else + if (self->monsterinfo.run) { self->monsterinfo.run(self); } @@ -446,6 +521,11 @@ FoundTarget(edict_t *self) /* let other monsters see this monster for a while */ if (self->enemy->client) { + if (self->enemy->flags & FL_DISGUISED) + { + self->enemy->flags &= ~FL_DISGUISED; + } + level.sight_entity = self; level.sight_entity_framenum = level.framenum; level.sight_entity->light_level = 128; @@ -455,6 +535,8 @@ FoundTarget(edict_t *self) VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; + VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); + self->monsterinfo.blind_fire_delay = 0; if (!self->combattarget) { @@ -544,6 +626,10 @@ FindTarget(edict_t *self) return false; } } + else if (level.disguise_violation_framenum > level.framenum) + { + client = level.disguise_violator; + } else if (level.sound_entity_framenum >= (level.framenum - 1)) { client = level.sound_entity; @@ -573,6 +659,11 @@ FindTarget(edict_t *self) return true; } + if ((self->monsterinfo.aiflags & AI_HINT_PATH) && (coop) && (coop->value)) + { + heardit = false; + } + if (client->client) { if (client->flags & FL_NOTARGET) @@ -594,7 +685,7 @@ FindTarget(edict_t *self) } else if (heardit) { - if (client->owner->flags & FL_NOTARGET) + if ((client->owner) && (client->owner->flags & FL_NOTARGET)) { return false; } @@ -694,15 +785,28 @@ FindTarget(edict_t *self) } self->ideal_yaw = vectoyaw(temp); - M_ChangeYaw(self); + + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } /* hunt the sound for a bit; hopefully find the real player */ self->monsterinfo.aiflags |= AI_SOUND_TARGET; self->enemy = client; } - /* got one */ - FoundTarget(self); + /* if we got an enemy, we need to bail out of + hint paths, so take over here */ + if (self->monsterinfo.aiflags & AI_HINT_PATH) + { + /* this calls foundtarget for us */ + hintpath_stop(self); + } + else + { + FoundTarget(self); + } if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight)) @@ -764,7 +868,52 @@ M_CheckAttack(edict_t *self) /* do we have a clear shot? */ if (tr.ent != self->enemy) { - return false; + /* we want them to go ahead and shoot at info_notnulls if they can. */ + if ((self->enemy->solid != SOLID_NOT) || (tr.fraction < 1.0)) + { + /* if we can't see our target, and we're not + blocked by a monster, go into blind fire + if available */ + if ((!(tr.ent->svflags & SVF_MONSTER)) && + (!visible(self, self->enemy))) + { + if ((self->monsterinfo.blindfire) && + (self->monsterinfo.blind_fire_delay <= 20.0)) + { + if (level.time < self->monsterinfo.attack_finished) + { + return false; + } + + if (level.time < + (self->monsterinfo.trail_time + + self->monsterinfo.blind_fire_delay)) + { + /* wait for our time */ + return false; + } + else + { + /* make sure we're not going to shoot a monster */ + tr = gi.trace(spot1, NULL, NULL, + self->monsterinfo.blind_fire_target, + self, CONTENTS_MONSTER); + + if (tr.allsolid || tr.startsolid || + ((tr.fraction < 1.0) && + (tr.ent != self->enemy))) + { + return false; + } + + self->monsterinfo.attack_state = AS_BLIND; + return true; + } + } + } + + return false; + } } } @@ -774,6 +923,8 @@ M_CheckAttack(edict_t *self) /* don't always melee in easy mode */ if ((skill->value == SKILL_EASY) && (randk() & 3)) { + /* fix for melee only monsters & strafing */ + self->monsterinfo.attack_state = AS_STRAIGHT; return false; } @@ -792,6 +943,8 @@ M_CheckAttack(edict_t *self) /* missile attack */ if (!self->monsterinfo.attack) { + /* fix for melee only monsters & strafing */ + self->monsterinfo.attack_state = AS_STRAIGHT; return false; } @@ -831,16 +984,51 @@ M_CheckAttack(edict_t *self) chance *= 2; } - if (random() < chance) + /* go ahead and shoot every time if it's a info_notnull */ + if ((random() < chance) || (self->enemy->solid == SOLID_NOT)) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } + /* daedalus should strafe more.. this can be done + here or in a customized check_attack code for + the hover. */ if (self->flags & FL_FLY) { - if (random() < 0.3) + /* originally, just 0.3 */ + float strafe_chance; + + if (!(strcmp(self->classname, "monster_daedalus"))) + { + strafe_chance = 0.8; + } + else + { + strafe_chance = 0.6; + } + + /* if enemy is tesla, never strafe */ + if ((self->enemy->classname) && + (!strcmp(self->enemy->classname, "tesla"))) + { + strafe_chance = 0; + } + + if (random() < strafe_chance) + { + self->monsterinfo.attack_state = AS_SLIDING; + } + else + { + self->monsterinfo.attack_state = AS_STRAIGHT; + } + } + else + { + /* do we want the monsters strafing? */ + if (random() < 0.4) { self->monsterinfo.attack_state = AS_SLIDING; } @@ -866,7 +1054,11 @@ ai_run_melee(edict_t *self) } self->ideal_yaw = enemy_yaw; - M_ChangeYaw(self); + + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } if (FacingIdeal(self)) { @@ -891,25 +1083,35 @@ ai_run_missile(edict_t *self) } self->ideal_yaw = enemy_yaw; - M_ChangeYaw(self); + + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } if (FacingIdeal(self)) { - if (self->monsterinfo.attack) { + if (self->monsterinfo.attack) + { self->monsterinfo.attack(self); - self->monsterinfo.attack_state = AS_STRAIGHT; + + if ((self->monsterinfo.attack_state == AS_MISSILE) || + (self->monsterinfo.attack_state == AS_BLIND)) { + self->monsterinfo.attack_state = AS_STRAIGHT; + } } } } /* * Strafe sideways, but stay at - * approximately the same range + * aproximately the same range */ void ai_run_slide(edict_t *self, float distance) { float ofs; + float angle; if (!self) { @@ -917,15 +1119,26 @@ ai_run_slide(edict_t *self, float distance) } self->ideal_yaw = enemy_yaw; - M_ChangeYaw(self); + angle = 90; if (self->monsterinfo.lefty) { - ofs = 90; + ofs = angle; } else { - ofs = -90; + ofs = -angle; + } + + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } + + /* clamp maximum sideways move for non flyers to make them look less jerky */ + if (!(self->flags & FL_FLY)) + { + distance = min(distance, 8.0); } if (M_walkmove(self, self->ideal_yaw + ofs, distance)) @@ -933,8 +1146,29 @@ ai_run_slide(edict_t *self, float distance) return; } + /* if we're dodging, give up on it and go straight */ + if (self->monsterinfo.aiflags & AI_DODGING) + { + monster_done_dodge(self); + self->monsterinfo.attack_state = AS_STRAIGHT; + return; + } + self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; - M_walkmove(self, self->ideal_yaw - ofs, distance); + + if (M_walkmove(self, self->ideal_yaw - ofs, distance)) + { + return; + } + + /* if we're dodging, give up on it and go straight */ + if (self->monsterinfo.aiflags & AI_DODGING) + { + monster_done_dodge(self); + } + + /* the move failed, so signal the caller (ai_run) to try going straight */ + self->monsterinfo.attack_state = AS_STRAIGHT; } /* @@ -974,6 +1208,7 @@ qboolean ai_checkattack(edict_t *self) { vec3_t temp; + qboolean retval; if (!self) { @@ -1037,6 +1272,14 @@ ai_checkattack(edict_t *self) self->oldenemy = NULL; HuntTarget(self); } + else if (self->monsterinfo.last_player_enemy && + (self->monsterinfo.last_player_enemy->health > 0)) + { + self->enemy = self->monsterinfo.last_player_enemy; + self->oldenemy = NULL; + self->monsterinfo.last_player_enemy = NULL; + HuntTarget(self); + } else { if (self->movetarget) @@ -1068,6 +1311,10 @@ ai_checkattack(edict_t *self) { self->monsterinfo.search_time = level.time + 5; VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); + self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; + self->monsterinfo.trail_time = level.time; + VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); + self->monsterinfo.blind_fire_delay = 0; } /* look for other coop players here */ @@ -1087,26 +1334,38 @@ ai_checkattack(edict_t *self) enemy_yaw = vectoyaw(temp); } - if (self->monsterinfo.attack_state == AS_MISSILE) + retval = self->monsterinfo.checkattack(self); + + if (retval) { - ai_run_missile(self); - return true; + if (self->monsterinfo.attack_state == AS_MISSILE) + { + ai_run_missile(self); + return true; + } + + if (self->monsterinfo.attack_state == AS_MELEE) + { + ai_run_melee(self); + return true; + } + + /* added so monsters can shoot blind */ + if (self->monsterinfo.attack_state == AS_BLIND) + { + ai_run_missile(self); + return true; + } + + /* if enemy is not currently visible, + we will never attack */ + if (!enemy_vis) + { + return false; + } } - if (self->monsterinfo.attack_state == AS_MELEE) - { - ai_run_melee(self); - return true; - } - - /* if enemy is not currently visible, - we will never attack */ - if (!enemy_vis) - { - return false; - } - - return self->monsterinfo.checkattack(self); + return retval; } /* @@ -1126,6 +1385,10 @@ ai_run(edict_t *self, float dist) vec3_t v_forward, v_right; float left, center, right; vec3_t left_target, right_target; + qboolean retval; + qboolean alreadyMoved = false; + qboolean gotcha = false; + edict_t *realEnemy; if (!self) { @@ -1139,6 +1402,79 @@ ai_run(edict_t *self, float dist) return; } + if (self->monsterinfo.aiflags & AI_DUCKED) + { + self->monsterinfo.aiflags &= ~AI_DUCKED; + } + + if (self->maxs[2] != self->monsterinfo.base_height) + { + monster_duck_up(self); + } + + /* if we're currently looking for a hint path */ + if (self->monsterinfo.aiflags & AI_HINT_PATH) + { + M_MoveToGoal(self, dist); + + if (!self->inuse) + { + return; + } + + /* first off, make sure we're looking for + the player, not a noise he made */ + if (self->enemy) + { + if (self->enemy->inuse) + { + if (strcmp(self->enemy->classname, "player_noise") != 0) + { + realEnemy = self->enemy; + } + else if (self->enemy->owner) + { + realEnemy = self->enemy->owner; + } + else /* uh oh, can't figure out enemy, bail */ + { + self->enemy = NULL; + hintpath_stop(self); + return; + } + } + else + { + self->enemy = NULL; + hintpath_stop(self); + return; + } + } + else + { + hintpath_stop(self); + return; + } + + if (visible(self, realEnemy)) + { + gotcha = true; + } + else if (coop->value) + { + FindTarget(self); + } + + /* if we see the player, stop following hintpaths. */ + if (gotcha) + { + /* disconnect from hintpaths and start looking normally for players. */ + hintpath_stop(self); + } + + return; + } + if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { /* Special case: Some projectiles like grenades or rockets are @@ -1151,16 +1487,23 @@ ai_run(edict_t *self, float dist) if (self->enemy) { VectorSubtract(self->s.origin, self->enemy->s.origin, v); + } - if (VectorLength(v) < 64) - { - self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); - self->monsterinfo.stand(self); - return; - } + if ((!self->enemy) || (VectorLength(v) < 64)) + { + self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); + self->monsterinfo.stand(self); + return; } M_MoveToGoal(self, dist); + /* prevent double moves for sound_targets */ + alreadyMoved = true; + + if (!self->inuse) + { + return; + } if (!FindTarget(self)) { @@ -1168,30 +1511,117 @@ ai_run(edict_t *self, float dist) } } - if (ai_checkattack(self)) + retval = ai_checkattack(self); + + /* don't strafe if we can't see our enemy */ + if ((!enemy_vis) && (self->monsterinfo.attack_state == AS_SLIDING)) { - return; + self->monsterinfo.attack_state = AS_STRAIGHT; + } + + /* unless we're dodging (dodging out of view looks smart) */ + if (self->monsterinfo.aiflags & AI_DODGING) + { + self->monsterinfo.attack_state = AS_SLIDING; } if (self->monsterinfo.attack_state == AS_SLIDING) { - ai_run_slide(self, dist); + /* protect against double moves */ + if (!alreadyMoved) + { + ai_run_slide(self, dist); + } + + /* we're using attack_state as the return value out of + ai_run_slide to indicate whether or not the move + succeeded. If the move succeeded, and we're still + sliding, we're done in here (since we've had our + chance to shoot in ai_checkattack, and have moved). + if the move failed, our state is as_straight, and + it will be taken care of below */ + if ((!retval) && (self->monsterinfo.attack_state == AS_SLIDING)) + { + return; + } + } + else if (self->monsterinfo.aiflags & AI_CHARGING) + { + self->ideal_yaw = enemy_yaw; + + if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) + { + M_ChangeYaw(self); + } + } + + if (retval) + { + if ((dist != 0) && (!alreadyMoved) && + (self->monsterinfo.attack_state == AS_STRAIGHT) && + (!(self->monsterinfo.aiflags & AI_STAND_GROUND))) + { + M_MoveToGoal(self, dist); + } + + if ((self->enemy) && (self->enemy->inuse) && (enemy_vis)) + { + self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; + VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); + self->monsterinfo.trail_time = level.time; + VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); + self->monsterinfo.blind_fire_delay = 0; + } + return; } - if (enemy_vis) + if ((self->enemy) && (self->enemy->inuse) && (enemy_vis)) { - M_MoveToGoal(self, dist); + /* check for alreadyMoved */ + if (!alreadyMoved) + { + M_MoveToGoal(self, dist); + } + + if (!self->inuse) + { + return; + } + self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; + VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); + self->monsterinfo.blind_fire_delay = 0; + return; } + if ((self->monsterinfo.trail_time + 5) <= level.time) + { + /* and we haven't checked for valid hint paths in the last 10 seconds */ + if ((self->monsterinfo.last_hint_time + 10) <= level.time) + { + /* check for hint_paths. */ + self->monsterinfo.last_hint_time = level.time; + + if (monsterlost_checkhint(self)) + { + return; + } + } + } + if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20))) { - M_MoveToGoal(self, dist); + /* double move protection */ + if (!alreadyMoved) + { + M_MoveToGoal(self, dist); + } + self->monsterinfo.search_time = 0; return; } @@ -1330,6 +1760,11 @@ ai_run(edict_t *self, float dist) M_MoveToGoal(self, dist); + if (!self->inuse) + { + return; + } + G_FreeEdict(tempgoal); self->goalentity = save; diff --git a/src/game/g_chase.c b/src/game/g_chase.c index a2ccbba7..4b9a60b7 100644 --- a/src/game/g_chase.c +++ b/src/game/g_chase.c @@ -60,7 +60,6 @@ UpdateChaseCam(edict_t *ent) targ = ent->client->chase_target; VectorCopy(targ->s.origin, ownerv); - ownerv[2] += targ->viewheight; VectorCopy(targ->client->v_angle, angles); diff --git a/src/game/g_combat.c b/src/game/g_combat.c index af364d4c..b8000fbd 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -147,11 +147,72 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, return; } - targ->enemy = attacker; + /* 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. */ + if (targ->monsterinfo.aiflags & AI_DUCKED) + { + targ->monsterinfo.aiflags &= ~AI_DUCKED; + } + + if (targ->monsterinfo.aiflags & AI_MEDIC) + { + if (targ->enemy) + { + cleanupHealTarget(targ->enemy); + } + + /* clean up self */ + targ->monsterinfo.aiflags &= ~AI_MEDIC; + targ->enemy = attacker; + } + else + { + targ->enemy = attacker; + } if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) { - if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) + /* free up slot for spawned monster if it's spawned */ + if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER) + { + if (targ->monsterinfo.commander && + targ->monsterinfo.commander->inuse && + !strcmp(targ->monsterinfo.commander->classname, "monster_carrier")) + { + targ->monsterinfo.commander->monsterinfo.monster_slots++; + } + } + + if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C) + { + if (targ->monsterinfo.commander) + { + if (targ->monsterinfo.commander->inuse && + !strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander")) + { + targ->monsterinfo.commander->monsterinfo.monster_slots++; + } + } + } + + if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW) + { + /* need to check this because we can + have variable numbers of coop players */ + if (targ->monsterinfo.commander && + targ->monsterinfo.commander->inuse && + !strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13)) + { + if (targ->monsterinfo.commander->monsterinfo.monster_used > 0) + { + targ->monsterinfo.commander->monsterinfo.monster_used--; + } + } + } + + if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) && + (!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT))) { level.killed_monsters++; @@ -243,7 +304,7 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, client = ent->client; - if (dflags & DAMAGE_NO_ARMOR) + if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR)) { return 0; } @@ -307,7 +368,15 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, damage = (2 * damage) / 3; } - save = power * damagePerCell; + /* etf rifle */ + if (dflags & DAMAGE_NO_REG_ARMOR) + { + save = (power * damagePerCell) / 2; + } + else + { + save = power * damagePerCell; + } if (!save) { @@ -322,7 +391,14 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, SpawnDamage(pa_te_type, point, normal); ent->powerarmor_time = level.time + 0.2; - power_used = save / damagePerCell; + if (dflags & DAMAGE_NO_REG_ARMOR) + { + power_used = (save / damagePerCell) * 2; + } + else + { + power_used = save / damagePerCell; + } if (client) { @@ -362,7 +438,7 @@ CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, return 0; } - if (dflags & DAMAGE_NO_ARMOR) + if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR)) { return 0; } @@ -402,9 +478,11 @@ CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, } void -M_ReactToDamage(edict_t *targ, edict_t *attacker) +M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) { - if (!targ || !attacker) + qboolean new_tesla; + + if (!targ || !attacker || !inflictor) { return; } @@ -419,13 +497,30 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker) return; } + /* logic for tesla - if you are hit by a tesla, + and can't see who you should be mad at (attacker) + attack the tesla also, target the tesla if it's + a "new" tesla */ + if (!strcmp(inflictor->classname, "tesla")) + { + new_tesla = MarkTeslaArea(targ, inflictor); + + if (new_tesla || !targ->enemy) + { + TargetTesla(targ, inflictor); + } + + return; + } + if ((attacker == targ) || (attacker == targ->enemy)) { return; } - /* if we are a good guy monster and our attacker is a player - or another good guy, do not get mad at them */ + /* if we are a good guy monster and + our attacker is a player or another + good guy, do not get mad at them */ if (targ->monsterinfo.aiflags & AI_GOOD_GUY) { if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) @@ -434,15 +529,55 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker) } } + /* if we're currently mad at something + a target_anger made us mad at, ignore + damage */ + if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER) + { + float percentHealth; + + /* make sure whatever we were pissed at is still around. */ + if (targ->enemy->inuse) + { + percentHealth = (float)(targ->health) / (float)(targ->max_health); + + if (percentHealth > 0.33) + { + return; + } + } + + /* remove the target anger flag */ + targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER; + } + + /* if we're healing someone, do like above and try to stay with them */ + if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC)) + { + float percentHealth; + + percentHealth = (float)(targ->health) / (float)(targ->max_health); + + /* ignore it some of the time */ + if (targ->enemy->inuse && (percentHealth > 0.25)) + { + return; + } + + /* remove the medic flag */ + targ->monsterinfo.aiflags &= ~AI_MEDIC; + cleanupHealTarget(targ->enemy); + } + /* if attacker is a client, get mad at them because he's good and we're not */ if (attacker->client) { targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET; - /* this can only happen in coop (both new and old - enemies are clients) only switch if can't see - the current enemy */ + /* this can only happen in coop (both new and + old enemies are clients) only switch if can't + see the current enemy */ if (targ->enemy && targ->enemy->client) { if (visible(targ, targ->enemy)) @@ -473,7 +608,9 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker) (strcmp(attacker->classname, "monster_tank") != 0) && (strcmp(attacker->classname, "monster_supertank") != 0) && (strcmp(attacker->classname, "monster_makron") != 0) && - (strcmp(attacker->classname, "monster_jorg") != 0)) + (strcmp(attacker->classname, "monster_jorg") != 0) && + !(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) && + !(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS)) { if (targ->enemy && targ->enemy->client) { @@ -520,6 +657,12 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker) } } +qboolean +CheckTeamDamage(edict_t *targ, edict_t *attacker) +{ + return false; +} + static void apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale) { @@ -539,9 +682,9 @@ apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale) } void -T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, - vec3_t dir, vec3_t point, vec3_t normal, int damage, - int knockback, int dflags, int mod) +T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, + vec3_t point, vec3_t normal, int damage, int knockback, int dflags, + int mod) { gclient_t *client; int take; @@ -549,6 +692,7 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, int asave; int psave; int te_sparks; + int sphere_notified; if (!targ || !inflictor || !attacker) { @@ -560,16 +704,20 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, return; } - /* friendly fire avoidance if enabled you - can't hurt teammates (but you can hurt - yourself) knockback still occurs */ + sphere_notified = false; + + /* friendly fire avoidance. If enabled you can't + hurt teammates (but you can hurt yourself) + knockback still occurs */ if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) { if (OnSameTeam(targ, attacker)) { - if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) + /* nukes kill everyone */ + if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && + (mod != MOD_NUKE)) { damage = 0; } @@ -582,6 +730,25 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, meansOfDeath = mod; + /* allow the deathmatch game to change values */ + if (deathmatch->value && gamerules && gamerules->value) + { + if (DMGame.ChangeDamage) + { + damage = DMGame.ChangeDamage(targ, attacker, damage, mod); + } + + if (DMGame.ChangeKnockback) + { + knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod); + } + + if (!damage) + { + return; + } + } + /* easy mode takes half damage */ if ((skill->value == SKILL_EASY) && (deathmatch->value == 0) && targ->client) { @@ -595,6 +762,18 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, client = targ->client; + /* defender sphere takes half damage */ + if ((client) && (client->owned_sphere) && + (client->owned_sphere->spawnflags == 1)) + { + damage *= 0.5; + + if (!damage) + { + damage = 1; + } + } + if (dflags & DAMAGE_BULLET) { te_sparks = TE_BULLET_SPARKS; @@ -639,7 +818,8 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, } /* check for invincibility */ - if ((client && (client->invincible_framenum > level.framenum)) && + if ((client && + (client->invincible_framenum > level.framenum)) && !(dflags & DAMAGE_NO_PROTECTION) && (mod != MOD_TRAP)) { if (targ->pain_debounce_time < level.time) @@ -653,6 +833,22 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, save = damage; } + /* check for monster invincibility */ + if (((targ->svflags & SVF_MONSTER) && + (targ->monsterinfo.invincible_framenum > level.framenum)) && + !(dflags & DAMAGE_NO_PROTECTION)) + { + if (targ->pain_debounce_time < level.time) + { + gi.sound(targ, CHAN_ITEM, gi.soundindex( + "items/protect4.wav"), 1, ATTN_NORM, 0); + targ->pain_debounce_time = level.time + 2; + } + + take = 0; + save = damage; + } + psave = CheckPowerArmor(targ, point, normal, take, dflags); take -= psave; @@ -662,15 +858,35 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, /* treat cheat/powerup savings the same as armor */ asave += save; + /* this option will do damage both to the armor + and person. originally for DPU rounds */ + if (dflags & DAMAGE_DESTROY_ARMOR) + { + if (!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) && + !(client && (client->invincible_framenum > level.framenum))) + { + take = damage; + } + } + /* do the damage */ if (take) { - if ((targ->svflags & SVF_MONSTER) || (client)) + /* need more blood for chainfist. */ + if (targ->flags & FL_MECHANICAL) + { + SpawnDamage(TE_ELECTRIC_SPARKS, point, normal); + } + else if ((targ->svflags & SVF_MONSTER) || (client)) { if (strcmp(targ->classname, "monster_gekk") == 0) { SpawnDamage(TE_GREENBLOOD, point, normal); } + else if (mod == MOD_CHAINFIST) + { + SpawnDamage(TE_MOREBLOOD, point, normal); + } else { SpawnDamage(TE_BLOOD, point, normal); @@ -683,6 +899,17 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, targ->health = targ->health - take; + /* spheres need to know who to shoot at */ + if (client && client->owned_sphere) + { + sphere_notified = true; + + if (client->owned_sphere->pain) + { + client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0); + } + } + if (targ->health <= 0) { if ((targ->svflags & SVF_MONSTER) || (client)) @@ -695,11 +922,24 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, } } + /* spheres need to know who to shoot at */ + if (!sphere_notified) + { + if (client && client->owned_sphere) + { + if (client->owned_sphere->pain) + { + client->owned_sphere->pain(client->owned_sphere, attacker, 0, + 0); + } + } + } + if (targ->svflags & SVF_MONSTER) { - M_ReactToDamage(targ, attacker); + M_ReactToDamage(targ, attacker, inflictor); - if (!(targ->monsterinfo.aiflags & (AI_DUCKED|AI_IGNORE_PAIN)) && (take)) + if (!(targ->monsterinfo.aiflags & (AI_DUCKED | AI_IGNORE_PAIN)) && (take)) { targ->pain(targ, attacker, knockback, take); diff --git a/src/game/g_items.c b/src/game/g_items.c index 3ec51726..9a1900bb 100644 --- a/src/game/g_items.c +++ b/src/game/g_items.c @@ -1792,7 +1792,7 @@ Drop_Item(edict_t *ent, gitem_t *item) dropped->item = item; dropped->spawnflags = DROPPED_ITEM; dropped->s.effects = item->world_model_flags; - dropped->s.renderfx = RF_GLOW; + dropped->s.renderfx = RF_GLOW | RF_IR_VISIBLE; if (frandk() > 0.5) { @@ -1803,8 +1803,8 @@ Drop_Item(edict_t *ent, gitem_t *item) dropped->s.angles[1] -= frandk()*45; } - VectorSet (dropped->mins, -16, -16, -16); - VectorSet (dropped->maxs, 16, 16, 16); + 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; @@ -2116,9 +2116,17 @@ SpawnItem(edict_t *ent, gitem_t *item) return; } - PrecacheItem(item); + if (!g_disruptor->value) + { + if ((!strcmp(ent->classname, "ammo_disruptor")) || + (!strcmp(ent->classname, "weapon_disintegrator"))) + { + G_FreeEdict(ent); + return; + } + } - if (ent->spawnflags) + if (ent->spawnflags > 1) { if (strcmp(ent->classname, "key_power_cube") != 0) { @@ -2212,6 +2220,25 @@ SpawnItem(edict_t *ent, gitem_t *item) } } + /* DM only items */ + if (!deathmatch->value) + { + if ((item->pickup == Pickup_Doppleganger) || + (item->pickup == Pickup_Nuke)) + { + G_FreeEdict(ent); + return; + } + + if ((item->use == Use_Vengeance) || (item->use == Use_Hunter)) + { + G_FreeEdict(ent); + return; + } + } + + PrecacheItem(item); + if (coop->value && !(ent->spawnflags & ITEM_NO_TOUCH) && (strcmp(ent->classname, "key_power_cube") == 0)) { ent->spawnflags |= (1 << (8 + level.power_cubes)); @@ -2274,7 +2301,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_armor_combat", @@ -2298,7 +2325,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_armor_jacket", @@ -2322,7 +2349,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_armor_shard", @@ -2346,7 +2373,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_power_screen", @@ -2370,7 +2397,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_power_shield", @@ -2419,7 +2446,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_shotgun", @@ -2443,7 +2470,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_supershotgun", @@ -2467,7 +2494,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_machinegun", @@ -2492,7 +2519,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_chaingun", @@ -2517,7 +2544,31 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_etf_rifle (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "weapon_etf_rifle", + Pickup_Weapon, + Use_Weapon, + Drop_Weapon, + Weapon_ETF_Rifle, + "misc/w_pkup.wav", + "models/weapons/g_etf_rifle/tris.md2", EF_ROTATE, + "models/weapons/v_etf_rifle/tris.md2", + "w_etf_rifle", + "ETF Rifle", + 0, + 1, + "Flechettes", + IT_WEAPON, + WEAP_ETFRIFLE, + NULL, + 0, + "weapons/nail1.wav models/proj/flechette/tris.md2", + }, + + /* + * QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "ammo_grenades", @@ -2567,7 +2618,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_grenadelauncher", @@ -2592,7 +2643,31 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_proxlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "weapon_proxlauncher", + Pickup_Weapon, + Use_Weapon, + Drop_Weapon, + Weapon_ProxLauncher, + "misc/w_pkup.wav", + "models/weapons/g_plaunch/tris.md2", EF_ROTATE, + "models/weapons/v_plaunch/tris.md2", + "w_proxlaunch", + "Prox Launcher", + 0, + 1, + "Prox", + IT_WEAPON, + WEAP_PROXLAUNCH, + NULL, + AMMO_PROX, + "weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav weapons/proxwarn.wav weapons/proxopen.wav", + }, + + /* + * QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_rocketlauncher", @@ -2617,7 +2692,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_hyperblaster", @@ -2641,6 +2716,30 @@ static const gitem_t gameitemlist[] = { "weapons/hyprbu1a.wav weapons/hyprbl1a.wav weapons/hyprbf1a.wav weapons/hyprbd1a.wav misc/lasfly.wav" }, + /* + * QUAKED weapon_plasmabeam (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "weapon_plasmabeam", + Pickup_Weapon, + Use_Weapon, + Drop_Weapon, + Weapon_Heatbeam, + "misc/w_pkup.wav", + "models/weapons/g_beamer/tris.md2", EF_ROTATE, + "models/weapons/v_beamer/tris.md2", + "w_heatbeam", + "Plasma Beam", + 0, + 2, + "Cells", + IT_WEAPON, + WEAP_PLASMA, + NULL, + 0, + "models/weapons/v_beamer2/tris.md2 weapons/bfg__l1a.wav", + }, + /* * QUAKED weapon_boomer (.3 .3 1) (-16 -16 -16) (16 16 16) */ @@ -2666,7 +2765,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_railgun", @@ -2715,7 +2814,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "weapon_bfg", @@ -2740,7 +2839,55 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED weapon_chainfist (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "weapon_chainfist", + Pickup_Weapon, + Use_Weapon, + Drop_Weapon, + Weapon_ChainFist, + "misc/w_pkup.wav", + "models/weapons/g_chainf/tris.md2", EF_ROTATE, + "models/weapons/v_chainf/tris.md2", + "w_chainfist", + "Chainfist", + 0, + 0, + NULL, + IT_WEAPON | IT_MELEE, + WEAP_CHAINFIST, + NULL, + 1, + "weapons/sawidle.wav weapons/sawhit.wav", + }, + + /* + * QUAKED weapon_disintegrator (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "weapon_disintegrator", + Pickup_Weapon, + Use_Weapon, + Drop_Weapon, + Weapon_Disintegrator, + "misc/w_pkup.wav", + "models/weapons/g_dist/tris.md2", EF_ROTATE, + "models/weapons/v_dist/tris.md2", + "w_disintegrator", + "Disruptor", + 0, + 1, + "Rounds", + IT_WEAPON, + WEAP_DISRUPTOR, + NULL, + 1, + "models/items/spawngro/tris.md2 models/proj/disintegrator/tris.md2 weapons/disrupt.wav weapons/disint2.wav weapons/disrupthit.wav", + }, + + /* + * QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "ammo_shells", @@ -2764,7 +2911,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "ammo_bullets", @@ -2788,7 +2935,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "ammo_cells", @@ -2812,7 +2959,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "ammo_rockets", @@ -2836,7 +2983,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "ammo_slugs", @@ -2859,6 +3006,124 @@ static const gitem_t gameitemlist[] = { "" }, + /* + * QUAKED ammo_flechettes (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "ammo_flechettes", + Pickup_Ammo, + NULL, + Drop_Ammo, + NULL, + "misc/am_pkup.wav", + "models/ammo/am_flechette/tris.md2", 0, + NULL, + "a_flechettes", + "Flechettes", + 3, + 50, + NULL, + IT_AMMO, + 0, + NULL, + AMMO_FLECHETTES + }, + + /* + * QUAKED ammo_prox (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "ammo_prox", + Pickup_Ammo, + NULL, + Drop_Ammo, + NULL, + "misc/am_pkup.wav", + "models/ammo/am_prox/tris.md2", 0, + NULL, + "a_prox", + "Prox", + 3, + 5, + NULL, + IT_AMMO, + 0, + NULL, + AMMO_PROX, + "models/weapons/g_prox/tris.md2 weapons/proxwarn.wav" + }, + + /* + * QUAKED ammo_tesla (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "ammo_tesla", + Pickup_Ammo, + Use_Weapon, + Drop_Ammo, + Weapon_Tesla, + "misc/am_pkup.wav", + "models/ammo/am_tesl/tris.md2", 0, + "models/weapons/v_tesla/tris.md2", + "a_tesla", + "Tesla", + 3, + 5, + "Tesla", + IT_AMMO | IT_WEAPON, + 0, + NULL, + AMMO_TESLA, + "models/weapons/v_tesla2/tris.md2 weapons/teslaopen.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav models/weapons/g_tesla/tris.md2" + }, + + /* + * QUAKED ammo_nuke (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "ammo_nuke", + Pickup_Nuke, + Use_Nuke, + Drop_Ammo, + NULL, + "misc/am_pkup.wav", + "models/weapons/g_nuke/tris.md2", EF_ROTATE, + NULL, + "p_nuke", + "A-M Bomb", + 3, + 300, + "A-M Bomb", + IT_POWERUP, + 0, + NULL, + 0, + "weapons/nukewarn2.wav world/rumble.wav" + }, + + /* + * QUAKED ammo_disruptor (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "ammo_disruptor", + Pickup_Ammo, + NULL, + Drop_Ammo, + NULL, + "misc/am_pkup.wav", + "models/ammo/am_disr/tris.md2", 0, + NULL, + "a_disruptor", + "Rounds", + 3, + 15, + NULL, + IT_AMMO, + 0, + NULL, + AMMO_DISRUPTOR + }, + /* * QUAKED ammo_magslug (.3 .3 1) (-16 -16 -16) (16 16 16) */ @@ -2884,7 +3149,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_quad", @@ -2933,7 +3198,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_invulnerability", @@ -2957,7 +3222,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_silencer", @@ -2981,7 +3246,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_breather", @@ -3005,7 +3270,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_enviro", @@ -3029,7 +3294,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * Special item that gives +2 to maximum health */ { @@ -3054,7 +3319,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * gives +1 to maximum health */ { @@ -3079,7 +3344,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_bandolier", @@ -3103,7 +3368,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16) + * QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ { "item_pack", @@ -3127,7 +3392,197 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED item_ir_goggles (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_ir_goggles", + Pickup_Powerup, + Use_IR, + Drop_General, + NULL, + "items/pkup.wav", + "models/items/goggles/tris.md2", EF_ROTATE, + NULL, + "p_ir", + "IR Goggles", + 2, + 60, + NULL, + IT_POWERUP | IT_INSTANT_USE, + 0, + NULL, + 0, + "misc/ir_start.wav" + }, + + /* + * QUAKED item_double (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_double", + Pickup_Powerup, + Use_Double, + Drop_General, + NULL, + "items/pkup.wav", + "models/items/ddamage/tris.md2", EF_ROTATE, + NULL, + "p_double", + "Double Damage", + 2, + 60, + NULL, + IT_POWERUP | IT_INSTANT_USE, + 0, + NULL, + 0, + "misc/ddamage1.wav misc/ddamage2.wav misc/ddamage3.wav" + }, + + /* + * QUAKED item_compass (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_compass", + Pickup_Powerup, + Use_Compass, + NULL, + NULL, + "items/pkup.wav", + "models/objects/fire/tris.md2", EF_ROTATE, + NULL, + "p_compass", + "compass", + 2, + 60, + NULL, + IT_POWERUP, + 0, + NULL, + 0, + }, + + /* + * QUAKED item_sphere_vengeance (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_sphere_vengeance", + Pickup_Sphere, + Use_Vengeance, + NULL, + NULL, + "items/pkup.wav", + "models/items/vengnce/tris.md2", EF_ROTATE, + NULL, + "p_vengeance", + "vengeance sphere", + 2, + 60, + NULL, + IT_POWERUP | IT_INSTANT_USE, + 0, + NULL, + 0, + "spheres/v_idle.wav" + }, + + /* + * QUAKED item_sphere_hunter (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_sphere_hunter", + Pickup_Sphere, + Use_Hunter, + NULL, + NULL, + "items/pkup.wav", + "models/items/hunter/tris.md2", EF_ROTATE, + NULL, + "p_hunter", + "hunter sphere", + 2, + 120, + NULL, + IT_POWERUP | IT_INSTANT_USE, + 0, + NULL, + 0, + "spheres/h_idle.wav spheres/h_active.wav spheres/h_lurk.wav" + }, + + /* + * QUAKED item_sphere_defender (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_sphere_defender", + Pickup_Sphere, + Use_Defender, + NULL, + NULL, + "items/pkup.wav", + "models/items/defender/tris.md2", EF_ROTATE, + NULL, + "p_defender", + "defender sphere", + 2, + 60, + NULL, + IT_POWERUP | IT_INSTANT_USE, + 0, + NULL, + 0, + "models/proj/laser2/tris.md2 models/items/shell/tris.md2 spheres/d_idle.wav" + }, + + /* + * QUAKED item_doppleganger (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "item_doppleganger", + Pickup_Doppleganger, + Use_Doppleganger, + Drop_General, + NULL, + "items/pkup.wav", + "models/items/dopple/tris.md2", + EF_ROTATE, + NULL, + "p_doppleganger", + "Doppleganger", + 0, + 90, + NULL, + IT_POWERUP, + 0, + NULL, + 0, + "models/objects/dopplebase/tris.md2 models/items/spawngro2/tris.md2 models/items/hunter/tris.md2 models/items/vengnce/tris.md2", + }, + + { + NULL, + Tag_PickupToken, + NULL, + NULL, + NULL, + "items/pkup.wav", + "models/items/tagtoken/tris.md2", + EF_ROTATE | EF_TAGTRAIL, + NULL, + "i_tagtoken", + "Tag Token", + 0, + 0, + NULL, + IT_POWERUP | IT_NOT_GIVEABLE, + 0, + NULL, + 1, + NULL, + }, + + /* + * QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * key for computer centers */ { @@ -3177,7 +3632,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * key for the entrance of jail3 */ { @@ -3202,7 +3657,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * key for the city computer */ { @@ -3227,7 +3682,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * security pass for the security level */ { @@ -3252,7 +3707,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * normal door key - blue */ { @@ -3277,7 +3732,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * normal door key - red */ { @@ -3301,7 +3756,6 @@ static const gitem_t gameitemlist[] = { "" }, - /* * QUAKED key_green_key (0 .5 .8) (-16 -16 -16) (16 16 16) * normal door key - blue @@ -3328,7 +3782,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * tank commander's head */ { @@ -3353,7 +3807,7 @@ static const gitem_t gameitemlist[] = { }, /* - * QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16) + * QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN * tank commander's head */ { @@ -3377,6 +3831,56 @@ static const gitem_t gameitemlist[] = { "" }, + /* + * QUAKED key_nuke_container (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "key_nuke_container", + Pickup_Key, + NULL, + Drop_General, + NULL, + "items/pkup.wav", + "models/weapons/g_nuke/tris.md2", + EF_ROTATE, + NULL, + "i_contain", + "Antimatter Pod", + 2, + 0, + NULL, + IT_STAY_COOP | IT_KEY, + 0, + NULL, + 0, + NULL, + }, + + /* + * QUAKED key_nuke (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN + */ + { + "key_nuke", + Pickup_Key, + NULL, + Drop_General, + NULL, + "items/pkup.wav", + "models/weapons/g_nuke/tris.md2", + EF_ROTATE, + NULL, + "i_nuke", + "Antimatter Bomb", + 2, + 0, + NULL, + IT_STAY_COOP | IT_KEY, + 0, + NULL, + 0, + NULL, + }, + { NULL, Pickup_Health, diff --git a/src/game/player/weapon.c b/src/game/player/weapon.c index 1323eb2b..dcd527a6 100644 --- a/src/game/player/weapon.c +++ b/src/game/player/weapon.c @@ -89,6 +89,15 @@ P_DamageModifier(edict_t *ent) } } + if (ent->client->quadfire_framenum > level.framenum) + { + if ((deathmatch->value) || (damage_multiplier == 1)) + { + damage_multiplier *= 2; + is_quadfire = 1; + } + } + return damage_multiplier; } @@ -520,6 +529,11 @@ ChangeWeapon(edict_t *ent) 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"))]) { @@ -527,6 +541,20 @@ NoAmmoWeaponChange(edict_t *ent) 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"))]) { @@ -593,8 +621,7 @@ Think_Weapon(edict_t *ent) /* call active weapon think routine */ if (ent->client->pers.weapon && ent->client->pers.weapon->weaponthink) { - is_quad = (ent->client->quad_framenum > level.framenum); - is_quadfire = (ent->client->quadfire_framenum > level.framenum); + P_DamageModifier(ent); if (ent->client->silencer_shots) { @@ -942,6 +969,11 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, 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); + } fire(ent); break; @@ -960,15 +992,19 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, } } -/* ====================================================================== */ - -/* GRENADE */ +/* + * ====================================================================== + * + * GRENADE + * + * ====================================================================== + */ void weapon_grenade_fire(edict_t *ent, qboolean held) { vec3_t offset; - vec3_t forward, right; + vec3_t forward, right, up; vec3_t start; int damage = 125; float timer; @@ -984,20 +1020,53 @@ weapon_grenade_fire(edict_t *ent, qboolean held) if (is_quad) { - damage *= 4; + damage *= damage_multiplier; - gi.sound(ent, CHAN_ITEM, gi.soundindex( - "items/damage3.wav"), 1, ATTN_NORM, 0); + if (damage_multiplier >= 4) + { + gi.sound(ent, CHAN_ITEM, gi.soundindex( + "items/damage3.wav"), 1, ATTN_NORM, 0); + } + else if (damage_multiplier == 2) + { + gi.sound(ent, CHAN_ITEM, gi.soundindex( + "misc/ddamage3.wav"), 1, ATTN_NORM, 0); + } } - VectorSet(offset, 8, 8, ent->viewheight - 8); - AngleVectors(ent->client->v_angle, forward, right, NULL); - P_ProjectSource(ent, offset, forward, right, start); + AngleVectors(ent->client->v_angle, forward, right, up); + + if (ent->client->pers.weapon->tag == AMMO_TESLA) + { + VectorSet(offset, 0, -4, ent->viewheight - 22); + } + else + { + VectorSet(offset, 2, 6, ent->viewheight - 14); + } + + P_ProjectSource2(ent, ent->s.origin, offset, + forward, right, up, 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 (speed > GRENADE_MAXSPEED) + { + speed = GRENADE_MAXSPEED; + } + + switch (ent->client->pers.weapon->tag) + { + case AMMO_GRENADES: + fire_grenade2(ent, start, forward, damage, speed, + timer, radius, held); + break; + default: + fire_tesla(ent, start, forward, damage_multiplier, speed); + break; + } if (!((int)dmflags->value & DF_INFINITE_AMMO)) { @@ -1031,9 +1100,13 @@ weapon_grenade_fire(edict_t *ent, qboolean held) } void -Weapon_Grenade(edict_t *ent) +Throw_Generic(edict_t *ent, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_THROW_SOUND, + int FRAME_THROW_HOLD, int FRAME_THROW_FIRE, int *pause_frames, int EXPLODE, + void (*fire)(edict_t *ent, qboolean held)) { - if (!ent) + int n; + + if (!ent || !pause_frames || !fire) { return; } @@ -1047,7 +1120,7 @@ Weapon_Grenade(edict_t *ent) if (ent->client->weaponstate == WEAPON_ACTIVATING) { ent->client->weaponstate = WEAPON_READY; - ent->client->ps.gunframe = 16; + ent->client->ps.gunframe = FRAME_IDLE_FIRST; return; } @@ -1079,48 +1152,59 @@ Weapon_Grenade(edict_t *ent) return; } - if ((ent->client->ps.gunframe == 29) || - (ent->client->ps.gunframe == 34) || - (ent->client->ps.gunframe == 39) || - (ent->client->ps.gunframe == 48)) + if (ent->client->ps.gunframe == FRAME_IDLE_LAST) { - if (randk() & 15) + ent->client->ps.gunframe = FRAME_IDLE_FIRST; + return; + } + + if (pause_frames) + { + for (n = 0; pause_frames[n]; n++) { - return; + if (ent->client->ps.gunframe == pause_frames[n]) + { + if (randk() & 15) + { + return; + } + } } } - if (++ent->client->ps.gunframe > 48) - { - ent->client->ps.gunframe = 16; - } - + ent->client->ps.gunframe++; return; } if (ent->client->weaponstate == WEAPON_FIRING) { - if (ent->client->ps.gunframe == 5) + if (ent->client->ps.gunframe == FRAME_THROW_SOUND) { gi.sound(ent, CHAN_WEAPON, gi.soundindex( "weapons/hgrena1b.wav"), 1, ATTN_NORM, 0); } - if (ent->client->ps.gunframe == 11) + if (ent->client->ps.gunframe == FRAME_THROW_HOLD) { if (!ent->client->grenade_time) { ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2; - ent->client->weapon_sound = gi.soundindex( - "weapons/hgrenc1b.wav"); + + switch (ent->client->pers.weapon->tag) + { + case AMMO_GRENADES: + ent->client->weapon_sound = gi.soundindex( + "weapons/hgrenc1b.wav"); + break; + } } /* they waited too long, detonate it in their hand */ - if (!ent->client->grenade_blew_up && + if (EXPLODE && !ent->client->grenade_blew_up && (level.time >= ent->client->grenade_time)) { ent->client->weapon_sound = 0; - weapon_grenade_fire(ent, true); + fire(ent, true); ent->client->grenade_blew_up = true; } @@ -1133,7 +1217,7 @@ Weapon_Grenade(edict_t *ent) { if (level.time >= ent->client->grenade_time) { - ent->client->ps.gunframe = 15; + ent->client->ps.gunframe = FRAME_FIRE_LAST; ent->client->grenade_blew_up = false; } else @@ -1143,13 +1227,13 @@ Weapon_Grenade(edict_t *ent) } } - if (ent->client->ps.gunframe == 12) + if (ent->client->ps.gunframe == FRAME_THROW_FIRE) { ent->client->weapon_sound = 0; - weapon_grenade_fire(ent, false); + fire(ent, true); } - if ((ent->client->ps.gunframe == 15) && + if ((ent->client->ps.gunframe == FRAME_FIRE_LAST) && (level.time < ent->client->grenade_time)) { return; @@ -1157,7 +1241,7 @@ Weapon_Grenade(edict_t *ent) ent->client->ps.gunframe++; - if (ent->client->ps.gunframe == 16) + if (ent->client->ps.gunframe == FRAME_IDLE_FIRST) { ent->client->grenade_time = 0; ent->client->weaponstate = WEAPON_READY; @@ -1165,9 +1249,49 @@ Weapon_Grenade(edict_t *ent) } } -/* ====================================================================== */ +void +Weapon_Grenade(edict_t *ent) +{ + static int pause_frames[] = {29, 34, 39, 48, 0}; -/* GRENADE LAUNCHER */ + if (!ent) + { + return; + } + + Throw_Generic(ent, 15, 48, 5, 11, 12, pause_frames, + GRENADE_TIMER, weapon_grenade_fire); +} + +void +Weapon_Tesla(edict_t *ent) +{ + static int pause_frames[] = {21, 0}; + + if (!ent) + { + return; + } + + if ((ent->client->ps.gunframe > 1) && (ent->client->ps.gunframe < 9)) + { + ent->client->ps.gunindex = gi.modelindex("models/weapons/v_tesla2/tris.md2"); + } + else + { + ent->client->ps.gunindex = gi.modelindex("models/weapons/v_tesla/tris.md2"); + } + + Throw_Generic(ent, 8, 32, 99, 1, 2, pause_frames, 0, weapon_grenade_fire); +} + +/* + * ====================================================================== + * + * GRENADE LAUNCHER + * + * ====================================================================== + */ void weapon_grenadelauncher_fire(edict_t *ent) @@ -1175,7 +1299,7 @@ weapon_grenadelauncher_fire(edict_t *ent) vec3_t offset; vec3_t forward, right; vec3_t start; - int damage = 120; + int damage; float radius; if (!ent) @@ -1183,11 +1307,21 @@ weapon_grenadelauncher_fire(edict_t *ent) return; } + switch (ent->client->pers.weapon->tag) + { + case AMMO_PROX: + damage = 90; + break; + default: + damage = 120; + break; + } + radius = damage + 40; if (is_quad) { - damage *= 4; + damage *= damage_multiplier; } VectorSet(offset, 8, 8, ent->viewheight - 8); @@ -1197,7 +1331,15 @@ weapon_grenadelauncher_fire(edict_t *ent) VectorScale(forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -1; - fire_grenade(ent, start, forward, damage, 600, 2.5, radius); + switch (ent->client->pers.weapon->tag) + { + case AMMO_PROX: + fire_prox(ent, start, forward, damage_multiplier, 600); + break; + default: + fire_grenade(ent, start, forward, damage, 600, 2.5, radius); + break; + } gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); @@ -1220,6 +1362,11 @@ Weapon_GrenadeLauncher(edict_t *ent) static int pause_frames[] = {34, 51, 59, 0}; static int fire_frames[] = {6, 0}; + if (!ent) + { + return; + } + Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire); @@ -1230,9 +1377,28 @@ Weapon_GrenadeLauncher(edict_t *ent) } } -/* ====================================================================== */ +void +Weapon_ProxLauncher(edict_t *ent) +{ + static int pause_frames[] = {34, 51, 59, 0}; + static int fire_frames[] = {6, 0}; -/* ROCKET */ + if (!ent) + { + return; + } + + Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, + fire_frames, weapon_grenadelauncher_fire); +} + +/* + * ====================================================================== + * + * ROCKET + * + * ====================================================================== + */ void Weapon_RocketLauncher_Fire(edict_t *ent) @@ -1254,8 +1420,8 @@ Weapon_RocketLauncher_Fire(edict_t *ent) if (is_quad) { - damage *= 4; - radius_damage *= 4; + damage *= damage_multiplier; + radius_damage *= damage_multiplier; } AngleVectors(ent->client->v_angle, forward, right, NULL); @@ -1289,7 +1455,7 @@ Weapon_RocketLauncher(edict_t *ent) static int pause_frames[] = {25, 33, 42, 50, 0}; static int fire_frames[] = {5, 0}; - if (!ent) + if (!ent) { return; } @@ -1304,9 +1470,13 @@ Weapon_RocketLauncher(edict_t *ent) } } -/* ====================================================================== */ - -/* BLASTER / HYPERBLASTER */ +/* + * ====================================================================== + * + * BLASTER / HYPERBLASTER + * + * ====================================================================== + */ void Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage, @@ -1323,7 +1493,7 @@ Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage, if (is_quad) { - damage *= 4; + damage *= damage_multiplier; } AngleVectors(ent->client->v_angle, forward, right, NULL); @@ -1515,9 +1685,13 @@ Weapon_HyperBlaster(edict_t *ent) } } -/* ====================================================================== */ - -/* MACHINEGUN / CHAINGUN */ +/* + * ====================================================================== + * + * MACHINEGUN / CHAINGUN + * + * ====================================================================== + */ void Machinegun_Fire(edict_t *ent) @@ -1568,8 +1742,8 @@ Machinegun_Fire(edict_t *ent) if (is_quad) { - damage *= 4; - kick *= 4; + damage *= damage_multiplier; + kick *= damage_multiplier; } for (i = 1; i < 3; i++) @@ -1591,7 +1765,7 @@ Machinegun_Fire(edict_t *ent) ent->client->machinegun_shots = 9; } } - + /* get start / end positions */ VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles); AngleVectors(angles, forward, right, NULL); @@ -1761,8 +1935,8 @@ Chaingun_Fire(edict_t *ent) if (is_quad) { - damage *= 4; - kick *= 4; + damage *= damage_multiplier; + kick *= damage_multiplier; } for (i = 0; i < 3; i++) @@ -1820,9 +1994,13 @@ Weapon_Chaingun(edict_t *ent) } } -/* ====================================================================== */ - -/* SHOTGUN / SUPERSHOTGUN */ +/* + * ====================================================================== + * + * SHOTGUN / SUPERSHOTGUN + * + * ====================================================================== + */ void weapon_shotgun_fire(edict_t *ent) @@ -1854,8 +2032,8 @@ weapon_shotgun_fire(edict_t *ent) if (is_quad) { - damage *= 4; - kick *= 4; + damage *= damage_multiplier; + kick *= damage_multiplier; } if (deathmatch->value) @@ -1932,8 +2110,8 @@ weapon_supershotgun_fire(edict_t *ent) if (is_quad) { - damage *= 4; - kick *= 4; + damage *= damage_multiplier; + kick *= damage_multiplier; } v[PITCH] = ent->client->v_angle[PITCH]; @@ -2010,7 +2188,13 @@ Weapon_SuperShotgun(edict_t *ent) } } -/* RAILGUN */ +/* + * ====================================================================== + * + * RAILGUN + * + * ====================================================================== + */ void weapon_railgun_fire(edict_t *ent) @@ -2040,8 +2224,8 @@ weapon_railgun_fire(edict_t *ent) if (is_quad) { - damage *= 4; - kick *= 4; + damage *= damage_multiplier; + kick *= damage_multiplier; } AngleVectors(ent->client->v_angle, forward, right, NULL); @@ -2089,9 +2273,13 @@ Weapon_Railgun(edict_t *ent) } } -/* ====================================================================== */ - -/* BFG10K */ +/* + * ====================================================================== + * + * BFG10K + * + * ====================================================================== + */ void weapon_bfg_fire(edict_t *ent) @@ -2139,7 +2327,7 @@ weapon_bfg_fire(edict_t *ent) if (is_quad) { - damage *= 4; + damage *= damage_multiplier; } AngleVectors(ent->client->v_angle, forward, right, NULL); diff --git a/src/game/savegame/tables/gamefunc_decs.h b/src/game/savegame/tables/gamefunc_decs.h index 541554e3..bc18a4ea 100644 --- a/src/game/savegame/tables/gamefunc_decs.h +++ b/src/game/savegame/tables/gamefunc_decs.h @@ -1209,7 +1209,7 @@ extern void Move_Final ( edict_t * ent ) ; extern void Move_Done ( edict_t * ent ) ; extern void T_RadiusDamage ( edict_t * inflictor , edict_t * attacker , float damage , edict_t * ignore , float radius , int mod ) ; extern void T_Damage ( edict_t * targ , edict_t * inflictor , edict_t * attacker , vec3_t dir , vec3_t point , vec3_t normal , int damage , int knockback , int dflags , int mod ) ; -extern void M_ReactToDamage ( edict_t * targ , edict_t * attacker ) ; +extern void M_ReactToDamage ( edict_t * targ , edict_t * attacker, edict_t *inflictor ) ; extern int CheckArmor ( edict_t * ent , vec3_t point , vec3_t normal , int damage , int te_sparks , int dflags ) ; extern int CheckPowerArmor ( edict_t * ent , vec3_t point , vec3_t normal , int damage , int dflags ) ; extern void SpawnDamage ( int type , vec3_t origin , vec3_t normal ) ; diff --git a/src/rogue/g_ai.c b/src/rogue/g_ai.c deleted file mode 100644 index dd911650..00000000 --- a/src/rogue/g_ai.c +++ /dev/null @@ -1,1751 +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. - * - * ======================================================================= - * - * The basic AI functions like enemy detection, attacking and so on. - * - * ======================================================================= - */ - -#include "header/local.h" - -extern cvar_t *maxclients; -int enemy_range; -float enemy_yaw; -qboolean ai_checkattack(edict_t *self, float dist); -qboolean enemy_infront; -qboolean enemy_vis; -qboolean FindTarget(edict_t *self); - -/* ========================================================================== */ - -/* - * Called once each frame to set level.sight_client - * to the player to be checked for in findtarget. - * If all clients are either dead or in notarget, - * sight_client will be null. - * In coop games, sight_client will cycle - * between the clients. - */ -void -AI_SetSightClient(void) -{ - edict_t *ent; - int start, check; - - if (level.sight_client == NULL) - { - start = 1; - } - else - { - start = level.sight_client - g_edicts; - } - - check = start; - - while (1) - { - check++; - - if (check > game.maxclients) - { - check = 1; - } - - ent = &g_edicts[check]; - - if (ent->inuse && (ent->health > 0) && - !(ent->flags & (FL_NOTARGET | FL_DISGUISED))) - { - level.sight_client = ent; - return; /* got one */ - } - - if (check == start) - { - level.sight_client = NULL; - return; /* nobody to see */ - } - } -} - -/* - * Move the specified distance at current facing. - */ -void -ai_move(edict_t *self, float dist) -{ - if (!self) - { - return; - } - - M_walkmove(self, self->s.angles[YAW], dist); -} - -/* - * - * Used for standing around and looking - * for players Distance is for slight - * position adjustments needed by the - * animations - */ -void -ai_stand(edict_t *self, float dist) -{ - vec3_t v; - qboolean retval; - - if (!self) - { - return; - } - - if (dist) - { - M_walkmove(self, self->s.angles[YAW], dist); - } - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - if (self->enemy) - { - VectorSubtract(self->enemy->s.origin, self->s.origin, v); - self->ideal_yaw = vectoyaw(v); - - if ((self->s.angles[YAW] != self->ideal_yaw) && - self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) - { - self->monsterinfo.aiflags &= - ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); - self->monsterinfo.run(self); - } - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } - - /* find out if we're going to be shooting */ - retval = ai_checkattack(self, 0); - - /* record sightings of player */ - if ((self->enemy) && (self->enemy->inuse) && - (visible(self, self->enemy))) - { - self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); - VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); - self->monsterinfo.trail_time = level.time; - self->monsterinfo.blind_fire_delay = 0; - } - /* check retval to make sure we're not blindfiring */ - else if (!retval) - { - FindTarget(self); - return; - } - } - else - { - FindTarget(self); - } - - return; - } - - if (FindTarget(self)) - { - return; - } - - if (level.time > self->monsterinfo.pausetime) - { - self->monsterinfo.walk(self); - return; - } - - if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && - (level.time > self->monsterinfo.idle_time)) - { - if (self->monsterinfo.idle_time) - { - self->monsterinfo.idle(self); - self->monsterinfo.idle_time = level.time + 15 + random() * 15; - } - else - { - self->monsterinfo.idle_time = level.time + random() * 15; - } - } -} - -/* - * The monster is walking it's beat - */ -void -ai_walk(edict_t *self, float dist) -{ - M_MoveToGoal(self, dist); - - if (!self) - { - return; - } - - /* check for noticing a player */ - if (FindTarget(self)) - { - return; - } - - if ((self->monsterinfo.search) && - (level.time > self->monsterinfo.idle_time)) - { - if (self->monsterinfo.idle_time) - { - self->monsterinfo.search(self); - self->monsterinfo.idle_time = level.time + 15 + random() * 15; - } - else - { - self->monsterinfo.idle_time = level.time + random() * 15; - } - } -} - -/* - * Turns towards target and advances - * Use this call with a distnace of 0 - * to replace ai_face - */ -void -ai_charge(edict_t *self, float dist) -{ - vec3_t v; - float ofs; - - if (!self) - { - return; - } - - if (!self->enemy || !self->enemy->inuse) - { - return; - } - - if (visible(self, self->enemy)) - { - VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); - } - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - VectorSubtract(self->enemy->s.origin, self->s.origin, v); - self->ideal_yaw = vectoyaw(v); - } - - M_ChangeYaw(self); - - if (dist) - { - if (self->monsterinfo.aiflags & AI_CHARGING) - { - M_MoveToGoal(self, dist); - return; - } - - /* circle strafe support */ - if (self->monsterinfo.attack_state == AS_SLIDING) - { - /* if we're fighting a tesla, NEVER circle strafe */ - if ((self->enemy) && (self->enemy->classname) && - (!strcmp(self->enemy->classname, "tesla"))) - { - ofs = 0; - } - else if (self->monsterinfo.lefty) - { - ofs = 90; - } - else - { - ofs = -90; - } - - if (M_walkmove(self, self->ideal_yaw + ofs, dist)) - { - return; - } - - self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; - M_walkmove(self, self->ideal_yaw - ofs, dist); - } - else - { - M_walkmove(self, self->s.angles[YAW], dist); - } - } -} - -/* - * Don't move, but turn towards - * ideal_yaw. Distance is for - * slight position adjustments - * needed by the animations - */ -void -ai_turn(edict_t *self, float dist) -{ - if (!self) - { - return; - } - - if (dist) - { - M_walkmove(self, self->s.angles[YAW], dist); - } - - if (FindTarget(self)) - { - return; - } - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } -} - -/* ========================================================================== */ - -/* - * .enemy - * Will be world if not currently angry at anyone. - * - * .movetarget - * The next path spot to walk toward. If .enemy, ignore .movetarget. - * When an enemy is killed, the monster will try to return to it's path. - * - * .hunt_time - * Set to time + something when the player is in sight, but movement straight for - * him is blocked. This causes the monster to use wall following code for - * movement direction instead of sighting on the player. - * - * .ideal_yaw - * A yaw angle of the intended direction, which will be turned towards at up - * to 45 deg / state. If the enemy is in view and hunt_time is not active, - * this will be the exact line towards the enemy. - * - * .pausetime - * A monster will leave it's stand state and head towards it's .movetarget when - * time > .pausetime. - */ - -/* ========================================================================== */ - -/* - * returns the range catagorization of an entity reletive to self - * 0 melee range, will become hostile even if back is turned - * 1 visibility and infront, or visibility and show hostile - * 2 infront and show hostile - * 3 only triggered by damage - */ -int -range(edict_t *self, edict_t *other) -{ - vec3_t v; - float len; - - if (!self || !other) - { - return 0; - } - - VectorSubtract(self->s.origin, other->s.origin, v); - len = VectorLength(v); - - if (len < MELEE_DISTANCE) - { - return RANGE_MELEE; - } - - if (len < 500) - { - return RANGE_NEAR; - } - - if (len < 1000) - { - return RANGE_MID; - } - - return RANGE_FAR; -} - -/* - * returns 1 if the entity is visible - * to self, even if not infront - */ -qboolean -visible(edict_t *self, edict_t *other) -{ - vec3_t spot1; - vec3_t spot2; - trace_t trace; - - if (!self || !other) - { - return false; - } - - VectorCopy(self->s.origin, spot1); - spot1[2] += self->viewheight; - VectorCopy(other->s.origin, spot2); - spot2[2] += other->viewheight; - trace = gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); - - if ((trace.fraction == 1.0) || (trace.ent == other)) - { - return true; - } - - return false; -} - -/* - * returns 1 if the entity is in - * front (in sight) of self - */ -qboolean -infront(edict_t *self, edict_t *other) -{ - vec3_t vec; - float dot; - vec3_t forward; - - if (!self || !other) - { - return false; - } - - AngleVectors(self->s.angles, forward, NULL, NULL); - - VectorSubtract(other->s.origin, self->s.origin, vec); - VectorNormalize(vec); - dot = DotProduct(vec, forward); - - if (dot > 0.3) - { - return true; - } - - return false; -} - -/* ============================================================================ */ - -void -HuntTarget(edict_t *self) -{ - vec3_t vec; - - if (!self) - { - return; - } - - self->goalentity = self->enemy; - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - self->monsterinfo.stand(self); - } - else - if (self->monsterinfo.run) - { - self->monsterinfo.run(self); - } - - if(visible(self, self->enemy)) - { - VectorSubtract(self->enemy->s.origin, self->s.origin, vec); - } - - self->ideal_yaw = vectoyaw(vec); - - /* wait a while before first attack */ - if (!(self->monsterinfo.aiflags & AI_STAND_GROUND)) - { - AttackFinished(self, 1); - } -} - -void -FoundTarget(edict_t *self) -{ - if (!self|| !self->enemy || !self->enemy->inuse) - { - return; - } - - /* let other monsters see this monster for a while */ - if (self->enemy->client) - { - if (self->enemy->flags & FL_DISGUISED) - { - self->enemy->flags &= ~FL_DISGUISED; - } - - level.sight_entity = self; - level.sight_entity_framenum = level.framenum; - level.sight_entity->light_level = 128; - } - - self->show_hostile = level.time + 1; /* wake up other monsters */ - - VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); - self->monsterinfo.trail_time = level.time; - VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); - self->monsterinfo.blind_fire_delay = 0; - - if (!self->combattarget) - { - HuntTarget(self); - return; - } - - self->goalentity = self->movetarget = G_PickTarget(self->combattarget); - - if (!self->movetarget) - { - self->goalentity = self->movetarget = self->enemy; - HuntTarget(self); - gi.dprintf("%s at %s, combattarget %s not found\n", - self->classname, vtos(self->s.origin), - self->combattarget); - return; - } - - /* clear out our combattarget, these are a one shot deal */ - self->combattarget = NULL; - self->monsterinfo.aiflags |= AI_COMBAT_POINT; - - /* clear the targetname, that point is ours! */ - self->movetarget->targetname = NULL; - self->monsterinfo.pausetime = 0; - - /* run for it */ - self->monsterinfo.run(self); -} - -/* - * Self is currently not attacking anything, - * so try to find a target - * - * Returns TRUE if an enemy was sighted - * - * When a player fires a missile, the point - * of impact becomes a fakeplayer so that - * monsters that see the impact will respond - * as if they had seen the player. - * - * To avoid spending too much time, only - * a single client (or fakeclient) is - * checked each frame. This means multi - * player games will have slightly - * slower noticing monsters. - */ -qboolean -FindTarget(edict_t *self) -{ - edict_t *client; - qboolean heardit; - int r; - - if (!self) - { - return false; - } - - if (self->monsterinfo.aiflags & AI_GOOD_GUY) - { - return false; - } - - /* if we're going to a combat point, just proceed */ - if (self->monsterinfo.aiflags & AI_COMBAT_POINT) - { - return false; - } - - /* if the first spawnflag bit is set, the monster - will only wake up on really seeing the player, - not another monster getting angry or hearing - something */ - - heardit = false; - - if ((level.sight_entity_framenum >= (level.framenum - 1)) && - !(self->spawnflags & 1)) - { - client = level.sight_entity; - - if (client->enemy == self->enemy) - { - return false; - } - } - else if (level.disguise_violation_framenum > level.framenum) - { - client = level.disguise_violator; - } - else if (level.sound_entity_framenum >= (level.framenum - 1)) - { - client = level.sound_entity; - heardit = true; - } - else if (!(self->enemy) && - (level.sound2_entity_framenum >= (level.framenum - 1)) && - !(self->spawnflags & 1)) - { - client = level.sound2_entity; - heardit = true; - } - else - { - client = level.sight_client; - } - - /* if the entity went away, forget it */ - if (!client || !client->inuse || - (client->client && level.intermissiontime)) - { - return false; - } - - if (client == self->enemy) - { - return true; - } - - if ((self->monsterinfo.aiflags & AI_HINT_PATH) && (coop) && (coop->value)) - { - heardit = false; - } - - if (client->client) - { - if (client->flags & FL_NOTARGET) - { - return false; - } - } - else if (client->svflags & SVF_MONSTER) - { - if (!client->enemy) - { - return false; - } - - if (client->enemy->flags & FL_NOTARGET) - { - return false; - } - } - else if (heardit) - { - if ((client->owner) && (client->owner->flags & FL_NOTARGET)) - { - return false; - } - } - else - { - return false; - } - - if (!heardit) - { - r = range(self, client); - - if (r == RANGE_FAR) - { - return false; - } - - /* is client in an spot too dark to be seen? */ - if (client->light_level <= 5) - { - return false; - } - - if (!visible(self, client)) - { - return false; - } - - if (r == RANGE_NEAR) - { - if ((client->show_hostile < level.time) && !infront(self, client)) - { - return false; - } - } - else if (r == RANGE_MID) - { - if (!infront(self, client)) - { - return false; - } - } - - self->enemy = client; - - if (strcmp(self->enemy->classname, "player_noise") != 0) - { - self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; - - if (!self->enemy->client) - { - self->enemy = self->enemy->enemy; - - if (!self->enemy->client) - { - self->enemy = NULL; - return false; - } - } - } - } - else /* heardit */ - { - vec3_t temp; - - if (self->spawnflags & 1) - { - if (!visible(self, client)) - { - return false; - } - } - else - { - if (!gi.inPHS(self->s.origin, client->s.origin)) - { - return false; - } - } - - VectorSubtract(client->s.origin, self->s.origin, temp); - - if (VectorLength(temp) > 1000) /* too far to hear */ - { - return false; - } - - /* check area portals - if they are different - and not connected then we can't hear it */ - if (client->areanum != self->areanum) - { - if (!gi.AreasConnected(self->areanum, client->areanum)) - { - return false; - } - } - - self->ideal_yaw = vectoyaw(temp); - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } - - /* hunt the sound for a bit; hopefully find the real player */ - self->monsterinfo.aiflags |= AI_SOUND_TARGET; - self->enemy = client; - } - - /* if we got an enemy, we need to bail out of - hint paths, so take over here */ - if (self->monsterinfo.aiflags & AI_HINT_PATH) - { - /* this calls foundtarget for us */ - hintpath_stop(self); - } - else - { - FoundTarget(self); - } - - if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && - (self->monsterinfo.sight)) - { - self->monsterinfo.sight(self, self->enemy); - } - - return true; -} - -/* ============================================================================= */ - -qboolean -FacingIdeal(edict_t *self) -{ - float delta; - - if (!self) - { - return false; - } - - delta = anglemod(self->s.angles[YAW] - self->ideal_yaw); - - if ((delta > 45) && (delta < 315)) - { - return false; - } - - return true; -} - -/* ============================================================================= */ - -qboolean -M_CheckAttack(edict_t *self) -{ - vec3_t spot1, spot2; - float chance; - trace_t tr; - - if (!self || !self->enemy || !self->enemy->inuse) - { - return false; - } - - if (self->enemy->health > 0) - { - /* see if any entities are in the way of the shot */ - VectorCopy(self->s.origin, spot1); - spot1[2] += self->viewheight; - VectorCopy(self->enemy->s.origin, spot2); - spot2[2] += self->enemy->viewheight; - - tr = gi.trace(spot1, NULL, NULL, spot2, self, - CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA | - CONTENTS_WINDOW); - - /* do we have a clear shot? */ - if (tr.ent != self->enemy) - { - /* we want them to go ahead and shoot at info_notnulls if they can. */ - if ((self->enemy->solid != SOLID_NOT) || (tr.fraction < 1.0)) - { - /* if we can't see our target, and we're not - blocked by a monster, go into blind fire - if available */ - if ((!(tr.ent->svflags & SVF_MONSTER)) && - (!visible(self, self->enemy))) - { - if ((self->monsterinfo.blindfire) && - (self->monsterinfo.blind_fire_delay <= 20.0)) - { - if (level.time < self->monsterinfo.attack_finished) - { - return false; - } - - if (level.time < - (self->monsterinfo.trail_time + - self->monsterinfo.blind_fire_delay)) - { - /* wait for our time */ - return false; - } - else - { - /* make sure we're not going to shoot a monster */ - tr = gi.trace(spot1, NULL, NULL, - self->monsterinfo.blind_fire_target, - self, CONTENTS_MONSTER); - - if (tr.allsolid || tr.startsolid || - ((tr.fraction < 1.0) && - (tr.ent != self->enemy))) - { - return false; - } - - self->monsterinfo.attack_state = AS_BLIND; - return true; - } - } - } - - return false; - } - } - } - - /* melee attack */ - if (enemy_range == RANGE_MELEE) - { - /* don't always melee in easy mode */ - if ((skill->value == SKILL_EASY) && (rand() & 3)) - { - /* fix for melee only monsters & strafing */ - self->monsterinfo.attack_state = AS_STRAIGHT; - return false; - } - - if (self->monsterinfo.melee) - { - self->monsterinfo.attack_state = AS_MELEE; - } - else - { - self->monsterinfo.attack_state = AS_MISSILE; - } - - return true; - } - - /* missile attack */ - if (!self->monsterinfo.attack) - { - /* fix for melee only monsters & strafing */ - self->monsterinfo.attack_state = AS_STRAIGHT; - return false; - } - - if (level.time < self->monsterinfo.attack_finished) - { - return false; - } - - if (enemy_range == RANGE_FAR) - { - return false; - } - - if (self->monsterinfo.aiflags & AI_STAND_GROUND) - { - chance = 0.4; - } - else if (enemy_range == RANGE_NEAR) - { - chance = 0.1; - } - else if (enemy_range == RANGE_MID) - { - chance = 0.02; - } - else - { - return false; - } - - if (skill->value == SKILL_EASY) - { - chance *= 0.5; - } - else if (skill->value >= SKILL_HARD) - { - chance *= 2; - } - - /* go ahead and shoot every time if it's a info_notnull */ - if ((random() < chance) || (self->enemy->solid == SOLID_NOT)) - { - self->monsterinfo.attack_state = AS_MISSILE; - self->monsterinfo.attack_finished = level.time + 2 * random(); - return true; - } - - /* daedalus should strafe more.. this can be done - here or in a customized check_attack code for - the hover. */ - if (self->flags & FL_FLY) - { - /* originally, just 0.3 */ - float strafe_chance; - - if (!(strcmp(self->classname, "monster_daedalus"))) - { - strafe_chance = 0.8; - } - else - { - strafe_chance = 0.6; - } - - /* if enemy is tesla, never strafe */ - if ((self->enemy->classname) && - (!strcmp(self->enemy->classname, "tesla"))) - { - strafe_chance = 0; - } - - if (random() < strafe_chance) - { - self->monsterinfo.attack_state = AS_SLIDING; - } - else - { - self->monsterinfo.attack_state = AS_STRAIGHT; - } - } - else - { - /* do we want the monsters strafing? */ - if (random() < 0.4) - { - self->monsterinfo.attack_state = AS_SLIDING; - } - else - { - self->monsterinfo.attack_state = AS_STRAIGHT; - } - } - - return false; -} - -/* - * Turn and close until within an - * angle to launch a melee attack - */ -void -ai_run_melee(edict_t *self) -{ - if (!self) - { - return; - } - - self->ideal_yaw = enemy_yaw; - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } - - if (FacingIdeal(self)) - { - if (self->monsterinfo.melee) - { - self->monsterinfo.melee(self); - self->monsterinfo.attack_state = AS_STRAIGHT; - } - } -} - -/* - * Turn in place until within an - * angle to launch a missile attack - */ -void -ai_run_missile(edict_t *self) -{ - if (!self) - { - return; - } - - self->ideal_yaw = enemy_yaw; - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } - - if (FacingIdeal(self)) - { - if (self->monsterinfo.attack) - { - self->monsterinfo.attack(self); - - if ((self->monsterinfo.attack_state == AS_MISSILE) || - (self->monsterinfo.attack_state == AS_BLIND)) { - self->monsterinfo.attack_state = AS_STRAIGHT; - } - } - } -} - -/* - * Strafe sideways, but stay at - * aproximately the same range - */ -void -ai_run_slide(edict_t *self, float distance) -{ - float ofs; - float angle; - - if (!self) - { - return; - } - - self->ideal_yaw = enemy_yaw; - angle = 90; - - if (self->monsterinfo.lefty) - { - ofs = angle; - } - else - { - ofs = -angle; - } - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } - - /* clamp maximum sideways move for non flyers to make them look less jerky */ - if (!(self->flags & FL_FLY)) - { - distance = min(distance, 8.0); - } - - if (M_walkmove(self, self->ideal_yaw + ofs, distance)) - { - return; - } - - /* if we're dodging, give up on it and go straight */ - if (self->monsterinfo.aiflags & AI_DODGING) - { - monster_done_dodge(self); - self->monsterinfo.attack_state = AS_STRAIGHT; - return; - } - - self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; - - if (M_walkmove(self, self->ideal_yaw - ofs, distance)) - { - return; - } - - /* if we're dodging, give up on it and go straight */ - if (self->monsterinfo.aiflags & AI_DODGING) - { - monster_done_dodge(self); - } - - /* the move failed, so signal the caller (ai_run) to try going straight */ - self->monsterinfo.attack_state = AS_STRAIGHT; -} - -/* - * Decides if we're going to attack - * or do something else used by - * ai_run and ai_stand - */ -static qboolean -hesDeadJim(const edict_t *self) -{ - const edict_t *enemy = self->enemy; - - if (!enemy || !enemy->inuse) - { - return true; - } - - if (self->monsterinfo.aiflags & AI_MEDIC) - { - return (enemy->health > 0); - } - - if (enemy->client && level.intermissiontime) - { - return true; - } - - if (self->monsterinfo.aiflags & AI_BRUTAL) - { - return (enemy->health <= -80); - } - - return (enemy->health <= 0); -} - -qboolean -ai_checkattack(edict_t *self, float dist) -{ - vec3_t temp; - qboolean retval; - - if (!self) - { - enemy_vis = false; - - return false; - } - - /* this causes monsters to run blindly - to the combat point w/o firing */ - if (self->goalentity) - { - if (self->monsterinfo.aiflags & AI_COMBAT_POINT) - { - return false; - } - - if ((self->monsterinfo.aiflags & AI_SOUND_TARGET) && !visible(self, self->goalentity)) - { - if ((level.time - self->enemy->last_sound_time) > 5.0) - { - if (self->goalentity == self->enemy) - { - if (self->movetarget) - { - self->goalentity = self->movetarget; - } - else - { - self->goalentity = NULL; - } - } - - self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; - - if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) - { - self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); - } - } - else - { - self->show_hostile = level.time + 1; - return false; - } - } - } - - enemy_vis = false; - - /* see if the enemy is dead */ - if (hesDeadJim(self)) - { - self->enemy = NULL; - self->monsterinfo.aiflags &= ~AI_MEDIC; - - if (self->oldenemy && (self->oldenemy->health > 0)) - { - self->enemy = self->oldenemy; - self->oldenemy = NULL; - HuntTarget(self); - } - else if (self->monsterinfo.last_player_enemy && - (self->monsterinfo.last_player_enemy->health > 0)) - { - self->enemy = self->monsterinfo.last_player_enemy; - self->oldenemy = NULL; - self->monsterinfo.last_player_enemy = NULL; - HuntTarget(self); - } - else - { - if (self->movetarget) - { - self->goalentity = self->movetarget; - self->monsterinfo.walk(self); - } - else - { - /* we need the pausetime otherwise the stand code - will just revert to walking with no target and - the monsters will wonder around aimlessly trying - to hunt the world entity */ - self->monsterinfo.pausetime = level.time + 100000000; - self->monsterinfo.stand(self); - } - - return true; - } - } - - self->show_hostile = level.time + 1; /* wake up other monsters */ - - /* check knowledge of enemy */ - enemy_vis = visible(self, self->enemy); - - if (enemy_vis) - { - self->monsterinfo.search_time = level.time + 5; - VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); - self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - self->monsterinfo.trail_time = level.time; - VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); - self->monsterinfo.blind_fire_delay = 0; - } - - if (coop && coop->value && (self->monsterinfo.search_time < level.time)) - { - if (FindTarget(self)) - { - return true; - } - } - - if (self->enemy) - { - enemy_infront = infront(self, self->enemy); - enemy_range = range(self, self->enemy); - VectorSubtract(self->enemy->s.origin, self->s.origin, temp); - enemy_yaw = vectoyaw(temp); - } - - retval = self->monsterinfo.checkattack(self); - - if (retval) - { - if (self->monsterinfo.attack_state == AS_MISSILE) - { - ai_run_missile(self); - return true; - } - - if (self->monsterinfo.attack_state == AS_MELEE) - { - ai_run_melee(self); - return true; - } - - /* added so monsters can shoot blind */ - if (self->monsterinfo.attack_state == AS_BLIND) - { - ai_run_missile(self); - return true; - } - - /* if enemy is not currently visible, - we will never attack */ - if (!enemy_vis) - { - return false; - } - } - - return retval; -} - -/* - * The monster has an enemy - * it is trying to kill - */ -void -ai_run(edict_t *self, float dist) -{ - vec3_t v; - edict_t *tempgoal; - edict_t *save; - qboolean new; - edict_t *marker; - float d1, d2; - trace_t tr; - vec3_t v_forward, v_right; - float left, center, right; - vec3_t left_target, right_target; - qboolean retval; - qboolean alreadyMoved = false; - qboolean gotcha = false; - edict_t *realEnemy; - - if (!self) - { - return; - } - - /* if we're going to a combat point, just proceed */ - if (self->monsterinfo.aiflags & AI_COMBAT_POINT) - { - M_MoveToGoal(self, dist); - return; - } - - if (self->monsterinfo.aiflags & AI_DUCKED) - { - self->monsterinfo.aiflags &= ~AI_DUCKED; - } - - if (self->maxs[2] != self->monsterinfo.base_height) - { - monster_duck_up(self); - } - - /* if we're currently looking for a hint path */ - if (self->monsterinfo.aiflags & AI_HINT_PATH) - { - M_MoveToGoal(self, dist); - - if (!self->inuse) - { - return; - } - - /* first off, make sure we're looking for - the player, not a noise he made */ - if (self->enemy) - { - if (self->enemy->inuse) - { - if (strcmp(self->enemy->classname, "player_noise") != 0) - { - realEnemy = self->enemy; - } - else if (self->enemy->owner) - { - realEnemy = self->enemy->owner; - } - else /* uh oh, can't figure out enemy, bail */ - { - self->enemy = NULL; - hintpath_stop(self); - return; - } - } - else - { - self->enemy = NULL; - hintpath_stop(self); - return; - } - } - else - { - hintpath_stop(self); - return; - } - - if (visible(self, realEnemy)) - { - gotcha = true; - } - else if (coop->value) - { - FindTarget(self); - } - - /* if we see the player, stop following hintpaths. */ - if (gotcha) - { - /* disconnect from hintpaths and start looking normally for players. */ - hintpath_stop(self); - } - - return; - } - - if (self->monsterinfo.aiflags & AI_SOUND_TARGET) - { - /* paranoia checking */ - if (self->enemy) - { - VectorSubtract(self->s.origin, self->enemy->s.origin, v); - } - - if ((!self->enemy) || (VectorLength(v) < 64)) - { - self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); - self->monsterinfo.stand(self); - return; - } - - M_MoveToGoal(self, dist); - /* prevent double moves for sound_targets */ - alreadyMoved = true; - - if (!self->inuse) - { - return; - } - - if (!FindTarget(self)) - { - return; - } - } - - retval = ai_checkattack(self, dist); - - /* don't strafe if we can't see our enemy */ - if ((!enemy_vis) && (self->monsterinfo.attack_state == AS_SLIDING)) - { - self->monsterinfo.attack_state = AS_STRAIGHT; - } - - /* unless we're dodging (dodging out of view looks smart) */ - if (self->monsterinfo.aiflags & AI_DODGING) - { - self->monsterinfo.attack_state = AS_SLIDING; - } - - if (self->monsterinfo.attack_state == AS_SLIDING) - { - /* protect against double moves */ - if (!alreadyMoved) - { - ai_run_slide(self, dist); - } - - /* we're using attack_state as the return value out of - ai_run_slide to indicate whether or not the move - succeeded. If the move succeeded, and we're still - sliding, we're done in here (since we've had our - chance to shoot in ai_checkattack, and have moved). - if the move failed, our state is as_straight, and - it will be taken care of below */ - if ((!retval) && (self->monsterinfo.attack_state == AS_SLIDING)) - { - return; - } - } - else if (self->monsterinfo.aiflags & AI_CHARGING) - { - self->ideal_yaw = enemy_yaw; - - if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING)) - { - M_ChangeYaw(self); - } - } - - if (retval) - { - if ((dist != 0) && (!alreadyMoved) && - (self->monsterinfo.attack_state == AS_STRAIGHT) && - (!(self->monsterinfo.aiflags & AI_STAND_GROUND))) - { - M_MoveToGoal(self, dist); - } - - if ((self->enemy) && (self->enemy->inuse) && (enemy_vis)) - { - self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); - self->monsterinfo.trail_time = level.time; - VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); - self->monsterinfo.blind_fire_delay = 0; - } - - return; - } - - if ((self->enemy) && (self->enemy->inuse) && (enemy_vis)) - { - /* check for alreadyMoved */ - if (!alreadyMoved) - { - M_MoveToGoal(self, dist); - } - - if (!self->inuse) - { - return; - } - - self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; - VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); - self->monsterinfo.trail_time = level.time; - VectorCopy(self->enemy->s.origin, self->monsterinfo.blind_fire_target); - self->monsterinfo.blind_fire_delay = 0; - - return; - } - - if ((self->monsterinfo.trail_time + 5) <= level.time) - { - /* and we haven't checked for valid hint paths in the last 10 seconds */ - if ((self->monsterinfo.last_hint_time + 10) <= level.time) - { - /* check for hint_paths. */ - self->monsterinfo.last_hint_time = level.time; - - if (monsterlost_checkhint(self)) - { - return; - } - } - } - - if ((self->monsterinfo.search_time) && - (level.time > (self->monsterinfo.search_time + 20))) - { - /* double move protection */ - if (!alreadyMoved) - { - M_MoveToGoal(self, dist); - } - - self->monsterinfo.search_time = 0; - return; - } - - tempgoal = G_SpawnOptional(); - - if (!tempgoal) - { - M_MoveToGoal(self, dist); - return; - } - - save = self->goalentity; - self->goalentity = tempgoal; - - new = false; - - if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT)) - { - /* just lost sight of the player, decide where to go first */ - self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN); - self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP); - new = true; - } - - if (self->monsterinfo.aiflags & AI_PURSUE_NEXT) - { - self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT; - - /* give ourself more time since we got this far */ - self->monsterinfo.search_time = level.time + 5; - - if (self->monsterinfo.aiflags & AI_PURSUE_TEMP) - { - self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP; - marker = NULL; - VectorCopy(self->monsterinfo.saved_goal, self->monsterinfo.last_sighting); - new = true; - } - else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN) - { - self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN; - marker = PlayerTrail_PickFirst(self); - } - else - { - marker = PlayerTrail_PickNext(self); - } - - if (marker) - { - VectorCopy(marker->s.origin, self->monsterinfo.last_sighting); - self->monsterinfo.trail_time = marker->timestamp; - self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW]; - new = true; - } - } - - VectorSubtract(self->s.origin, self->monsterinfo.last_sighting, v); - d1 = VectorLength(v); - - if (d1 <= dist) - { - self->monsterinfo.aiflags |= AI_PURSUE_NEXT; - dist = d1; - } - - VectorCopy(self->monsterinfo.last_sighting, self->goalentity->s.origin); - - if (new) - { - tr = gi.trace(self->s.origin, self->mins, self->maxs, - self->monsterinfo.last_sighting, self, - MASK_PLAYERSOLID); - - if (tr.fraction < 1) - { - VectorSubtract(self->goalentity->s.origin, self->s.origin, v); - d1 = VectorLength(v); - center = tr.fraction; - d2 = d1 * ((center + 1) / 2); - self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); - AngleVectors(self->s.angles, v_forward, v_right, NULL); - - VectorSet(v, d2, -16, 0); - G_ProjectSource(self->s.origin, v, v_forward, v_right, left_target); - tr = gi.trace(self->s.origin, self->mins, self->maxs, - left_target, self, MASK_PLAYERSOLID); - left = tr.fraction; - - VectorSet(v, d2, 16, 0); - G_ProjectSource(self->s.origin, v, v_forward, v_right, right_target); - tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, - self, MASK_PLAYERSOLID); - right = tr.fraction; - - center = (d1 * center) / d2; - - if ((left >= center) && (left > right)) - { - if (left < 1) - { - VectorSet(v, d2 * left * 0.5, -16, 0); - G_ProjectSource(self->s.origin, v, v_forward, - v_right, left_target); - } - - VectorCopy(self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); - self->monsterinfo.aiflags |= AI_PURSUE_TEMP; - VectorCopy(left_target, self->goalentity->s.origin); - VectorCopy(left_target, self->monsterinfo.last_sighting); - VectorSubtract(self->goalentity->s.origin, self->s.origin, v); - self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); - } - else if ((right >= center) && (right > left)) - { - if (right < 1) - { - VectorSet(v, d2 * right * 0.5, 16, 0); - G_ProjectSource(self->s.origin, v, v_forward, v_right, - right_target); - } - - VectorCopy(self->monsterinfo.last_sighting, - self->monsterinfo.saved_goal); - self->monsterinfo.aiflags |= AI_PURSUE_TEMP; - VectorCopy(right_target, self->goalentity->s.origin); - VectorCopy(right_target, self->monsterinfo.last_sighting); - VectorSubtract(self->goalentity->s.origin, self->s.origin, v); - self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); - } - } - } - - M_MoveToGoal(self, dist); - - if (!self->inuse) - { - return; - } - - G_FreeEdict(tempgoal); - - self->goalentity = save; -} diff --git a/src/rogue/g_chase.c b/src/rogue/g_chase.c deleted file mode 100644 index 75f61d18..00000000 --- a/src/rogue/g_chase.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (c) ZeniMax Media Inc. - * Licensed under the GNU General Public License 2.0. - */ - -/* - * ======================================================================= - * - * Chase cam. Only used in multiplayer mode. - * - * ======================================================================= - */ - -#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; - - if (!ent) - { - return; - } - - /* is our chase target gone? */ - if (!ent->client->chase_target->inuse || - ent->client->chase_target->client->resp.spectator) - { - edict_t *old = ent->client->chase_target; - ChaseNext(ent); - - if (ent->client->chase_target == old) - { - ent->client->chase_target = NULL; - ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; - 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; - } - - if (targ->deadflag) - { - ent->client->ps.pmove.pm_type = PM_DEAD; - } - else - { - 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]); - } - - if (targ->deadflag) - { - ent->client->ps.viewangles[ROLL] = 40; - ent->client->ps.viewangles[PITCH] = -15; - ent->client->ps.viewangles[YAW] = targ->client->killer_yaw; - } - else - { - 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); -} - -void -ChaseNext(edict_t *ent) -{ - int i; - edict_t *e; - - if (!ent) - { - return; - } - - 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->client->resp.spectator) - { - 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) - { - return; - } - - 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->client->resp.spectator) - { - break; - } - } - while (e != ent->client->chase_target); - - ent->client->chase_target = e; - ent->client->update_chase = true; -} - -void -GetChaseTarget(edict_t *ent) -{ - int i; - edict_t *other; - - if (!ent) - { - return; - } - - for (i = 1; i <= maxclients->value; i++) - { - other = g_edicts + i; - - if (other->inuse && !other->client->resp.spectator) - { - ent->client->chase_target = other; - ent->client->update_chase = true; - UpdateChaseCam(ent); - return; - } - } - - gi.centerprintf(ent, "No other players to chase."); -} diff --git a/src/rogue/g_combat.c b/src/rogue/g_combat.c deleted file mode 100644 index 4424ac6b..00000000 --- a/src/rogue/g_combat.c +++ /dev/null @@ -1,1177 +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. - * - * ======================================================================= - * - * Combat code like damage, death and so on. - * - * ======================================================================= - */ - -#include "header/local.h" - -void M_SetEffects(edict_t *self); - -/* - * clean up heal targets for medic - */ -void -cleanupHealTarget(edict_t *ent) -{ - if (!ent) - { - return; - } - - ent->monsterinfo.healer = NULL; - ent->takedamage = DAMAGE_YES; - ent->monsterinfo.aiflags &= ~AI_RESURRECTING; - M_SetEffects(ent); -} - -/* - * Returns true if the inflictor can - * directly damage the target. Used for - * explosions and melee attacks. - */ -qboolean -CanDamage(edict_t *targ, edict_t *inflictor) -{ - vec3_t dest; - trace_t trace; - - if (!targ || !inflictor) - { - return false; - } - - /* bmodels need special checking because their origin is 0,0,0 */ - if (targ->movetype == MOVETYPE_PUSH) - { - VectorAdd(targ->absmin, targ->absmax, dest); - VectorScale(dest, 0.5, dest); - trace = gi.trace(inflictor->s.origin, vec3_origin, - vec3_origin, dest, inflictor, MASK_SOLID); - - if (trace.fraction == 1.0) - { - return true; - } - - if (trace.ent == targ) - { - return true; - } - - return false; - } - - trace = gi.trace(inflictor->s.origin, vec3_origin, - vec3_origin, targ->s.origin, inflictor, - MASK_SOLID); - - if (trace.fraction == 1.0) - { - return true; - } - - VectorCopy(targ->s.origin, dest); - dest[0] += 15.0; - dest[1] += 15.0; - trace = gi.trace(inflictor->s.origin, vec3_origin, - vec3_origin, dest, inflictor, MASK_SOLID); - - if (trace.fraction == 1.0) - { - return true; - } - - VectorCopy(targ->s.origin, dest); - dest[0] += 15.0; - dest[1] -= 15.0; - trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, - dest, inflictor, MASK_SOLID); - - if (trace.fraction == 1.0) - { - return true; - } - - VectorCopy(targ->s.origin, dest); - dest[0] -= 15.0; - dest[1] += 15.0; - trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, - dest, inflictor, MASK_SOLID); - - if (trace.fraction == 1.0) - { - return true; - } - - VectorCopy(targ->s.origin, dest); - dest[0] -= 15.0; - dest[1] -= 15.0; - trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, - dest, inflictor, MASK_SOLID); - - if (trace.fraction == 1.0) - { - return true; - } - - return false; -} - -void -Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, - int damage, vec3_t point) -{ - if (!targ || !inflictor || !attacker) - { - return; - } - - /* 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. */ - if (targ->monsterinfo.aiflags & AI_DUCKED) - { - targ->monsterinfo.aiflags &= ~AI_DUCKED; - } - - if (targ->monsterinfo.aiflags & AI_MEDIC) - { - if (targ->enemy) - { - cleanupHealTarget(targ->enemy); - } - - /* clean up self */ - targ->monsterinfo.aiflags &= ~AI_MEDIC; - targ->enemy = attacker; - } - else - { - targ->enemy = attacker; - } - - if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) - { - /* free up slot for spawned monster if it's spawned */ - if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER) - { - if (targ->monsterinfo.commander && - targ->monsterinfo.commander->inuse && - !strcmp(targ->monsterinfo.commander->classname, "monster_carrier")) - { - targ->monsterinfo.commander->monsterinfo.monster_slots++; - } - } - - if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C) - { - if (targ->monsterinfo.commander) - { - if (targ->monsterinfo.commander->inuse && - !strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander")) - { - targ->monsterinfo.commander->monsterinfo.monster_slots++; - } - } - } - - if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW) - { - /* need to check this because we can - have variable numbers of coop players */ - if (targ->monsterinfo.commander && - targ->monsterinfo.commander->inuse && - !strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13)) - { - if (targ->monsterinfo.commander->monsterinfo.monster_used > 0) - { - targ->monsterinfo.commander->monsterinfo.monster_used--; - } - } - } - - if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) && - (!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT))) - { - level.killed_monsters++; - - if (coop->value && attacker->client) - { - attacker->client->resp.score++; - } - } - } - - if ((targ->movetype == MOVETYPE_PUSH) || - (targ->movetype == MOVETYPE_STOP) || - (targ->movetype == MOVETYPE_NONE)) - { - /* doors, triggers, etc */ - targ->die(targ, inflictor, attacker, damage, point); - return; - } - - if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) - { - targ->touch = NULL; - monster_death_use(targ); - } - - targ->die(targ, inflictor, attacker, damage, point); -} - -void -SpawnDamage(int type, vec3_t origin, vec3_t normal) -{ - gi.WriteByte(svc_temp_entity); - gi.WriteByte(type); - gi.WritePosition(origin); - gi.WriteDir(normal); - gi.multicast(origin, MULTICAST_PVS); -} - -/* - * targ entity that is being damaged - * inflictor entity that is causing the damage - * attacker entity that caused the inflictor to damage targ - * example: targ=monster, inflictor=rocket, attacker=player - * - * dir direction of the attack - * point point at which the damage is being inflicted - * normal normal vector from that point - * damage amount of damage being inflicted - * knockback force to be applied against targ as a result of the damage - * - * dflags -> these flags are used to control how T_Damage works - * DAMAGE_RADIUS damage was indirect (from a nearby explosion) - * DAMAGE_NO_ARMOR armor does not protect from this damage - * DAMAGE_ENERGY damage is from an energy based weapon - * DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles - * DAMAGE_BULLET damage is from a bullet (used for ricochets) - * DAMAGE_NO_PROTECTION kills godmode, armor, everything - */ -int -CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, - int damage, int dflags) -{ - gclient_t *client; - int save; - int power_armor_type; - int index = 0; - int damagePerCell; - int pa_te_type; - int power = 0; - int power_used; - - if (!ent) - { - return 0; - } - - if (!damage) - { - return 0; - } - - client = ent->client; - - if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR)) - { - return 0; - } - - if (client) - { - power_armor_type = PowerArmorType(ent); - - if (power_armor_type != POWER_ARMOR_NONE) - { - index = ITEM_INDEX(FindItem("Cells")); - power = client->pers.inventory[index]; - } - } - else if (ent->svflags & SVF_MONSTER) - { - power_armor_type = ent->monsterinfo.power_armor_type; - power = ent->monsterinfo.power_armor_power; - } - else - { - return 0; - } - - if (power_armor_type == POWER_ARMOR_NONE) - { - return 0; - } - - if (!power) - { - return 0; - } - - if (power_armor_type == POWER_ARMOR_SCREEN) - { - vec3_t vec; - float dot; - vec3_t forward; - - /* only works if damage point is in front */ - AngleVectors(ent->s.angles, forward, NULL, NULL); - VectorSubtract(point, ent->s.origin, vec); - VectorNormalize(vec); - dot = DotProduct(vec, forward); - - if (dot <= 0.3) - { - return 0; - } - - damagePerCell = 1; - pa_te_type = TE_SCREEN_SPARKS; - damage = damage / 3; - } - else - { - damagePerCell = 2; - pa_te_type = TE_SHIELD_SPARKS; - damage = (2 * damage) / 3; - } - - /* etf rifle */ - if (dflags & DAMAGE_NO_REG_ARMOR) - { - save = (power * damagePerCell) / 2; - } - else - { - save = power * damagePerCell; - } - - if (!save) - { - return 0; - } - - if (save > damage) - { - save = damage; - } - - SpawnDamage(pa_te_type, point, normal); - ent->powerarmor_time = level.time + 0.2; - - if (dflags & DAMAGE_NO_REG_ARMOR) - { - power_used = (save / damagePerCell) * 2; - } - else - { - power_used = save / damagePerCell; - } - - if (client) - { - client->pers.inventory[index] -= power_used; - } - else - { - ent->monsterinfo.power_armor_power -= power_used; - } - - return save; -} - -int -CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, - int damage, int te_sparks, int dflags) -{ - gclient_t *client; - int save; - int index; - gitem_t *armor; - - if (!ent) - { - return 0; - } - - if (!damage) - { - return 0; - } - - client = ent->client; - - if (!client) - { - return 0; - } - - if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR)) - { - return 0; - } - - index = ArmorIndex(ent); - - if (!index) - { - return 0; - } - - armor = GetItemByIndex(index); - - if (dflags & DAMAGE_ENERGY) - { - save = ceil(((gitem_armor_t *)armor->info)->energy_protection * damage); - } - else - { - save = ceil(((gitem_armor_t *)armor->info)->normal_protection * damage); - } - - if (save >= client->pers.inventory[index]) - { - save = client->pers.inventory[index]; - } - - if (!save) - { - return 0; - } - - client->pers.inventory[index] -= save; - SpawnDamage(te_sparks, point, normal); - - return save; -} - -void -M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) -{ - qboolean new_tesla; - - if (!targ || !attacker || !inflictor) - { - return; - } - - if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) - { - return; - } - - /* logic for tesla - if you are hit by a tesla, - and can't see who you should be mad at (attacker) - attack the tesla also, target the tesla if it's - a "new" tesla */ - if (!strcmp(inflictor->classname, "tesla")) - { - new_tesla = MarkTeslaArea(targ, inflictor); - - if (new_tesla || !targ->enemy) - { - TargetTesla(targ, inflictor); - } - - return; - } - - if ((attacker == targ) || (attacker == targ->enemy)) - { - return; - } - - /* if we are a good guy monster and - our attacker is a player or another - good guy, do not get mad at them */ - if (targ->monsterinfo.aiflags & AI_GOOD_GUY) - { - if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) - { - return; - } - } - - /* if we're currently mad at something - a target_anger made us mad at, ignore - damage */ - if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER) - { - float percentHealth; - - /* make sure whatever we were pissed at is still around. */ - if (targ->enemy->inuse) - { - percentHealth = (float)(targ->health) / (float)(targ->max_health); - - if (percentHealth > 0.33) - { - return; - } - } - - /* remove the target anger flag */ - targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER; - } - - /* if we're healing someone, do like above and try to stay with them */ - if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC)) - { - float percentHealth; - - percentHealth = (float)(targ->health) / (float)(targ->max_health); - - /* ignore it some of the time */ - if (targ->enemy->inuse && (percentHealth > 0.25)) - { - return; - } - - /* remove the medic flag */ - targ->monsterinfo.aiflags &= ~AI_MEDIC; - cleanupHealTarget(targ->enemy); - } - - /* if attacker is a client, get mad at them - because he's good and we're not */ - if (attacker->client) - { - targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET; - - /* this can only happen in coop (both new and - old enemies are clients) only switch if can't - see the current enemy */ - if (targ->enemy && targ->enemy->client) - { - if (visible(targ, targ->enemy)) - { - targ->oldenemy = attacker; - return; - } - - targ->oldenemy = targ->enemy; - } - - targ->enemy = attacker; - - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - { - FoundTarget(targ); - } - - return; - } - - if (((targ->flags & (FL_FLY | FL_SWIM)) == - (attacker->flags & (FL_FLY | FL_SWIM))) && - (strcmp(targ->classname, attacker->classname) != 0) && - !(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) && - !(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS)) - { - if (targ->enemy && targ->enemy->client) - { - targ->oldenemy = targ->enemy; - } - - targ->enemy = attacker; - - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - { - FoundTarget(targ); - } - } - /* if they *meant* to shoot us, then shoot back */ - else if (attacker->enemy == targ) - { - if (targ->enemy && targ->enemy->client) - { - targ->oldenemy = targ->enemy; - } - - targ->enemy = attacker; - - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - { - FoundTarget(targ); - } - } - /* otherwise get mad at whoever they are mad at (help our buddy) unless it is us! */ - else if (attacker->enemy) - { - if (targ->enemy && targ->enemy->client) - { - targ->oldenemy = targ->enemy; - } - - targ->enemy = attacker->enemy; - - if (!(targ->monsterinfo.aiflags & AI_DUCKED)) - { - FoundTarget(targ); - } - } -} - -qboolean -CheckTeamDamage(edict_t *targ, edict_t *attacker) -{ - return false; -} - -static void -apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale) -{ - vec3_t kvel; - float mass; - - if (!knockback) - { - return; - } - - mass = (targ->mass < 50) ? 50.0f : (float)targ->mass; - - VectorNormalize2(dir, kvel); - VectorScale(kvel, scale * (knockback / mass), kvel); - VectorAdd(targ->velocity, kvel, targ->velocity); -} - -void -T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, - vec3_t point, vec3_t normal, int damage, int knockback, int dflags, - int mod) -{ - gclient_t *client; - int take; - int save; - int asave; - int psave; - int te_sparks; - int sphere_notified; - - if (!targ || !inflictor || !attacker) - { - return; - } - - if (!targ->takedamage) - { - return; - } - - sphere_notified = false; - - /* friendly fire avoidance. If enabled you can't - hurt teammates (but you can hurt yourself) - knockback still occurs */ - if ((targ != attacker) && ((deathmatch->value && - ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || - coop->value)) - { - if (OnSameTeam(targ, attacker)) - { - /* nukes kill everyone */ - if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && - (mod != MOD_NUKE)) - { - damage = 0; - } - else - { - mod |= MOD_FRIENDLY_FIRE; - } - } - } - - meansOfDeath = mod; - - /* allow the deathmatch game to change values */ - if (deathmatch->value && gamerules && gamerules->value) - { - if (DMGame.ChangeDamage) - { - damage = DMGame.ChangeDamage(targ, attacker, damage, mod); - } - - if (DMGame.ChangeKnockback) - { - knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod); - } - - if (!damage) - { - return; - } - } - - /* easy mode takes half damage */ - if ((skill->value == SKILL_EASY) && (deathmatch->value == 0) && targ->client) - { - damage *= 0.5; - - if (!damage) - { - damage = 1; - } - } - - client = targ->client; - - /* defender sphere takes half damage */ - if ((client) && (client->owned_sphere) && - (client->owned_sphere->spawnflags == 1)) - { - damage *= 0.5; - - if (!damage) - { - damage = 1; - } - } - - if (dflags & DAMAGE_BULLET) - { - te_sparks = TE_BULLET_SPARKS; - } - else - { - te_sparks = TE_SPARKS; - } - - /* bonus damage for suprising a monster */ - if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && - (attacker->client) && (!targ->enemy) && (targ->health > 0)) - { - damage *= 2; - } - - if (targ->flags & FL_NO_KNOCKBACK) - { - knockback = 0; - } - - /* figure momentum add */ - if (!(dflags & DAMAGE_NO_KNOCKBACK) && - (targ->movetype != MOVETYPE_NONE) && - (targ->movetype != MOVETYPE_BOUNCE) && - (targ->movetype != MOVETYPE_PUSH) && - (targ->movetype != MOVETYPE_STOP)) - { - apply_knockback (targ, dir, knockback, - ((client && attacker == targ) ? 1600.0f : 500.0f)); - } - - take = damage; - save = 0; - - /* check for godmode */ - if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION)) - { - take = 0; - save = damage; - SpawnDamage(te_sparks, point, normal); - } - - /* check for invincibility */ - if ((client && - (client->invincible_framenum > level.framenum)) && - !(dflags & DAMAGE_NO_PROTECTION)) - { - if (targ->pain_debounce_time < level.time) - { - gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); - targ->pain_debounce_time = level.time + 2; - } - - take = 0; - save = damage; - } - - /* check for monster invincibility */ - if (((targ->svflags & SVF_MONSTER) && - (targ->monsterinfo.invincible_framenum > level.framenum)) && - !(dflags & DAMAGE_NO_PROTECTION)) - { - if (targ->pain_debounce_time < level.time) - { - gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); - targ->pain_debounce_time = level.time + 2; - } - - take = 0; - save = damage; - } - - psave = CheckPowerArmor(targ, point, normal, take, dflags); - take -= psave; - - asave = CheckArmor(targ, point, normal, take, te_sparks, dflags); - take -= asave; - - /* treat cheat/powerup savings the same as armor */ - asave += save; - - /* this option will do damage both to the armor - and person. originally for DPU rounds */ - if (dflags & DAMAGE_DESTROY_ARMOR) - { - if (!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) && - !(client && (client->invincible_framenum > level.framenum))) - { - take = damage; - } - } - - /* do the damage */ - if (take) - { - /* need more blood for chainfist. */ - if (targ->flags & FL_MECHANICAL) - { - SpawnDamage(TE_ELECTRIC_SPARKS, point, normal); - } - else if ((targ->svflags & SVF_MONSTER) || (client)) - { - if (mod == MOD_CHAINFIST) - { - SpawnDamage(TE_MOREBLOOD, point, normal); - } - else - { - SpawnDamage(TE_BLOOD, point, normal); - } - } - else - { - SpawnDamage(te_sparks, point, normal); - } - - targ->health = targ->health - take; - - /* spheres need to know who to shoot at */ - if (client && client->owned_sphere) - { - sphere_notified = true; - - if (client->owned_sphere->pain) - { - client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0); - } - } - - if (targ->health <= 0) - { - if ((targ->svflags & SVF_MONSTER) || (client)) - { - targ->flags |= FL_NO_KNOCKBACK; - } - - Killed(targ, inflictor, attacker, take, point); - return; - } - } - - /* spheres need to know who to shoot at */ - if (!sphere_notified) - { - if (client && client->owned_sphere) - { - if (client->owned_sphere->pain) - { - client->owned_sphere->pain(client->owned_sphere, attacker, 0, - 0); - } - } - } - - if (targ->svflags & SVF_MONSTER) - { - M_ReactToDamage(targ, attacker, inflictor); - - if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) - { - targ->pain(targ, attacker, knockback, take); - - /* nightmare mode monsters don't go into pain frames often */ - if (skill->value == SKILL_HARDPLUS) - { - targ->pain_debounce_time = level.time + 5; - } - } - } - else if (client) - { - if (!(targ->flags & FL_GODMODE) && (take)) - { - targ->pain(targ, attacker, knockback, take); - } - } - else if (take) - { - if (targ->pain) - { - targ->pain(targ, attacker, knockback, take); - } - } - - /* add to the damage inflicted on a player this frame - the total will be turned into screen blends and view - angle kicks at the end of the frame */ - if (client) - { - client->damage_parmor += psave; - client->damage_armor += asave; - client->damage_blood += take; - client->damage_knockback += knockback; - VectorCopy(point, client->damage_from); - } -} - -void -T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, - edict_t *ignore, float radius, int mod) -{ - float points; - edict_t *ent = NULL; - vec3_t v; - vec3_t dir; - - if (!inflictor || !attacker) - { - return; - } - - while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) - { - if (ent == ignore) - { - continue; - } - - if (!ent->takedamage) - { - continue; - } - - VectorAdd(ent->mins, ent->maxs, v); - VectorMA(ent->s.origin, 0.5, v, v); - VectorSubtract(inflictor->s.origin, v, v); - points = damage - 0.5 * VectorLength(v); - - if (ent == attacker) - { - points = points * 0.5; - } - - if (points > 0) - { - if (CanDamage(ent, inflictor)) - { - VectorSubtract(ent->s.origin, inflictor->s.origin, dir); - T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, - vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, - mod); - } - } - } -} - -void -T_RadiusNukeDamage(edict_t *inflictor, edict_t *attacker, float damage, - edict_t *ignore, float radius, int mod) -{ - float points; - edict_t *ent = NULL; - vec3_t v; - vec3_t dir; - float len; - float killzone, killzone2; - trace_t tr; - float dist; - - killzone = radius; - killzone2 = radius * 2.0; - - if (!inflictor || !attacker || !ignore) - { - return; - } - - while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL) - { - /* ignore nobody */ - if (ent == ignore) - { - continue; - } - - if (!ent->takedamage) - { - continue; - } - - if (!ent->inuse) - { - continue; - } - - if (!(ent->client || (ent->svflags & SVF_MONSTER) || - (ent->svflags & SVF_DAMAGEABLE))) - { - continue; - } - - VectorAdd(ent->mins, ent->maxs, v); - VectorMA(ent->s.origin, 0.5, v, v); - VectorSubtract(inflictor->s.origin, v, v); - len = VectorLength(v); - - if (len <= killzone) - { - if (ent->client) - { - ent->flags |= FL_NOGIB; - } - - points = 10000; - } - else if (len <= killzone2) - { - points = (damage / killzone) * (killzone2 - len); - } - else - { - points = 0; - } - - if (points > 0) - { - if (ent->client) - { - ent->client->nuke_framenum = level.framenum + 20; - } - - VectorSubtract(ent->s.origin, inflictor->s.origin, dir); - T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, - vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, - mod); - } - } - - /* skip the worldspawn */ - ent = g_edicts + 1; - - /* cycle through players */ - while (ent) - { - if ((ent->client) && - (ent->client->nuke_framenum != level.framenum + 20) && (ent->inuse)) - { - tr = gi.trace(inflictor->s.origin, NULL, NULL, ent->s.origin, - inflictor, MASK_SOLID); - - if (tr.fraction == 1.0) - { - ent->client->nuke_framenum = level.framenum + 20; - } - else - { - dist = realrange(ent, inflictor); - - if (dist < 2048) - { - ent->client->nuke_framenum = max(ent->client->nuke_framenum, - level.framenum + 15); - } - else - { - ent->client->nuke_framenum = max(ent->client->nuke_framenum, - level.framenum + 10); - } - } - - ent++; - } - else - { - ent = NULL; - } - } -} - -/* - * Like T_RadiusDamage, but ignores - * anything with classname=ignoreClass - */ -void -T_RadiusClassDamage(edict_t *inflictor, edict_t *attacker, float damage, - char *ignoreClass, float radius, int mod) -{ - float points; - edict_t *ent = NULL; - vec3_t v; - vec3_t dir; - - if (!inflictor || !attacker || !ignoreClass) - { - return; - } - - while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) - { - if (ent->classname && !strcmp(ent->classname, ignoreClass)) - { - continue; - } - - if (!ent->takedamage) - { - continue; - } - - VectorAdd(ent->mins, ent->maxs, v); - VectorMA(ent->s.origin, 0.5, v, v); - VectorSubtract(inflictor->s.origin, v, v); - points = damage - 0.5 * VectorLength(v); - - if (ent == attacker) - { - points = points * 0.5; - } - - if (points > 0) - { - if (CanDamage(ent, inflictor)) - { - VectorSubtract(ent->s.origin, inflictor->s.origin, dir); - T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, - vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, - mod); - } - } - } -} diff --git a/src/rogue/g_func.c b/src/rogue/g_func.c deleted file mode 100644 index 12a0a819..00000000 --- a/src/rogue/g_func.c +++ /dev/null @@ -1,4249 +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. - * - * ======================================================================= - * - * Level functions. Platforms, buttons, dooors and so on. - * - * ======================================================================= - */ - -#include "header/local.h" - -#define PLAT_LOW_TRIGGER 1 -#define PLAT2_TOGGLE 2 -#define PLAT2_TOP 4 -#define PLAT2_TRIGGER_TOP 8 -#define PLAT2_TRIGGER_BOTTOM 16 -#define PLAT2_BOX_LIFT 32 - -#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 -#define DOOR_INACTIVE 8192 - -#define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2) - -#define PLAT2_CALLED 1 -#define PLAT2_MOVING 2 -#define PLAT2_WAITING 4 - -#define TRAIN_START_ON 1 -#define TRAIN_TOGGLE 2 -#define TRAIN_BLOCK_STOPS 4 - -#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 train_next(edict_t *self); -void door_go_down(edict_t *self); -void plat2_go_down(edict_t *ent); -void plat2_go_up(edict_t *ent); -void plat2_spawn_danger_area(edict_t *ent); -void plat2_kill_danger_area(edict_t *ent); -void Think_AccelMove(edict_t *ent); -void plat_go_down(edict_t *ent); - -/* - * ========================================================= - * - * 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 - * - * - * ========================================================= - */ - -/* Support routines for movement (changes in origin using velocity) */ - -void -Move_Done(edict_t *ent) -{ - if (!ent) - { - return; - } - - VectorClear(ent->velocity); - ent->moveinfo.endfunc(ent); -} - -void -Move_Final(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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) - { - return; - } - - 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 -Move_Calc(edict_t *ent, vec3_t dest, void (*func)(edict_t *)) -{ - if (!ent || !func) - { - return; - } - - 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) -{ - if (!ent) - { - return; - } - - VectorClear(ent->avelocity); - ent->moveinfo.endfunc(ent); -} - -void -AngleMove_Final(edict_t *ent) -{ - vec3_t move; - - if (!ent) - { - return; - } - - 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; - - if (!ent) - { - return; - } - - /* accelerate as needed */ - if (ent->moveinfo.speed < ent->speed) - { - ent->moveinfo.speed += ent->accel; - - if (ent->moveinfo.speed > ent->speed) - { - ent->moveinfo.speed = ent->speed; - } - } - - /* 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); - - /* if we're done accelerating, act as a normal rotation */ - if (ent->moveinfo.speed >= ent->speed) - { - /* set nextthink to trigger a think when dest is reached */ - ent->nextthink = level.time + frames * FRAMETIME; - ent->think = AngleMove_Final; - } - else - { - ent->nextthink = level.time + FRAMETIME; - ent->think = AngleMove_Begin; - } -} - -void -AngleMove_Calc(edict_t *ent, void (*func)(edict_t *)) -{ - if (!ent || !func) - { - return; - } - - VectorClear(ent->avelocity); - ent->moveinfo.endfunc = func; - - /* if we're supposed to accelerate, this will - tell anglemove_begin to do so */ - if (ent->accel != ent->speed) - { - ent->moveinfo.speed = 0; - } - - 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 - */ - -void -plat_CalcAcceleratedMove(moveinfo_t *moveinfo) -{ - float accel_dist; - float decel_dist; - - if (!moveinfo) - { - return; - } - - 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) -{ - if (!moveinfo) - { - return; - } - - /* 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; -} - -/* - * The team has completed a frame of movement, - * so change the speed for the next frame - */ -void -Think_AccelMove(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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_hit_top(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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) - { - return; - } - - 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; - - plat2_kill_danger_area(ent); -} - -void -plat_go_down(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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) - { - return; - } - - 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); - - plat2_spawn_danger_area(ent); -} - -void -plat_blocked(edict_t *self, edict_t *other) -{ - if (!self || !other) - { - return; - } - - 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->inuse) - { - /* Hack for entity without it's origin near the model */ - VectorMA(other->absmin, 0.5, other->size, other->s.origin); - BecomeExplosion1(other); - } - - return; - } - - /* gib dead things */ - if (other->health < 1) - { - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100, 1, 0, MOD_CRUSH); - } - - 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 /* unused */) -{ - if (!ent || !other) - { - return; - } - - /* if a monster is using us, then allow the activity when stopped. */ - if (other->svflags & SVF_MONSTER) - { - if (ent->moveinfo.state == STATE_TOP) - { - plat_go_down(ent); - } - else if (ent->moveinfo.state == STATE_BOTTOM) - { - plat_go_up(ent); - } - - return; - } - - if (ent->think) - { - return; /* already down */ - } - - plat_go_down(ent); -} - -void -wait_and_change_think(edict_t* ent) -{ - void (*afterwaitfunc)(edict_t *) = ent->moveinfo.endfunc; - ent->moveinfo.endfunc = NULL; - afterwaitfunc(ent); -} - -/* - * In coop mode, this waits for coop_elevator_delay seconds - * before calling afterwaitfunc(ent); otherwise it just calls - * afterwaitfunc(ent); - */ -static void -wait_and_change(edict_t* ent, void (*afterwaitfunc)(edict_t *)) -{ - float waittime = coop_elevator_delay->value; - if (coop->value && waittime > 0.0f) - { - if(ent->nextthink == 0) - { - ent->moveinfo.endfunc = afterwaitfunc; - ent->think = wait_and_change_think; - ent->nextthink = level.time + waittime; - } - } - else - { - afterwaitfunc(ent); - } -} - -void -Touch_Plat_Center(edict_t *ent, edict_t *other, cplane_t *plane /* unsed */, - csurface_t *surf /* unused */) -{ - if (!ent || !other) - { - return; - } - - 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) - { - wait_and_change(ent, plat_go_up); - } - else if (ent->moveinfo.state == STATE_TOP) - { - /* the player is still on the plat, so delay going down */ - ent->nextthink = level.time + 1; - } -} - -edict_t * -plat_spawn_inside_trigger(edict_t *ent) -{ - edict_t *trigger; - vec3_t tmin, tmax; - - if (!ent) - { - return NULL; - } - - /* 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; - - 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 trigger; -} - -/* - * 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) -{ - if (!ent) - { - return; - } - - 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"); -} - -void -plat2_spawn_danger_area(edict_t *ent) -{ - if (!ent) - { - return; - } - - vec3_t mins, maxs; - - VectorCopy(ent->mins, mins); - VectorCopy(ent->maxs, maxs); - maxs[2] = ent->mins[2] + 64; - - SpawnBadArea(mins, maxs, 0, ent); -} - -void -plat2_kill_danger_area(edict_t *ent) -{ - edict_t *t; - - if (!ent) - { - return; - } - - t = NULL; - - while ((t = G_Find(t, FOFS(classname), "bad_area"))) - { - if (t->owner == ent) - { - G_FreeEdict(t); - } - } -} - -void -plat2_hit_top(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - - if (ent->plat2flags & PLAT2_CALLED) - { - ent->plat2flags = PLAT2_WAITING; - - if (!(ent->spawnflags & PLAT2_TOGGLE)) - { - ent->think = plat2_go_down; - ent->nextthink = level.time + 5.0; - } - - if (deathmatch->value) - { - ent->last_move_time = level.time - 1.0; - } - else - { - ent->last_move_time = level.time - 2.0; - } - } - else if (!(ent->spawnflags & PLAT2_TOP) && - !(ent->spawnflags & PLAT2_TOGGLE)) - { - ent->plat2flags = 0; - ent->think = plat2_go_down; - ent->nextthink = level.time + 2.0; - ent->last_move_time = level.time; - } - else - { - ent->plat2flags = 0; - ent->last_move_time = level.time; - } - - G_UseTargets(ent, ent); -} - -void -plat2_hit_bottom(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - - if (ent->plat2flags & PLAT2_CALLED) - { - ent->plat2flags = PLAT2_WAITING; - - if (!(ent->spawnflags & PLAT2_TOGGLE)) - { - ent->think = plat2_go_up; - ent->nextthink = level.time + 5.0; - } - - if (deathmatch->value) - { - ent->last_move_time = level.time - 1.0; - } - else - { - ent->last_move_time = level.time - 2.0; - } - } - else if ((ent->spawnflags & PLAT2_TOP) && !(ent->spawnflags & PLAT2_TOGGLE)) - { - ent->plat2flags = 0; - ent->think = plat2_go_up; - ent->nextthink = level.time + 2.0; - ent->last_move_time = level.time; - } - else - { - ent->plat2flags = 0; - ent->last_move_time = level.time; - } - - plat2_kill_danger_area(ent); - G_UseTargets(ent, ent); -} - -void -plat2_go_down(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - ent->plat2flags |= PLAT2_MOVING; - - Move_Calc(ent, ent->moveinfo.end_origin, plat2_hit_bottom); -} - -void -plat2_go_up(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - ent->plat2flags |= PLAT2_MOVING; - - plat2_spawn_danger_area(ent); - - Move_Calc(ent, ent->moveinfo.start_origin, plat2_hit_top); -} - -void -plat2_operate(edict_t *ent, edict_t *other) -{ - int otherState; - float pauseTime; - float platCenter; - edict_t *trigger; - - if (!ent || !other) - { - return; - } - - trigger = ent; - ent = ent->enemy; /* now point at the plat, not the trigger */ - - if (ent->plat2flags & PLAT2_MOVING) - { - return; - } - - if ((ent->last_move_time + 2) > level.time) - { - return; - } - - platCenter = (trigger->absmin[2] + trigger->absmax[2]) / 2; - - if (ent->moveinfo.state == STATE_TOP) - { - otherState = STATE_TOP; - - if (ent->spawnflags & PLAT2_BOX_LIFT) - { - if (platCenter > other->s.origin[2]) - { - otherState = STATE_BOTTOM; - } - } - else - { - if (trigger->absmax[2] > other->s.origin[2]) - { - otherState = STATE_BOTTOM; - } - } - } - else - { - otherState = STATE_BOTTOM; - - if (other->s.origin[2] > platCenter) - { - otherState = STATE_TOP; - } - } - - ent->plat2flags = PLAT2_MOVING; - - if (deathmatch->value) - { - pauseTime = 0.3; - } - else - { - pauseTime = 0.5; - } - - if (ent->moveinfo.state != otherState) - { - ent->plat2flags |= PLAT2_CALLED; - pauseTime = 0.1; - } - - ent->last_move_time = level.time; - - if (ent->moveinfo.state == STATE_BOTTOM) - { - ent->think = plat2_go_up; - ent->nextthink = level.time + pauseTime; - } - else - { - ent->think = plat2_go_down; - ent->nextthink = level.time + pauseTime; - } -} - -void -Touch_Plat_Center2(edict_t *ent, edict_t *other, - cplane_t *plane /* unused */, csurface_t *surf /* unused */) -{ - if (!ent || !other) - { - return; - } - - /* this requires monsters to actively trigger plats, not just step on them. */ - if (other->health <= 0) - { - return; - } - - /* don't let non-monsters activate plat2s */ - if ((!(other->svflags & SVF_MONSTER)) && (!other->client)) - { - return; - } - - plat2_operate(ent, other); -} - -void -plat2_blocked(edict_t *self, edict_t *other) -{ - if (!self || !other) - { - return; - } - - 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->inuse) - { - BecomeExplosion1(other); - } - - return; - } - - /* gib dead things */ - if (other->health < 1) - { - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100, 1, 0, MOD_CRUSH); - } - - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, self->dmg, 1, 0, MOD_CRUSH); - - if (self->moveinfo.state == STATE_UP) - { - plat2_go_down(self); - } - else if (self->moveinfo.state == STATE_DOWN) - { - plat2_go_up(self); - } -} - -void -Use_Plat2(edict_t *ent, edict_t *other /* unused */, - edict_t *activator) -{ - edict_t *trigger; - int i; - - if (!ent || !activator) - { - return; - } - - if (ent->moveinfo.state > STATE_BOTTOM) - { - return; - } - - if ((ent->last_move_time + 2) > level.time) - { - return; - } - - for (i = 1, trigger = g_edicts + 1; i < globals.num_edicts; i++, trigger++) - { - if (!trigger->inuse) - { - continue; - } - - if (trigger->touch == Touch_Plat_Center2) - { - if (trigger->enemy == ent) - { - plat2_operate(trigger, activator); - return; - } - } - } -} - -void -plat2_activate(edict_t *ent, edict_t *other /* unused */, - edict_t *activator /* unused */) -{ - edict_t *trigger; - - if (!ent) - { - return; - } - - ent->use = Use_Plat2; - trigger = plat_spawn_inside_trigger(ent); /* the "start moving" trigger */ - - trigger->maxs[0] += 10; - trigger->maxs[1] += 10; - trigger->mins[0] -= 10; - trigger->mins[1] -= 10; - - gi.linkentity(trigger); - - trigger->touch = Touch_Plat_Center2; /* Override trigger touch function */ - - plat2_go_down(ent); -} - -/* QUAKED func_plat2 (0 .5 .8) ? PLAT_LOW_TRIGGER PLAT2_TOGGLE PLAT2_TOP PLAT2_TRIGGER_TOP PLAT2_TRIGGER_BOTTOM BOX_LIFT - * speed default 150 - * - * PLAT_LOW_TRIGGER - creates a short trigger field at the bottom - * PLAT2_TOGGLE - plat will not return to default position. - * PLAT2_TOP - plat's default position will the the top. - * PLAT2_TRIGGER_TOP - plat will trigger it's targets each time it hits top - * PLAT2_TRIGGER_BOTTOM - plat will trigger it's targets each time it hits bottom - * BOX_LIFT - this indicates that the lift is a box, rather than just a platform - * - * 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" no default - * - * If the "height" key is set, that will determine the amount the plat moves, - * instead of being implicitly determoveinfoned by the model's height. - * - */ -void -SP_func_plat2(edict_t *ent) -{ - edict_t *trigger; - - if (!ent) - { - return; - } - - VectorClear(ent->s.angles); - ent->solid = SOLID_BSP; - ent->movetype = MOVETYPE_PUSH; - - gi.setmodel(ent, ent->model); - - ent->blocked = plat2_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 (deathmatch->value) - { - ent->speed *= 2; - ent->accel *= 2; - ent->decel *= 2; - } - - /* Added to kill things it's being blocked by */ - if (!ent->dmg) - { - ent->dmg = 2; - } - - /* 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 - st.lip); - } - else - { - ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip; - } - - ent->moveinfo.state = STATE_TOP; - - if (ent->targetname) - { - ent->use = plat2_activate; - } - else - { - ent->use = Use_Plat2; - - trigger = plat_spawn_inside_trigger(ent); /* the "start moving" trigger */ - - trigger->maxs[0] += 10; - trigger->maxs[1] += 10; - trigger->mins[0] -= 10; - trigger->mins[1] -= 10; - - gi.linkentity(trigger); - trigger->touch = Touch_Plat_Center2; /* Override trigger touch function */ - - if (!(ent->spawnflags & PLAT2_TOP)) - { - VectorCopy(ent->pos2, ent->s.origin); - ent->moveinfo.state = STATE_BOTTOM; - } - } - - gi.linkentity(ent); - - 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 EAST MED HARD DM COOP ACCEL - * - * 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. - * - * func_rotating will use it's targets when it stops and starts. - * - * "speed" determines how fast it moves; default value is 100. - * "dmg" damage to inflict when blocked (2 default) - * "accel" if specified, is how much the rotation speed will increase per .1sec. - * - * REVERSE will cause the it to rotate in the opposite direction. - * STOP mean it will stop moving instead of pushing entities - * ACCEL means it will accelerate to it's final speed and decelerate when shutting down. - */ -void -rotating_accel(edict_t *self) -{ - float current_speed; - - if (!self) - { - return; - } - - current_speed = VectorLength(self->avelocity); - - if (current_speed >= (self->speed - self->accel)) /* done */ - { - VectorScale(self->movedir, self->speed, self->avelocity); - G_UseTargets(self, self); - } - else - { - current_speed += self->accel; - VectorScale(self->movedir, current_speed, self->avelocity); - self->think = rotating_accel; - self->nextthink = level.time + FRAMETIME; - } -} - -void -rotating_decel(edict_t *self) -{ - float current_speed; - - if (!self) - { - return; - } - - current_speed = VectorLength(self->avelocity); - - if (current_speed <= self->decel) /* done */ - { - VectorClear(self->avelocity); - G_UseTargets(self, self); - self->touch = NULL; - } - else - { - current_speed -= self->decel; - VectorScale(self->movedir, current_speed, self->avelocity); - self->think = rotating_decel; - self->nextthink = level.time + FRAMETIME; - } -} - -void -rotating_blocked(edict_t *self, edict_t *other) -{ - if (!self || !other) - { - return; - } - - 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 /* unused */, - csurface_t *surf /* unused */) -{ - if (!self || !other) - { - return; - } - - 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 /* unused */, - edict_t *activator /* unused */) -{ - if (!self) - { - return; - } - - if (!VectorCompare(self->avelocity, vec3_origin)) - { - self->s.sound = 0; - - if (self->spawnflags & 8192) /* Decelerate */ - { - rotating_decel(self); - } - else - { - VectorClear(self->avelocity); - G_UseTargets(self, self); - self->touch = NULL; - } - } - else - { - self->s.sound = self->moveinfo.sound_middle; - - if (self->spawnflags & 8192) /* accelerate */ - { - rotating_accel(self); - } - else - { - VectorScale(self->movedir, self->speed, self->avelocity); - G_UseTargets(self, self); - } - - if (self->spawnflags & 16) - { - self->touch = rotating_touch; - } - } -} - -void -SP_func_rotating(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - 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; - } - - if (ent->spawnflags & 8192) /* Accelerate / Decelerate */ - { - if (!ent->accel) - { - ent->accel = 1; - } - else if (ent->accel > ent->speed) - { - ent->accel = ent->speed; - } - - if (!ent->decel) - { - ent->decel = 1; - } - else if (ent->decel > ent->speed) - { - ent->decel = ent->speed; - } - } - - 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) -{ - if (!self) - { - return; - } - - self->moveinfo.state = STATE_BOTTOM; - self->s.effects &= ~EF_ANIM23; - self->s.effects |= EF_ANIM01; -} - -void -button_return(edict_t *self) -{ - if (!self) - { - return; - } - - 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) -{ - if (!self) - { - return; - } - - 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) - { - return; - } - - 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 /* unused */, edict_t *activator) -{ - if (!self || !activator) - { - return; - } - - self->activator = activator; - button_fire(self); -} - -void -button_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, - csurface_t *surf /* unused */) -{ - if (!self || !other) - { - return; - } - - if (!other->client) - { - return; - } - - if (other->health <= 0) - { - return; - } - - self->activator = other; - button_fire(self); -} - -void -button_killed(edict_t *self, edict_t *inflictor /* unused */, - edict_t *attacker, int damage /* unused */, - vec3_t point /* unused */) -{ - if (!self || !attacker) - { - return; - } - - 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; - - if (!ent) - { - return; - } - - 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 already 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) - { - return; - } - - 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_hit_top(edict_t *self) -{ - if (!self) - { - return; - } - - 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) - { - return; - } - - 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) - { - 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; - } - - 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) - { - return; - } - - 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 -smart_water_go_up(edict_t *self) -{ - float distance; - edict_t *lowestPlayer; - edict_t *ent; - float lowestPlayerPt; - int i; - - if (!self) - { - return; - } - - 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->health) - { - if (self->absmax[2] >= self->health) - { - VectorClear(self->velocity); - self->nextthink = 0; - self->moveinfo.state = STATE_TOP; - 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; - } - - /* find the lowest player point. */ - lowestPlayerPt = 999999; - lowestPlayer = NULL; - - for (i = 0; i < game.maxclients; i++) - { - ent = &g_edicts[1 + i]; - - /* don't count dead or unused player slots */ - if ((ent->inuse) && (ent->health > 0)) - { - if (ent->absmin[2] < lowestPlayerPt) - { - lowestPlayerPt = ent->absmin[2]; - lowestPlayer = ent; - } - } - } - - if (!lowestPlayer) - { - return; - } - - distance = lowestPlayerPt - self->absmax[2]; - - /* for the calculations, make sure we - intend to go up at least a little. */ - if (distance < self->accel) - { - distance = 100; - self->moveinfo.speed = 5; - } - else - { - self->moveinfo.speed = distance / self->accel; - } - - if (self->moveinfo.speed < 5) - { - self->moveinfo.speed = 5; - } - else if (self->moveinfo.speed > self->speed) - { - self->moveinfo.speed = self->speed; - } - - /* should this allow any movement other than straight up? */ - VectorSet(self->moveinfo.dir, 0, 0, 1); - VectorScale(self->moveinfo.dir, self->moveinfo.speed, self->velocity); - self->moveinfo.remaining_distance = distance; - - if (self->moveinfo.state != STATE_UP) - { - G_UseTargets(self, lowestPlayer); - door_use_areaportals(self, true); - self->moveinfo.state = STATE_UP; - } - - self->think = smart_water_go_up; - self->nextthink = level.time + FRAMETIME; -} - -void -door_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) -{ - edict_t *ent; - vec3_t center; - - if (!self || !activator) - { - return; - } - - 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; - } - } - - /* smart water is different */ - VectorAdd(self->mins, self->maxs, center); - VectorScale(center, 0.5, center); - - if ((gi.pointcontents(center) & MASK_WATER) && self->spawnflags & 2) - { - self->message = NULL; - self->touch = NULL; - self->enemy = activator; - smart_water_go_up(self); - 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 /* unused */, - csurface_t *surf /* unused */) -{ - if (!self || !other) - { - return; - } - - 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) - { - return; - } - - 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) - { - return; - } - - 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 (!self || !other) - { - return; - } - - 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->inuse) - { - /* 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 /* unused */, - edict_t *attacker, int damage /* unused */, - vec3_t point /* unused */) -{ - edict_t *ent; - - if (!self || !attacker) - { - return; - } - - 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 /* unused */, - csurface_t *surf /* unused */) -{ - if (!self || !other) - { - return; - } - - 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) - { - return; - } - - 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; - } -} - -void -Door_Activate(edict_t *self, edict_t *other /* unused */, - edict_t *activator /* unused */) -{ - if (!self) - { - return; - } - - self->use = NULL; - - if (self->health) - { - self->takedamage = DAMAGE_YES; - self->die = door_killed; - self->max_health = self->health; - } - - if (self->health) - { - self->think = Think_CalcMoveSpeed; - } - else - { - self->think = Think_SpawnDoorTrigger; - } - - self->nextthink = level.time + FRAMETIME; -} - -/* - * 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) -{ - if (!ent) - { - return; - } - - 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; - } - - if (ent->spawnflags & DOOR_INACTIVE) - { - ent->takedamage = DAMAGE_NO; - ent->die = NULL; - ent->think = NULL; - ent->nextthink = 0; - ent->use = Door_Activate; - } -} - -void -smart_water_blocked(edict_t *self, edict_t *other) -{ - if (!self || !other) - { - return; - } - - 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_LAVA); - - /* if it's still there, nuke it */ - if (other->inuse) - { - BecomeExplosion1(other); - } - - return; - } - - T_Damage(other, self, self, vec3_origin, other->s.origin, - vec3_origin, 100, 1, 0, MOD_LAVA); -} - -/* ==================================================================== */ - -/* - * 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; - - if (!self) - { - return; - } - - 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 */ - 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->spawnflags & 2) /* smart water */ - { - if (!self->accel) - { - self->accel = 20; - } - - self->blocked = smart_water_blocked; - } - - 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); -} - -/* ==================================================================== */ - -/* - * 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_blocked(edict_t *self, edict_t *other) -{ - if (!self || !other) - { - return; - } - - 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->inuse) - { - /* 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) - { - return; - } - - 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) - { - self->target_ent = NULL; - 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_piece_wait(edict_t *self) -{ -} - -void -train_next(edict_t *self) -{ - edict_t *ent; - vec3_t dest; - qboolean first; - - if (!self) - { - return; - } - - 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); - self->s.event = EV_OTHER_TELEPORT; - gi.linkentity(self); - goto again; - } - - if (ent->speed) - { - self->speed = ent->speed; - self->moveinfo.speed = ent->speed; - - if (ent->accel) - { - self->moveinfo.accel = ent->accel; - } - else - { - self->moveinfo.accel = ent->speed; - } - - if (ent->decel) - { - self->moveinfo.decel = ent->decel; - } - else - { - self->moveinfo.decel = ent->speed; - } - - self->moveinfo.current_speed = 0; - } - - 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; - - if (self->team) - { - edict_t *e; - vec3_t dir, dst; - - VectorSubtract(dest, self->s.origin, dir); - - for (e = self->teamchain; e; e = e->teamchain) - { - VectorAdd(dir, e->s.origin, dst); - VectorCopy(e->s.origin, e->moveinfo.start_origin); - VectorCopy(dst, e->moveinfo.end_origin); - - e->moveinfo.state = STATE_TOP; - e->speed = self->speed; - e->moveinfo.speed = self->moveinfo.speed; - e->moveinfo.accel = self->moveinfo.accel; - e->moveinfo.decel = self->moveinfo.decel; - e->movetype = MOVETYPE_PUSH; - Move_Calc(e, dst, train_piece_wait); - } - } -} - -void -train_resume(edict_t *self) -{ - edict_t *ent; - vec3_t dest; - - if (!self) - { - return; - } - - 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) - { - return; - } - - 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 /* unused */, - edict_t *activator) -{ - if (!self || !activator) - { - return; - } - - 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) -{ - if (!self) - { - return; - } - - 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 /* unused */) -{ - edict_t *target; - - if (!self || !other) - { - return; - } - - 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) - { - return; - } - - 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) -{ - if (!self) - { - return; - } - - 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) -{ - if (!self) - { - return; - } - - G_UseTargets(self, self->activator); - self->nextthink = level.time + self->wait + crandom() * self->random; -} - -void -func_timer_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) -{ - if (!self || !activator) - { - return; - } - - 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) - { - return; - } - - 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 /* unused */, - edict_t *activator /* unused */) -{ - if (!self) - { - return; - } - - 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) - { - return; - } - - 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) - */ -void -door_secret_use(edict_t *self, edict_t *other /* unused */, - edict_t *activator /*unused */) -{ - if (!self) - { - return; - } - - /* 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) -{ - if (!self) - { - return; - } - - self->nextthink = level.time + 1.0; - self->think = door_secret_move2; -} - -void -door_secret_move2(edict_t *self) -{ - if (!self) - { - return; - } - - Move_Calc(self, self->pos2, door_secret_move3); -} - -void -door_secret_move3(edict_t *self) -{ - if (!self) - { - return; - } - - if (self->wait == -1) - { - return; - } - - self->nextthink = level.time + self->wait; - self->think = door_secret_move4; -} - -void -door_secret_move4(edict_t *self) -{ - if (!self) - { - return; - } - - Move_Calc(self, self->pos1, door_secret_move5); -} - -void -door_secret_move5(edict_t *self) -{ - if (!self) - { - return; - } - - self->nextthink = level.time + 1.0; - self->think = door_secret_move6; -} - -void -door_secret_move6(edict_t *self) -{ - if (!self) - { - return; - } - - Move_Calc(self, vec3_origin, door_secret_done); -} - -void -door_secret_done(edict_t *self) -{ - if (!self) - { - return; - } - - 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 (!self || !other) - { - return; - } - - 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->inuse) - { - /* 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 /* unused */, - edict_t *attacker, int damage /* unused */, - vec3_t point /* unused */) -{ - if (!self || !attacker) - { - return; - } - - 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; - - if (!ent) - { - return; - } - - 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 /* unused */, - edict_t *activator /* unused */) -{ - if (!self) - { - return; - } - - 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) -{ - if (!ent) - { - return; - } - - gi.setmodel(ent, ent->model); - ent->use = use_killbox; - ent->svflags = SVF_NOCLIENT; -} - -/* - * QUAKED rotating_light (0 .5 .8) (-8 -8 -8) (8 8 8) START_OFF ALARM - * "health" if set, the light may be killed. - */ - -#define START_OFF 1 - -void -rotating_light_alarm(edict_t *self) -{ - if (!self) - { - return; - } - - if (self->spawnflags & START_OFF) - { - self->think = NULL; - self->nextthink = 0; - } - else - { - gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, - self->moveinfo.sound_start, 1, - ATTN_STATIC, 0); - self->nextthink = level.time + 1; - } -} - -void -rotating_light_killed(edict_t *self, edict_t *inflictor /* unused */, - edict_t *attacker /* unused */, int damage /* unused */, - vec3_t point /* unused */) -{ - if (!self) - { - return; - } - - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_WELDING_SPARKS); - gi.WriteByte(30); - gi.WritePosition(self->s.origin); - gi.WriteDir(vec3_origin); - gi.WriteByte(0xe0 + (rand() & 7)); - gi.multicast(self->s.origin, MULTICAST_PVS); - - self->s.effects &= ~EF_SPINNINGLIGHTS; - self->use = NULL; - - self->think = G_FreeEdict; - self->nextthink = level.time + 0.1; -} - -void -rotating_light_use(edict_t *self, edict_t *other /* unused */, - edict_t *activator /* unused */) -{ - if (!self) - { - return; - } - - if (self->spawnflags & START_OFF) - { - self->spawnflags &= ~START_OFF; - self->s.effects |= EF_SPINNINGLIGHTS; - - if (self->spawnflags & 2) - { - self->think = rotating_light_alarm; - self->nextthink = level.time + 0.1; - } - } - else - { - self->spawnflags |= START_OFF; - self->s.effects &= ~EF_SPINNINGLIGHTS; - } -} - -void -SP_rotating_light(edict_t *self) -{ - if (!self) - { - return; - } - - self->movetype = MOVETYPE_STOP; - self->solid = SOLID_BBOX; - - self->s.modelindex = gi.modelindex("models/objects/light/tris.md2"); - - self->s.frame = 0; - - self->use = rotating_light_use; - - if (self->spawnflags & START_OFF) - { - self->s.effects &= ~EF_SPINNINGLIGHTS; - } - else - { - self->s.effects |= EF_SPINNINGLIGHTS; - } - - if (!self->speed) - { - self->speed = 32; - } - - if (!self->health) - { - self->health = 10; - self->max_health = self->health; - self->die = rotating_light_killed; - self->takedamage = DAMAGE_YES; - } - else - { - self->max_health = self->health; - self->die = rotating_light_killed; - self->takedamage = DAMAGE_YES; - } - - if (self->spawnflags & 2) - { - self->moveinfo.sound_start = gi.soundindex("misc/alarm.wav"); - } - - gi.linkentity(self); -} - -/* - * QUAKED func_object_repair (1 .5 0) (-8 -8 -8) (8 8 8) - * object to be repaired. - * The default delay is 1 second - * "delay" the delay in seconds for spark to occur - */ -void -object_repair_fx(edict_t *ent) -{ - if (!ent) - { - return; - } - - ent->nextthink = level.time + ent->delay; - - if (ent->health <= 100) - { - ent->health++; - } - else - { - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_WELDING_SPARKS); - gi.WriteByte(10); - gi.WritePosition(ent->s.origin); - gi.WriteDir(vec3_origin); - gi.WriteByte(0xe0 + (rand() & 7)); - gi.multicast(ent->s.origin, MULTICAST_PVS); - } -} - -void -object_repair_dead(edict_t *ent) -{ - if (!ent) - { - return; - } - - G_UseTargets(ent, ent); - ent->nextthink = level.time + 0.1; - ent->think = object_repair_fx; -} - -void -object_repair_sparks(edict_t *ent) -{ - if (!ent) - { - return; - } - - if (ent->health < 0) - { - ent->nextthink = level.time + 0.1; - ent->think = object_repair_dead; - return; - } - - ent->nextthink = level.time + ent->delay; - - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_WELDING_SPARKS); - gi.WriteByte(10); - gi.WritePosition(ent->s.origin); - gi.WriteDir(vec3_origin); - gi.WriteByte(0xe0 + (rand() & 7)); - gi.multicast(ent->s.origin, MULTICAST_PVS); -} - -void -SP_object_repair(edict_t *ent) -{ - if (!ent) - { - return; - } - - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->classname = "object_repair"; - ent->think = object_repair_sparks; - ent->nextthink = level.time + 1.0; - ent->health = 100; - - if (!ent->delay) - { - ent->delay = 1.0; - } - - gi.linkentity(ent); -} diff --git a/src/rogue/g_items.c b/src/rogue/g_items.c deleted file mode 100644 index 44a1a134..00000000 --- a/src/rogue/g_items.c +++ /dev/null @@ -1,3845 +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; - -void Use_Quad(edict_t *ent, gitem_t *item); -void Use_QuadFire(edict_t *ent, gitem_t *item); - -static int quad_drop_timeout_hack; -static int quad_fire_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; - - if (!classname) - { - return NULL; - } - - 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; - - if (!pickup_name) - { - return NULL; - } - - 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) - { - return; - } - - if (ent->team) - { - edict_t *master; - int count; - int choice; - - master = ent->teammaster; - - 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++) - { - } - } - - if (randomrespawn && randomrespawn->value) - { - edict_t *newEnt; - - newEnt = DoRandomRespawn(ent); - - /* if we've changed entities, then do some sleight - * of hand. otherwise, the old entity will respawn */ - if (newEnt) - { - G_FreeEdict(ent); - ent = newEnt; - } - } - - 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) -{ - if (!ent) - { - return; - } - - 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; - - if (!ent || !other) - { - return false; - } - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (((skill->value == SKILL_MEDIUM) && - (quantity >= 2)) || ((skill->value >= SKILL_HARD) && (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); - } - } - - return true; -} - -void -Drop_General(edict_t *ent, gitem_t *item) -{ - if (!ent || !item) - { - return; - } - - Drop_Item(ent, item); - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); -} - -/* ====================================================================== */ - -qboolean -Pickup_Adrenaline(edict_t *ent, edict_t *other) -{ - if (!ent || !other) - { - return false; - } - - 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) -{ - if (!ent || !other) - { - return false; - } - - 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 (!ent || !other) - { - return false; - } - - 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; - } - - if (other->client->pers.max_magslug < 75) - { - other->client->pers.max_magslug = 75; - } - - if (other->client->pers.max_flechettes < 250) - { - other->client->pers.max_flechettes = 250; - } - - if (g_disruptor->value) - { - if (other->client->pers.max_rounds < 150) - { - other->client->pers.max_rounds = 150; - } - } - - 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 (!ent || !other) - { - return false; - } - - 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; - } - - if (other->client->pers.max_magslug < 100) - { - other->client->pers.max_magslug = 100; - } - - if (other->client->pers.max_flechettes < 200) - { - other->client->pers.max_flechettes = 200; - } - - if (g_disruptor->value) - { - if (other->client->pers.max_rounds < 200) - { - other->client->pers.max_rounds = 200; - } - } - - 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; - } - } - - item = FindItem("Mag Slug"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_magslug) - { - other->client->pers.inventory[index] = - other->client->pers.max_magslug; - } - } - - item = FindItem("Flechettes"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_flechettes) - { - other->client->pers.inventory[index] = - other->client->pers.max_flechettes; - } - } - - item = FindItem("Rounds"); - - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - - if (other->client->pers.inventory[index] > - other->client->pers.max_rounds) - { - other->client->pers.inventory[index] = - other->client->pers.max_rounds; - } - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - { - SetRespawn(ent, ent->item->quantity); - } - - return true; -} - -/* ====================================================================== */ - -qboolean -Pickup_Nuke(edict_t *ent, edict_t *other) -{ - int quantity; - - if (!ent || !other) - { - return false; - } - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (quantity >= 1) - { - return false; - } - - if ((coop->value) && (ent->item->flags & IT_STAY_COOP)) - { - return false; - } - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - - if (deathmatch->value) - { - if (!(ent->spawnflags & DROPPED_ITEM)) - { - SetRespawn(ent, ent->item->quantity); - } - } - - return true; -} - -void -Use_IR(edict_t *ent, gitem_t *item) -{ - if (!ent || !item) - { - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (ent->client->ir_framenum > level.framenum) - { - ent->client->ir_framenum += 600; - } - else - { - ent->client->ir_framenum = level.framenum + 600; - } - - gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ir_start.wav"), 1, ATTN_NORM, 0); -} - -void -Use_Double(edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (ent->client->double_framenum > level.framenum) - { - ent->client->double_framenum += 300; - } - else - { - ent->client->double_framenum = level.framenum + 300; - } - - gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage1.wav"), 1, ATTN_NORM, 0); -} - -void -Use_Compass(edict_t *ent, gitem_t *item) -{ - int ang; - - if (!ent || !item) - { - return; - } - - ang = (int)(ent->client->v_angle[1]); - - if (ang < 0) - { - ang += 360; - } - - gi.cprintf(ent, PRINT_HIGH, "Origin: %0.0f,%0.0f,%0.0f Dir: %d\n", - ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], ang); -} - -void -Use_Nuke(edict_t *ent, gitem_t *item) -{ - vec3_t forward, right, start; - float speed; - - if (!ent || !item) - { - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - AngleVectors(ent->client->v_angle, forward, right, NULL); - - VectorCopy(ent->s.origin, start); - speed = 100; - fire_nuke(ent, start, forward, speed); -} - -void -Use_Doppleganger(edict_t *ent, gitem_t *item) -{ - vec3_t forward, right; - vec3_t createPt, spawnPt; - vec3_t ang; - - if (!ent || !item) - { - return; - } - - VectorClear(ang); - ang[YAW] = ent->client->v_angle[YAW]; - AngleVectors(ang, forward, right, NULL); - - VectorMA(ent->s.origin, 48, forward, createPt); - - if (!FindSpawnPoint(createPt, ent->mins, ent->maxs, spawnPt, 32)) - { - return; - } - - if (!CheckGroundSpawnPoint(spawnPt, ent->mins, ent->maxs, 64, -1)) - { - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - SpawnGrow_Spawn(spawnPt, 0); - fire_doppleganger(ent, spawnPt, forward); -} - -qboolean -Pickup_Doppleganger(edict_t *ent, edict_t *other) -{ - int quantity; - - if (!ent || !other) - { - return false; - } - - if (!(deathmatch->value)) - { - return false; - } - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (quantity >= 1) - { - return false; - } - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - - if (!(ent->spawnflags & DROPPED_ITEM)) - { - SetRespawn(ent, ent->item->quantity); - } - - return true; -} - -qboolean -Pickup_Sphere(edict_t *ent, edict_t *other) -{ - int quantity; - - if (!ent || !other) - { - return false; - } - - if (other->client && other->client->owned_sphere) - { - return false; - } - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (((skill->value == SKILL_MEDIUM) && - (quantity >= 2)) || ((skill->value >= SKILL_HARD) && (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); - } - } - - return true; -} - -void -Use_Defender(edict_t *ent, gitem_t *item) -{ - if (!ent || !item) - { - return; - } - - if (ent->client && ent->client->owned_sphere) - { - gi.cprintf(ent, PRINT_HIGH, "Only one sphere at a time!\n"); - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - Defender_Launch(ent); -} - -void -Use_Hunter(edict_t *ent, gitem_t *item) -{ - if (!ent || !item) - { - return; - } - - if (ent->client && ent->client->owned_sphere) - { - gi.cprintf(ent, PRINT_HIGH, "Only one sphere at a time!\n"); - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - Hunter_Launch(ent); -} - -void -Use_Vengeance(edict_t *ent, gitem_t *item) -{ - if (!ent || !item) - { - return; - } - - if (ent->client && ent->client->owned_sphere) - { - gi.cprintf(ent, PRINT_HIGH, "Only one sphere at a time!\n"); - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - Vengeance_Launch(ent); -} - -/* ====================================================================== */ - -void -Use_Quad(edict_t *ent, gitem_t *item) -{ - int timeout; - - if (!ent || !item) - { - return; - } - - 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_QuadFire(edict_t *ent, gitem_t *item) -{ - int timeout; - - if (!ent || !item) - { - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - - if (quad_fire_drop_timeout_hack) - { - timeout = quad_fire_drop_timeout_hack; - quad_fire_drop_timeout_hack = 0; - } - else - { - timeout = 300; - } - - if (ent->client->quadfire_framenum > level.framenum) - { - ent->client->quadfire_framenum += timeout; - } - else - { - ent->client->quadfire_framenum = level.framenum + timeout; - } - - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/quadfire1.wav"), 1, ATTN_NORM, 0); -} - -/* ====================================================================== */ - -void -Use_Breather(edict_t *ent, gitem_t *item) -{ - if (!ent || !item) - { - return; - } - - 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) -{ - if (!ent || !item) - { - return; - } - - 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) -{ - if (!ent || !item) - { - return; - } - - 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) -{ - if (!ent || !item) - { - return; - } - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem(ent); - ent->client->silencer_shots += 30; -} - -/* ====================================================================== */ - -qboolean -Pickup_Key(edict_t *ent, edict_t *other) -{ - if (!ent || !other) - { - return false; - } - - 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 || !item) - { - return false; - } - - 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 if (item->tag == AMMO_MAGSLUG) - { - max = ent->client->pers.max_magslug; - } - else if (item->tag == AMMO_TRAP) - { - max = ent->client->pers.max_trap; - } - else if (item->tag == AMMO_FLECHETTES) - { - max = ent->client->pers.max_flechettes; - } - else if (item->tag == AMMO_PROX) - { - max = ent->client->pers.max_prox; - } - else if (item->tag == AMMO_TESLA) - { - max = ent->client->pers.max_tesla; - } - else if (item->tag == AMMO_DISRUPTOR) - { - max = ent->client->pers.max_rounds; - } - else - { - gi.dprintf("undefined ammo type\n"); - 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; - - if (!ent || !other) - { - return false; - } - - 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) - { - /* don't switch to tesla */ - if ((other->client->pers.weapon != ent->item) && - (!deathmatch->value || (other->client->pers.weapon == FindItem("blaster"))) && - (strcmp(ent->classname, "ammo_tesla"))) - { - 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; - - if (!ent || !item) - { - return; - } - - 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]; - } - - if (ent->client->pers.weapon && - (ent->client->pers.weapon->tag == AMMO_GRENADES) && - (item->tag == AMMO_GRENADES) && - (ent->client->pers.inventory[index] - dropped->count <= 0)) - { - gi.cprintf(ent, PRINT_HIGH, "Can't drop current weapon\n"); - G_FreeEdict(dropped); - return; - } - - ent->client->pers.inventory[index] -= dropped->count; - ValidateSelectedItem(ent); -} - -/* ====================================================================== */ - -void -MegaHealth_think(edict_t *self) -{ - if (!self) - { - return; - } - - if (self->owner->health > self->owner->max_health) - { - 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 || !other) - { - return false; - } - - if (!(ent->style & HEALTH_IGNORE_MAX)) - { - if (other->health >= other->max_health) - { - return false; - } - } - - other->health += ent->count; - - if (!(ent->style & HEALTH_IGNORE_MAX)) - { - if (other->health > other->max_health) - { - other->health = other->max_health; - } - } - - if (ent->style & HEALTH_TIMED) - { - 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) - { - return 0; - } - - 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; - - if (!ent || !other) - { - return false; - } - - /* 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) - { - return POWER_ARMOR_NONE; - } - - 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 || !item) - { - return; - } - - 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; - - if (!ent || !other) - { - return false; - } - - 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 || !item) - { - return; - } - - 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 /* unused */, csurface_t *surf /* unused */) -{ - qboolean taken; - - if (!ent || !other) - { - return; - } - - if (!other->client) - { - return; - } - - if (other->health < 1) - { - return; /* dead people can't pickup */ - } - - if (!ent->item->pickup) - { - return; /* not a grabbable item? */ - } - - 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 /* (ent->count == 100) */ - { - 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); - } - - /* activate item instantly if appropriate */ - /* moved down here so activation sounds override the pickup sound */ - if (deathmatch->value) - { - if ((((int)dmflags->value & DF_INSTANT_ITEMS) && - (ent->item->flags & IT_INSTANT_USE)) || - ((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; - } - - if (ent->item->use) - { - ent->item->use(other, ent->item); - } - else - { - gi.dprintf("Powerup has no use function!\n"); - } - } - } - } - - 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 (!ent || !other) - { - return; - } - - if (other == ent->owner) - { - return; - } - - /* plane and surf are unused in Touch_Item - but since the function is part of the - game <-> client interface dropping - them is too much pain. */ - Touch_Item(ent, other, plane, surf); -} - -void -drop_make_touchable(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - - if (!ent || !item) - { - return NULL; - } - - 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 | RF_IR_VISIBLE; - 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 /* unused */, edict_t *activator /* unused */) -{ - if (!ent) - { - return; - } - - 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; - - if (!ent) - { - return; - } - - 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 if (ent->item->world_model) - { - 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) - { - if (strcmp(ent->classname, "foodcube") == 0) - { - VectorCopy(ent->s.origin, tr.endpos); - ent->velocity[2] = 0; - } - else - { - 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); - } - } -} - -/* - * Create the item marked for spawn creation - */ -void -Item_TriggeredSpawn(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) -{ - self->svflags &= ~SVF_NOCLIENT; - self->use = NULL; - - if (strcmp(self->classname, "key_power_cube")) - { - self->spawnflags = 0; - } - - droptofloor(self); -} - -/* - * Set up an item to spawn in later. - */ -void -SetTriggeredSpawn(edict_t *ent) -{ - if (!ent) - { - return; - } - - /* don't do anything on key_power_cubes. */ - if (!strcmp(ent->classname, "key_power_cube")) - { - return; - } - - ent->think = NULL; - ent->nextthink = 0; - ent->use = Item_TriggeredSpawn; - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; -} - -/* - * ============ - * 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) -{ - if (!ent || !item) - { - return; - } - - if (!g_disruptor->value) - { - if ((!strcmp(ent->classname, "ammo_disruptor")) || - (!strcmp(ent->classname, "weapon_disintegrator"))) - { - G_FreeEdict(ent); - return; - } - } - - if (ent->spawnflags > 1) - { - 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 (item->pickup == Pickup_Sphere) - { - G_FreeEdict(ent); - return; - } - - if (item->pickup == Pickup_Doppleganger) - { - 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 ((int)dmflags->value & DF_NO_MINES) - { - if (!strcmp(ent->classname, "ammo_prox") || - !strcmp(ent->classname, "ammo_tesla")) - { - G_FreeEdict(ent); - return; - } - } - - if ((int)dmflags->value & DF_NO_NUKES) - { - if (!strcmp(ent->classname, "ammo_nuke")) - { - G_FreeEdict(ent); - return; - } - } - - if ((int)dmflags->value & DF_NO_SPHERES) - { - if (item->pickup == Pickup_Sphere) - { - G_FreeEdict(ent); - return; - } - } - } - - /* DM only items */ - if (!deathmatch->value) - { - if ((item->pickup == Pickup_Doppleganger) || - (item->pickup == Pickup_Nuke)) - { - G_FreeEdict(ent); - return; - } - - if ((item->use == Use_Vengeance) || (item->use == Use_Hunter)) - { - G_FreeEdict(ent); - return; - } - } - - PrecacheItem(item); - - if (coop->value && !(ent->spawnflags & ITEM_NO_TOUCH) && (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; - } - - 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); - } - - if (ent->spawnflags & 1) - { - SetTriggeredSpawn(ent); - } -} - -/* ====================================================================== */ - -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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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_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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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 weapon_etf_rifle (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "weapon_etf_rifle", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_ETF_Rifle, - "misc/w_pkup.wav", - "models/weapons/g_etf_rifle/tris.md2", EF_ROTATE, - "models/weapons/v_etf_rifle/tris.md2", - "w_etf_rifle", - "ETF Rifle", - 0, - 1, - "Flechettes", - IT_WEAPON, - WEAP_ETFRIFLE, - NULL, - 0, - "weapons/nail1.wav models/proj/flechette/tris.md2", - }, - - /* QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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_proxlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "weapon_proxlauncher", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_ProxLauncher, - "misc/w_pkup.wav", - "models/weapons/g_plaunch/tris.md2", EF_ROTATE, - "models/weapons/v_plaunch/tris.md2", - "w_proxlaunch", - "Prox Launcher", - 0, - 1, - "Prox", - IT_WEAPON, - WEAP_PROXLAUNCH, - NULL, - AMMO_PROX, - "weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav weapons/proxwarn.wav weapons/proxopen.wav", - }, - - /* QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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_plasmabeam (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "weapon_plasmabeam", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Heatbeam, - "misc/w_pkup.wav", - "models/weapons/g_beamer/tris.md2", EF_ROTATE, - "models/weapons/v_beamer/tris.md2", - "w_heatbeam", - "Plasma Beam", - 0, - 2, - "Cells", - IT_WEAPON, - WEAP_PLASMA, - NULL, - 0, - "models/weapons/v_beamer2/tris.md2 weapons/bfg__l1a.wav", - }, - - /* QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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 weapon_chainfist (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "weapon_chainfist", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_ChainFist, - "misc/w_pkup.wav", - "models/weapons/g_chainf/tris.md2", EF_ROTATE, - "models/weapons/v_chainf/tris.md2", - "w_chainfist", - "Chainfist", - 0, - 0, - NULL, - IT_WEAPON | IT_MELEE, - WEAP_CHAINFIST, - NULL, - 1, - "weapons/sawidle.wav weapons/sawhit.wav", - }, - - /* QUAKED weapon_disintegrator (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "weapon_disintegrator", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Disintegrator, - "misc/w_pkup.wav", - "models/weapons/g_dist/tris.md2", EF_ROTATE, - "models/weapons/v_dist/tris.md2", - "w_disintegrator", - "Disruptor", - 0, - 1, - "Rounds", - IT_WEAPON, - WEAP_DISRUPTOR, - NULL, - 1, - "models/items/spawngro/tris.md2 models/proj/disintegrator/tris.md2 weapons/disrupt.wav weapons/disint2.wav weapons/disrupthit.wav", - }, - - /* QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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 ammo_flechettes (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "ammo_flechettes", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/ammo/am_flechette/tris.md2", 0, - NULL, - "a_flechettes", - "Flechettes", - 3, - 50, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_FLECHETTES - }, - - /* QUAKED ammo_prox (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "ammo_prox", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/ammo/am_prox/tris.md2", 0, - NULL, - "a_prox", - "Prox", - 3, - 5, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_PROX, - "models/weapons/g_prox/tris.md2 weapons/proxwarn.wav" - }, - - /* QUAKED ammo_tesla (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "ammo_tesla", - Pickup_Ammo, - Use_Weapon, - Drop_Ammo, - Weapon_Tesla, - "misc/am_pkup.wav", - "models/ammo/am_tesl/tris.md2", 0, - "models/weapons/v_tesla/tris.md2", - "a_tesla", - "Tesla", - 3, - 5, - "Tesla", - IT_AMMO | IT_WEAPON, - 0, - NULL, - AMMO_TESLA, - "models/weapons/v_tesla2/tris.md2 weapons/teslaopen.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav models/weapons/g_tesla/tris.md2" - }, - - /* QUAKED ammo_nuke (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "ammo_nuke", - Pickup_Nuke, - Use_Nuke, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/weapons/g_nuke/tris.md2", EF_ROTATE, - NULL, - "p_nuke", - "A-M Bomb", - 3, - 300, - "A-M Bomb", - IT_POWERUP, - 0, - NULL, - 0, - "weapons/nukewarn2.wav world/rumble.wav" - }, - - /* QUAKED ammo_disruptor (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "ammo_disruptor", - Pickup_Ammo, - NULL, - Drop_Ammo, - NULL, - "misc/am_pkup.wav", - "models/ammo/am_disr/tris.md2", 0, - NULL, - "a_disruptor", - "Rounds", - 3, - 15, - NULL, - IT_AMMO, - 0, - NULL, - AMMO_DISRUPTOR - }, - - /* QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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|IT_INSTANT_USE, - 0, - NULL, - 0, - "items/damage.wav items/damage2.wav items/damage3.wav" - }, - - /* QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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 | IT_INSTANT_USE, - 0, - NULL, - 0, - "items/protect.wav items/protect2.wav items/protect4.wav" - }, - - /* QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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 | IT_INSTANT_USE, - 0, - NULL, - 0, - "" - }, - - /* QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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 | IT_INSTANT_USE, - 0, - NULL, - 0, - "items/airout.wav" - }, - - /* QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "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 | IT_INSTANT_USE, - 0, - NULL, - 0, - "items/airout.wav" - }, - - /* QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN */ - { - "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) TRIGGER_SPAWN */ - { - "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 item_ir_goggles (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_ir_goggles", - Pickup_Powerup, - Use_IR, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/goggles/tris.md2", EF_ROTATE, - NULL, - "p_ir", - "IR Goggles", - 2, - 60, - NULL, - IT_POWERUP | IT_INSTANT_USE, - 0, - NULL, - 0, - "misc/ir_start.wav" - }, - - /* QUAKED item_double (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_double", - Pickup_Powerup, - Use_Double, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/ddamage/tris.md2", EF_ROTATE, - NULL, - "p_double", - "Double Damage", - 2, - 60, - NULL, - IT_POWERUP | IT_INSTANT_USE, - 0, - NULL, - 0, - "misc/ddamage1.wav misc/ddamage2.wav misc/ddamage3.wav" - }, - - /* QUAKED item_compass (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_compass", - Pickup_Powerup, - Use_Compass, - NULL, - NULL, - "items/pkup.wav", - "models/objects/fire/tris.md2", EF_ROTATE, - NULL, - "p_compass", - "compass", - 2, - 60, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - }, - - /* QUAKED item_sphere_vengeance (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_sphere_vengeance", - Pickup_Sphere, - Use_Vengeance, - NULL, - NULL, - "items/pkup.wav", - "models/items/vengnce/tris.md2", EF_ROTATE, - NULL, - "p_vengeance", - "vengeance sphere", - 2, - 60, - NULL, - IT_POWERUP | IT_INSTANT_USE, - 0, - NULL, - 0, - "spheres/v_idle.wav" - }, - - /* QUAKED item_sphere_hunter (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_sphere_hunter", - Pickup_Sphere, - Use_Hunter, - NULL, - NULL, - "items/pkup.wav", - "models/items/hunter/tris.md2", EF_ROTATE, - NULL, - "p_hunter", - "hunter sphere", - 2, - 120, - NULL, - IT_POWERUP | IT_INSTANT_USE, - 0, - NULL, - 0, - "spheres/h_idle.wav spheres/h_active.wav spheres/h_lurk.wav" - }, - - /* QUAKED item_sphere_defender (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_sphere_defender", - Pickup_Sphere, - Use_Defender, - NULL, - NULL, - "items/pkup.wav", - "models/items/defender/tris.md2", EF_ROTATE, - NULL, - "p_defender", - "defender sphere", - 2, - 60, - NULL, - IT_POWERUP | IT_INSTANT_USE, - 0, - NULL, - 0, - "models/proj/laser2/tris.md2 models/items/shell/tris.md2 spheres/d_idle.wav" - }, - - /* QUAKED item_doppleganger (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "item_doppleganger", - Pickup_Doppleganger, - Use_Doppleganger, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/dopple/tris.md2", - EF_ROTATE, - NULL, - "p_doppleganger", - "Doppleganger", - 0, - 90, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - "models/objects/dopplebase/tris.md2 models/items/spawngro2/tris.md2 models/items/hunter/tris.md2 models/items/vengnce/tris.md2", - }, - - { - NULL, - Tag_PickupToken, - NULL, - NULL, - NULL, - "items/pkup.wav", - "models/items/tagtoken/tris.md2", - EF_ROTATE | EF_TAGTRAIL, - NULL, - "i_tagtoken", - "Tag Token", - 0, - 0, - NULL, - IT_POWERUP | IT_NOT_GIVEABLE, - 0, - NULL, - 1, - NULL, - }, - - /* QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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) TRIGGER_SPAWN - 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, - "" - }, - - /* QUAKED key_nuke_container (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "key_nuke_container", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/weapons/g_nuke/tris.md2", - EF_ROTATE, - NULL, - "i_contain", - "Antimatter Pod", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - NULL, - }, - - /* QUAKED key_nuke (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */ - { - "key_nuke", - Pickup_Key, - NULL, - Drop_General, - NULL, - "items/pkup.wav", - "models/weapons/g_nuke/tris.md2", - EF_ROTATE, - NULL, - "i_nuke", - "Antimatter Bomb", - 2, - 0, - NULL, - IT_STAY_COOP | IT_KEY, - 0, - NULL, - 0, - NULL, - }, - - { - 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" - }, - - /* 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/rogue/player/weapon.c b/src/rogue/player/weapon.c deleted file mode 100644 index 7babb406..00000000 --- a/src/rogue/player/weapon.c +++ /dev/null @@ -1,2649 +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 - -static qboolean is_quad; -static byte damage_multiplier; -static byte is_silenced; - -void weapon_grenade_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; - } - } - - 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->flags & FL_DISGUISED) - { - if (type == PNOISE_WEAPON) - { - level.disguise_violator = who; - level.disguise_violation_framenum = level.framenum + 5; - } - else - { - return; - } - } - - PlayerNoise_Verify(who); - - if ((type == PNOISE_SELF) || (type == PNOISE_WEAPON)) - { - if (level.framenum <= (level.sound_entity_framenum + 3)) - { - return; - } - - if (!who->mynoise) - { - return; - } - - noise = who->mynoise; - level.sound_entity = noise; - level.sound_entity_framenum = level.framenum; - } - else - { - if (level.framenum <= (level.sound2_entity_framenum + 3)) - { - return; - } - - if (!who->mynoise2) - { - return; - } - - 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)) && - (!coop_pickup_weapons->value || (ent->flags & FL_COOP_TAKEN))) - { - 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 */ - if (ent->item->ammo) - { - 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("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) - { - P_DamageModifier(ent); - - if (ent->client->silencer_shots) - { - is_silenced = MZ_SILENCED; - } - else - { - is_silenced = 0; - } - - ent->client->pers.weapon->weaponthink(ent); - } -} - -/* - * Client (player) animation for changing weapon - */ -static void -Change_Weap_Animation(edict_t *ent) -{ - if (!ent) - { - return; - } - - 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; - } -} - -/* - * 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 -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 - */ - -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 n; - const unsigned short int change_speed = (g_swap_speed->value > 1)? - (g_swap_speed->value < USHRT_MAX)? (unsigned short int)g_swap_speed->value : 1 - : 1; - - if (!ent || !fire) - { - return; - } - - 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 - change_speed + 1) - { - ChangeWeapon(ent); - return; - } - else if ( (FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) >= (4 * change_speed) ) - { - unsigned short int remainder = FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe; - // "if (remainder == 4)" at change_speed == 1 - if ( ( remainder <= (4 * change_speed) ) - && ( remainder > (3 * change_speed) ) ) - { - Change_Weap_Animation(ent); - } - } - - ent->client->ps.gunframe += change_speed; - return; - } - - if (ent->client->weaponstate == WEAPON_ACTIVATING) - { - if (ent->client->ps.gunframe >= FRAME_ACTIVATE_LAST - change_speed + 1) - { - ent->client->weaponstate = WEAPON_READY; - ent->client->ps.gunframe = FRAME_IDLE_FIRST; - return; - } - - ent->client->ps.gunframe += change_speed; - return; - } - - if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING)) - { - ent->client->weaponstate = WEAPON_DROPPING; - ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST; - - if ( (FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < (4 * change_speed) ) - { - Change_Weap_Animation(ent); - } - - 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 (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); - } - - 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; - } - } -} - -/* - * ====================================================================== - * - * GRENADE - * - * ====================================================================== - */ - -void -weapon_grenade_fire(edict_t *ent, qboolean held) -{ - vec3_t offset; - vec3_t forward, right, up; - vec3_t start; - int damage = 125; - float timer; - int speed; - float radius; - - if (!ent) - { - return; - } - - radius = damage + 40; - - if (is_quad) - { - damage *= damage_multiplier; - - if (damage_multiplier >= 4) - { - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); - } - else if (damage_multiplier == 2) - { - gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage3.wav"), 1, ATTN_NORM, 0); - } - } - - AngleVectors(ent->client->v_angle, forward, right, up); - - if (ent->client->pers.weapon->tag == AMMO_TESLA) - { - VectorSet(offset, 0, -4, ent->viewheight - 22); - } - else - { - VectorSet(offset, 2, 6, ent->viewheight - 14); - } - - P_ProjectSource2(ent, ent->s.origin, offset, - forward, right, up, start); - - timer = ent->client->grenade_time - level.time; - speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER); - - if (speed > GRENADE_MAXSPEED) - { - speed = GRENADE_MAXSPEED; - } - - switch (ent->client->pers.weapon->tag) - { - case AMMO_GRENADES: - fire_grenade2(ent, start, forward, damage, speed, - timer, radius, held); - break; - default: - fire_tesla(ent, start, forward, damage_multiplier, speed); - break; - } - - 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->health <= 0) - { - 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 -Throw_Generic(edict_t *ent, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_THROW_SOUND, - int FRAME_THROW_HOLD, int FRAME_THROW_FIRE, int *pause_frames, int EXPLODE, - void (*fire)(edict_t *ent, qboolean held)) -{ - int n; - - if (!ent || !pause_frames || !fire) - { - return; - } - - 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 = FRAME_IDLE_FIRST; - 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 == 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) - { - if (ent->client->ps.gunframe == FRAME_THROW_SOUND) - { - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/hgrena1b.wav"), 1, ATTN_NORM, 0); - } - - if (ent->client->ps.gunframe == FRAME_THROW_HOLD) - { - if (!ent->client->grenade_time) - { - ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2; - - switch (ent->client->pers.weapon->tag) - { - case AMMO_GRENADES: - ent->client->weapon_sound = gi.soundindex("weapons/hgrenc1b.wav"); - break; - } - } - - /* they waited too long, detonate it in their hand */ - if (EXPLODE && !ent->client->grenade_blew_up && - (level.time >= ent->client->grenade_time)) - { - ent->client->weapon_sound = 0; - 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 = FRAME_FIRE_LAST; - ent->client->grenade_blew_up = false; - } - else - { - return; - } - } - } - - if (ent->client->ps.gunframe == FRAME_THROW_FIRE) - { - ent->client->weapon_sound = 0; - fire(ent, true); - } - - if ((ent->client->ps.gunframe == FRAME_FIRE_LAST) && - (level.time < ent->client->grenade_time)) - { - return; - } - - ent->client->ps.gunframe++; - - if (ent->client->ps.gunframe == FRAME_IDLE_FIRST) - { - ent->client->grenade_time = 0; - ent->client->weaponstate = WEAPON_READY; - } - } -} - -void -Weapon_Grenade(edict_t *ent) -{ - static int pause_frames[] = {29, 34, 39, 48, 0}; - - if (!ent) - { - return; - } - - Throw_Generic(ent, 15, 48, 5, 11, 12, pause_frames, - GRENADE_TIMER, weapon_grenade_fire); -} - -void -Weapon_Tesla(edict_t *ent) -{ - static int pause_frames[] = {21, 0}; - - if (!ent) - { - return; - } - - if ((ent->client->ps.gunframe > 1) && (ent->client->ps.gunframe < 9)) - { - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_tesla2/tris.md2"); - } - else - { - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_tesla/tris.md2"); - } - - Throw_Generic(ent, 8, 32, 99, 1, 2, pause_frames, 0, weapon_grenade_fire); -} - -/* - * ====================================================================== - * - * GRENADE LAUNCHER - * - * ====================================================================== - */ - -void -weapon_grenadelauncher_fire(edict_t *ent) -{ - vec3_t offset; - vec3_t forward, right; - vec3_t start; - int damage; - float radius; - - if (!ent) - { - return; - } - - switch (ent->client->pers.weapon->tag) - { - case AMMO_PROX: - damage = 90; - break; - default: - damage = 120; - break; - } - - radius = damage + 40; - - if (is_quad) - { - damage *= damage_multiplier; - } - - 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; - - switch (ent->client->pers.weapon->tag) - { - case AMMO_PROX: - fire_prox(ent, start, forward, damage_multiplier, 600); - break; - default: - fire_grenade(ent, start, forward, damage, 600, 2.5, radius); - break; - } - - 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}; - - if (!ent) - { - return; - } - - Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, - fire_frames, weapon_grenadelauncher_fire); -} - -void -Weapon_ProxLauncher(edict_t *ent) -{ - static int pause_frames[] = {34, 51, 59, 0}; - static int fire_frames[] = {6, 0}; - - if (!ent) - { - return; - } - - 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; - - if (!ent) - { - return; - } - - damage = 100 + (int)(random() * 20.0); - radius_damage = 120; - damage_radius = 120; - - if (is_quad) - { - damage *= damage_multiplier; - radius_damage *= damage_multiplier; - } - - 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}; - - if (!ent) - { - return; - } - - 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 (!ent) - { - return; - } - - if (is_quad) - { - damage *= damage_multiplier; - } - - 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 (!ent) - { - return; - } - - 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}; - - if (!ent) - { - return; - } - - 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; - - if (!ent) - { - return; - } - - 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}; - - if (!ent) - { - return; - } - - 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) - { - return; - } - - 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 *= damage_multiplier; - kick *= damage_multiplier; - } - - 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 || g_machinegun_norecoil->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}; - - if (!ent) - { - return; - } - - 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 (!ent) - { - return; - } - - 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 *= damage_multiplier; - kick *= damage_multiplier; - } - - 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}; - - if (!ent) - { - return; - } - - 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) - { - return; - } - - 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 *= damage_multiplier; - kick *= damage_multiplier; - } - - 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}; - - if (!ent) - { - return; - } - - 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; - - if (!ent) - { - 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 *= damage_multiplier; - kick *= damage_multiplier; - } - - 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); - 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); - 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}; - - if (!ent) - { - return; - } - - 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 (!ent) - { - return; - } - - if (deathmatch->value) - { - /* normal damage is too extreme in dm */ - damage = 100; - kick = 200; - } - else - { - damage = 150; - kick = 250; - } - - if (is_quad) - { - damage *= damage_multiplier; - kick *= damage_multiplier; - } - - 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}; - - if (!ent) - { - return; - } - - 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 (!ent) - { - return; - } - - 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 *= damage_multiplier; - } - - 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}; - - if (!ent) - { - return; - } - - Weapon_Generic(ent, 8, 32, 55, 58, pause_frames, - fire_frames, weapon_bfg_fire); -} - -/* CHAINFIST */ - -void -weapon_chainfist_fire(edict_t *ent) -{ - vec3_t offset; - vec3_t forward, right, up; - vec3_t start; - int damage; - - if (!ent) - { - return; - } - - damage = 15; - - if (deathmatch->value) - { - damage = 30; - } - - if (is_quad) - { - damage *= damage_multiplier; - } - - AngleVectors(ent->client->v_angle, forward, right, up); - - /* kick back */ - VectorScale(forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - - /* set start point */ - VectorSet(offset, 0, 8, ent->viewheight - 4); - P_ProjectSource(ent, offset, forward, right, start); - - fire_player_melee(ent, start, forward, CHAINFIST_REACH, damage, - 100, 1, MOD_CHAINFIST); - - PlayerNoise(ent, start, PNOISE_WEAPON); - - ent->client->ps.gunframe++; - ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity; -} - -/* - * this spits out some smoke from the motor. it's a two-stroke, you know. - */ -void -chainfist_smoke(edict_t *ent) -{ - vec3_t tempVec, forward, right, up; - vec3_t offset; - - if (!ent) - { - return; - } - - AngleVectors(ent->client->v_angle, forward, right, up); - VectorSet(offset, 8, 8, ent->viewheight - 4); - P_ProjectSource(ent, offset, forward, right, tempVec); - - gi.WriteByte(svc_temp_entity); - gi.WriteByte(TE_CHAINFIST_SMOKE); - gi.WritePosition(tempVec); - gi.unicast(ent, 0); -} - -void -Weapon_ChainFist(edict_t *ent) -{ - static int pause_frames[] = {0}; - static int fire_frames[] = {8, 9, 16, 17, 18, 30, 31, 0}; - - /* these are caches for the sound index. there's probably a better way to do this. */ - float chance; - int last_sequence; - - last_sequence = 0; - - if ((ent->client->ps.gunframe == 13) || - (ent->client->ps.gunframe == 23)) /* end of attack, go idle */ - { - ent->client->ps.gunframe = 32; - } - - /* holds for idle sequence */ - else if ((ent->client->ps.gunframe == 42) && (rand() & 7)) - { - if ((ent->client->pers.hand != CENTER_HANDED) && (random() < 0.4)) - { - chainfist_smoke(ent); - } - } - else if ((ent->client->ps.gunframe == 51) && (rand() & 7)) - { - if ((ent->client->pers.hand != CENTER_HANDED) && (random() < 0.4)) - { - chainfist_smoke(ent); - } - } - - /* set the appropriate weapon sound. */ - if (ent->client->weaponstate == WEAPON_FIRING) - { - ent->client->weapon_sound = gi.soundindex("weapons/sawhit.wav"); - } - else if (ent->client->weaponstate == WEAPON_DROPPING) - { - ent->client->weapon_sound = 0; - } - else - { - ent->client->weapon_sound = gi.soundindex("weapons/sawidle.wav"); - } - - Weapon_Generic(ent, 4, 32, 57, 60, pause_frames, - fire_frames, weapon_chainfist_fire); - - if ((ent->client->buttons) & BUTTON_ATTACK) - { - if ((ent->client->ps.gunframe == 13) || - (ent->client->ps.gunframe == 23) || - (ent->client->ps.gunframe == 32)) - { - last_sequence = ent->client->ps.gunframe; - ent->client->ps.gunframe = 6; - } - } - - if (ent->client->ps.gunframe == 6) - { - chance = random(); - - if (last_sequence == 13) /* if we just did sequence 1, do 2 or 3. */ - { - chance -= 0.34; - } - else if (last_sequence == 23) /* if we just did sequence 2, do 1 or 3 */ - { - chance += 0.33; - } - else if (last_sequence == 32) /* if we just did sequence 3, do 1 or 2 */ - { - if (chance >= 0.33) - { - chance += 0.34; - } - } - - if (chance < 0.33) - { - ent->client->ps.gunframe = 14; - } - else if (chance < 0.66) - { - ent->client->ps.gunframe = 24; - } - } -} - -/* Disintegrator */ - -void -weapon_tracker_fire(edict_t *self) -{ - vec3_t forward, right; - vec3_t start; - vec3_t end; - vec3_t offset; - edict_t *enemy; - trace_t tr; - int damage; - vec3_t mins, maxs; - - if (!self) - { - return; - } - - if (deathmatch->value) - { - damage = 30; - } - else - { - damage = 45; - } - - if (is_quad) - { - damage *= damage_multiplier; - } - - VectorSet(mins, -16, -16, -16); - VectorSet(maxs, 16, 16, 16); - AngleVectors(self->client->v_angle, forward, right, NULL); - VectorSet(offset, 24, 8, self->viewheight - 8); - P_ProjectSource(self, offset, forward, right, start); - - VectorMA(start, 8192, forward, end); - enemy = NULL; - tr = gi.trace(start, vec3_origin, vec3_origin, end, self, MASK_SHOT); - - if (tr.ent != world) - { - if (tr.ent->svflags & SVF_MONSTER || tr.ent->client || tr.ent->svflags & SVF_DAMAGEABLE) - { - if (tr.ent->health > 0) - { - enemy = tr.ent; - } - } - } - else - { - tr = gi.trace(start, mins, maxs, end, self, MASK_SHOT); - - if (tr.ent != world) - { - if (tr.ent->svflags & SVF_MONSTER || tr.ent->client || - tr.ent->svflags & SVF_DAMAGEABLE) - { - if (tr.ent->health > 0) - { - enemy = tr.ent; - } - } - } - } - - VectorScale(forward, -2, self->client->kick_origin); - self->client->kick_angles[0] = -1; - - fire_tracker(self, start, forward, damage, 1000, enemy); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(self - g_edicts); - gi.WriteByte(MZ_TRACKER); - gi.multicast(self->s.origin, MULTICAST_PVS); - - PlayerNoise(self, start, PNOISE_WEAPON); - - self->client->ps.gunframe++; - self->client->pers.inventory[self->client->ammo_index] -= self->client->pers.weapon->quantity; -} - -void -Weapon_Disintegrator(edict_t *ent) -{ - static int pause_frames[] = {14, 19, 23, 0}; - static int fire_frames[] = {5, 0}; - - Weapon_Generic(ent, 4, 9, 29, 34, pause_frames, - fire_frames, weapon_tracker_fire); -} - -/* - * ====================================================================== - * - * ETF RIFLE - * - * ====================================================================== - */ -void -weapon_etf_rifle_fire(edict_t *ent) -{ - vec3_t forward, right, up; - vec3_t start, tempPt; - int damage = 10; - int kick = 3; - int i; - vec3_t offset; - - if (!ent) - { - return; - } - - if (ent->client->pers.inventory[ent->client->ammo_index] < ent->client->pers.weapon->quantity) - { - VectorClear(ent->client->kick_origin); - VectorClear(ent->client->kick_angles); - ent->client->ps.gunframe = 8; - - 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 *= damage_multiplier; - kick *= damage_multiplier; - } - - for (i = 0; i < 3; i++) - { - ent->client->kick_origin[i] = crandom() * 0.85; - ent->client->kick_angles[i] = crandom() * 0.85; - } - - /* get start / end positions */ - AngleVectors(ent->client->v_angle, forward, right, up); - - if (ent->client->ps.gunframe == 6) /* right barrel */ - { - VectorSet(offset, 15, 8, -8); - } - else /* left barrel */ - { - VectorSet(offset, 15, 6, -8); - } - - VectorCopy(ent->s.origin, tempPt); - tempPt[2] += ent->viewheight; - P_ProjectSource2(ent, tempPt, offset, forward, right, up, start); - fire_flechette(ent, start, forward, damage, 750, kick); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_ETF_RIFLE); - gi.multicast(ent->s.origin, MULTICAST_PVS); - - PlayerNoise(ent, start, PNOISE_WEAPON); - - ent->client->ps.gunframe++; - - if (!((int)dmflags->value & DF_INFINITE_AMMO)) - { - ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity; - } - - 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; - } -} - -void -Weapon_ETF_Rifle(edict_t *ent) -{ - static int pause_frames[] = {18, 28, 0}; - static int fire_frames[] = {6, 7, 0}; - - if (!ent) - { - return; - } - - if (ent->client->weaponstate == WEAPON_FIRING) - { - if (ent->client->pers.inventory[ent->client->ammo_index] <= 0) - { - ent->client->ps.gunframe = 8; - } - } - - Weapon_Generic(ent, 4, 7, 37, 41, pause_frames, - fire_frames, weapon_etf_rifle_fire); - - if ((ent->client->ps.gunframe == 8) && - (ent->client->buttons & BUTTON_ATTACK)) - { - ent->client->ps.gunframe = 6; - } -} - -void -Heatbeam_Fire(edict_t *ent) -{ - vec3_t start; - vec3_t forward, right, up; - vec3_t offset; - int damage; - int kick; - - if (!ent) - { - return; - } - - if (deathmatch->value) - { - damage = HEATBEAM_DM_DMG; - kick = 75; /* really knock 'em around in deathmatch */ - } - else - { - damage = HEATBEAM_SP_DMG; - kick = 30; - } - - ent->client->ps.gunframe++; - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_beamer2/tris.md2"); - - if (is_quad) - { - damage *= damage_multiplier; - kick *= damage_multiplier; - } - - VectorClear(ent->client->kick_origin); - VectorClear(ent->client->kick_angles); - - /* get start / end positions */ - AngleVectors(ent->client->v_angle, forward, right, up); - - /* This offset is the "view" offset for the beam start (used by trace) */ - VectorSet(offset, 7, 2, ent->viewheight - 3); - P_ProjectSource(ent, offset, forward, right, start); - - /* This offset is the entity offset */ - VectorSet(offset, 2, 7, -3); - - fire_heatbeam(ent, start, forward, offset, damage, kick, false); - - /* send muzzle flash */ - gi.WriteByte(svc_muzzleflash); - gi.WriteShort(ent - g_edicts); - gi.WriteByte(MZ_HEATBEAM | 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->pers.weapon->quantity; - } - - 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; - } -} - -void -Weapon_Heatbeam(edict_t *ent) -{ - static int pause_frames[] = {35, 0}; - static int fire_frames[] = {9, 10, 11, 12, 0}; - - if (!ent) - { - return; - } - - if (ent->client->weaponstate == WEAPON_FIRING) - { - ent->client->weapon_sound = gi.soundindex("weapons/bfg__l1a.wav"); - - if ((ent->client->pers.inventory[ent->client->ammo_index] >= 2) && - ((ent->client->buttons) & BUTTON_ATTACK)) - { - if (ent->client->ps.gunframe >= 13) - { - ent->client->ps.gunframe = 9; - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_beamer2/tris.md2"); - } - else - { - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_beamer2/tris.md2"); - } - } - else - { - ent->client->ps.gunframe = 13; - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_beamer/tris.md2"); - } - } - else - { - ent->client->ps.gunindex = gi.modelindex("models/weapons/v_beamer/tris.md2"); - ent->client->weapon_sound = 0; - } - - Weapon_Generic(ent, 8, 12, 39, 44, pause_frames, fire_frames, Heatbeam_Fire); -} diff --git a/src/rogue/savegame/tables/gamefunc_decs.h b/src/rogue/savegame/tables/gamefunc_decs.h index bb1b0804..798e026c 100644 --- a/src/rogue/savegame/tables/gamefunc_decs.h +++ b/src/rogue/savegame/tables/gamefunc_decs.h @@ -1459,7 +1459,7 @@ extern void ChasePrev ( edict_t * ent ) ; extern void ChaseNext ( edict_t * ent ) ; extern void UpdateChaseCam ( edict_t * ent ) ; extern void ai_run ( edict_t * self , float dist ) ; -extern qboolean ai_checkattack ( edict_t * self , float dist ) ; +extern qboolean ai_checkattack ( edict_t * self ) ; extern void ai_run_slide ( edict_t * self , float distance ) ; extern void ai_run_missile ( edict_t * self ) ; extern void ai_run_melee ( edict_t * self ) ;