UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_match.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "g_match.h"
27 #include "g_local.h"
28 #include "g_actor.h"
29 #include "g_ai.h"
30 #include "g_edicts.h"
31 #include "g_trigger.h"
32 #include "g_utils.h"
33 #include "g_vis.h"
34 
43 {
44  character_t* chr = &ent->chr;
45 
46  int experience = 0;
47 
48  switch (skill) {
49  case ABILITY_POWER: {
50  const float weight = chr->scoreMission->carriedWeight / level.actualRound;
51  const float penalty = GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]);
52  experience = 50 * (weight / WEIGHT_FACTOR / chr->score.skills[ABILITY_POWER]) / penalty;
53  break;
54  }
55  case ABILITY_ACCURACY:
56  /* skip skills < ABILITY_NUM_TYPES, they are abilities not real skills */
57  for (int i = ABILITY_NUM_TYPES; i < SKILL_NUM_TYPES; i++)
58  if (i == SKILL_SNIPER)
59  experience += 60 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
60  else
61  experience += 40 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
62  break;
63  case ABILITY_MIND:
64  experience = 50 + 100 * chr->scoreMission->kills[KILLED_ENEMIES];
65  break;
66  case SKILL_CLOSE:
67  experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
68  break;
69 #if 0
70  case SKILL_HEAVY:
71  experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
72  break;
73 #endif
74  case SKILL_ASSAULT:
75  experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
76  break;
77  case SKILL_SNIPER:
78  experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
79  break;
80  case SKILL_EXPLOSIVE:
81  experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
82  break;
83  default:
84  break;
85  }
86 
87  return experience;
88 }
89 
98 {
99  character_t* chr = &ent->chr;
100 
101  /* Robots/UGVs do not get skill-upgrades. */
102  if (chr->teamDef->robot)
103  return;
104 
105  unsigned int totalGainedXP = 0;
106  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
107  const abilityskills_t skill = static_cast<abilityskills_t>(i);
108  const int gainedXP = G_GetEarnedExperience(skill, ent);
109 
110  chr->score.experience[i] += gainedXP;
111  totalGainedXP += gainedXP;
112  }
113 
114  /* Speed and health are handled separately */
115  chr->score.experience[ABILITY_SPEED] += totalGainedXP / 5;
116  /* This is health */
117  chr->score.experience[SKILL_NUM_TYPES] += totalGainedXP / 5;
118 }
119 
125 void G_MatchEndTrigger (int team, int timeGap)
126 {
127  bool foundNextMap = false;
128  Edict* ent = nullptr;
129 
130  while ((ent = G_EdictsGetTriggerNextMaps(ent)) != nullptr) {
131  if (ent->getTeam() == team) {
133  ent->nextthink = 1;
134  foundNextMap = true;
135  }
136  }
137 
138  if (!foundNextMap) {
139  const int realTimeGap = timeGap > 0 ? level.time + timeGap : 1;
140  level.winningTeam = team;
141  level.intermissionTime = realTimeGap;
142  }
143 }
144 
153 static void G_SendCharacterData (const Actor* actor)
154 {
155  assert(actor);
156 
157  /* write character number */
158  gi.WriteShort(actor->chr.ucn);
159 
160  gi.WriteShort(actor->HP);
161  gi.WriteByte(actor->getStun());
162  gi.WriteByte(actor->morale);
163 
164  for (int k = 0; k < BODYPART_MAXTYPE; ++k)
165  gi.WriteByte(actor->chr.wounds.woundLevel[k] + actor->chr.wounds.treatmentLevel[k]);
166 
168  for (int k = 0; k < SKILL_NUM_TYPES + 1; k++)
169  gi.WriteLong(actor->chr.score.experience[k]);
170  for (int k = 0; k < KILLED_NUM_TYPES; k++)
171  gi.WriteShort(actor->chr.score.kills[k]);
172  for (int k = 0; k < KILLED_NUM_TYPES; k++)
173  gi.WriteShort(actor->chr.score.stuns[k]);
174  gi.WriteShort(actor->chr.score.assignedMissions);
175 }
176 
184 static void G_MatchSendResults (int team, bool nextmap)
185 {
186  Edict* attacker = nullptr;
187  Actor* actor = nullptr;
188  /* Calculate new scores/skills for the soldiers. */
189  while ((actor = G_EdictsGetNextLivingActor(actor))) {
190  if (!G_IsAI(actor))
192  else if (actor->getTeam() == team)
193  attacker = actor;
194  }
195 
196  /* if aliens won, make sure every soldier that is not in the rescue zone dies */
197  if (team == TEAM_ALIEN) {
198  actor = nullptr;
199  while ((actor = G_EdictsGetNextLivingActor(actor)))
200  if (actor->getTeam() != team && !actor->isInRescueZone()) {
201  actor->HP = 0;
202  G_ActorDieOrStun(actor, attacker);
203  }
204  }
205 
207 
208  /* send results */
210  gi.WriteByte(MAX_TEAMS);
211  gi.WriteByte(team);
212  gi.WriteByte(nextmap);
213 
214  for (int i = 0; i < MAX_TEAMS; i++) {
215  gi.WriteByte(level.num_spawned[i]);
216  gi.WriteByte(level.num_alive[i]);
217  }
218 
219  for (int i = 0; i <= MAX_TEAMS; i++)
220  for (int j = 0; j < MAX_TEAMS; j++)
221  gi.WriteByte(level.num_kills[i][j]);
222 
223  for (int i = 0; i <= MAX_TEAMS; i++)
224  for (int j = 0; j < MAX_TEAMS; j++)
225  gi.WriteByte(level.num_stuns[i][j]);
226 
227  /* how many actors */
228  int n = 0;
229  actor = nullptr;
230  while ((actor = G_EdictsGetNextActor(actor)))
231  if (!G_IsAI(actor))
232  n++;
233 
234  /* number of soldiers */
235  gi.WriteByte(n);
236 
237  if (n) {
238  actor = nullptr;
239  while ((actor = G_EdictsGetNextActor(actor))) {
240  if (!G_IsAI(actor)) {
241  G_SendCharacterData(actor);
242  }
243  }
244  }
245 
246  G_EventEnd();
247 }
248 
253 bool G_MatchDoEnd (void)
254 {
255  /* check for intermission */
257  G_PrintStats("End of game - Team %i is the winner", level.winningTeam);
259 
260  /* now we cleanup the AI */
261  AIL_Cleanup();
262 
263  if (level.mapEndCommand != nullptr) {
264  gi.AddCommandString("%s\n", level.mapEndCommand);
265  }
266 
267  level.intermissionTime = 0.0f;
268  level.winningTeam = 0;
269  return true;
270  }
271 
272  return false;
273 }
274 
280 void G_MatchEndCheck (void)
281 {
282  if (level.intermissionTime > 0.0f) /* already decided */
283  return;
284 
285  if (!level.numplayers) {
286  G_MatchEndTrigger(0, 0);
287  return;
288  }
289 
290  int last = 0;
291  int activeTeams = 0;
293  for (int i = 1; i < MAX_TEAMS; i++) {
294  Actor* actor = nullptr;
295  /* search for living but not stunned actors - there must at least be one actor
296  * that is still able to attack or defend himself */
297  while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, i)) != nullptr) {
298  if (!actor->isStunned()) {
299  last = i;
300  activeTeams++;
301  break;
302  }
303  }
304  }
305 
307  /* prepare for sending results */
308  if (activeTeams < 2) {
309  const int timeGap = (level.activeTeam == TEAM_ALIEN ? 10 : 3);
310  G_MatchEndTrigger(activeTeams == 1 ? last : 0, timeGap);
311  }
312 }
313 
320 bool G_MatchIsRunning (void)
321 {
322  if (level.intermissionTime > 0.0f)
323  return false;
324  return (level.activeTeam != TEAM_NO_ACTIVE);
325 }
void G_EventEnd(void)
Definition: g_events.cpp:711
static int G_GetEarnedExperience(abilityskills_t skill, Edict *ent)
Determines the amount of XP earned by a given soldier for a given skill, based on the soldier's perfo...
Definition: g_match.cpp:42
bool isInRescueZone() const
Checks whether the given actor is currently standing in a rescue zone.
Definition: g_edict.h:407
#define BODYPART_MAXTYPE
Definition: chr_shared.h:255
float intermissionTime
Definition: g_local.h:93
abilityskills_t
Definition: chr_shared.h:36
void G_MatchEndTrigger(int team, int timeGap)
Triggers the end of the game. Will be executed in the next server (or game) frame.
Definition: g_match.cpp:125
chrScoreGlobal_t score
Definition: chr_shared.h:387
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
float nextthink
Definition: g_edict.h:149
#define MAX_TEAMS
Definition: defines.h:98
static void G_UpdateCharacterExperience(Edict *ent)
Updates character experience after a mission.
Definition: g_match.cpp:97
int getStun() const
Definition: g_edict.h:308
void G_PrintStats(const char *format,...)
Prints stats to game console and stats log file.
Definition: g_utils.cpp:304
#define TEAM_ALIEN
Definition: q_shared.h:63
const teamDef_t * teamDef
Definition: chr_shared.h:394
unsigned num_stuns[MAX_TEAMS+1][MAX_TEAMS]
Definition: g_local.h:114
bool robot
Definition: chr_shared.h:322
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
Artificial Intelligence functions.
static void G_MatchSendResults(int team, bool nextmap)
Handles the end of a match.
Definition: g_match.cpp:184
Misc utility functions for game module.
int activeTeam
Definition: g_local.h:101
int hitsSplash[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:95
character_t chr
Definition: g_edict.h:116
bool nextMapSwitch
Definition: g_local.h:90
void Think_NextMapTrigger(Edict *self)
Register this think function once you would like to end the match This think function will register t...
Definition: g_trigger.cpp:153
Trigger functions.
bool isStunned() const
Definition: g_edict.h:355
#define PM_ALL
Definition: g_events.h:36
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:127
#define ABILITY_NUM_TYPES
Definition: chr_shared.h:54
chrScoreMission_t * scoreMission
Definition: chr_shared.h:388
int actualRound
Definition: g_local.h:104
int hits[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:89
#define WEIGHT_FACTOR
Definition: q_shared.h:285
unsigned num_kills[MAX_TEAMS+1][MAX_TEAMS]
Definition: g_local.h:113
int getTeam() const
Definition: g_edict.h:269
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time)
Definition: g_match.cpp:320
Match related functions.
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:82
int treatmentLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:352
game_import_t gi
Definition: g_main.cpp:39
void(* think)(Edict *self)
Definition: g_edict.h:150
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:126
int woundLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:351
An Edict of type Actor.
Definition: g_edict.h:348
byte num_alive[MAX_TEAMS]
Definition: g_local.h:115
static void G_SendCharacterData(const Actor *actor)
Sends character stats like assigned missions and kills back to client.
Definition: g_match.cpp:153
void G_EventAdd(playermask_t playerMask, int eType, int entnum)
Definition: g_events.cpp:705
void G_VisMakeEverythingVisible(void)
Make everything visible to anyone who can't already see it.
Definition: g_vis.cpp:390
woundInfo_t wounds
Definition: chr_shared.h:383
#define GET_ENCUMBRANCE_PENALTY(weight, max)
Definition: q_shared.h:287
int winningTeam
Definition: g_local.h:96
Edict * G_EdictsGetTriggerNextMaps(Edict *lastEnt)
Iterator through all the trigger_nextmap edicts.
Definition: g_edicts.cpp:181
#define G_IsAI(ent)
Definition: g_local.h:141
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
void AIL_Cleanup(void)
Closes the LUA AI.
Definition: g_ai_lua.cpp:2308
QGL_EXTERN GLint i
Definition: r_gl.h:113
Actor * G_EdictsGetNextActor(Actor *lastEnt)
Iterate through the actor entities (even the dead!)
Definition: g_edicts.cpp:231
Local definitions for game module.
functions to handle the storage and lifecycle of all edicts in the game module.
unsigned num_spawned[MAX_TEAMS]
Definition: g_local.h:112
bool G_ActorDieOrStun(Actor *actor, Edict *attacker)
Reports and handles death or stun of an actor. If the HP of an actor is zero the actor will die...
Definition: g_actor.cpp:435
int morale
Definition: g_edict.h:91
void G_MatchEndCheck(void)
Checks whether there are still actors to fight with left. If none are the match end will be triggered...
Definition: g_match.cpp:280
int numplayers
Definition: g_local.h:100
#define TEAM_NO_ACTIVE
Definition: q_shared.h:60
Definition: g_edict.h:45
char * mapEndCommand
Definition: g_local.h:86
bool G_MatchDoEnd(void)
Checks whether a match is over.
Definition: g_match.cpp:253
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
level_locals_t level
Definition: g_main.cpp:38
int HP
Definition: g_edict.h:89
Describes a character with all its attributes.
Definition: chr_shared.h:369
float time
Definition: g_local.h:82