g_match.c

Go to the documentation of this file.
00001 
00006 /*
00007 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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: /* This is health. */
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     /* Robots/UGVs do not get skill-upgrades. */
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     /* Health isn't part of abilityskills_t, so it needs to be handled separately. */
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     /* write character number */
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     /* Calculate new scores/skills for the soldiers. */
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     /* if aliens won, make sure every soldier that is not in the rescue zone dies */
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     /* Make everything visible to anyone who can't already see it */
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     /* send results */
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     /* how many actors */
00268     j = 0;
00269     ent = NULL;
00270     while ((ent = G_EdictsGetNextActor(ent)))
00271         if (!G_IsAI(ent))
00272             j++;
00273 
00274     /* number of soldiers */
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     /* now we cleanup the AI */
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     /* check for intermission */
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) /* already decided */
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         /* search for living but not stunned actors - there must at least be one actor
00342          * that is still able to attack or defend himself */
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     /* prepare for sending results */
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 }

Generated by  doxygen 1.6.2