From c114cae7640d3dfcfbdfc7aa68371d2b5ea062ce Mon Sep 17 00:00:00 2001 From: Richard Allen Date: Wed, 19 Jun 2002 18:13:57 +0000 Subject: [PATCH] New TNG spawning system :) --- reaction/game/bg_public.h | 4 + reaction/game/g_active.c | 5 +- reaction/game/g_client.c | 26 ++-- reaction/game/g_local.h | 15 +++ reaction/game/g_main.c | 6 +- reaction/game/g_svcmds.c | 4 +- reaction/game/g_teamplay.c | 270 +++++++++++++++++++++++++++++++++++++ reaction/game/g_teamplay.h | 10 ++ 8 files changed, 328 insertions(+), 12 deletions(-) diff --git a/reaction/game/bg_public.h b/reaction/game/bg_public.h index 9d9ec111..3975a34f 100644 --- a/reaction/game/bg_public.h +++ b/reaction/game/bg_public.h @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.81 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.80 2002/06/16 20:06:14 jbravo // Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap" // @@ -1319,6 +1322,7 @@ qboolean BG_CanItemBeGrabbed(int gametype, const entityState_t * ent, const play #define DF_NO_FALLING 8 #define DF_FIXED_FOV 16 #define DF_NO_FOOTSTEPS 32 +#define DF_SPAWN_FARTHEST 512 // content masks #define MASK_ALL (-1) diff --git a/reaction/game/g_active.c b/reaction/game/g_active.c index 6aa587ab..30367765 100644 --- a/reaction/game/g_active.c +++ b/reaction/game/g_active.c @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.80 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.79 2002/06/18 06:15:30 niceass // m4 kick now smooth // @@ -140,8 +143,6 @@ // Copyright (C) 1999-2000 Id Software, Inc. // #include "g_local.h" -// JBravo: need TP functions -#include "g_teamplay.h" #include "zcam.h" //Elder: moved kick to g_weapon.c where it belongs diff --git a/reaction/game/g_client.c b/reaction/game/g_client.c index a731ec29..1514bfff 100644 --- a/reaction/game/g_client.c +++ b/reaction/game/g_client.c @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.96 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.95 2002/06/17 00:23:59 slicer // Lasersight problem fixed // @@ -332,7 +335,8 @@ SelectNearestDeathmatchSpawnPoint Find the spot that we DON'T want to use ================ */ -#define MAX_SPAWN_POINTS 128 +// Moved to g_local.h +//#define MAX_SPAWN_POINTS 128 gentity_t *SelectNearestDeathmatchSpawnPoint(vec3_t from) { gentity_t *spot; @@ -364,7 +368,8 @@ SelectRandomDeathmatchSpawnPoint go to a random point that doesn't telefrag ================ */ -#define MAX_SPAWN_POINTS 128 +// Moved to g_local.h +//#define MAX_SPAWN_POINTS 128 gentity_t *SelectRandomDeathmatchSpawnPoint(void) { gentity_t *spot; @@ -1563,7 +1568,7 @@ void ClientSpawn(gentity_t * ent) client = ent->client; // JBravo: Check if team spawnpoints have been located. If not find a spot for each team ala AQ2. - if (g_gametype.integer == GT_TEAMPLAY) { +/* if (g_gametype.integer == GT_TEAMPLAY) { if (!level.spawnPointsLocated) { client->pers.initialSpawn = qfalse; do { @@ -1582,7 +1587,7 @@ void ClientSpawn(gentity_t * ent) level.team2spawn_angles); level.spawnPointsLocated = qtrue; } - } + } */ // End JBravo. // find a spawn point @@ -1604,16 +1609,21 @@ void ClientSpawn(gentity_t * ent) client->pers.teamState.state, spawn_origin, spawn_angles); // JBravo: If we are in Teamplay mode, use the teamspawnpoints. } else if (g_gametype.integer == GT_TEAMPLAY) { + + // Freud: Assign the spawns from the spawning system (g_teamplay.c) + level.team1spawnpoint = level.teamplay_spawns[0]; + level.team2spawnpoint = level.teamplay_spawns[1]; + if (client->sess.sessionTeam == TEAM_RED) { client->sess.spawnPoint = level.team1spawnpoint; spawnPoint = level.team1spawnpoint; - VectorCopy(level.team1spawn_angles, spawn_angles); - VectorCopy(level.team1spawn_origin, spawn_origin); + VectorCopy(level.team1spawnpoint->s.angles, spawn_angles); + VectorCopy(level.team1spawnpoint->s.origin, spawn_origin); } else { client->sess.spawnPoint = level.team2spawnpoint; spawnPoint = level.team2spawnpoint; - VectorCopy(level.team2spawn_angles, spawn_angles); - VectorCopy(level.team2spawn_origin, spawn_origin); + VectorCopy(level.team2spawnpoint->s.angles, spawn_angles); + VectorCopy(level.team2spawnpoint->s.origin, spawn_origin); } // End JBravo. } else { diff --git a/reaction/game/g_local.h b/reaction/game/g_local.h index aa0a2281..6398836c 100644 --- a/reaction/game/g_local.h +++ b/reaction/game/g_local.h @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.97 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.96 2002/06/18 09:22:16 niceass // file exist function // @@ -213,6 +216,8 @@ // JBravo: Max number of killed enemys to track #define RQ3_MAXKILLS 5 +#define MAX_TEAMS 2 +#define MAX_SPAWN_POINTS 128 // Blaze: How long someone bleeds for // Elder: This doesn't work the same as Q2 because clients and servers can @@ -748,6 +753,16 @@ typedef struct { //Slicer: int team1gender; int team2gender; + + // Freud: spawning system + int randteam; + int num_used_farteamplay_spawns[MAX_TEAMS]; + int num_potential_spawns[MAX_TEAMS]; + gentity_t *teamplay_spawns[MAX_TEAMS]; + qboolean teams_assigned[MAX_TEAMS]; + gentity_t *potential_spawns[MAX_TEAMS][MAX_SPAWN_POINTS]; + gentity_t *used_farteamplay_spawns[MAX_TEAMS][MAX_SPAWN_POINTS]; + } level_locals_t; // diff --git a/reaction/game/g_main.c b/reaction/game/g_main.c index d7a9d3f5..91bb6832 100644 --- a/reaction/game/g_main.c +++ b/reaction/game/g_main.c @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.91 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.90 2002/06/19 05:21:43 niceass // scoreboard stuff // @@ -981,6 +984,8 @@ void G_InitGame(int levelTime, int randomSeed, int restart) level.team_game_going = 0; level.team_round_going = 0; level.fps = trap_Cvar_VariableIntegerValue("sv_fps"); + level.num_potential_spawns[0] = 0; + level.num_potential_spawns[1] = 0; } // Slicer: reset matchmode vars if (g_RQ3_matchmode.integer && g_gametype.integer == GT_TEAMPLAY) { @@ -998,7 +1003,6 @@ void G_InitGame(int levelTime, int randomSeed, int restart) } G_RemapTeamShaders(); - } /* diff --git a/reaction/game/g_svcmds.c b/reaction/game/g_svcmds.c index 7a4ef3ab..596c7091 100644 --- a/reaction/game/g_svcmds.c +++ b/reaction/game/g_svcmds.c @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.13 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.12 2002/06/18 03:57:38 jbravo // Committing for aasimon. Callvote nextmap removed and replaced with cyclemap for .ini // @@ -40,7 +43,6 @@ // this file holds commands that can be executed by the server console, but not remote clients #include "g_local.h" -#include "g_teamplay.h" /* ============================================================================== diff --git a/reaction/game/g_teamplay.c b/reaction/game/g_teamplay.c index ed59cdf2..e3f4392e 100644 --- a/reaction/game/g_teamplay.c +++ b/reaction/game/g_teamplay.c @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.109 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.108 2002/06/17 03:30:33 jbravo // More color fixes // @@ -353,6 +356,8 @@ #include "g_local.h" #include "zcam.h" +void RQ3_SetupTeamSpawnPoints (void); + gitem_t *BG_FindItemForHoldable(holdable_t pw); char *ConcatArgs(int start); int touch[MAX_GENTITIES]; @@ -853,6 +858,7 @@ void SpawnPlayers() int clientNum, i; level.spawnPointsLocated = qfalse; + RQ3_SetupTeamSpawnPoints (); for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; @@ -2286,3 +2292,267 @@ void Cmd_Playerlist_f(gentity_t * ent) trap_SendServerCommand(ent - g_entities, va("print \"%i - %s^7\n\"", i, other->client->pers.netname)); } } + +// Freud: RQ3_compare_spawn_distances +// +// Sorting mechanism for spawn point distances feeded to qsort +int QDECL RQ3_compare_spawn_distances (const void *sd1, const void *sd2) +{ + if (((spawn_distances_t *) sd1)->distance < ((spawn_distances_t *) sd2)->distance) + return -1; + else if (((spawn_distances_t *) sd1)->distance > ((spawn_distances_t *) sd2)->distance) + return 1; + else + return 0; +} + +// Freud: SpawnPointDistance +// +// Returns the distance between two spawn points (or any entities, actually...) +float +RQ3_SpawnPointDistance (gentity_t * spot1, gentity_t * spot2) +{ + vec3_t v; + VectorSubtract (spot1->s.origin, spot2->s.origin, v); + return VectorLength (v); +} + +// RQ3_GetSpawnPoints () +// +// Called whenever no spawn points are available. +void RQ3_GetSpawnPoints () +{ + gentity_t *spot; + int x, spawns; + + spot = NULL; + + // The team that is called with SetupRandomTeamplaySpawnPoint + level.randteam = rand() % MAX_TEAMS; + + // Reset the spawns for each team + for (x = 0;x < MAX_TEAMS;x++) { + level.num_potential_spawns[x] = 0; + level.num_used_farteamplay_spawns[x] = 0; + } + + spawns = 0; + + // Read spawn points from the map + while ((spot = G_Find (spot, FOFS (classname), "info_player_deathmatch")) != NULL) { + spawns++; + for (x = 0;x < MAX_TEAMS;x++) { + level.potential_spawns[x][level.num_potential_spawns[x]] = spot; + level.num_potential_spawns[x]++; + } + } + G_Printf("%i spawns read from map\n", spawns); +} + +// Freud: RQ3_SelectRandomTeamplaySpawnPoint +// +// Selects a random spawns point from potential team spawns. +qboolean RQ3_SelectRandomTeamplaySpawnPoint (int team) +{ + int spawn_point, y, ok, i, z; + float distance; + gentity_t *spot; + + spot = NULL; + + i = 0; + ok = qfalse; + + // Done with potential spawns, re-reading and re-assigning + if (level.num_potential_spawns[team] < 1) + { + RQ3_GetSpawnPoints (); + RQ3_SetupTeamSpawnPoints (); + return qfalse; + } + + // Randomizing from potential spawn points + spawn_point = rand() % level.num_potential_spawns[team]; + + // decrementing potential spawns counter + level.num_potential_spawns[team]--; + + while ((ok == qfalse) && (i < MAX_TEAMS)) + { + ok = qtrue; + for (y = 0; y < MAX_TEAMS; y++) + { + if (level.teams_assigned[y] == qtrue) + { + distance = RQ3_SpawnPointDistance (level.potential_spawns[team][spawn_point], + level.teamplay_spawns[y]); + + if (distance == 0) + { + ok = qfalse; + } + } + } + + if (ok == qfalse) + { + spawn_point++; + if (spawn_point == level.num_potential_spawns[team]) + { + spawn_point = 0; + } + i++; + } + } + // Assigning the spawn point to the team + level.teamplay_spawns[team] = level.potential_spawns[team][spawn_point]; + level.teams_assigned[team] = qtrue; + + // Removing used spawn point from potential_spawns + for (z = spawn_point;z < level.num_potential_spawns[team];z++) + { + level.potential_spawns[team][z] = level.potential_spawns[team][z + 1]; + } + + if (i == MAX_TEAMS) + { + G_Printf("Arrrggh: More teams than potential spawnpoints!\n"); + + if ((spot = G_Find (spot, FOFS (classname), "info_player_start")) != NULL) + { + G_Printf("Well, guess I'm using info_player_start\n"); + level.teamplay_spawns[team] = spot; + } + else + return qfalse; + } + return qtrue; +} + +// Freud: RQ3_SelectFarTeamplaySpawnPoint +// +// Selects farthest teamplay spawn point(s) from available spawns. +qboolean RQ3_SelectFarTeamplaySpawnPoint (int team) +{ + int u, x, y, z; + int spawn_to_use, preferred_spawn_points, num_already_used; + int total_good_spawn_points, num_usable; + + float closest_spawn_distance, distance; + gentity_t *usable_spawns[MAX_SPAWN_POINTS]; + qboolean used; + + spawn_distances_t spawn_distances[MAX_SPAWN_POINTS]; + + num_already_used = 0; + + // Reset the spawn_distances structure + for (x = 0; x < MAX_SPAWN_POINTS; x++) { + memset(&spawn_distances[x], 0 , sizeof(spawn_distances[x])); + } + + // + for (x = 0; x < level.num_potential_spawns[team]; x++) + { + closest_spawn_distance = 2000000000; + + for (y = 0; y < MAX_TEAMS; y++) + { + if (level.teams_assigned[y] == qtrue) + { + distance = RQ3_SpawnPointDistance (level.potential_spawns[team][x], level.teamplay_spawns[y]); + + if (distance < closest_spawn_distance) + { + closest_spawn_distance = distance; + } + } + } + + if (closest_spawn_distance == 0) + num_already_used++; + + spawn_distances[x].s = level.potential_spawns[team][x]; + spawn_distances[x].distance = closest_spawn_distance; + } + + // Sort the farthest spawn points to the end of the array + qsort (spawn_distances, MAX_SPAWN_POINTS, + sizeof (spawn_distances_t), RQ3_compare_spawn_distances); + + total_good_spawn_points = level.num_potential_spawns[team] - num_already_used; + + // Support Spawn farthest and the old AQ system for spawn possibilities + if (g_dmflags.integer & DF_SPAWN_FARTHEST || total_good_spawn_points <= 4) + preferred_spawn_points = 1; + else if (total_good_spawn_points <= 10) + preferred_spawn_points = 2; + else + preferred_spawn_points = 3; + + num_usable = 0; + + // Now lets go through the spawn points and see if they have been used up. + for (z = 0;z < preferred_spawn_points;z++) + { + used = qfalse; + for (u = 0; u < level.num_used_farteamplay_spawns[team]; u++) + { + if (level.used_farteamplay_spawns[team][u] == spawn_distances[MAX_SPAWN_POINTS - z - 1].s) { + used = qtrue; + } + + } + if (used == qfalse) + { + usable_spawns[num_usable] = spawn_distances[MAX_SPAWN_POINTS-z-1].s; + num_usable++; + } + } + + // Can't use any of the far spawn points, let's go through the whole thing again. + if (num_usable < 1) + { + RQ3_SetupTeamSpawnPoints(); + return qfalse; + } + + // Randomize through the usable spawns. + spawn_to_use = rand() % num_usable; + + // Marking the spawn as used. + level.used_farteamplay_spawns[team][level.num_used_farteamplay_spawns[team]] = usable_spawns[spawn_to_use]; + level.num_used_farteamplay_spawns[team]++; + + // Setting the team to assigned and assigning the spawn point. + level.teams_assigned[team] = qtrue; + level.teamplay_spawns[team] = usable_spawns[spawn_to_use]; + + if (!level.teamplay_spawns[team]) { + G_Printf("Argh, Invalid Far Spawn\n"); + return qfalse; + } + + return qtrue; +} + +// Freud: RQ3_SetupTeamSpawnPoints +// +// Call this for assigning the spawn points to the teams +void RQ3_SetupTeamSpawnPoints() +{ + int i; + + for (i = 0; i < MAX_TEAMS; i++) { + level.teamplay_spawns[i] = NULL; + level.teams_assigned[i] = qfalse; + } + + if (RQ3_SelectRandomTeamplaySpawnPoint (level.randteam) == qfalse) + return; + + // If we ever decide to have more teams then 2.. :) + for (i = 0;i < MAX_TEAMS;i++) + if (i != level.randteam && RQ3_SelectFarTeamplaySpawnPoint (i) == qfalse) + return; +} diff --git a/reaction/game/g_teamplay.h b/reaction/game/g_teamplay.h index 2bd57492..365dcc33 100644 --- a/reaction/game/g_teamplay.h +++ b/reaction/game/g_teamplay.h @@ -5,6 +5,9 @@ //----------------------------------------------------------------------------- // // $Log$ +// Revision 1.19 2002/06/19 18:13:57 jbravo +// New TNG spawning system :) +// // Revision 1.18 2002/06/16 20:06:14 jbravo // Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap" // @@ -74,6 +77,12 @@ //Slicer TeamName Size. #define TEAM_NAME_SIZE 30 +typedef struct { + float distance; + gentity_t *s; +} spawn_distances_t; + + void CheckTeamRules(); void StartLCA(); void ContinueLCA(); @@ -109,3 +118,4 @@ void Cmd_Ignorenum_f(gentity_t * ent); void Cmd_Ignoreclear_f(gentity_t * ent); void Cmd_Playerlist_f(gentity_t * ent); int IsInIgnoreList(gentity_t * source, gentity_t * subject); +void RQ3_GetSpawnPoints (void);