diff --git a/src/game/baseq2/g_cmds.c b/src/game/baseq2/g_cmds.c index 79b8ab08..d0608f03 100644 --- a/src/game/baseq2/g_cmds.c +++ b/src/game/baseq2/g_cmds.c @@ -103,11 +103,24 @@ SelectNextItem(edict_t *ent, int itflags) cl = ent->client; +#ifdef CTF + if (cl->menu) + { + PMenu_Next(ent); + return; + } + else if (cl->chase_target) + { + ChaseNext(ent); + return; + } +#else if (cl->chase_target) { ChaseNext(ent); return; } +#endif /* scan for the next valid one */ for (i = 1; i <= MAX_ITEMS; i++) @@ -152,11 +165,24 @@ SelectPrevItem(edict_t *ent, int itflags) cl = ent->client; +#ifdef CTF + if (cl->menu) + { + PMenu_Prev(ent); + return; + } + else if (cl->chase_target) + { + ChasePrev(ent); + return; + } +#else if (cl->chase_target) { ChasePrev(ent); return; } +#endif /* scan for the next valid one */ for (i = 1; i <= MAX_ITEMS; i++) @@ -578,6 +604,16 @@ Cmd_Drop_f(edict_t *ent) return; } +#ifdef CTF + /* Special case for the + tech powerup */ + if (Q_stricmp(gi.args(), "tech") == 0 && (it = CTFWhat_Tech(ent)) != NULL) + { + it->drop (ent, it); + return; + } +#endif + s = gi.args(); it = FindItem(s); @@ -620,12 +656,28 @@ Cmd_Inven_f(edict_t *ent) cl->showscores = false; cl->showhelp = false; +#ifdef CTF + if (ent->client->menu) + { + PMenu_Close(ent); + ent->client->update_chase = true; + return; + } +#endif + if (cl->showinventory) { cl->showinventory = false; return; } +#ifdef CTF + if (ctf->value && cl->resp.ctf_team == CTF_NOTEAM) + { + CTFOpenJoinMenu(ent); + return; + } +#endif cl->showinventory = true; gi.WriteByte(svc_inventory); @@ -648,6 +700,14 @@ Cmd_InvUse_f(edict_t *ent) return; } +#ifdef CTF + if (ent->client->menu) + { + PMenu_Select(ent); + return; + } +#endif + ValidateSelectedItem(ent); if (ent->client->pers.selected_item == -1) @@ -667,6 +727,30 @@ Cmd_InvUse_f(edict_t *ent) it->use(ent, it); } +#ifdef CTF + +void +Cmd_LastWeap_f (edict_t *ent) +{ + gclient_t *cl; + + if (!ent) + { + return; + } + + cl = ent->client; + + if (!cl->pers.weapon || !cl->pers.lastweapon) + { + return; + } + + cl->pers.lastweapon->use (ent, cl->pers.lastweapon); +} + +#endif + void Cmd_WeapPrev_f(edict_t *ent) { @@ -851,6 +935,13 @@ Cmd_Kill_f(edict_t *ent) return; } +#ifdef CTF + if (ent->solid == SOLID_NOT) + { + return; + } +#endif + if (((level.time - ent->client->respawn_time) < 5) || (ent->client->resp.spectator)) { @@ -874,6 +965,15 @@ Cmd_PutAway_f(edict_t *ent) ent->client->showscores = false; ent->client->showhelp = false; ent->client->showinventory = false; + +#ifdef CTF + if (ent->client->menu) + { + PMenu_Close(ent); + } + + ent->client->update_chase = true; +#endif } int @@ -1319,6 +1419,60 @@ ClientCommand(edict_t *ent) { Cmd_PlayerList_f(ent); } +#ifdef CTF + else if (Q_stricmp (cmd, "team") == 0) + { + CTFTeam_f (ent); + } + else if (Q_stricmp(cmd, "id") == 0) + { + CTFID_f (ent); + } + else if (Q_stricmp(cmd, "yes") == 0) + { + CTFVoteYes(ent); + } + else if (Q_stricmp(cmd, "no") == 0) + { + CTFVoteNo(ent); + } + else if (Q_stricmp(cmd, "ready") == 0) + { + CTFReady(ent); + } + else if (Q_stricmp(cmd, "notready") == 0) + { + CTFNotReady(ent); + } + else if (Q_stricmp(cmd, "ghost") == 0) + { + CTFGhost(ent); + } + else if (Q_stricmp(cmd, "admin") == 0) + { + CTFAdmin(ent); + } + else if (Q_stricmp(cmd, "stats") == 0) + { + CTFStats(ent); + } + else if (Q_stricmp(cmd, "warp") == 0) + { + CTFWarp(ent); + } + else if (Q_stricmp(cmd, "boot") == 0) + { + CTFBoot(ent); + } + else if (Q_stricmp(cmd, "playerlist") == 0) + { + CTFPlayerList(ent); + } + else if (Q_stricmp(cmd, "observer") == 0) + { + CTFObserver(ent); + } +#endif else /* anything that doesn't match a command will be a chat */ { Cmd_Say_f(ent, false, true); diff --git a/src/game/baseq2/g_combat.c b/src/game/baseq2/g_combat.c index 13b3571f..1061867b 100644 --- a/src/game/baseq2/g_combat.c +++ b/src/game/baseq2/g_combat.c @@ -506,6 +506,29 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker) } } + +qboolean +CheckTeamDamage (edict_t *targ, edict_t *attacker) +{ + if (!targ || !attacker) + { + return false; + } + +#ifdef CTF + if (ctf->value && targ->client && attacker->client) + { + if (targ->client->resp.ctf_team == + attacker->client->resp.ctf_team && targ != attacker) + { + return true; + } +#endif + + return false; +} + + void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, @@ -581,6 +604,11 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, damage *= 2; } +#ifdef CTF + /* strength tech */ + damage = CTFApplyStrength(attacker, damage); +#endif + if (targ->flags & FL_NO_KNOCKBACK) { knockback = 0; @@ -646,21 +674,48 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, save = damage; } +#ifdef CTF + /* 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; + } +#else psave = CheckPowerArmor(targ, point, normal, take, dflags); take -= psave; asave = CheckArmor(targ, point, normal, take, te_sparks, dflags); take -= asave; +#endif /* treat cheat/powerup savings the same as armor */ asave += save; +#ifdef CTF + /* Resistance tech */ + take = CTFApplyResistance(targ, take); +#endif + /* team damage avoidance */ - if ((dflags & DAMAGE_NO_PROTECTION)) + if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) { return; } +#ifdef CTF + CTFCheckHurtCarrier(targ, attacker); +#endif + /* do the damage */ if (take) { diff --git a/src/game/baseq2/g_items.c b/src/game/baseq2/g_items.c index c7d463a0..23deada0 100644 --- a/src/game/baseq2/g_items.c +++ b/src/game/baseq2/g_items.c @@ -147,6 +147,27 @@ DoRespawn(edict_t *ent) master = ent->teammaster; +#ifdef CTF + /* in ctf, when we are weapons stay, only the + master of a team of weapons is spawned */ + if (ctf->value && ((int)dmflags->value & DF_WEAPONS_STAY) && + master->item && (master->item->flags & IT_WEAPON)) + { + ent = master; + } + else + { + for (count = 0, ent = master; ent; ent = ent->chain, count++) + { + } + + choice = rand() % count; + + for (count = 0, ent = master; count < choice; ent = ent->chain, count++) + { + } + } +#else for (count = 0, ent = master; ent; ent = ent->chain, count++) { } @@ -156,6 +177,7 @@ DoRespawn(edict_t *ent) for (count = 0, ent = master; count < choice; ent = ent->chain, count++) { } +#endif } ent->svflags &= ~SVF_NOCLIENT; @@ -824,7 +846,11 @@ MegaHealth_think(edict_t *self) return; } - if (self->owner->health > self->owner->max_health) + if (self->owner->health > self->owner->max_health +#ifdef CTF + && !CTFHasRegeneration(self->owner) +#endif + ) { self->nextthink = level.time + 1; self->owner->health -= 1; @@ -857,8 +883,22 @@ Pickup_Health(edict_t *ent, edict_t *other) } } +#ifdef CTF + if (other->health >= 250 && ent->count > 25) + { + return false; + } +#endif + other->health += ent->count; +#ifdef CTF + if (other->health > 250 && ent->count > 25) + { + other->health = 250; + } +#endif + if (!(ent->style & HEALTH_IGNORE_MAX)) { if (other->health > other->max_health) @@ -867,7 +907,11 @@ Pickup_Health(edict_t *ent, edict_t *other) } } - if (ent->style & HEALTH_TIMED) + if ((ent->style & HEALTH_TIMED) +#ifdef CTF + && !CTFHasRegeneration(other) +#endif + ) { ent->think = MegaHealth_think; ent->nextthink = level.time + 5; @@ -1641,6 +1685,16 @@ SpawnItem(edict_t *ent, gitem_t *item) item->drop = NULL; } +#ifdef CTF + /* Don't spawn the flags unless enabled */ + if (!ctf->value && (strcmp(ent->classname, "item_flag_team1") == 0 || + strcmp(ent->classname, "item_flag_team2") == 0)) + { + G_FreeEdict(ent); + return; + } +#endif + ent->item = item; ent->nextthink = level.time + 2 * FRAMETIME; /* items start after other solids */ ent->think = droptofloor; @@ -1651,6 +1705,15 @@ SpawnItem(edict_t *ent, gitem_t *item) { gi.modelindex(ent->model); } + +#ifdef CTF + /* flags are server animated */ + if (strcmp(ent->classname, "item_flag_team1") == 0 || + strcmp(ent->classname, "item_flag_team2") == 0) + { + ent->think = CTFFlagSetup; + } +#endif } /* ====================================================================== */ @@ -2572,6 +2635,140 @@ gitem_t itemlist[] = { "items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav" }, +#ifdef CTF + /* QUAKED item_flag_team1 (1 0.2 0) (-16 -16 -24) (16 16 32) */ + { + "item_flag_team1", + CTFPickup_Flag, + NULL, + CTFDrop_Flag, + NULL, + "ctf/flagtk.wav", + "players/male/flag1.md2", EF_FLAG1, + NULL, + "i_ctf1", + "Red Flag", + 2, + 0, + NULL, + 0, + 0, + NULL, + 0, + "ctf/flagcap.wav" + }, + + /* QUAKED item_flag_team2 (1 0.2 0) (-16 -16 -24) (16 16 32) */ + { + "item_flag_team2", + CTFPickup_Flag, + NULL, + CTFDrop_Flag, + NULL, + "ctf/flagtk.wav", + "players/male/flag2.md2", EF_FLAG2, + NULL, + "i_ctf2", + "Blue Flag", + 2, + 0, + NULL, + 0, + 0, + NULL, + 0, + "ctf/flagcap.wav" + }, + + /* Resistance Tech */ + { + "item_tech1", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/resistance/tris.md2", EF_ROTATE, + NULL, + "tech1", + "Disruptor Shield", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech1.wav" + }, + + /* Strength Tech */ + { + "item_tech2", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/strength/tris.md2", EF_ROTATE, + NULL, + "tech2", + "Power Amplifier", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech2.wav ctf/tech2x.wav" + }, + + /* Haste Tech */ + { + "item_tech3", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/haste/tris.md2", EF_ROTATE, + NULL, + "tech3", + "Time Accel", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech3.wav" + }, + + /* Regeneration Tech */ + { + "item_tech4", + CTFPickup_Tech, + NULL, + CTFDrop_Tech, + NULL, + "items/pkup.wav", + "models/ctf/regeneration/tris.md2", EF_ROTATE, + NULL, + "tech4", + "AutoDoc", + 2, + 0, + NULL, + IT_TECH, + 0, + NULL, + 0, + "ctf/tech4.wav" + }, +#endif + /* end of list marker */ {NULL} }; diff --git a/src/game/baseq2/g_local.h b/src/game/baseq2/g_local.h index b73b0105..b100b112 100644 --- a/src/game/baseq2/g_local.h +++ b/src/game/baseq2/g_local.h @@ -32,8 +32,16 @@ #define GAME_INCLUDE #include "game.h" +#ifdef CTF +#include "p_menu.h" +#endif + /* the "gameversion" client command will print this plus compile date */ -#define GAMEVERSION "baseq2" +#ifdef CTF + #define GAMEVERSION "ctf" +#else + #define GAMEVERSION "baseq2" +#endif /* protocol bytes that can be directly added to messages */ #define svc_muzzleflash 1 @@ -208,6 +216,9 @@ typedef struct #define IT_STAY_COOP 8 #define IT_KEY 16 #define IT_POWERUP 32 +#ifdef CTF + #define IT_TECH 64 +#endif /* gitem_t->weapmodel for weapons indicates model index */ #define WEAP_BLASTER 1 @@ -221,6 +232,9 @@ typedef struct #define WEAP_HYPERBLASTER 9 #define WEAP_RAILGUN 10 #define WEAP_BFG 11 +#ifdef CTF + #define WEAP_GRAPPLE 12 +#endif typedef struct gitem_s { @@ -494,6 +508,12 @@ extern cvar_t *dmflags; extern cvar_t *skill; extern cvar_t *fraglimit; extern cvar_t *timelimit; + +#ifdef CTF +extern cvar_t *capturelimit; +extern cvar_t *instantweap; +#endif + extern cvar_t *password; extern cvar_t *spectator_password; extern cvar_t *needpass; @@ -828,6 +848,22 @@ typedef struct client_persistant_t coop_respawn; /* what to set client->pers to on a respawn */ int enterframe; /* level.framenum the client entered the game */ int score; /* frags, etc */ + +#ifdef CTF + int ctf_team; /*CTF team */ + int ctf_state; + float ctf_lasthurtcarrier; + float ctf_lastreturnedflag; + float ctf_flagsince; + float ctf_lastfraggedcarrier; + qboolean id_state; + float lastidtime; + qboolean voted; /* for elections */ + qboolean ready; + qboolean admin; + struct ghost_s *ghost; +#endif + vec3_t cmd_angles; /* angles sent over in the last command */ qboolean spectator; /* client is a spectator */ @@ -847,6 +883,11 @@ struct gclient_s pmove_state_t old_pmove; /* for detecting out-of-pmove changes */ qboolean showscores; /* set layout stat */ + +#ifdef CTF + qboolean inmenu; /* in menu */ + pmenuhnd_t *menu; /* current menu */ +#endif qboolean showinventory; /* set layout stat */ qboolean showhelp; qboolean showhelpicon; @@ -915,6 +956,19 @@ struct gclient_s float respawn_time; /* can respawn when time > this */ +#ifdef CTF + void *ctf_grapple; /* entity of grapple */ + int ctf_grapplestate; /* true if pulling */ + float ctf_grapplereleasetime; /* time of grapple release */ + float ctf_regentime; /* regen tech */ + float ctf_techsndtime; + float ctf_lasttechmsg; + edict_t *chase_target; + qboolean update_chase; + float menutime; /* time to update menu */ + qboolean menudirty; +#endif + edict_t *chase_target; /* player we are chasing */ qboolean update_chase; /* need to update chase info? */ }; @@ -1066,3 +1120,7 @@ struct edict_s monsterinfo_t monsterinfo; }; +#ifdef CTF + #include "g_ctf.h" +#endif + diff --git a/src/game/baseq2/g_main.c b/src/game/baseq2/g_main.c index 3d4af694..c7274f30 100644 --- a/src/game/baseq2/g_main.c +++ b/src/game/baseq2/g_main.c @@ -44,6 +44,12 @@ cvar_t *dmflags; cvar_t *skill; cvar_t *fraglimit; cvar_t *timelimit; + +#ifdef CTF +cvar_t *capturelimit; +cvar_t *instantweap; +#endif + cvar_t *password; cvar_t *spectator_password; cvar_t *needpass; @@ -336,6 +342,19 @@ CheckDMRules(void) return; } +#ifdef CTF + if (ctf->value && CTFCheckRules()) + { + EndDMLevel (); + return; + } + + if (CTFInMatch()) + { + return; + } +#endif + if (timelimit->value) { if (level.time >= timelimit->value * 60) diff --git a/src/game/baseq2/g_misc.c b/src/game/baseq2/g_misc.c index be29a707..5b9ce12f 100644 --- a/src/game/baseq2/g_misc.c +++ b/src/game/baseq2/g_misc.c @@ -421,6 +421,32 @@ BecomeExplosion1(edict_t *self) return; } +#ifdef CTF + /* Flags are important! */ + if (strcmp(self->classname, "item_flag_team1") == 0) + { + CTFResetFlag(CTF_TEAM1); + gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n", + CTFTeamName(CTF_TEAM1)); + return; + } + + if (strcmp(self->classname, "item_flag_team2") == 0) + { + CTFResetFlag(CTF_TEAM2); + gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n", + CTFTeamName(CTF_TEAM1)); + return; + } + + /* techs are important too */ + if (self->item && (self->item->flags & IT_TECH)) + { + CTFRespawnTech(self); + return; + } +#endif + gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_EXPLOSION1); gi.WritePosition(self->s.origin); @@ -2584,6 +2610,10 @@ teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, return; } +#ifdef CTF + CTFPlayerResetGrapple(other); +#endif + /* unlink to make sure it can't possibly interfere with KillBox */ gi.unlinkentity(other); diff --git a/src/game/baseq2/g_spawn.c b/src/game/baseq2/g_spawn.c index 007367bb..1a12ec46 100644 --- a/src/game/baseq2/g_spawn.c +++ b/src/game/baseq2/g_spawn.c @@ -160,6 +160,11 @@ spawn_t spawns[] = { {"info_player_coop", SP_info_player_coop}, {"info_player_intermission", SP_info_player_intermission}, +#ifdef CTF + {"info_player_team1", SP_info_player_team1}, + {"info_player_team2", SP_info_player_team2}, +#endif + {"func_plat", SP_func_plat}, {"func_button", SP_func_button}, {"func_door", SP_func_door}, @@ -222,6 +227,10 @@ spawn_t spawns[] = { {"misc_explobox", SP_misc_explobox}, {"misc_banner", SP_misc_banner}, +#ifdef CTF + {"misc_ctf_banner", SP_misc_ctf_banner}, + {"misc_ctf_small_banner", SP_misc_ctf_small_banner}, +#endif {"misc_satellite_dish", SP_misc_satellite_dish}, {"misc_actor", SP_misc_actor}, {"misc_gib_arm", SP_misc_gib_arm}, @@ -235,11 +244,16 @@ spawn_t spawns[] = { {"misc_strogg_ship", SP_misc_strogg_ship}, {"misc_teleporter", SP_misc_teleporter}, {"misc_teleporter_dest", SP_misc_teleporter_dest}, +#ifdef CTF + {"trigger_teleport", SP_trigger_teleport}, + {"info_teleport_destination", SP_info_teleport_destination}, +#endif {"misc_blackhole", SP_misc_blackhole}, {"misc_eastertank", SP_misc_eastertank}, {"misc_easterchick", SP_misc_easterchick}, {"misc_easterchick2", SP_misc_easterchick2}, +#ifndef CTF {"monster_berserk", SP_monster_berserk}, {"monster_gladiator", SP_monster_gladiator}, {"monster_gunner", SP_monster_gunner}, @@ -268,6 +282,7 @@ spawn_t spawns[] = { {"turret_breach", SP_turret_breach}, {"turret_base", SP_turret_base}, {"turret_driver", SP_turret_driver}, +#endif {NULL, NULL} }; @@ -707,6 +722,10 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint) G_FindTeams(); PlayerTrail_Init(); + +#ifdef CTF + CTFSpawn(); +#endif } /* =================================================================== */ @@ -917,7 +936,19 @@ SP_worldspawn(edict_t *ent) /* status bar program */ if (deathmatch->value) { +#ifdef CTF + if (ctf->value) + { + gi.configstring (CS_STATUSBAR, ctf_statusbar); + CTFPrecache(); + } + else + { + gi.configstring (CS_STATUSBAR, dm_statusbar); + } +#else gi.configstring(CS_STATUSBAR, dm_statusbar); +#endif } else { diff --git a/src/game/baseq2/g_weapon.c b/src/game/baseq2/g_weapon.c index 4177d639..985c3c30 100644 --- a/src/game/baseq2/g_weapon.c +++ b/src/game/baseq2/g_weapon.c @@ -1103,6 +1103,16 @@ bfg_think(edict_t *self) continue; } +#ifdef CTF + /* Don't target players in CTF */ + if (ctf->value && ent->client && + self->owner->client && + ent->client->resp.ctf_team == self->owner->client->resp.ctf_team) + { + continue; + } +#endif + VectorMA(ent->absmin, 0.5, ent->size, point); VectorSubtract(point, self->s.origin, dir); diff --git a/src/game/baseq2/game.h b/src/game/baseq2/game.h index e029ebbe..1d8067a3 100644 --- a/src/game/baseq2/game.h +++ b/src/game/baseq2/game.h @@ -40,6 +40,10 @@ #define SVF_DEADMONSTER 0x00000002 /* treat as CONTENTS_DEADMONSTER for collision */ #define SVF_MONSTER 0x00000004 /* treat as CONTENTS_MONSTER for collision */ +#ifdef CTF + #define SVF_PROJECTILE 0x00000008 +#endif + #define MAX_ENT_CLUSTERS 16 typedef enum diff --git a/src/game/baseq2/p_client.c b/src/game/baseq2/p_client.c index 83877e88..79f4a519 100644 --- a/src/game/baseq2/p_client.c +++ b/src/game/baseq2/p_client.c @@ -520,6 +520,12 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */, message = "tried to invade"; message2 = "'s personal space"; break; +#ifdef CTF + case MOD_GRAPPLE: + message = "was caught by"; + message2 = "'s grapple"; + break; +#endif } if (message) @@ -691,6 +697,10 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, self->s.modelindex2 = 0; /* remove linked weapon model */ +#ifdef CTF + self->s.modelindex3 = 0; +#endif + self->s.angles[0] = 0; self->s.angles[2] = 0; @@ -707,8 +717,28 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, LookAtKiller(self, inflictor, attacker); self->client->ps.pmove.pm_type = PM_DEAD; ClientObituary(self, inflictor, attacker); + +#ifdef CTF + /* if at start and same team, clear */ + if (ctf->value && meansOfDeath == MOD_TELEFRAG && + self->client->resp.ctf_state < 2 && + self->client->resp.ctf_team == attacker->client->resp.ctf_team) + { + attacker->client->resp.score--; + self->client->resp.ctf_state = 0; + } + + CTFFragBonuses(self, inflictor, attacker); +#endif + TossClientWeapon(self); +#ifdef CTF + CTFPlayerResetGrapple(self); + CTFDeadDropFlag(self); + CTFDeadDropTech(self); +#endif + if (deathmatch->value) { Cmd_Help_f(self); /* show scores */ @@ -749,6 +779,11 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, ThrowClientHead(self, damage); +#ifdef CTF + self->client->anim_priority = ANIM_DEATH; + self->client->anim_end = 0; +#endif + self->takedamage = DAMAGE_NO; } else @@ -822,6 +857,13 @@ InitClientPersistant(gclient_t *client) client->pers.weapon = item; +#ifdef CTF + client->pers.lastweapon = item; + + item = FindItem("Grapple"); + client->pers.inventory[ITEM_INDEX(item)] = 1; +#endif + client->pers.health = 100; client->pers.max_health = 100; @@ -843,9 +885,27 @@ InitClientResp(gclient_t *client) return; } +#ifdef CTF + int ctf_team = client->resp.ctf_team; + qboolean id_state = client->resp.id_state; +#endif + memset(&client->resp, 0, sizeof(client->resp)); + +#ifdef CTF + client->resp.ctf_team = ctf_team; + client->resp.id_state = id_state; +#endif + client->resp.enterframe = level.framenum; client->resp.coop_respawn = client->pers; + +#ifdef CTF + if (ctf->value && client->resp.ctf_team < CTF_TEAM1) + { + CTFAssignTeam(client); + } +#endif } /* @@ -1139,7 +1199,18 @@ SelectSpawnPoint(edict_t *ent, vec3_t origin, vec3_t angles) if (deathmatch->value) { +#ifdef CTF + if (ctf->value) + { + spot = SelectCTFSpawnPoint(ent); + } + else + { + spot = SelectDeathmatchSpawnPoint (); + } +#else spot = SelectDeathmatchSpawnPoint(); +#endif } else if (coop->value) { @@ -1523,6 +1594,10 @@ PutClientInServer(edict_t *ent) client->ps.pmove.origin[1] = spawn_origin[1] * 8; client->ps.pmove.origin[2] = spawn_origin[2] * 8; +#ifdef CTF + client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; +#endif + if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { client->ps.fov = 90; @@ -1589,6 +1664,13 @@ PutClientInServer(edict_t *ent) client->resp.spectator = false; } +#ifdef CTF + if (CTFStartClient(ent)) + { + return; + } +#endif + if (!KillBox(ent)) { /* could't spawn in? */ @@ -1755,8 +1837,23 @@ ClientUserinfoChanged(edict_t *ent, char *userinfo) playernum = ent - g_edicts - 1; /* combine name and skin into a configstring */ +#ifdef CTF + if (ctf->value) + { + CTFAssignSkin(ent, s); + } + else + { + gi.configstring (CS_PLAYERSKINS+playernum, + va("%s\\%s", ent->client->pers.netname, s) ); + + } + /* set player name field (used in id_state view) */ + gi.configstring (CS_GENERAL+playernum, ent->client->pers.netname); +#else gi.configstring(CS_PLAYERSKINS + playernum, va("%s\\%s", ent->client->pers.netname, s)); +#endif /* fov */ if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) @@ -1870,6 +1967,12 @@ ClientConnect(edict_t *ent, char *userinfo) if (ent->inuse == false) { /* clear the respawning variables */ +#ifdef CTF + /* Force team join */ + ent->client->resp.ctf_team = -1; + ent->client->resp.id_state = true; +#endif + InitClientResp(ent->client); if (!game.autosaved || !ent->client->pers.weapon) @@ -1911,6 +2014,11 @@ ClientDisconnect(edict_t *ent) gi.bprintf(PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname); +#ifdef CTF + CTFDeadDropFlag(ent); + CTFDeadDropTech(ent); +#endif + /* send effect */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); @@ -2117,6 +2225,13 @@ ClientThink(edict_t *ent, usercmd_t *ucmd) VectorCopy(pm.viewangles, client->ps.viewangles); } +#ifdef CTF + if (client->ctf_grapple) + { + CTFGrapplePull(client->ctf_grapple); + } +#endif + gi.linkentity(ent); if (ent->movetype != MOVETYPE_NOCLIP) @@ -2160,7 +2275,11 @@ ClientThink(edict_t *ent, usercmd_t *ucmd) ent->light_level = ucmd->lightlevel; /* fire weapon from final position if needed */ - if (client->latched_buttons & BUTTON_ATTACK) + if (client->latched_buttons & BUTTON_ATTACK +#ifdef CTF + && ent->movetype != MOVETYPE_NOCLIP +#endif + ) { if (client->resp.spectator) { @@ -2183,6 +2302,29 @@ ClientThink(edict_t *ent, usercmd_t *ucmd) } } +#ifdef CTF + /* Regen tech */ + CTFApplyRegeneration(ent); + + for (i = 1; i <= maxclients->value; i++) + { + other = g_edicts + i; + + if (other->inuse && other->client->chase_target == ent) + { + UpdateChaseCam(other); + } + } + + if (client->menudirty && client->menutime <= level.time) + { + PMenu_Do_Update(ent); + gi.unicast (ent, true); + client->menutime = level.time; + client->menudirty = false; + } +#endif + if (client->resp.spectator) { if (ucmd->upmove >= 10) @@ -2251,7 +2393,11 @@ ClientBeginServerFrame(edict_t *ent) } /* run weapon animations if it hasn't been done by a ucmd_t */ - if (!client->weapon_thunk && !client->resp.spectator) + if (!client->weapon_thunk && !client->resp.spectator +#ifdef CTF + && ent->movetype != MOVETYPE_NOCLIP +#endif + ) { Think_Weapon(ent); } diff --git a/src/game/baseq2/p_hud.c b/src/game/baseq2/p_hud.c index 9d5ac5bf..1ec672ea 100644 --- a/src/game/baseq2/p_hud.c +++ b/src/game/baseq2/p_hud.c @@ -90,6 +90,13 @@ BeginIntermission(edict_t *targ) return; /* already activated */ } +#ifdef CTF + if (deathmatch->value && ctf->value) + { + CTFCalcScores(); + } +#endif + game.autosaved = false; /* respawn any dead clients */ @@ -213,6 +220,14 @@ DeathmatchScoreboardMessage(edict_t *ent, edict_t *killer) return; } +#ifdef CTF + if (ctf->value) + { + CTFScoreboardMessage (ent, killer); + return; + } +#endif + /* sort the clients by score */ total = 0; @@ -346,6 +361,13 @@ Cmd_Score_f(edict_t *ent) ent->client->showinventory = false; ent->client->showhelp = false; +#ifdef CTF + if (ent->client->menu) + { + PMenu_Close(ent); + } +#endif + if (!deathmatch->value && !coop->value) { return; @@ -698,5 +720,9 @@ G_SetSpectatorStats(edict_t *ent) { cl->ps.stats[STAT_CHASE] = 0; } + +#ifdef CTF + SetCTFStats(ent); +#endif } diff --git a/src/game/baseq2/p_view.c b/src/game/baseq2/p_view.c index 01935c1b..7178170b 100644 --- a/src/game/baseq2/p_view.c +++ b/src/game/baseq2/p_view.c @@ -670,6 +670,16 @@ P_FallingDamage(edict_t *ent) delta = delta * delta * 0.0001; +#ifdef CTF + /* never take damage if just release grapple or on grapple */ + if (level.time - ent->client->ctf_grapplereleasetime <= FRAMETIME * 2 || + (ent->client->ctf_grapple && + ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)) + { + return; + } +#endif + /* never take falling damage if completely underwater */ if (ent->waterlevel == 3) { @@ -985,6 +995,10 @@ G_SetClientEffects(edict_t *ent) } } +#ifdef CTF + CTFEffects(ent); +#endif + if (ent->client->quad_framenum > level.framenum) { remaining = ent->client->quad_framenum - level.framenum; @@ -1185,6 +1199,23 @@ newanim: if (!ent->groundentity) { +#ifdef CTF + if (client->ctf_grapple) + { + ent->s.frame = FRAME_stand01; + client->anim_end = FRAME_stand40; + } + else + { + client->anim_priority = ANIM_JUMP; + + if (ent->s.frame != FRAME_jump2) + { + ent->s.frame = FRAME_jump1; + } + client->anim_end = FRAME_jump2; + } +#else client->anim_priority = ANIM_JUMP; if (ent->s.frame != FRAME_jump2) @@ -1193,6 +1224,7 @@ newanim: } client->anim_end = FRAME_jump2; +#endif } else if (run) { @@ -1338,6 +1370,28 @@ ClientEndServerFrame(edict_t *ent) can be accurately determined */ SV_CalcBlend(ent); +#ifdef CTF + if (!ent->client->chase_target) + { + G_SetStats (ent); + } + + /* update chasecam follower stats */ + for (i = 1; i <= maxclients->value; i++) + { + edict_t *e = g_edicts + i; + + if (!e->inuse || e->client->chase_target != ent) + { + continue; + } + + memcpy(e->client->ps.stats, ent->client->ps.stats, + sizeof(ent->client->ps.stats)); + e->client->ps.stats[STAT_LAYOUTS] = 1; + break; + } +#else /* chase cam stuff */ if (ent->client->resp.spectator) { @@ -1350,6 +1404,8 @@ ClientEndServerFrame(edict_t *ent) G_CheckChaseStats(ent); +#endif + G_SetClientEvent(ent); G_SetClientEffects(ent); @@ -1368,7 +1424,20 @@ ClientEndServerFrame(edict_t *ent) /* if the scoreboard is up, update it */ if (ent->client->showscores && !(level.framenum & 31)) { +#ifdef CTF + if (ent->client->menu) + { + PMenu_Do_Update(ent); + ent->client->menudirty = false; + ent->client->menutime = level.time; + } + else + { + DeathmatchScoreboardMessage (ent, ent->enemy); + } +#else DeathmatchScoreboardMessage(ent, ent->enemy); +#endif gi.unicast(ent, false); } } diff --git a/src/game/baseq2/p_weapon.c b/src/game/baseq2/p_weapon.c index 09ded740..8af2a6d9 100644 --- a/src/game/baseq2/p_weapon.c +++ b/src/game/baseq2/p_weapon.c @@ -451,10 +451,17 @@ Drop_Weapon(edict_t *ent, gitem_t *item) * A generic function to handle * the basics of weapon thinking */ +#ifdef CTF +void +Weapon_Generic2(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, + int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, + int *fire_frames, void (*fire)(edict_t *ent)) +#else 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)) +#endif { int n; @@ -605,11 +612,23 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, { if (ent->client->ps.gunframe == fire_frames[n]) { +#ifdef CTF + if (!CTFApplyStrengthSound(ent)) + { + if (ent->client->quad_framenum > level.framenum) + { + gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); + } + } + + CTFApplyHasteSound(ent); +#else if (ent->client->quad_framenum > level.framenum) { gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/damage3.wav"), 1, ATTN_NORM, 0); } +#endif fire(ent); break; @@ -628,6 +647,39 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, } } +#ifdef CTF +void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, + int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, + int *fire_frames, void (*fire)(edict_t *ent)) +{ + int oldstate = ent->client->weaponstate; + + if (!ent || !fire_frames || !fire) + { + return; + } + + Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, + FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, + fire_frames, fire); + + /* run the weapon frame again if hasted */ + if (Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0 && + ent->client->weaponstate == WEAPON_FIRING) + return; + + if ((CTFApplyHaste(ent) || + (Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0 && + ent->client->weaponstate != WEAPON_FIRING)) + && oldstate == ent->client->weaponstate) + { + Weapon_Generic2 (ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST, + FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames, + fire_frames, fire); + } +} +#endif + /* ====================================================================== */ /* GRENADE */ diff --git a/src/game/baseq2/savegame/savegame.c b/src/game/baseq2/savegame/savegame.c index d7245b34..3b87481b 100644 --- a/src/game/baseq2/savegame/savegame.c +++ b/src/game/baseq2/savegame/savegame.c @@ -215,10 +215,30 @@ InitGame(void) skill = gi.cvar("skill", "1", CVAR_LATCH); maxentities = gi.cvar("maxentities", "1024", CVAR_LATCH); +#ifdef CTF + /* This game.so only supports deathmatch */ + if (!deathmatch->value) + { + gi.dprintf("Forcing deathmatch.\n"); + gi.cvar_set("deathmatch", "1"); + } + + if (coop->value) + { + gi.cvar_set("coop", "0"); + } +#endif + /* change anytime vars */ dmflags = gi.cvar("dmflags", "0", CVAR_SERVERINFO); fraglimit = gi.cvar("fraglimit", "0", CVAR_SERVERINFO); timelimit = gi.cvar("timelimit", "0", CVAR_SERVERINFO); + +#ifdef CTF + capturelimit = gi.cvar ("capturelimit", "0", CVAR_SERVERINFO); + instantweap = gi.cvar ("instantweap", "0", CVAR_SERVERINFO); +#endif + password = gi.cvar("password", "", CVAR_USERINFO); spectator_password = gi.cvar("spectator_password", "", CVAR_USERINFO); needpass = gi.cvar("needpass", "0", CVAR_SERVERINFO); @@ -254,6 +274,10 @@ InitGame(void) game.maxclients = maxclients->value; game.clients = gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME); globals.num_edicts = game.maxclients + 1; + +#ifdef CTF + CTFInit(); +#endif } /* ========================================================= */