UFO: Alien Invasion
Loading...
Searching...
No Matches
g_match.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, 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 }
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
125void 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
153static 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
184static 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
253bool G_MatchDoEnd (void)
254{
255 /* check for intermission */
256 if (level.intermissionTime > 0.0f && level.time > level.intermissionTime) {
257 G_PrintStats("End of game - Team %i is the winner", level.winningTeam);
258 G_MatchSendResults(level.winningTeam, level.nextMapSwitch);
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
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
321{
322 if (level.intermissionTime > 0.0f)
323 return false;
324 return (level.activeTeam != TEAM_NO_ACTIVE);
325}
abilityskills_t
Definition chr_shared.h:36
@ SKILL_HEAVY
Definition chr_shared.h:43
@ ABILITY_POWER
Definition chr_shared.h:37
@ ABILITY_SPEED
Definition chr_shared.h:38
@ SKILL_NUM_TYPES
Definition chr_shared.h:51
@ SKILL_SNIPER
Definition chr_shared.h:45
@ ABILITY_MIND
Definition chr_shared.h:40
@ SKILL_CLOSE
Definition chr_shared.h:42
@ SKILL_EXPLOSIVE
Definition chr_shared.h:46
@ ABILITY_ACCURACY
Definition chr_shared.h:39
@ SKILL_ASSAULT
Definition chr_shared.h:44
@ KILLED_ENEMIES
Definition chr_shared.h:28
@ KILLED_NUM_TYPES
Definition chr_shared.h:32
#define BODYPART_MAXTYPE
Definition chr_shared.h:266
#define ABILITY_NUM_TYPES
Definition chr_shared.h:54
An Edict of type Actor.
Definition g_edict.h:348
bool isStunned() const
Definition g_edict.h:355
bool isInRescueZone() const
Checks whether the given actor is currently standing in a rescue zone.
Definition g_edict.h:407
character_t chr
Definition g_edict.h:116
void(* think)(Edict *self)
Definition g_edict.h:150
int HP
Definition g_edict.h:89
int getTeam() const
Definition g_edict.h:269
float nextthink
Definition g_edict.h:149
int getStun() const
Definition g_edict.h:308
int morale
Definition g_edict.h:91
#define MAX_TEAMS
Definition defines.h:98
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
Artificial Intelligence functions.
void AIL_Cleanup(void)
Closes the LUA AI.
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition g_edicts.cpp:216
Edict * G_EdictsGetTriggerNextMaps(Edict *lastEnt)
Iterator through all the trigger_nextmap edicts.
Definition g_edicts.cpp:181
Actor * G_EdictsGetNextActor(Actor *lastEnt)
Iterate through the actor entities (even the dead!).
Definition g_edicts.cpp:231
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition g_edicts.cpp:196
functions to handle the storage and lifecycle of all edicts in the game module.
void G_EventEnd(void)
Definition g_events.cpp:711
void G_EventAdd(playermask_t playerMask, int eType, int entnum)
Definition g_events.cpp:705
#define PM_ALL
Definition g_events.h:36
Local definitions for game module.
level_locals_t level
Definition g_main.cpp:38
#define G_IsAI(ent)
Definition g_local.h:141
game_import_t gi
Definition g_main.cpp:39
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
static void G_MatchSendResults(int team, bool nextmap)
Handles the end of a match.
Definition g_match.cpp:184
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
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time).
Definition g_match.cpp:320
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
static void G_UpdateCharacterExperience(Edict *ent)
Updates character experience after a mission.
Definition g_match.cpp:97
bool G_MatchDoEnd(void)
Checks whether a match is over.
Definition g_match.cpp:253
static void G_SendCharacterData(const Actor *actor)
Sends character stats like assigned missions and kills back to client.
Definition g_match.cpp:153
Match related functions.
void Think_NextMapTrigger(Edict *self)
Register this think function once you would like to end the match This think function will register t...
Trigger functions.
void G_PrintStats(const char *format,...)
Prints stats to game console and stats log file.
Definition g_utils.cpp:304
Misc utility functions for game module.
void G_VisMakeEverythingVisible(void)
Make everything visible to anyone who can't already see it.
Definition g_vis.cpp:390
@ EV_RESULTS
Definition q_shared.h:86
#define TEAM_ALIEN
Definition q_shared.h:63
#define WEIGHT_FACTOR
Definition q_shared.h:285
#define GET_ENCUMBRANCE_PENALTY(weight, max)
Definition q_shared.h:287
#define TEAM_NO_ACTIVE
Definition q_shared.h:60
QGL_EXTERN GLint i
Definition r_gl.h:113
Describes a character with all its attributes.
Definition chr_shared.h:388
const teamDef_t * teamDef
Definition chr_shared.h:413
chrScoreMission_t * scoreMission
Definition chr_shared.h:407
chrScoreGlobal_t score
Definition chr_shared.h:406
woundInfo_t wounds
Definition chr_shared.h:402
int stuns[KILLED_NUM_TYPES]
Definition chr_shared.h:127
int kills[KILLED_NUM_TYPES]
Definition chr_shared.h:126
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
int experience[SKILL_NUM_TYPES+1]
Definition chr_shared.h:120
int hitsSplash[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition chr_shared.h:95
int hits[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition chr_shared.h:89
int kills[KILLED_NUM_TYPES]
Definition chr_shared.h:82
int woundLevel[BODYPART_MAXTYPE]
Definition chr_shared.h:362
int treatmentLevel[BODYPART_MAXTYPE]
Definition chr_shared.h:363