g_match.c
Go to the documentation of this file.00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "g_local.h"
00027 #include "g_ai.h"
00028
00036 static int G_GetEarnedExperience (abilityskills_t skill, character_t *chr)
00037 {
00038 int exp = 0;
00039 abilityskills_t i;
00040
00041 switch (skill) {
00042 case ABILITY_POWER:
00043 exp = 46;
00044 break;
00045 case ABILITY_SPEED:
00046 exp = chr->scoreMission->movedNormal / 2 + chr->scoreMission->movedCrouched + (chr->scoreMission->firedTUs[skill] + chr->scoreMission->firedSplashTUs[skill]) / 10;
00047 break;
00048 case ABILITY_ACCURACY:
00049 for (i = 0; i < SKILL_NUM_TYPES; i++) {
00050 if (i == SKILL_SNIPER)
00051 exp += 30 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
00052 else
00053 exp += 20 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
00054 }
00055 break;
00056 case ABILITY_MIND:
00057 exp = 50 + 200 * chr->scoreMission->kills[KILLED_ENEMIES];
00058 break;
00059 case SKILL_CLOSE:
00060 exp = 150 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
00061 break;
00062 case SKILL_HEAVY:
00063 exp = 200 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
00064 break;
00065 case SKILL_ASSAULT:
00066 exp = 100 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
00067 break;
00068 case SKILL_SNIPER:
00069 exp = 200 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
00070 break;
00071 case SKILL_EXPLOSIVE:
00072 exp = 200 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
00073 break;
00074 default:
00075 break;
00076 }
00077
00078 return exp;
00079 }
00080
00096 static int G_CharacterGetMaxExperiencePerMission (const abilityskills_t skill)
00097 {
00098 switch (skill) {
00099 case ABILITY_POWER:
00100 return 46;
00101 case ABILITY_SPEED:
00102 return 91;
00103 case ABILITY_ACCURACY:
00104 return 290;
00105 case ABILITY_MIND:
00106 return 450;
00107 case SKILL_CLOSE:
00108 return 680;
00109 case SKILL_HEAVY:
00110 return 680;
00111 case SKILL_ASSAULT:
00112 return 680;
00113 case SKILL_SNIPER:
00114 return 680;
00115 case SKILL_EXPLOSIVE:
00116 return 680;
00117 case SKILL_NUM_TYPES:
00118 return 2154;
00119 default:
00120 gi.Error("G_GetMaxExperiencePerMission: invalid skill type\n");
00121 }
00122 }
00123
00131 static void G_UpdateCharacterSkills (character_t *chr)
00132 {
00133 abilityskills_t i = 0;
00134 unsigned int maxXP, gainedXP, totalGainedXP;
00135
00136
00137 if (chr->teamDef->race == RACE_ROBOT)
00138 return;
00139
00140 totalGainedXP = 0;
00141 for (i = 0; i < SKILL_NUM_TYPES; i++) {
00142 maxXP = G_CharacterGetMaxExperiencePerMission(i);
00143 gainedXP = G_GetEarnedExperience(i, chr);
00144
00145 gainedXP = min(gainedXP, maxXP);
00146 chr->score.experience[i] += gainedXP;
00147 totalGainedXP += gainedXP;
00148 chr->score.skills[i] = chr->score.initialSkills[i] + (int) (pow((float) (chr->score.experience[i])/100, 0.6f));
00149 G_PrintStats(va("Soldier %s earned %d experience points in skill #%d (total experience: %d). It is now %d higher.",
00150 chr->name, gainedXP, i, chr->score.experience[i], chr->score.skills[i] - chr->score.initialSkills[i]));
00151 }
00152
00153
00154 assert(i == SKILL_NUM_TYPES);
00155 maxXP = G_CharacterGetMaxExperiencePerMission(i);
00156 gainedXP = min(maxXP, totalGainedXP / 2);
00157
00158 chr->score.experience[i] += gainedXP;
00159 chr->maxHP = chr->score.initialSkills[i] + (int) (pow((float) (chr->score.experience[i]) / 100, 0.6f));
00160 G_PrintStats(va("Soldier %s earned %d experience points in skill #%d (total experience: %d). It is now %d higher.",
00161 chr->name, gainedXP, i, chr->score.experience[i], chr->maxHP - chr->score.initialSkills[i]));
00162 }
00163
00169 void G_MatchEndTrigger (int team, int timeGap)
00170 {
00171 const int realTimeGap = timeGap > 0 ? level.time + timeGap : 1;
00172 level.winningTeam = team;
00173 level.intermissionTime = realTimeGap;
00174 }
00175
00184 static void G_SendCharacterData (const edict_t* ent)
00185 {
00186 int k;
00187
00188 assert(ent);
00189
00190
00191 gi.WriteShort(ent->chr.ucn);
00192
00193 gi.WriteShort(ent->HP);
00194 gi.WriteByte(ent->STUN);
00195 gi.WriteByte(ent->morale);
00196
00198 for (k = 0; k < SKILL_NUM_TYPES + 1; k++)
00199 gi.WriteLong(ent->chr.score.experience[k]);
00200 for (k = 0; k < SKILL_NUM_TYPES; k++)
00201 gi.WriteByte(ent->chr.score.skills[k]);
00202 for (k = 0; k < KILLED_NUM_TYPES; k++)
00203 gi.WriteShort(ent->chr.score.kills[k]);
00204 for (k = 0; k < KILLED_NUM_TYPES; k++)
00205 gi.WriteShort(ent->chr.score.stuns[k]);
00206 gi.WriteShort(ent->chr.score.assignedMissions);
00207 }
00208
00215 static void G_MatchSendResults (int team)
00216 {
00217 edict_t *ent, *attacker;
00218 int i, j = 0;
00219
00220 attacker = NULL;
00221 ent = NULL;
00222
00223 while ((ent = G_EdictsGetNextLivingActor(ent))) {
00224 if (!G_IsAI(ent))
00225 G_UpdateCharacterSkills(&ent->chr);
00226 else if (ent->team == team)
00227 attacker = ent;
00228 }
00229
00230
00231 if (team == TEAM_ALIEN) {
00232 ent = NULL;
00233 while ((ent = G_EdictsGetNextLivingActor(ent)))
00234 if (ent->team != team && !G_ActorIsInRescueZone(ent)) {
00235 ent->HP = 0;
00236 G_ActorDieOrStun(ent, attacker);
00237 }
00238 }
00239
00240
00241 ent = NULL;
00242 while ((ent = G_EdictsGetNextInUse(ent))) {
00243 const int playerMask = G_VisToPM(ent->visflags);
00244 G_AppearPerishEvent(~playerMask, qtrue, ent, NULL);
00245 if (G_IsActor(ent))
00246 G_SendInventory(~G_TeamToPM(ent->team), ent);
00247 }
00248
00249
00250 gi.AddEvent(PM_ALL, EV_RESULTS);
00251 gi.WriteByte(MAX_TEAMS);
00252 gi.WriteByte(team);
00253
00254 for (i = 0; i < MAX_TEAMS; i++) {
00255 gi.WriteByte(level.num_spawned[i]);
00256 gi.WriteByte(level.num_alive[i]);
00257 }
00258
00259 for (i = 0; i < MAX_TEAMS; i++)
00260 for (j = 0; j < MAX_TEAMS; j++)
00261 gi.WriteByte(level.num_kills[i][j]);
00262
00263 for (i = 0; i < MAX_TEAMS; i++)
00264 for (j = 0; j < MAX_TEAMS; j++)
00265 gi.WriteByte(level.num_stuns[i][j]);
00266
00267
00268 j = 0;
00269 ent = NULL;
00270 while ((ent = G_EdictsGetNextActor(ent)))
00271 if (!G_IsAI(ent))
00272 j++;
00273
00274
00275 gi.WriteByte(j);
00276
00277 if (j) {
00278 ent = NULL;
00279 while ((ent = G_EdictsGetNextActor(ent))) {
00280 if (!G_IsAI(ent)) {
00281 G_SendCharacterData(ent);
00282 }
00283 }
00284 }
00285
00286 gi.EndEvents();
00287
00288
00289 AIL_Cleanup();
00290
00291 if (level.nextmap[0] != '\0') {
00292 char command[MAX_VAR];
00296 Com_sprintf(command, sizeof(command), "map %s %s\n",
00297 level.day ? "day" : "night", level.nextmap);
00298 gi.AddCommandString(command);
00299 }
00300 }
00301
00306 qboolean G_MatchDoEnd (void)
00307 {
00308
00309 if (level.intermissionTime && level.time > level.intermissionTime) {
00310 G_PrintStats(va("End of game - Team %i is the winner", level.winningTeam));
00311 G_MatchSendResults(level.winningTeam);
00312 level.intermissionTime = 0.0;
00313 level.winningTeam = 0;
00314 return qtrue;
00315 }
00316
00317 return qfalse;
00318 }
00319
00325 void G_MatchEndCheck (void)
00326 {
00327 int activeTeams;
00328 int i, last;
00329
00330 if (level.intermissionTime)
00331 return;
00332
00333 if (!level.numplayers) {
00334 G_MatchEndTrigger(0, 0);
00335 return;
00336 }
00337
00339 for (i = 1, activeTeams = 0, last = 0; i < MAX_TEAMS; i++) {
00340 edict_t *ent = NULL;
00341
00342
00343 while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, i)) != NULL) {
00344 if (!G_IsStunned(ent)) {
00345 last = i;
00346 activeTeams++;
00347 break;
00348 }
00349 }
00350 }
00351
00353
00354 if (activeTeams < 2) {
00355 const int timeGap = (level.activeTeam == TEAM_ALIEN ? 10.0 : 3.0);
00356 G_MatchEndTrigger(activeTeams == 1 ? last : 0, timeGap);
00357 }
00358 }
00359
00366 qboolean G_MatchIsRunning (void)
00367 {
00368 if (level.intermissionTime)
00369 return qfalse;
00370 return (level.activeTeam != TEAM_NO_ACTIVE);
00371 }