From 172e8b743efff594fe22729a78d12ca5e1620c26 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sat, 4 Nov 2023 21:03:05 +0200 Subject: [PATCH] game: merge ctf --- Makefile | 136 +---- src/ctf/g_combat.c | 1268 ---------------------------------------- src/ctf/g_main.c | 518 ---------------- src/ctf/header/game.h | 22 - src/ctf/header/local.h | 22 - src/game/g_combat.c | 14 + src/game/g_main.c | 25 + 7 files changed, 41 insertions(+), 1964 deletions(-) delete mode 100644 src/ctf/g_combat.c delete mode 100644 src/ctf/g_main.c delete mode 100644 src/ctf/header/game.h delete mode 100644 src/ctf/header/local.h diff --git a/Makefile b/Makefile index 05585682..e64d9275 100644 --- a/Makefile +++ b/Makefile @@ -380,12 +380,12 @@ endif # ---------- # Phony targets -.PHONY : all client game icon server ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk ref_gl4 ctf +.PHONY : all client game icon server ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk ref_gl4 # ---------- # Builds everything -all: config client server game ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk ref_gl4 ctf +all: config client server game ref_gl1 ref_gl3 ref_gles3 ref_soft ref_vk ref_gl4 # ---------- @@ -1446,135 +1446,3 @@ release/baseq2/game.so : $(GAME_OBJS) endif # ---------- - -# The ctf game -ifeq ($(YQ2_OSTYPE), Windows) -ctf: - @echo "===> Building ctf/game.dll" - ${Q}mkdir -p release/ctf - $(MAKE) release/ctf/game.dll -else ifeq ($(YQ2_OSTYPE), Darwin) -ctf: - @echo "===> Building ctf/game.dylib" - ${Q}mkdir -p release/ctf - $(MAKE) release/ctf/game.dylib -else -ctf: - @echo "===> Building ctf/game.so" - ${Q}mkdir -p release/ctf - $(MAKE) release/ctf/game.so - -release/ctf/game.so : CFLAGS += -fPIC -Wno-unused-result -release/ctf/game.so : LDFLAGS += -shared - -endif - -build/ctf/%.o: %.c - @echo "===> CC $<" - ${Q}mkdir -p $(@D) - ${Q}$(CC) -c $(CFLAGS) -o $@ $< - -# ---------- - -CTF_OBJS_ = \ - src/common/shared/flash.o \ - src/common/shared/rand.o \ - src/common/shared/shared.o \ - src/game/g_ai.o \ - src/game/g_chase.o \ - src/game/g_cmds.o \ - src/ctf/g_combat.o \ - src/game/g_ctf.o \ - src/game/g_func.o \ - src/game/g_items.o \ - src/game/g_newai.o \ - src/game/g_newdm.o \ - src/game/g_newfnc.o \ - src/game/g_newtarg.o \ - src/game/g_newtrig.o \ - src/game/g_newweap.o \ - src/ctf/g_main.o \ - src/game/g_misc.o \ - src/game/g_monster.o \ - src/game/g_phys.o \ - src/game/g_sphere.o \ - src/game/g_spawn.o \ - src/game/g_svcmds.o \ - src/game/g_target.o \ - src/game/g_trigger.o \ - src/game/g_turret.o \ - src/game/g_utils.o \ - src/game/g_weapon.o \ - src/game/menu/menu.o \ - src/game/dm/ball.o \ - src/game/dm/tag.o \ - src/game/monster/berserker/berserker.o \ - src/game/monster/boss2/boss2.o \ - src/game/monster/boss3/boss3.o \ - src/game/monster/boss3/boss31.o \ - src/game/monster/boss3/boss32.o \ - src/game/monster/boss5/boss5.o \ - src/game/monster/brain/brain.o \ - src/game/monster/carrier/carrier.o \ - src/game/monster/chick/chick.o \ - src/game/monster/fixbot/fixbot.o \ - src/game/monster/flipper/flipper.o \ - src/game/monster/float/float.o \ - src/game/monster/flyer/flyer.o \ - src/game/monster/gekk/gekk.o \ - src/game/monster/gladiator/gladb.o \ - src/game/monster/gladiator/gladiator.o \ - src/game/monster/gunner/gunner.o \ - src/game/monster/hover/hover.o \ - src/game/monster/infantry/infantry.o \ - src/game/monster/insane/insane.o \ - src/game/monster/medic/medic.o \ - src/game/monster/misc/move.o \ - src/game/monster/mutant/mutant.o \ - src/game/monster/parasite/parasite.o \ - src/game/monster/soldier/soldier.o \ - src/game/monster/stalker/stalker.o \ - src/game/monster/supertank/supertank.o \ - src/game/monster/tank/tank.o \ - src/game/monster/turret/turret.o \ - src/game/monster/widow/widow2.o \ - src/game/monster/widow/widow.o \ - src/game/player/client.o \ - src/game/player/hud.o \ - src/game/player/trail.o \ - src/game/player/view.o \ - src/game/player/weapon.o \ - src/game/savegame/savegame.o - -# ---------- - -# Rewrite paths to our object directory -CTF_OBJS = $(patsubst %,build/ctf/%,$(CTF_OBJS_)) - -# ---------- - -# Generate header dependencies -CTF_DEPS= $(CTF_OBJS:.o=.d) - -# ---------- - -# Suck header dependencies in --include $(CTF_DEPS) - -# ---------- - -ifeq ($(YQ2_OSTYPE), Windows) -release/ctf/game.dll : $(CTF_OBJS) - @echo "===> LD $@" - ${Q}$(CC) $(LDFLAGS) $(CTF_OBJS) $(LDLIBS) -o $@ -else ifeq ($(YQ2_OSTYPE), Darwin) -release/ctf/game.dylib : $(CTF_OBJS) - @echo "===> LD $@" - ${Q}$(CC) $(LDFLAGS) $(CTF_OBJS) $(LDLIBS) -o $@ -else -release/ctf/game.so : $(CTF_OBJS) - @echo "===> LD $@" - ${Q}$(CC) $(LDFLAGS) $(CTF_OBJS) $(LDLIBS) -o $@ -endif - -# ---------- diff --git a/src/ctf/g_combat.c b/src/ctf/g_combat.c deleted file mode 100644 index 6403688e..00000000 --- a/src/ctf/g_combat.c +++ /dev/null @@ -1,1268 +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; - } - - if (targ->health < -999) - { - targ->health = -999; - } - - /* Reset AI flag for being ducked. This fixes a corner case - were the monster is ressurected by a medic and get's stuck - in the next frame for mmove_t not matching the AI state. */ - 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++; - } - - /* medics won't heal monsters that they kill themselves */ - if (attacker->classname && strcmp(attacker->classname, "monster_medic") == 0) - { - targ->owner = attacker; - } - } - } - - 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; - } - - index = 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; - index = 0; - } - 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 - { - if (ctf->value) - { - /* power armor is weaker in CTF */ - damagePerCell = 1; - } - 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 (targ->health <= 0) - { - 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; - } - - /* it's the same base (walk/swim/fly) type and a - different classname and it's not a tank - (they spray too much), get mad at them */ - if (((targ->flags & (FL_FLY | FL_SWIM)) == - (attacker->flags & (FL_FLY | FL_SWIM))) && - (strcmp(targ->classname, attacker->classname) != 0) && - (strcmp(attacker->classname, "monster_tank") != 0) && - (strcmp(attacker->classname, "monster_supertank") != 0) && - (strcmp(attacker->classname, "monster_makron") != 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) - { - 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) -{ - if (ctf->value && targ->client && attacker->client) - { - if ((targ->client->resp.ctf_team == attacker->client->resp.ctf_team) && - (targ != attacker)) - { - return true; - } - } - - 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; - } - - VectorNormalize(dir); - - /* bonus damage for suprising a monster */ - if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && - (attacker->client) && (!targ->enemy) && (targ->health > 0)) - { - damage *= 2; - } - - /* strength tech */ - damage = CTFApplyStrength(attacker, damage); - - if (targ->flags & FL_NO_KNOCKBACK) - { - knockback = 0; - } - - /* figure momentum add */ - if (!(dflags & DAMAGE_NO_KNOCKBACK)) - { - if ((knockback) && (targ->movetype != MOVETYPE_NONE) && - (targ->movetype != MOVETYPE_BOUNCE) && - (targ->movetype != MOVETYPE_PUSH) && - (targ->movetype != MOVETYPE_STOP)) - { - vec3_t kvel; - float mass; - - if (targ->mass < 50) - { - mass = 50; - } - else - { - mass = targ->mass; - } - - if (targ->client && (attacker == targ)) - { - VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); /* the rocket jump hack... */ - } - else - { - VectorScale(dir, 500.0 * (float)knockback / mass, kvel); - } - - VectorAdd(targ->velocity, kvel, targ->velocity); - } - } - - 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) && (mod != MOD_TRAP)) - { - 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; - } - - /* team armor protect */ - if (ctf->value && targ->client && attacker->client && - (targ->client->resp.ctf_team == attacker->client->resp.ctf_team) && - (targ != attacker) && ((int)dmflags->value & DF_ARMOR_PROTECT)) - { - psave = asave = 0; - } - else - { - 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; - - /* resistance tech */ - take = CTFApplyResistance(targ, take); - - /* team damage avoidance */ - if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage(targ, attacker)) - { - return; - } - - CTFCheckHurtCarrier(targ, attacker); - - /* 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 (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); - } - } - else - { - SpawnDamage(te_sparks, point, normal); - } - - if (!CTFMatchSetup()) - { - 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 | AI_IGNORE_PAIN)) && (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) && !CTFMatchSetup()) - { - 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/ctf/g_main.c b/src/ctf/g_main.c deleted file mode 100644 index 3441cc1a..00000000 --- a/src/ctf/g_main.c +++ /dev/null @@ -1,518 +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. - * - * ======================================================================= - * - * Jump in into the game.so and support functions. - * - * ======================================================================= - */ - -#include "header/local.h" - -game_locals_t game; -level_locals_t level; -game_import_t gi; -game_export_t globals; -spawn_temp_t st; - -int sm_meat_index; -int snd_fry; -int meansOfDeath; - -edict_t *g_edicts; - -cvar_t *deathmatch; -cvar_t *coop; -cvar_t *coop_baseq2; /* treat spawnflags according to baseq2 rules */ -cvar_t *coop_pickup_weapons; -cvar_t *coop_elevator_delay; -cvar_t *dmflags; -cvar_t *skill; -cvar_t *fraglimit; -cvar_t *timelimit; -cvar_t *capturelimit; -cvar_t *instantweap; -cvar_t *password; -cvar_t *spectator_password; -cvar_t *needpass; -cvar_t *maxclients; -cvar_t *maxspectators; -cvar_t *maxentities; -cvar_t *g_select_empty; -cvar_t *dedicated; -cvar_t *g_footsteps; -cvar_t *g_monsterfootsteps; -cvar_t *g_fix_triggered; -cvar_t *g_commanderbody_nogod; - -cvar_t *filterban; - -cvar_t *sv_maxvelocity; -cvar_t *sv_gravity; - -cvar_t *sv_rollspeed; -cvar_t *sv_rollangle; -cvar_t *gun_x; -cvar_t *gun_y; -cvar_t *gun_z; - -cvar_t *run_pitch; -cvar_t *run_roll; -cvar_t *bob_up; -cvar_t *bob_pitch; -cvar_t *bob_roll; - -cvar_t *sv_cheats; - -cvar_t *flood_msgs; -cvar_t *flood_persecond; -cvar_t *flood_waitdelay; - -cvar_t *sv_maplist; -cvar_t *sv_stopspeed; - -cvar_t *gib_on; -cvar_t *g_showlogic; -cvar_t *gamerules; -cvar_t *huntercam; -cvar_t *strong_mines; -cvar_t *randomrespawn; - -cvar_t *g_disruptor; - -cvar_t *aimfix; -cvar_t *g_machinegun_norecoil; -cvar_t *g_swap_speed; - -void G_RunFrame(void); - -/* =================================================================== */ - -void -ShutdownGame(void) -{ - gi.dprintf("==== ShutdownGame ====\n"); - - gi.FreeTags(TAG_LEVEL); - gi.FreeTags(TAG_GAME); -} - -/* - * Returns a pointer to the structure - * with all entry points and global - * variables - */ -Q2_DLL_EXPORTED game_export_t * -GetGameAPI(game_import_t *import) -{ - gi = *import; - - globals.apiversion = GAME_API_VERSION; - globals.Init = InitGame; - globals.Shutdown = ShutdownGame; - globals.SpawnEntities = SpawnEntities; - - globals.WriteGame = WriteGame; - globals.ReadGame = ReadGame; - globals.WriteLevel = WriteLevel; - globals.ReadLevel = ReadLevel; - - globals.ClientThink = ClientThink; - globals.ClientConnect = ClientConnect; - globals.ClientUserinfoChanged = ClientUserinfoChanged; - globals.ClientDisconnect = ClientDisconnect; - globals.ClientBegin = ClientBegin; - globals.ClientCommand = ClientCommand; - - globals.RunFrame = G_RunFrame; - - globals.ServerCommand = ServerCommand; - - globals.edict_size = sizeof(edict_t); - - /* Initalize the PRNG */ - randk_seed(); - - return &globals; -} - -/* - * this is only here so the functions - * in shared source files can link - */ -void -Sys_Error(const char *error, ...) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, error); - vsnprintf(text, sizeof(text), error, argptr); - va_end(argptr); - - gi.error("%s", text); -} - -void -Com_Printf(const char *msg, ...) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, msg); - vsnprintf(text, sizeof(text), msg, argptr); - va_end(argptr); - - gi.dprintf("%s", text); -} - -/* ====================================================================== */ - -void -ClientEndServerFrames(void) -{ - int i; - edict_t *ent; - - /* calc the player views now that all - pushing and damage has been added */ - for (i = 0; i < maxclients->value; i++) - { - ent = g_edicts + 1 + i; - - if (!ent->inuse || !ent->client) - { - continue; - } - - ClientEndServerFrame(ent); - } -} - -/* - * Returns the created target changelevel - */ -edict_t * -CreateTargetChangeLevel(char *map) -{ - edict_t *ent; - - if (!map) - { - return NULL; - } - - ent = G_Spawn(); - ent->classname = "target_changelevel"; - Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map); - ent->map = level.nextmap; - return ent; -} - -/* - * The timelimit or fraglimit has been exceeded - */ -void -EndDMLevel(void) -{ - edict_t *ent; - char *s, *t, *f; - static const char *seps = " ,\n\r"; - - /* stay on same level flag */ - if ((int)dmflags->value & DF_SAME_LEVEL) - { - BeginIntermission(CreateTargetChangeLevel(level.mapname)); - return; - } - - if (*level.forcemap) - { - BeginIntermission(CreateTargetChangeLevel(level.forcemap)); - return; - } - - /* see if it's in the map list */ - if (*sv_maplist->string) - { - s = strdup(sv_maplist->string); - f = NULL; - t = strtok(s, seps); - - while (t != NULL) - { - if (Q_stricmp(t, level.mapname) == 0) - { - /* it's in the list, go to the next one */ - t = strtok(NULL, seps); - - if (t == NULL) /* end of list, go to first one */ - { - if (f == NULL) /* there isn't a first one, same level */ - { - BeginIntermission(CreateTargetChangeLevel(level.mapname)); - } - else - { - BeginIntermission(CreateTargetChangeLevel(f)); - } - } - else - { - BeginIntermission(CreateTargetChangeLevel(t)); - } - - free(s); - return; - } - - if (!f) - { - f = t; - } - - t = strtok(NULL, seps); - } - - free(s); - } - - if (level.nextmap[0]) /* go to a specific map */ - { - BeginIntermission(CreateTargetChangeLevel(level.nextmap)); - } - else /* search for a changelevel */ - { - ent = G_Find(NULL, FOFS(classname), "target_changelevel"); - - if (!ent) - { - /* the map designer didn't include a changelevel, - so create a fake ent that goes back to the same - level */ - BeginIntermission(CreateTargetChangeLevel(level.mapname)); - return; - } - - BeginIntermission(ent); - } -} - -void -CheckNeedPass(void) -{ - int need; - - /* if password or spectator_password has - changed, update needpass as needed */ - if (password->modified || spectator_password->modified) - { - password->modified = spectator_password->modified = false; - - need = 0; - - if (*password->string && Q_stricmp(password->string, "none")) - { - need |= 1; - } - - if (*spectator_password->string && - Q_stricmp(spectator_password->string, "none")) - { - need |= 2; - } - - gi.cvar_set("needpass", va("%d", need)); - } -} - -void -CheckDMRules(void) -{ - int i; - gclient_t *cl; - - if (level.intermissiontime) - { - return; - } - - if (!deathmatch->value) - { - return; - } - - if (ctf->value && CTFCheckRules()) - { - EndDMLevel(); - return; - } - - if (CTFInMatch()) - { - return; /* no checking in match mode */ - } - - if (timelimit->value) - { - if (level.time >= timelimit->value * 60) - { - gi.bprintf(PRINT_HIGH, "Timelimit hit.\n"); - EndDMLevel(); - return; - } - } - - if (fraglimit->value) - { - for (i = 0; i < maxclients->value; i++) - { - cl = game.clients + i; - - if (!g_edicts[i + 1].inuse) - { - continue; - } - - if (cl->resp.score >= fraglimit->value) - { - gi.bprintf(PRINT_HIGH, "Fraglimit hit.\n"); - EndDMLevel(); - return; - } - } - } -} - -void -ExitLevel(void) -{ - int i; - edict_t *ent; - char command[256]; - - level.exitintermission = 0; - level.intermissiontime = 0; - - if (CTFNextMap()) - { - return; - } - - Com_sprintf(command, sizeof(command), "gamemap \"%s\"\n", level.changemap); - gi.AddCommandString(command); - level.changemap = NULL; - level.exitintermission = 0; - level.intermissiontime = 0; - ClientEndServerFrames(); - - /* clear some things before going to next level */ - for (i = 0; i < maxclients->value; i++) - { - ent = g_edicts + 1 + i; - - if (!ent->inuse) - { - continue; - } - - if (ent->health > ent->client->pers.max_health) - { - ent->health = ent->client->pers.max_health; - } - } - - debristhisframe = 0; - gibsthisframe = 0; -} - -/* - * Advances the world by 0.1 seconds - */ -void -G_RunFrame(void) -{ - int i; - edict_t *ent; - - level.framenum++; - level.time = level.framenum * FRAMETIME; - - gibsthisframe = 0; - debristhisframe = 0; - - /* choose a client for monsters to target this frame */ - AI_SetSightClient(); - - /* exit intermissions */ - if (level.exitintermission) - { - ExitLevel(); - return; - } - - /* treat each object in turn - even the world gets a chance - to think */ - ent = &g_edicts[0]; - - for (i = 0; i < globals.num_edicts; i++, ent++) - { - if (!ent->inuse) - { - continue; - } - - level.current_entity = ent; - - VectorCopy(ent->s.origin, ent->s.old_origin); - - /* if the ground entity moved, make sure we are still on it */ - if ((ent->groundentity) && - (ent->groundentity->linkcount != ent->groundentity_linkcount)) - { - ent->groundentity = NULL; - - if (!(ent->flags & (FL_SWIM | FL_FLY)) && - (ent->svflags & SVF_MONSTER)) - { - M_CheckGround(ent); - } - } - - if ((i > 0) && (i <= maxclients->value)) - { - ClientBeginServerFrame(ent); - continue; - } - - G_RunEntity(ent); - } - - /* see if it is time to end a deathmatch */ - CheckDMRules(); - - /* see if needpass needs updated */ - CheckNeedPass(); - - /* build the playerstate_t structures for all players */ - ClientEndServerFrames(); -} diff --git a/src/ctf/header/game.h b/src/ctf/header/game.h deleted file mode 100644 index 51446c50..00000000 --- a/src/ctf/header/game.h +++ /dev/null @@ -1,22 +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. - */ - -#include "../../game/header/game.h" diff --git a/src/ctf/header/local.h b/src/ctf/header/local.h deleted file mode 100644 index a71b22f2..00000000 --- a/src/ctf/header/local.h +++ /dev/null @@ -1,22 +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. - */ - -#include "../../game/header/local.h" diff --git a/src/game/g_combat.c b/src/game/g_combat.c index 0655e8b8..23f38abd 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -812,6 +812,9 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, damage *= 2; } + /* strength tech */ + damage = CTFApplyStrength(attacker, damage); + if (targ->flags & FL_NO_KNOCKBACK) { knockback = 0; @@ -901,6 +904,17 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, } } + /* resistance tech */ + take = CTFApplyResistance(targ, take); + + /* team damage avoidance */ + if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage(targ, attacker)) + { + return; + } + + CTFCheckHurtCarrier(targ, attacker); + /* do the damage */ if (take) { diff --git a/src/game/g_main.c b/src/game/g_main.c index f90eaae4..1c078994 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -244,6 +244,12 @@ EndDMLevel(void) return; } + if (*level.forcemap) + { + BeginIntermission(CreateTargetChangeLevel(level.forcemap)); + return; + } + /* see if it's in the map list */ if (*sv_maplist->string) { @@ -362,6 +368,17 @@ CheckDMRules(void) } } + if (ctf->value && CTFCheckRules()) + { + EndDMLevel(); + return; + } + + if (CTFInMatch()) + { + return; /* no checking in match mode */ + } + if (timelimit->value) { if (level.time >= timelimit->value * 60) @@ -400,6 +417,14 @@ ExitLevel(void) edict_t *ent; char command[256]; + level.exitintermission = 0; + level.intermissiontime = 0; + + if (CTFNextMap()) + { + return; + } + Com_sprintf(command, sizeof(command), "gamemap \"%s\"\n", level.changemap); gi.AddCommandString(command); level.changemap = NULL;