diff --git a/Makefile b/Makefile index 49549598..f2ad1e5c 100644 --- a/Makefile +++ b/Makefile @@ -1482,7 +1482,7 @@ CTF_OBJS_ = \ src/common/shared/shared.o \ src/ctf/g_ai.o \ src/game/g_chase.o \ - src/ctf/g_cmds.o \ + src/game/g_cmds.o \ src/ctf/g_combat.o \ src/game/g_ctf.o \ src/game/g_func.o \ diff --git a/src/ctf/g_cmds.c b/src/ctf/g_cmds.c deleted file mode 100644 index 0c879a39..00000000 --- a/src/ctf/g_cmds.c +++ /dev/null @@ -1,1460 +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. - * - * ======================================================================= - * - * Game command processing. - * - * ======================================================================= - */ - -#include "header/local.h" -#include "monster/misc/player.h" - -gitem_t *CTFWhat_Tech(edict_t *ent); - -static char * -ClientTeam(edict_t *ent, char* value) -{ - char *p; - - value[0] = 0; - - if (!ent) - { - return value; - } - - if (!ent->client) - { - return value; - } - - strcpy(value, Info_ValueForKey(ent->client->pers.userinfo, "skin")); - p = strchr(value, '/'); - - if (!p) - { - return value; - } - - if ((int)(dmflags->value) & DF_MODELTEAMS) - { - *p = 0; - return value; - } - - return ++p; -} - -qboolean -OnSameTeam(edict_t *ent1, edict_t *ent2) -{ - char ent1Team[512]; - char ent2Team[512]; - - if (!ent1 || !ent2) - { - return false; - } - - if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) - { - return false; - } - - ClientTeam(ent1, ent1Team); - ClientTeam(ent2, ent2Team); - - if (ent1Team[0] != '\0' && strcmp(ent1Team, ent2Team) == 0) - { - return true; - } - - return false; -} - -void -SelectNextItem(edict_t *ent, int itflags) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - - if (!ent) - { - return; - } - - cl = ent->client; - - if (cl->menu) - { - PMenu_Next(ent); - return; - } - else if (cl->chase_target) - { - ChaseNext(ent); - return; - } - - /* scan for the next valid one */ - for (i = 1; i <= MAX_ITEMS; i++) - { - index = (cl->pers.selected_item + i) % MAX_ITEMS; - - if (!cl->pers.inventory[index]) - { - continue; - } - - it = &itemlist[index]; - - if (!it->use) - { - continue; - } - - if (!(it->flags & itflags)) - { - continue; - } - - cl->pers.selected_item = index; - return; - } - - cl->pers.selected_item = -1; -} - -void -SelectPrevItem(edict_t *ent, int itflags) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - - if (!ent) - { - return; - } - - cl = ent->client; - - if (cl->menu) - { - PMenu_Prev(ent); - return; - } - else if (cl->chase_target) - { - ChasePrev(ent); - return; - } - - /* scan for the next valid one */ - for (i = 1; i <= MAX_ITEMS; i++) - { - index = (cl->pers.selected_item + MAX_ITEMS - i) % MAX_ITEMS; - - if (!cl->pers.inventory[index]) - { - continue; - } - - it = &itemlist[index]; - - if (!it->use) - { - continue; - } - - if (!(it->flags & itflags)) - { - continue; - } - - cl->pers.selected_item = index; - return; - } - - cl->pers.selected_item = -1; -} - -void -ValidateSelectedItem(edict_t *ent) -{ - gclient_t *cl; - - if (!ent) - { - return; - } - - cl = ent->client; - - if (cl->pers.inventory[cl->pers.selected_item]) - { - return; /* valid */ - } - - SelectNextItem(ent, -1); -} - -/* ================================================================================= */ - -/* - * Give items to a client - */ -void -Cmd_Give_f(edict_t *ent) -{ - char *name; - gitem_t *it; - int index; - int i; - qboolean give_all; - edict_t *it_ent; - - if (!ent) - { - return; - } - - if ((deathmatch->value || coop->value) && !sv_cheats->value) - { - gi.cprintf(ent, PRINT_HIGH, - "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - name = gi.args(); - - if (Q_stricmp(name, "all") == 0) - { - give_all = true; - } - else - { - give_all = false; - } - - if (give_all || (Q_stricmp(gi.argv(1), "health") == 0)) - { - if (gi.argc() == 3) - { - ent->health = (int)strtol(gi.argv(2), (char **)NULL, 10); - ent->health = ent->health < 1 ? 1 : ent->health; - } - else - { - ent->health = ent->max_health; - } - - if (!give_all) - { - return; - } - } - - if (give_all || (Q_stricmp(name, "weapons") == 0)) - { - for (i = 0; i < game.num_items; i++) - { - it = itemlist + i; - - if (!it->pickup) - { - continue; - } - - if (!(it->flags & IT_WEAPON)) - { - continue; - } - - ent->client->pers.inventory[i] += 1; - } - - if (!give_all) - { - return; - } - } - - if (give_all || (Q_stricmp(name, "ammo") == 0)) - { - for (i = 0; i < game.num_items; i++) - { - it = itemlist + i; - - if (!it->pickup) - { - continue; - } - - if (!(it->flags & IT_AMMO)) - { - continue; - } - - Add_Ammo(ent, it, 1000); - } - - if (!give_all) - { - return; - } - } - - if (give_all || (Q_stricmp(name, "armor") == 0)) - { - gitem_armor_t *info; - - it = FindItem("Jacket Armor"); - ent->client->pers.inventory[ITEM_INDEX(it)] = 0; - - it = FindItem("Combat Armor"); - ent->client->pers.inventory[ITEM_INDEX(it)] = 0; - - it = FindItem("Body Armor"); - info = (gitem_armor_t *)it->info; - ent->client->pers.inventory[ITEM_INDEX(it)] = info->max_count; - - if (!give_all) - { - return; - } - } - - if (give_all || (Q_stricmp(name, "Power Shield") == 0)) - { - it = FindItem("Power Shield"); - it_ent = G_Spawn(); - it_ent->classname = it->classname; - SpawnItem(it_ent, it); - Touch_Item(it_ent, ent, NULL, NULL); - - if (it_ent->inuse) - { - G_FreeEdict(it_ent); - } - - if (!give_all) - { - return; - } - } - - if (give_all) - { - for (i = 0; i < game.num_items; i++) - { - it = itemlist + i; - - if (!it->pickup) - { - continue; - } - - if (it->flags & IT_NOT_GIVEABLE) - { - continue; - } - - if (it->flags & (IT_ARMOR | IT_WEAPON | IT_AMMO)) - { - continue; - } - - ent->client->pers.inventory[i] = 1; - } - - return; - } - - it = FindItem(name); - - if (!it) - { - name = gi.argv(1); - it = FindItem(name); - - if (!it) - { - gi.cprintf(ent, PRINT_HIGH, "unknown item\n"); - return; - } - } - - if (!it->pickup) - { - gi.cprintf(ent, PRINT_HIGH, "non-pickup item\n"); - return; - } - - if (it->flags & IT_NOT_GIVEABLE) - { - gi.dprintf("item cannot be given\n"); - return; - } - - index = ITEM_INDEX(it); - - if (it->flags & IT_AMMO) - { - if (gi.argc() == 3) - { - ent->client->pers.inventory[index] = (int)strtol(gi.argv(2), (char **)NULL, 10); - } - else - { - ent->client->pers.inventory[index] += it->quantity; - } - } - else - { - it_ent = G_Spawn(); - it_ent->classname = it->classname; - SpawnItem(it_ent, it); - - /* since some items don't actually spawn when you say to .. */ - if (!it_ent->inuse) - { - return; - } - - Touch_Item(it_ent, ent, NULL, NULL); - - if (it_ent->inuse) - { - G_FreeEdict(it_ent); - } - } -} - -/* - * Sets client to godmode - */ -void -Cmd_God_f(edict_t *ent) -{ - char *msg; - - if (!ent) - { - return; - } - - if ((deathmatch->value || coop->value) && !sv_cheats->value) - { - gi.cprintf(ent, PRINT_HIGH, - "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - ent->flags ^= FL_GODMODE; - - if (!(ent->flags & FL_GODMODE)) - { - msg = "godmode OFF\n"; - } - else - { - msg = "godmode ON\n"; - } - - gi.cprintf(ent, PRINT_HIGH, msg); -} - -/* - * Sets client to notarget - */ -void -Cmd_Notarget_f(edict_t *ent) -{ - char *msg; - - if (!ent) - { - return; - } - - if ((deathmatch->value || coop->value) && !sv_cheats->value) - { - gi.cprintf(ent, PRINT_HIGH, - "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - ent->flags ^= FL_NOTARGET; - - if (!(ent->flags & FL_NOTARGET)) - { - msg = "notarget OFF\n"; - } - else - { - msg = "notarget ON\n"; - } - - gi.cprintf(ent, PRINT_HIGH, msg); -} - -/* - * argv(0) noclip - */ -void -Cmd_Noclip_f(edict_t *ent) -{ - char *msg; - - if (!ent) - { - return; - } - - if ((deathmatch->value || coop->value) && !sv_cheats->value) - { - gi.cprintf(ent, PRINT_HIGH, - "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - if (ent->movetype == MOVETYPE_NOCLIP) - { - ent->movetype = MOVETYPE_WALK; - msg = "noclip OFF\n"; - } - else - { - ent->movetype = MOVETYPE_NOCLIP; - msg = "noclip ON\n"; - } - - gi.cprintf(ent, PRINT_HIGH, msg); -} - -/* - * Use an inventory item - */ -void -Cmd_Use_f(edict_t *ent) -{ - int index; - gitem_t *it; - char *s; - - if (!ent) - { - return; - } - - s = gi.args(); - it = FindItem(s); - - if (!it) - { - gi.cprintf(ent, PRINT_HIGH, "unknown item: %s\n", s); - return; - } - - if (!it->use) - { - gi.cprintf(ent, PRINT_HIGH, "Item is not usable.\n"); - return; - } - - index = ITEM_INDEX(it); - - if (!ent->client->pers.inventory[index]) - { - gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s); - return; - } - - it->use(ent, it); -} - -/* - * Drop an inventory item - */ -void -Cmd_Drop_f(edict_t *ent) -{ - int index; - gitem_t *it; - char *s; - - if (!ent) - { - return; - } - - if ((Q_stricmp(gi.args(), "tech") == 0) && ((it = CTFWhat_Tech(ent)) != NULL)) - { - it->drop(ent, it); - return; - } - - s = gi.args(); - it = FindItem(s); - - if (!it) - { - gi.cprintf(ent, PRINT_HIGH, "unknown item: %s\n", s); - return; - } - - if (!it->drop) - { - gi.cprintf(ent, PRINT_HIGH, "Item is not dropable.\n"); - return; - } - - index = ITEM_INDEX(it); - - if (!ent->client->pers.inventory[index]) - { - gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s); - return; - } - - it->drop(ent, it); -} - -/* - * Display the scoreboard - */ -void -Cmd_Score_f(edict_t *ent) -{ - if (!ent) - { - return; - } - - ent->client->showinventory = false; - ent->client->showhelp = false; - - if (ent->client->menu) - { - PMenu_Close(ent); - } - - if (!deathmatch->value && !coop->value) - { - return; - } - - if (ent->client->showscores) - { - ent->client->showscores = false; - ent->client->update_chase = true; - return; - } - - ent->client->showscores = true; - DeathmatchScoreboardMessage(ent, ent->enemy); - gi.unicast(ent, true); -} - -/* - * Display the current help message - */ -void -Cmd_Help_f(edict_t *ent) -{ - /* this is for backwards compatability */ - if (deathmatch->value) - { - Cmd_Score_f(ent); - return; - } - - ent->client->showinventory = false; - ent->client->showscores = false; - - if (ent->client->showhelp && - (ent->client->resp.game_helpchanged == game.helpchanged)) - { - ent->client->showhelp = false; - return; - } - - ent->client->showhelp = true; - ent->client->resp.helpchanged = 0; - HelpComputerMessage(ent); -} - -void -Cmd_Inven_f(edict_t *ent) -{ - int i; - gclient_t *cl; - - if (!ent) - { - return; - } - - cl = ent->client; - - cl->showscores = false; - cl->showhelp = false; - - if (ent->client->menu) - { - PMenu_Close(ent); - ent->client->update_chase = true; - return; - } - - if (cl->showinventory) - { - cl->showinventory = false; - return; - } - - if (ctf->value && (cl->resp.ctf_team == CTF_NOTEAM)) - { - CTFOpenJoinMenu(ent); - return; - } - - cl->showinventory = true; - - gi.WriteByte(svc_inventory); - - for (i = 0; i < MAX_ITEMS; i++) - { - gi.WriteShort(cl->pers.inventory[i]); - } - - gi.unicast(ent, true); -} - -void -Cmd_InvUse_f(edict_t *ent) -{ - gitem_t *it; - - if (ent->client->menu) - { - PMenu_Select(ent); - return; - } - - ValidateSelectedItem(ent); - - if (ent->client->pers.selected_item == -1) - { - gi.cprintf(ent, PRINT_HIGH, "No item to use.\n"); - return; - } - - it = &itemlist[ent->client->pers.selected_item]; - - if (!it->use) - { - gi.cprintf(ent, PRINT_HIGH, "Item is not usable.\n"); - return; - } - - it->use(ent, it); -} - -void -Cmd_LastWeap_f(edict_t *ent) -{ - gclient_t *cl; - - cl = ent->client; - - if (!cl->pers.weapon || !cl->pers.lastweapon) - { - return; - } - - cl->pers.lastweapon->use(ent, cl->pers.lastweapon); -} - -void -Cmd_WeapPrev_f(edict_t *ent) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - int selected_weapon; - - cl = ent->client; - - if (!cl->pers.weapon) - { - return; - } - - selected_weapon = ITEM_INDEX(cl->pers.weapon); - - /* scan for the next valid one */ - for (i = 1; i <= MAX_ITEMS; i++) - { - index = (selected_weapon + i) % MAX_ITEMS; - - if (!cl->pers.inventory[index]) - { - continue; - } - - it = &itemlist[index]; - - if (!it->use) - { - continue; - } - - if (!(it->flags & IT_WEAPON)) - { - continue; - } - - it->use(ent, it); - - if (cl->pers.weapon == it) - { - return; /* successful */ - } - } -} - -void -Cmd_WeapNext_f(edict_t *ent) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - int selected_weapon; - - if (!ent) - { - return; - } - - cl = ent->client; - - if (!cl->pers.weapon) - { - return; - } - - selected_weapon = ITEM_INDEX(cl->pers.weapon); - - /* scan for the next valid one */ - for (i = 1; i <= MAX_ITEMS; i++) - { - index = (selected_weapon + MAX_ITEMS - i) % MAX_ITEMS; - - if (!cl->pers.inventory[index]) - { - continue; - } - - it = &itemlist[index]; - - if (!it->use) - { - continue; - } - - if (!(it->flags & IT_WEAPON)) - { - continue; - } - - it->use(ent, it); - - if (cl->pers.weapon == it) - { - return; /* successful */ - } - } -} - -void -Cmd_WeapLast_f(edict_t *ent) -{ - gclient_t *cl; - int index; - gitem_t *it; - - if (!ent) - { - return; - } - - cl = ent->client; - - if (!cl->pers.weapon || !cl->pers.lastweapon) - { - return; - } - - index = ITEM_INDEX(cl->pers.lastweapon); - - if (!cl->pers.inventory[index]) - { - return; - } - - it = &itemlist[index]; - - if (!it->use) - { - return; - } - - if (!(it->flags & IT_WEAPON)) - { - return; - } - - it->use(ent, it); -} - -void -Cmd_InvDrop_f(edict_t *ent) -{ - gitem_t *it; - - if (!ent) - { - return; - } - - ValidateSelectedItem(ent); - - if (ent->client->pers.selected_item == -1) - { - gi.cprintf(ent, PRINT_HIGH, "No item to drop.\n"); - return; - } - - it = &itemlist[ent->client->pers.selected_item]; - - if (!it->drop) - { - gi.cprintf(ent, PRINT_HIGH, "Item is not dropable.\n"); - return; - } - - it->drop(ent, it); -} - -void -Cmd_Kill_f(edict_t *ent) -{ - if (ent->solid == SOLID_NOT) - { - return; - } - - if ((level.time - ent->client->respawn_time) < 5) - { - return; - } - - ent->flags &= ~FL_GODMODE; - ent->health = 0; - meansOfDeath = MOD_SUICIDE; - player_die(ent, ent, ent, 100000, vec3_origin); -} - -void -Cmd_PutAway_f(edict_t *ent) -{ - ent->client->showscores = false; - ent->client->showhelp = false; - ent->client->showinventory = false; - - if (ent->client->menu) - { - PMenu_Close(ent); - } - - ent->client->update_chase = true; -} - -int -PlayerSort(void const *a, void const *b) -{ - int anum, bnum; - - if (!a || !b) - { - return 0; - } - - anum = *(int *)a; - bnum = *(int *)b; - - anum = game.clients[anum].ps.stats[STAT_FRAGS]; - bnum = game.clients[bnum].ps.stats[STAT_FRAGS]; - - if (anum < bnum) - { - return -1; - } - - if (anum > bnum) - { - return 1; - } - - return 0; -} - -void -Cmd_Players_f(edict_t *ent) -{ - int i; - int count; - char small[64]; - char large[1280]; - int index[256]; - - if (!ent) - { - return; - } - - count = 0; - - for (i = 0; i < maxclients->value; i++) - { - if (game.clients[i].pers.connected) - { - index[count] = i; - count++; - } - } - - /* sort by frags */ - qsort(index, count, sizeof(index[0]), PlayerSort); - - /* print information */ - large[0] = 0; - - for (i = 0; i < count; i++) - { - Com_sprintf(small, sizeof(small), "%3i %s\n", - game.clients[index[i]].ps.stats[STAT_FRAGS], - game.clients[index[i]].pers.netname); - - if (strlen(small) + strlen(large) > sizeof(large) - 100) - { - /* can't print all of them in one packet */ - strcat(large, "...\n"); - break; - } - - strcat(large, small); - } - - gi.cprintf(ent, PRINT_HIGH, "%s\n%i players\n", large, count); -} - -void -Cmd_Wave_f(edict_t *ent) -{ - int i; - - if (!ent) - { - return; - } - - i = (int)strtol(gi.argv(1), (char **)NULL, 10); - - /* can't wave when ducked */ - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - return; - } - - if (ent->client->anim_priority > ANIM_WAVE) - { - return; - } - - ent->client->anim_priority = ANIM_WAVE; - - switch (i) - { - case 0: - gi.cprintf(ent, PRINT_HIGH, "flipoff\n"); - ent->s.frame = FRAME_flip01 - 1; - ent->client->anim_end = FRAME_flip12; - break; - case 1: - gi.cprintf(ent, PRINT_HIGH, "salute\n"); - ent->s.frame = FRAME_salute01 - 1; - ent->client->anim_end = FRAME_salute11; - break; - case 2: - gi.cprintf(ent, PRINT_HIGH, "taunt\n"); - ent->s.frame = FRAME_taunt01 - 1; - ent->client->anim_end = FRAME_taunt17; - break; - case 3: - gi.cprintf(ent, PRINT_HIGH, "wave\n"); - ent->s.frame = FRAME_wave01 - 1; - ent->client->anim_end = FRAME_wave11; - break; - case 4: - default: - gi.cprintf(ent, PRINT_HIGH, "point\n"); - ent->s.frame = FRAME_point01 - 1; - ent->client->anim_end = FRAME_point12; - break; - } -} - -qboolean -CheckFlood(edict_t *ent) -{ - int i; - gclient_t *cl; - - if (flood_msgs->value) - { - cl = ent->client; - - if (level.time < cl->flood_locktill) - { - gi.cprintf(ent, PRINT_HIGH, "You can't talk for %d more seconds\n", - (int)(cl->flood_locktill - level.time)); - return true; - } - - i = cl->flood_whenhead - flood_msgs->value + 1; - - if (i < 0) - { - i = (sizeof(cl->flood_when) / sizeof(cl->flood_when[0])) + i; - } - - if (cl->flood_when[i] && - (level.time - cl->flood_when[i] < flood_persecond->value)) - { - cl->flood_locktill = level.time + flood_waitdelay->value; - gi.cprintf(ent, PRINT_CHAT, - "Flood protection: You can't talk for %d seconds.\n", - (int)flood_waitdelay->value); - return true; - } - - cl->flood_whenhead = (cl->flood_whenhead + 1) % - (sizeof(cl->flood_when) / sizeof(cl->flood_when[0])); - cl->flood_when[cl->flood_whenhead] = level.time; - } - - return false; -} - -void -Cmd_Say_f(edict_t *ent, qboolean team, qboolean arg0) -{ - int j; - edict_t *other; - char *p; - char text[2048]; - - if ((gi.argc() < 2) && !arg0) - { - return; - } - - if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) - { - team = false; - } - - if (team) - { - Com_sprintf(text, sizeof(text), "(%s): ", ent->client->pers.netname); - } - else - { - Com_sprintf(text, sizeof(text), "%s: ", ent->client->pers.netname); - } - - if (arg0) - { - strcat(text, gi.argv(0)); - strcat(text, " "); - strcat(text, gi.args()); - } - else - { - p = gi.args(); - - if (*p == '"') - { - p++; - p[strlen(p) - 1] = 0; - } - - strcat(text, p); - } - - /* don't let text be too long for malicious reasons */ - if (strlen(text) > 150) - { - text[150] = 0; - } - - strcat(text, "\n"); - - if (CheckFlood(ent)) - { - return; - } - - if (dedicated->value) - { - gi.cprintf(NULL, PRINT_CHAT, "%s", text); - } - - for (j = 1; j <= game.maxclients; j++) - { - other = &g_edicts[j]; - - if (!other->inuse) - { - continue; - } - - if (!other->client) - { - continue; - } - - if (team) - { - if (!OnSameTeam(ent, other)) - { - continue; - } - } - - gi.cprintf(other, PRINT_CHAT, "%s", text); - } -} - -void -ClientCommand(edict_t *ent) -{ - char *cmd; - - if (!ent->client) - { - return; /* not fully in game yet */ - } - - cmd = gi.argv(0); - - if (Q_stricmp(cmd, "players") == 0) - { - Cmd_Players_f(ent); - return; - } - - if (Q_stricmp(cmd, "say") == 0) - { - Cmd_Say_f(ent, false, false); - return; - } - - if ((Q_stricmp(cmd, "say_team") == 0) || (Q_stricmp(cmd, "steam") == 0)) - { - CTFSay_Team(ent, gi.args()); - return; - } - - if (Q_stricmp(cmd, "score") == 0) - { - Cmd_Score_f(ent); - return; - } - - if (Q_stricmp(cmd, "help") == 0) - { - Cmd_Help_f(ent); - return; - } - - if (level.intermissiontime) - { - return; - } - - if (Q_stricmp(cmd, "use") == 0) - { - Cmd_Use_f(ent); - } - else if (Q_stricmp(cmd, "drop") == 0) - { - Cmd_Drop_f(ent); - } - else if (Q_stricmp(cmd, "give") == 0) - { - Cmd_Give_f(ent); - } - else if (Q_stricmp(cmd, "god") == 0) - { - Cmd_God_f(ent); - } - else if (Q_stricmp(cmd, "notarget") == 0) - { - Cmd_Notarget_f(ent); - } - else if (Q_stricmp(cmd, "noclip") == 0) - { - Cmd_Noclip_f(ent); - } - else if (Q_stricmp(cmd, "inven") == 0) - { - Cmd_Inven_f(ent); - } - else if (Q_stricmp(cmd, "invnext") == 0) - { - SelectNextItem(ent, -1); - } - else if (Q_stricmp(cmd, "invprev") == 0) - { - SelectPrevItem(ent, -1); - } - else if (Q_stricmp(cmd, "invnextw") == 0) - { - SelectNextItem(ent, IT_WEAPON); - } - else if (Q_stricmp(cmd, "invprevw") == 0) - { - SelectPrevItem(ent, IT_WEAPON); - } - else if (Q_stricmp(cmd, "invnextp") == 0) - { - SelectNextItem(ent, IT_POWERUP); - } - else if (Q_stricmp(cmd, "invprevp") == 0) - { - SelectPrevItem(ent, IT_POWERUP); - } - else if (Q_stricmp(cmd, "invuse") == 0) - { - Cmd_InvUse_f(ent); - } - else if (Q_stricmp(cmd, "invdrop") == 0) - { - Cmd_InvDrop_f(ent); - } - else if (Q_stricmp(cmd, "weapprev") == 0) - { - Cmd_WeapPrev_f(ent); - } - else if (Q_stricmp(cmd, "weapnext") == 0) - { - Cmd_WeapNext_f(ent); - } - else if (Q_stricmp(cmd, "weaplast") == 0) - { - Cmd_WeapLast_f(ent); - } - else if (Q_stricmp(cmd, "kill") == 0) - { - Cmd_Kill_f(ent); - } - else if (Q_stricmp(cmd, "putaway") == 0) - { - Cmd_PutAway_f(ent); - } - else if (Q_stricmp(cmd, "wave") == 0) - { - Cmd_Wave_f(ent); - } - /* ZOID */ - 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); - } - else /* anything that doesn't match a command will be a chat */ - { - Cmd_Say_f(ent, false, true); - } -} diff --git a/src/ctf/g_combat.c b/src/ctf/g_combat.c index 23ec8717..6403688e 100644 --- a/src/ctf/g_combat.c +++ b/src/ctf/g_combat.c @@ -142,16 +142,82 @@ 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; } - 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++; @@ -161,7 +227,7 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, } /* medics won't heal monsters that they kill themselves */ - if (strcmp(attacker->classname, "monster_medic") == 0) + if (attacker->classname && strcmp(attacker->classname, "monster_medic") == 0) { targ->owner = attacker; } @@ -169,7 +235,8 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, } if ((targ->movetype == MOVETYPE_PUSH) || - (targ->movetype == MOVETYPE_STOP) || (targ->movetype == MOVETYPE_NONE)) + (targ->movetype == MOVETYPE_STOP) || + (targ->movetype == MOVETYPE_NONE)) { /* doors, triggers, etc */ targ->die(targ, inflictor, attacker, damage, point); @@ -301,7 +368,15 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, } else { - damagePerCell = 1; /* power armor is weaker in CTF */ + if (ctf->value) + { + /* power armor is weaker in CTF */ + damagePerCell = 1; + } + else + { + damagePerCell = 2; + } pa_te_type = TE_SHIELD_SPARKS; damage = (2 * damage) / 3; } @@ -418,19 +493,47 @@ CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, 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 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)) @@ -439,14 +542,55 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) } } + /* 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) { - /* this can only happen in coop (both - new and old enemies are clients) - only switch if can't see the - current enemy */ + 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)) @@ -468,8 +612,8 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) return; } - /* it's the same base (walk/swim/fly) type - and a different classname and it's not a tank + /* 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))) && @@ -477,14 +621,13 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) (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) + if (targ->enemy && targ->enemy->client) { - if (targ->enemy->client) - { - targ->oldenemy = targ->enemy; - } + targ->oldenemy = targ->enemy; } targ->enemy = attacker; @@ -494,18 +637,36 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) FoundTarget(targ); } } - else /* otherwise get mad at whoever they are mad at (help our buddy) */ + /* if they *meant* to shoot us, then shoot back */ + else if (attacker->enemy == targ) { - if (targ->enemy) + if (targ->enemy && targ->enemy->client) { - if (targ->enemy->client) - { - targ->oldenemy = targ->enemy; - } + 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; - FoundTarget(targ); + + if (!(targ->monsterinfo.aiflags & AI_DUCKED)) + { + FoundTarget(targ); + } } } @@ -524,10 +685,28 @@ 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) +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; @@ -535,24 +714,32 @@ 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) + { + return; + } if (!targ->takedamage) { return; } - /* 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))) || + 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; } @@ -565,6 +752,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) { @@ -578,6 +784,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; @@ -651,11 +869,28 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, /* 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); + gi.sound(targ, CHAN_ITEM, gi.soundindex( + "items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } @@ -696,9 +931,25 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, /* do the damage */ if (take) { - if ((targ->svflags & SVF_MONSTER) || (client)) + /* need more blood for chainfist. */ + if (targ->flags & FL_MECHANICAL) { - SpawnDamage(TE_BLOOD, point, normal); + 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 { @@ -710,6 +961,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)) @@ -722,6 +984,19 @@ 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, inflictor); @@ -776,6 +1051,11 @@ T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, vec3_t v; vec3_t dir; + if (!inflictor || !attacker) + { + return; + } + while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) { if (ent == ignore) diff --git a/src/ctf/player/client.c b/src/ctf/player/client.c index 3b5a2fec..221a5199 100644 --- a/src/ctf/player/client.c +++ b/src/ctf/player/client.c @@ -28,6 +28,9 @@ #include "../header/local.h" #include "../monster/misc/player.h" +edict_t *pm_passent; + +void ClientUserinfoChanged(edict_t *ent, char *userinfo); void SP_misc_teleporter_dest(edict_t *ent); void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); @@ -2370,3 +2373,34 @@ ClientBeginServerFrame(edict_t *ent) client->latched_buttons = 0; } +/* + * This is called to clean up the pain daemons that + * the disruptor attaches to clients to damage them. + */ +void +RemoveAttackingPainDaemons(edict_t *self) +{ + edict_t *tracker; + + if (!self) + { + return; + } + + tracker = G_Find(NULL, FOFS(classname), "pain daemon"); + + while (tracker) + { + if (tracker->enemy == self) + { + G_FreeEdict(tracker); + } + + tracker = G_Find(tracker, FOFS(classname), "pain daemon"); + } + + if (self->client) + { + self->client->tracker_pain_framenum = 0; + } +} diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index 5c7373dc..3fdb16a2 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -28,6 +28,8 @@ #include "header/local.h" #include "monster/misc/player.h" +gitem_t *CTFWhat_Tech(edict_t *ent); + static char * ClientTeam(edict_t *ent, char* value) { @@ -103,7 +105,12 @@ SelectNextItem(edict_t *ent, int itflags) cl = ent->client; - if (cl->chase_target) + if (cl->menu) + { + PMenu_Next(ent); + return; + } + else if (cl->chase_target) { ChaseNext(ent); return; @@ -152,7 +159,12 @@ SelectPrevItem(edict_t *ent, int itflags) cl = ent->client; - if (cl->chase_target) + if (cl->menu) + { + PMenu_Prev(ent); + return; + } + else if (cl->chase_target) { ChasePrev(ent); return; @@ -622,6 +634,12 @@ Cmd_Drop_f(edict_t *ent) return; } + if ((Q_stricmp(gi.args(), "tech") == 0) && ((it = CTFWhat_Tech(ent)) != NULL)) + { + it->drop(ent, it); + return; + } + s = gi.args(); it = FindItem(s); @@ -700,6 +718,7 @@ Cmd_Score_f(edict_t *ent) if (ent->client->showscores) { ent->client->showscores = false; + ent->client->update_chase = true; return; } @@ -708,6 +727,9 @@ Cmd_Score_f(edict_t *ent) gi.unicast(ent, true); } +/* + * Display the current help message + */ void Cmd_Help_f(edict_t *ent) { @@ -726,7 +748,8 @@ Cmd_Help_f(edict_t *ent) ent->client->showinventory = false; ent->client->showscores = false; - if (ent->client->showhelp) + if (ent->client->showhelp && + (ent->client->resp.game_helpchanged == game.helpchanged)) { ent->client->showhelp = false; return; @@ -753,12 +776,25 @@ Cmd_Inven_f(edict_t *ent) cl->showscores = false; cl->showhelp = false; + if (ent->client->menu) + { + PMenu_Close(ent); + ent->client->update_chase = true; + return; + } + if (cl->showinventory) { cl->showinventory = false; return; } + if (ctf->value && (cl->resp.ctf_team == CTF_NOTEAM)) + { + CTFOpenJoinMenu(ent); + return; + } + cl->showinventory = true; InventoryMessage(ent); @@ -775,6 +811,12 @@ Cmd_InvUse_f(edict_t *ent) return; } + if (ent->client->menu) + { + PMenu_Select(ent); + return; + } + ValidateSelectedItem(ent); if (ent->client->pers.selected_item == -1) @@ -794,6 +836,21 @@ Cmd_InvUse_f(edict_t *ent) it->use(ent, it); } +void +Cmd_LastWeap_f(edict_t *ent) +{ + gclient_t *cl; + + cl = ent->client; + + if (!cl->pers.weapon || !cl->pers.lastweapon) + { + return; + } + + cl->pers.lastweapon->use(ent, cl->pers.lastweapon); +} + void Cmd_WeapPrev_f(edict_t *ent) { @@ -982,6 +1039,11 @@ Cmd_Kill_f(edict_t *ent) return; } + if (ent->solid == SOLID_NOT) + { + return; + } + if (((level.time - ent->client->respawn_time) < 5) || (ent->client->resp.spectator)) { @@ -1018,6 +1080,13 @@ Cmd_PutAway_f(edict_t *ent) ent->client->showscores = false; ent->client->showhelp = false; ent->client->showinventory = false; + + if (ent->client->menu) + { + PMenu_Close(ent); + } + + ent->client->update_chase = true; } int @@ -1973,9 +2042,9 @@ ClientCommand(edict_t *ent) return; } - if (Q_stricmp(cmd, "say_team") == 0) + if ((Q_stricmp(cmd, "say_team") == 0) || (Q_stricmp(cmd, "steam") == 0)) { - Cmd_Say_f(ent, true, false); + CTFSay_Team(ent, gi.args()); return; } @@ -2080,9 +2149,61 @@ ClientCommand(edict_t *ent) { Cmd_Wave_f(ent); } + /* ZOID */ + 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) { - Cmd_PlayerList_f(ent); + if (ctf->value) + { + CTFPlayerList(ent); + } + else + { + Cmd_PlayerList_f(ent); + } } else if (Q_stricmp(cmd, "entcount") == 0) { @@ -2116,6 +2237,10 @@ ClientCommand(edict_t *ent) { Cmd_PrefWeap_f(ent); } + else if (Q_stricmp(cmd, "observer") == 0) + { + CTFObserver(ent); + } else /* anything that doesn't match a command will be a chat */ { Cmd_Say_f(ent, false, true); diff --git a/src/game/g_combat.c b/src/game/g_combat.c index c348003d..0655e8b8 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -368,7 +368,15 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, } else { - damagePerCell = 2; + if (ctf->value) + { + /* power armor is weaker in CTF */ + damagePerCell = 1; + } + else + { + damagePerCell = 2; + } pa_te_type = TE_SHIELD_SPARKS; damage = (2 * damage) / 3; } @@ -863,11 +871,21 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, save = damage; } - psave = CheckPowerArmor(targ, point, normal, take, dflags); - take -= psave; + /* 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; + asave = CheckArmor(targ, point, normal, take, te_sparks, dflags); + take -= asave; + } /* treat cheat/powerup savings the same as armor */ asave += save; @@ -911,7 +929,10 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, SpawnDamage(te_sparks, point, normal); } - targ->health = targ->health - take; + if (!CTFMatchSetup()) + { + targ->health = targ->health - take; + } /* spheres need to know who to shoot at */ if (client && client->owned_sphere) @@ -966,7 +987,7 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, } else if (client) { - if (!(targ->flags & FL_GODMODE) && (take)) + if (!(targ->flags & FL_GODMODE) && (take) && !CTFMatchSetup()) { targ->pain(targ, attacker, knockback, take); } @@ -979,9 +1000,11 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, } } - /* 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 */ + /* 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;