diff --git a/source/a_menu.c b/source/a_menu.c index 0c5a3fd22..37e408971 100644 --- a/source/a_menu.c +++ b/source/a_menu.c @@ -64,6 +64,7 @@ void PMenu_Close (edict_t * ent) memset(&ent->client->menu, 0, sizeof(ent->client->menu)); ent->client->layout = LAYOUT_NONE; + ent->client->curr_menu = MENU_NONE; } void PMenu_Update (edict_t * ent) diff --git a/source/a_team.c b/source/a_team.c index e9271fcc0..3077027f7 100644 --- a/source/a_team.c +++ b/source/a_team.c @@ -338,6 +338,7 @@ edict_t *NS_potential_spawns[MAX_TEAMS][MAX_SPAWNS]; edict_t *NS_used_farteamplay_spawns[MAX_TEAMS][MAX_SPAWNS]; int NS_randteam; // +weapon_status_t weapon_status[WEAPON_MAX][MAX_TEAMS]; void CreditsMenu (edict_t * ent, pmenu_t * p); static transparent_list_t transparentList[MAX_CLIENTS]; @@ -588,6 +589,9 @@ void LeaveTeams (edict_t * ent, pmenu_t * p) void SelectWeapon2(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, MP5_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(MP5_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -600,6 +604,9 @@ void SelectWeapon2(edict_t *ent, pmenu_t *p) void SelectWeapon3(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, M3_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(M3_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -612,6 +619,9 @@ void SelectWeapon3(edict_t *ent, pmenu_t *p) void SelectWeapon4(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, HC_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(HC_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -624,6 +634,9 @@ void SelectWeapon4(edict_t *ent, pmenu_t *p) void SelectWeapon5(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, SNIPER_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(SNIPER_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -636,6 +649,9 @@ void SelectWeapon5(edict_t *ent, pmenu_t *p) void SelectWeapon6(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, M4_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(M4_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -648,6 +664,9 @@ void SelectWeapon6(edict_t *ent, pmenu_t *p) void SelectWeapon0(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, KNIFE_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(KNIFE_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -660,6 +679,9 @@ void SelectWeapon0(edict_t *ent, pmenu_t *p) void SelectWeapon9(edict_t *ent, pmenu_t *p) { + if (highlander->value && !Highlander_Check(ent, DUAL_NUM)) + return; + ent->client->pers.chosenWeapon = GET_ITEM(DUAL_NUM); PMenu_Close(ent); if(!item_kit_mode->value){ @@ -670,6 +692,13 @@ void SelectWeapon9(edict_t *ent, pmenu_t *p) unicastSound(ent, gi.soundindex("weapons/mk23slide.wav"), 1.0); } +void UnequipWeapon(edict_t *ent) +{ + ent->client->pers.chosenWeapon = NULL; + PMenu_Close(ent); + unicastSound(ent, gi.soundindex("weapons/swish.wav"), 1.0); +} + void SelectItem1(edict_t *ent, pmenu_t *p) { ent->client->pers.chosenItem = GET_ITEM(KEV_NUM); @@ -1120,6 +1149,7 @@ pmenu_t pmitemmenu[] = { void OpenPMItemMenu(edict_t *ent) { PMenu_Open(ent, pmitemmenu, 2, sizeof(pmitemmenu) / sizeof(pmenu_t)); + ent->client->curr_menu = MENU_PMITEM; } //End PaTMaN's jmod add @@ -1129,6 +1159,7 @@ void VotingMenu (edict_t * ent, pmenu_t * p) { PMenu_Close (ent); vShowMenu (ent, ""); + ent->client->curr_menu = MENU_VOTING; } //AQ2:TNG END @@ -1164,6 +1195,7 @@ void CreditsMenu (edict_t * ent, pmenu_t * p) { PMenu_Close (ent); PMenu_Open (ent, creditsmenu, 4, sizeof (creditsmenu) / sizeof (pmenu_t)); + ent->client->curr_menu = MENU_CREDITS; unicastSound(ent, gi.soundindex("world/elv.wav"), 1.0); } @@ -1361,6 +1393,36 @@ int TP_GetTeamFromArg(const char *name) return -1; } +qboolean TeamSizeLimiter(edict_t *ent, int teamNum, int teamSize) +{ + // // Count number of players on each team + // int team1count = 0; + // int team2count = 0; + // int team3count = 0; + // int i; + // for (i = 0; i < maxclients->value; i++) { + // edict_t *player = &g_edicts[i + 1]; + // if (!player->inuse || !player->client) + // continue; + // if (player->client->resp.team == TEAM1) + // team1count++; + // else if (player->client->resp.team == TEAM2) + // team2count++; + // else if (player->client->resp.team == TEAM3) + // team3count++; + // } + // Check if the desired team size exceeds the maximum allowed size + if (highlander->value) { + if ((teamNum == TEAM1 && level.teamcount[TEAM1] >= teamSize) || + (teamNum == TEAM2 && level.teamcount[TEAM2] >= teamSize) || + (teamNum == TEAM3 && level.teamcount[TEAM3] >= teamSize)) { + gi.cprintf(ent, PRINT_HIGH, "Highlander Mode: Cannot join team %s: Max players per team is %i\n", TeamName(teamNum), teamSize); + return false; + } + } + return true; +} + void Team_f (edict_t * ent) { char *t; @@ -1433,6 +1495,7 @@ void JoinTeam (edict_t * ent, int desired_team, int skip_menuclose) if (!skip_menuclose) PMenu_Close (ent); + oldTeam = ent->client->resp.team; if (oldTeam == desired_team || ent->client->pers.mvdspec) return; @@ -1463,11 +1526,24 @@ void JoinTeam (edict_t * ent, int desired_team, int skip_menuclose) } } } - + + if (highlander->value){ + int i; + for (i = TEAM1; i < TEAM_TOP; i++) { + if (!TeamSizeLimiter(ent, i, HIGHLANDER_MAX_PLAYERS)) { + return; + } + } + } + MM_LeftTeam( ent ); a = (oldTeam == NOTEAM) ? "joined" : "changed to"; + // Team count update + level.teamcount[ent->client->resp.team]--; + level.teamcount[desired_team]++; + ent->client->resp.team = desired_team; s = Info_ValueForKey (ent->client->pers.userinfo, "skin"); AssignSkin(ent, s, false); @@ -1552,6 +1628,7 @@ void LeaveTeam (edict_t * ent) MM_LeftTeam( ent ); + level.teamcount[ent->client->resp.team]--; if (esp->value) EspLeaderLeftTeam ( ent ); @@ -1644,6 +1721,7 @@ void OpenItemMenu (edict_t * ent) } PMenu_Open(ent, itemmenu, 4, sizeof(itemmenu) / sizeof(pmenu_t)); + ent->client->curr_menu = MENU_ITEMS; return; } } @@ -1678,6 +1756,7 @@ void OpenItemKitMenu (edict_t * ent) } PMenu_Open(ent, itemkitmenu, 4, sizeof(itemkitmenu) / sizeof(pmenu_t)); + ent->client->curr_menu = MENU_ITEMKITS; return; } @@ -1689,6 +1768,7 @@ void OpenWeaponMenu (edict_t * ent) if (use_randoms->value) { PMenu_Open(ent, randmenu, 4, sizeof(randmenu) / sizeof(pmenu_t)); + ent->client->curr_menu = MENU_RANDOM; return; } @@ -1711,9 +1791,20 @@ void OpenWeaponMenu (edict_t * ent) if (!WPF_ALLOWED(menuEntry->itemNum)) continue; - weapmenu[pos].text = menu_itemnames[menuEntry->itemNum]; - weapmenu[pos].SelectFunc = menuEntry->SelectFunc; + char *weaponName = menu_itemnames[menu_items[i].itemNum]; + if (ent->client->pers.chosenWeapon && ent->client->pers.chosenWeapon->typeNum == menu_items[i].itemNum) { + char *modifiedWeaponName = (char *)malloc(sizeof(char) * 32); // Dynamically allocate memory + snprintf(modifiedWeaponName, 32, "@%s", weaponName); + weaponName = modifiedWeaponName; + } + + weapmenu[pos].text = weaponName; + weapmenu[pos].SelectFunc = menu_items[i].SelectFunc; pos++; + // If weaponName was modified, free the allocated memory + if (ent->client->pers.chosenWeapon && ent->client->pers.chosenWeapon->typeNum == menu_items[i].itemNum) { + free(weaponName); + } } if (pos > 4) @@ -1725,6 +1816,7 @@ void OpenWeaponMenu (edict_t * ent) } PMenu_Open(ent, weapmenu, 4, sizeof(weapmenu) / sizeof(pmenu_t)); + ent->client->curr_menu = MENU_WEAPONS; return; } } @@ -1839,6 +1931,7 @@ void OpenJoinMenu (edict_t * ent) UpdateJoinMenu(); PMenu_Open (ent, joinmenu, 11 /* magic for Auto-join menu item */, sizeof (joinmenu) / sizeof (pmenu_t)); + ent->client->curr_menu = MENU_TEAM; } void gib_die( edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point ); // g_misc @@ -2232,9 +2325,13 @@ static void SpawnPlayers(void) // make sure teamplay spawners always have some weapon, warmup starts only after weapon selected if (!ent->client->pers.chosenWeapon) { - if (WPF_ALLOWED(MP5_NUM)) { + if (!highlander->value){ + ent->client->pers.chosenWeapon = GET_ITEM(MP5_NUM); // Highlander mode removes default MP5 + } else if (WPF_ALLOWED(MP5_NUM) && !highlander->value) { ent->client->pers.chosenWeapon = GET_ITEM(MP5_NUM); - } else if (WPF_ALLOWED(MK23_NUM)) { + } + + if (WPF_ALLOWED(MK23_NUM)) { ent->client->pers.chosenWeapon = GET_ITEM(MK23_NUM); } else if (WPF_ALLOWED(KNIFE_NUM)) { ent->client->pers.chosenWeapon = GET_ITEM(KNIFE_NUM); @@ -4140,6 +4237,89 @@ void NS_SetupTeamSpawnPoints () } } +qboolean Highlander_Check(edict_t *ent, int weaponNum) +{ + char *playername = ent->client->pers.netname; + gitem_t *chosenWeapon = ent->client->pers.chosenWeapon; + qboolean available = false; + qboolean weaponChange = false; + int i, j = 0; + + // gi.dprintf("Is weapon available: %d\n", weapon_status[weaponNum].available); + // gi.dprintf("Which team owns it: %d\n", weapon_status[weaponNum].team); + // if (owner != NULL && owner->client->pers.netname != NULL) + // gi.dprintf("Which player owns it: %s\n", owner->client->pers.netname); + // gi.dprintf("Which team wants it: %d\n", ent->client->resp.team); + // gi.dprintf("Which player wants it: %s\n", playername); + + // Run a check for availability + for (i = 0; i < WEAPON_MAX; i++) { + for (j = 0; j < TEAM_TOP; j++) { + // If the weapon has an owner in the current team, update its availability + if (weapon_status[i][j].owner != NULL) { + available = false; + } else { + available = true; + } + } + } + + // If the player had a different weapon and is selecting this one, and it's available, return true, + // and release the previous weapon so that it made be selectable by another member of this team + if (ent->client->pers.chosenWeapon != NULL && available) { + weapon_status[ent->client->pers.chosenWeapon->typeNum][ent->client->resp.team].owner = NULL; + //gi.cprintf(ent, PRINT_HIGH, "Highlander Mode: %s selected %d, releasing %d\n", ent->client->pers.netname, weaponNum, ent->client->pers.chosenWeapon->typeNum); + weapon_status[weaponNum][ent->client->resp.team].owner = ent; + weaponChange = true; + } + + if (available){ + gi.cprintf(ent, PRINT_HIGH, "Highlander Mode: That weapon is available, selecting it\n"); + gi.dprintf("Highlander Mode: That weapon is available, selecting it\n"); + weapon_status[weaponNum][ent->client->resp.team].owner = ent; + weaponChange = true; + } + + gi.dprintf("Owner team: %d\n", weapon_status[weaponNum][ent->client->resp.team].owner->client->resp.team); + gi.dprintf("Ent team: %d\n", ent->client->resp.team); + // The expectation is that the weapon is available always, unless it's owned by another player on your team + if (weapon_status[weaponNum][ent->client->resp.team].owner != ent && + weapon_status[weaponNum][ent->client->resp.team].owner->client->resp.team == ent->client->resp.team && + !available) { + gi.cprintf(ent, PRINT_HIGH, "Highlander Mode: That weapon is not available, %s owns it, select another\n", weapon_status[weaponNum][ent->client->resp.team].owner->client->pers.netname); + gi.cprintf(weapon_status[weaponNum][ent->client->resp.team].owner, PRINT_HIGH, "%s wants your weapon!\n", playername); + return false; + } + + // If the player already selected this weapon, unequip it + if (ent->client->pers.chosenWeapon != NULL && ent->client->pers.chosenWeapon->typeNum == weaponNum) { + gi.cprintf(ent, PRINT_HIGH, "Highlander Mode: Unequipping weapon to make it available to others\n"); + weapon_status[ent->client->pers.chosenWeapon->typeNum][ent->client->resp.team].owner = NULL; + UnequipWeapon(ent); + } + + if (weaponChange){ + + // Attempt to auto-refresh the menu: + + // for (int i = 0; i < maxclients->value; i++) { + // edict_t *ent = g_edicts + 1 + i; + + // if (!ent->inuse || !ent->client || ent->is_bot) + // continue; + + // if (ent->client->layout == LAYOUT_MENU && ent->client->curr_menu == MENU_WEAPONS) { + // gi.dprintf("Refreshing menu for %s, they were in menu %d\n", ent->client->pers.netname, ent->client->curr_menu); + // PMenu_Close(ent); + // PMenu_Open(ent, weapmenu, 4, sizeof(weapmenu) / sizeof(pmenu_t)); + // } + // } + return true; + } + + return false; +} + /* Simple function that just returns the opposite team number Obviously, do not use this for 3team functions diff --git a/source/a_team.h b/source/a_team.h index c241b0770..00d784690 100644 --- a/source/a_team.h +++ b/source/a_team.h @@ -42,6 +42,8 @@ #define MAX_TEAMS 3 #define TEAM_TOP (MAX_TEAMS+1) +#define HIGHLANDER_MAX_PLAYERS 7 + #define WINNER_NONE NOTEAM #define WINNER_TIE TEAM_TOP @@ -104,6 +106,9 @@ int OtherTeam(int teamNum); //PaTMaN - Menu support void OpenPMItemMenu (edict_t * ent); +//Highlander mode check +qboolean Highlander_Check(edict_t *ent, int weaponNum); + //Expose auto-join functionality void JoinTeamAutobalance (edict_t * ent); @@ -121,6 +126,11 @@ typedef struct transparent_list_s } transparent_list_t; +//Highlander mode +typedef struct { + edict_t* owner; +} weapon_status_t; +extern weapon_status_t weapon_status[10][MAX_TEAMS]; extern qboolean team_game_going; extern qboolean team_round_going; @@ -152,3 +162,16 @@ typedef struct menu_list_item char name[40]; } menu_list_item; + +typedef enum { + MENU_NONE, + MENU_TEAM, + MENU_WEAPONS, + MENU_ITEMS, + MENU_ITEMKITS, + MENU_PMITEM, + MENU_CREDITS, + MENU_RANDOM, + MENU_VOTING, + MENU_MAX +} menu_type_t; diff --git a/source/g_local.h b/source/g_local.h index 921d61af9..f02a58ef9 100644 --- a/source/g_local.h +++ b/source/g_local.h @@ -839,6 +839,7 @@ typedef struct float matchTime; float emptyTime; int weapon_sound_framenum; + int teamcount[MAX_TEAMS]; int pic_teamplay_timer_icon; int pic_leaderskin[TEAM_TOP]; @@ -1255,6 +1256,7 @@ extern cvar_t *am_delay; extern cvar_t *am_team; extern cvar_t *zoom_comp; extern cvar_t *item_kit_mode; +extern cvar_t *highlander; extern cvar_t *gun_dualmk23_enhance; extern cvar_t *printrules; extern cvar_t *timedmsgs; @@ -2068,6 +2070,8 @@ struct gclient_s // visiblity mask unsigned int dimension_observe; + + int curr_menu; // Used to determine which menu a player is currently in }; diff --git a/source/g_main.c b/source/g_main.c index aa9f036cd..344bc08df 100644 --- a/source/g_main.c +++ b/source/g_main.c @@ -520,6 +520,7 @@ cvar_t *am_delay; // Attract mode delay, unused at the moment cvar_t *am_team; // Attract mode team, which team do you want the bots to join cvar_t *zoom_comp; // Compensates zoom-in frames with ping (high ping = fewer frames) cvar_t *item_kit_mode; // Toggles item kit mode +cvar_t *highlander; // Toggles highlander mode cvar_t *gun_dualmk23_enhance; // Enables laser sight for dual mk23 pistols cvar_t *printrules; // Centerprint game rules when the countdown begins cvar_t *timedmsgs; // Toggles timed messages diff --git a/source/g_save.c b/source/g_save.c index 6914217ad..6d6fcc23a 100644 --- a/source/g_save.c +++ b/source/g_save.c @@ -607,6 +607,7 @@ void InitGame( void ) am_team = gi.cvar("am_team", "0", 0); zoom_comp = gi.cvar("zoom_comp", "0", 0); item_kit_mode = gi.cvar("item_kit_mode", "0", CVAR_LATCH); + highlander = gi.cvar("highlander", "0", CVAR_LATCH); gun_dualmk23_enhance = gi.cvar("gun_dualmk23_enhance", "0", 0); printrules = gi.cvar("printrules", "0", 0); timedmsgs = gi.cvar("timedmsgs", "0", 0); diff --git a/source/g_spawn.c b/source/g_spawn.c index 41610ff5c..617732872 100644 --- a/source/g_spawn.c +++ b/source/g_spawn.c @@ -1124,6 +1124,35 @@ void SpawnEntities (char *mapname, char *entities, char *spawnpoint) gi.cvar_forceset(use_tourney->name, "0"); } } + else if (highlander) + { + gi.cvar_forceset(gm->name, "tp"); + gameSettings |= (GS_ROUNDBASED | GS_WEAPONCHOOSE); + + if (!teamplay->value) + { + gi.dprintf ("Highlander Enabled - Forcing teamplay on\n"); + gi.cvar_forceset(teamplay->name, "1"); + } + if (use_tourney->value) + { + gi.dprintf ("Highlander Enabled - Forcing Tourney off\n"); + gi.cvar_forceset(use_tourney->name, "0"); + } + // We can't play Highlander mode with more than 7 players per team + if (teamCount == TEAM2 && maxclients->value > 14){ + gi.dprintf ("Highlander Enabled - Maxclient setting too high: Forcing maxclients to %d\n", 14); + gi.cvar_forceset(maxclients->name, va("%d", 14)); + } else if (teamCount == TEAM3 && maxclients->value > 21){ + gi.dprintf ("Highlander Enabled - Maxclient setting too high: Forcing maxclients to %d\n", 21); + gi.cvar_forceset(maxclients->name, va("%d", 21)); + } + if (ltk_loadbots->value || am->value){ + gi.dprintf ("Highlander Enabled - Forcing bots off\n"); + gi.cvar_forceset(ltk_loadbots->name, "0"); + gi.cvar_forceset(am->name, "0"); + } + } else if (matchmode->value) { gameSettings |= (GS_ROUNDBASED | GS_WEAPONCHOOSE); @@ -1609,7 +1638,7 @@ Only used for the world. void SP_worldspawn (edict_t * ent) { - int i, bullets, shells; + int i, j, bullets, shells; char *picname; ent->movetype = MOVETYPE_PUSH; @@ -1896,6 +1925,14 @@ void SP_worldspawn (edict_t * ent) gi.configstring(CS_LIGHTS + 11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); // 11 SLOW PULSE NOT FADE TO BLACK // styles 32-62 are assigned by the light program for switchable lights gi.configstring(CS_LIGHTS + 63, "a"); // 63 testing + + if (highlander->value){ + for (i = 0; i < WEAPON_MAX; i++) { + for (j = 0; j < TEAM_TOP; j++) { + weapon_status[i][j].owner = NULL; + } + } +} } int LoadFlagsFromFile (const char *mapname) diff --git a/source/p_client.c b/source/p_client.c index 3c66dd0a0..b789014b3 100644 --- a/source/p_client.c +++ b/source/p_client.c @@ -2943,7 +2943,7 @@ void ClientBeginDeathmatch(edict_t * ent) if (!dm_choose->value && !warmup->value) { if (!ent->client->pers.chosenWeapon) { - if (WPF_ALLOWED(MP5_NUM)) + if (WPF_ALLOWED(MP5_NUM) && !highlander->value) // No default MP5 if highlander is enabled ent->client->pers.chosenWeapon = GET_ITEM(MP5_NUM); else if (WPF_ALLOWED(MK23_NUM)) ent->client->pers.chosenWeapon = GET_ITEM(MK23_NUM);