UFO: Alien Invasion
g_cmds.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2022 UFO: Alien Invasion.
8 
9 Original file from Quake 2 v3.21: quake2-2.31/game/g_cmds.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11 
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21 See the GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 
27 */
28 
29 #include "g_local.h"
30 #include "g_actor.h"
31 #include "g_client.h"
32 #include "g_edicts.h"
33 #include "g_match.h"
34 #include "../shared/parse.h"
35 
36 static void G_Players_f (const Player& player)
37 {
38  int count = 0;
39  char smallBuf[64];
40  char largeBuf[1280];
41 
42  /* print information */
43  largeBuf[0] = 0;
44 
45  Player* p = nullptr;
46  while ((p = G_PlayerGetNextActiveHuman(p))) {
47  Com_sprintf(smallBuf, sizeof(smallBuf), "(%i) Team %i %s status: %s\n", p->getNum(),
48  p->getTeam(), p->pers.netname, (p->roundDone ? "waiting" : "playing"));
49 
50  /* can't print all of them in one packet */
51  if (strlen(smallBuf) + strlen(largeBuf) > sizeof(largeBuf) - 100) {
52  Q_strcat(largeBuf, sizeof(largeBuf), "...\n");
53  break;
54  }
55  Q_strcat(largeBuf, sizeof(largeBuf), "%s", smallBuf);
56  count++;
57  }
58 
59  G_ClientPrintf(player, PRINT_CONSOLE, "%s\n%i players\n", largeBuf, count);
60 }
61 
65 static bool G_CheckFlood (Player& player)
66 {
67  if (flood_msgs->integer) {
68  if (level.time < player.pers.flood_locktill) {
69  G_ClientPrintf(player, PRINT_CHAT, _("You can't talk for %d more seconds\n"), (int)(player.pers.flood_locktill - level.time));
70  return true;
71  }
72  int i = player.pers.flood_whenhead - flood_msgs->value + 1;
73  if (i < 0)
74  i = (sizeof(player.pers.flood_when)/sizeof(player.pers.flood_when[0])) + i;
75  if (player.pers.flood_when[i] && level.time - player.pers.flood_when[i] < flood_persecond->value) {
76  player.pers.flood_locktill = level.time + flood_waitdelay->value;
77  G_ClientPrintf(player, PRINT_CHAT, _("Flood protection: You can't talk for %d seconds.\n"), flood_waitdelay->integer);
78  return true;
79  }
80  player.pers.flood_whenhead = (player.pers.flood_whenhead + 1) %
81  (sizeof(player.pers.flood_when)/sizeof(player.pers.flood_when[0]));
82  player.pers.flood_when[player.pers.flood_whenhead] = level.time;
83  }
84  return false;
85 }
86 
87 static void G_Say_f (Player& player, bool arg0, bool team)
88 {
89  if (gi.Cmd_Argc() < 2 && !arg0)
90  return;
91 
92  if (G_CheckFlood(player))
93  return;
94 
95  char text[256];
96  if (arg0) {
97  Com_sprintf(text, sizeof(text), "%s %s", gi.Cmd_Argv(0), gi.Cmd_Args());
98  } else {
99  Com_sprintf(text, sizeof(text), "%s", gi.Cmd_Args());
100  }
101 
102  /* strip quotes */
103  char* s = text;
104  if (s[0] == '"' && s[strlen(s) - 1] == '"') {
105  s[strlen(s) - 1] = '\0';
106  s++;
107  }
108 
109  if (sv_dedicated->integer) {
110  if (!team)
111  gi.DPrintf("%s: %s\n", player.pers.netname, s);
112  else
113  gi.DPrintf("^B%s (team): %s\n", player.pers.netname, s);
114  }
115 
116  Player* p = nullptr;
117  while ((p = G_PlayerGetNextActiveHuman(p))) {
118  if (team && p->getTeam() != player.getTeam())
119  continue;
120  if (!team)
121  G_ClientPrintf(*p, PRINT_CHAT, "%s: %s\n", player.pers.netname, s);
122  else
123  G_ClientPrintf(*p, PRINT_CHAT, "^B%s (team): %s\n", player.pers.netname, s);
124  }
125 }
126 
127 #ifdef DEBUG
128 
133 static void G_KillTeam_f (void)
134 {
135  /* default is to kill all teams */
136  int teamToKill = -1;
137  int amount = -1;
138 
139  /* with a parameter we will be able to kill a specific team */
140  if (gi.Cmd_Argc() >= 2) {
141  teamToKill = atoi(gi.Cmd_Argv(1));
142  if (gi.Cmd_Argc() >= 3)
143  amount = atoi(gi.Cmd_Argv(2));
144  }
145 
146  Com_DPrintf(DEBUG_GAME, "G_KillTeam: kill team %i\n", teamToKill);
147 
148  if (teamToKill >= 0) {
149  Actor* actor = nullptr;
150  while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, teamToKill))) {
151  if (amount == 0)
152  break;
153  /* die */
154  actor->HP = 0;
155  G_ActorDieOrStun(actor, nullptr);
156 
157  if (teamToKill == TEAM_ALIEN)
159  else
160  level.num_kills[TEAM_ALIEN][teamToKill]++;
161  amount--;
162  }
163  }
164 
165  /* check for win conditions */
166  G_MatchEndCheck();
167 }
168 
172 static void G_StunTeam_f (void)
173 {
174  /* default is to kill all teams */
175  int teamToKill = -1;
176 
177  /* with a parameter we will be able to kill a specific team */
178  if (gi.Cmd_Argc() >= 2)
179  teamToKill = atoi(gi.Cmd_Argv(1));
180 
181  if (teamToKill >= 0) {
182  Actor* actor = nullptr;
183  while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, teamToKill))) {
184  /* stun */
185  G_ActorDieOrStun(actor, nullptr);
186 
187  if (teamToKill == TEAM_ALIEN)
189  else
190  level.num_stuns[TEAM_ALIEN][teamToKill]++;
191  }
192  }
193 
194  /* check for win conditions */
195  G_MatchEndCheck();
196 }
197 
202 static void G_ListMissionScore_f (void)
203 {
204  int team = -1;
205 
206  /* With a parameter we will be able to get the info for a specific team */
207  if (gi.Cmd_Argc() >= 2) {
208  team = atoi(gi.Cmd_Argv(1));
209  } else {
210  gi.DPrintf("Usage: %s <teamnumber>\n", gi.Cmd_Argv(0));
211  return;
212  }
213 
214  Actor* actor = nullptr;
215  while ((actor = G_EdictsGetNextLivingActor(actor))) {
216  if (team >= 0 && actor->getTeam() != team)
217  continue;
218 
219  assert(actor->chr.scoreMission);
220 
221  gi.DPrintf("Soldier: %s\n", actor->chr.name);
222 
223  /* ===================== */
224  gi.DPrintf(" Move: Normal=%i Crouched=%i\n", actor->chr.scoreMission->movedNormal, actor->chr.scoreMission->movedCrouched);
225 
226  gi.DPrintf(" Kills:");
227  for (int i = 0; i < KILLED_NUM_TYPES; i++) {
228  gi.DPrintf(" %i", actor->chr.scoreMission->kills[i]);
229  }
230  gi.DPrintf("\n");
231 
232  gi.DPrintf(" Stuns:");
233  for (int i = 0; i < KILLED_NUM_TYPES; i++) {
234  gi.DPrintf(" %i", actor->chr.scoreMission->stuns[i]);
235  }
236  gi.DPrintf("\n");
237 
238  /* ===================== */
239  gi.DPrintf(" Fired:");
240  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
241  gi.DPrintf(" %i", actor->chr.scoreMission->fired[i]);
242  }
243  gi.DPrintf("\n");
244 
245  gi.DPrintf(" Hits:\n");
246  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
247  gi.DPrintf(" Skill%i: ",i);
248  for (int j = 0; j < KILLED_NUM_TYPES; j++) {
249  gi.DPrintf(" %i", actor->chr.scoreMission->hits[i][j]);
250  }
251  gi.DPrintf("\n");
252  }
253 
254  /* ===================== */
255  gi.DPrintf(" Fired Splash:");
256  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
257  gi.DPrintf(" %i", actor->chr.scoreMission->firedSplash[i]);
258  }
259  gi.DPrintf("\n");
260 
261  gi.DPrintf(" Hits Splash:\n");
262  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
263  gi.DPrintf(" Skill%i: ",i);
264  for (int j = 0; j < KILLED_NUM_TYPES; j++) {
265  gi.DPrintf(" %i", actor->chr.scoreMission->hitsSplash[i][j]);
266  }
267  gi.DPrintf("\n");
268  }
269 
270  gi.DPrintf(" Splash Damage:\n");
271  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
272  gi.DPrintf(" Skill%i: ",i);
273  for (int j = 0; j < KILLED_NUM_TYPES; j++) {
274  gi.DPrintf(" %i", actor->chr.scoreMission->hitsSplashDamage[i][j]);
275  }
276  gi.DPrintf("\n");
277  }
278 
279  /* ===================== */
280  gi.DPrintf(" Kills per skill:");
281  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
282  gi.DPrintf(" %i", actor->chr.scoreMission->skillKills[i]);
283  }
284  gi.DPrintf("\n");
285 
286  /* ===================== */
287  gi.DPrintf(" Heal (received): %i\n", actor->chr.scoreMission->heal);
288  }
289 }
290 
294 void G_InvList_f (const Player& player)
295 {
296  gi.DPrintf("Print inventory for '%s'\n", player.pers.netname);
297 
298  Actor* actor = nullptr;
299  while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, player.getTeam()))) {
300  gi.DPrintf("actor: '%s'\n", actor->chr.name);
301 
302  const Container* cont = nullptr;
303  while ((cont = actor->chr.inv.getNextCont(cont, true))) {
304  Com_Printf("Container: %i\n", cont->id);
305  Item* item = nullptr;
306  while ((item = cont->getNextItem(item))) {
307  Com_Printf(".. item.def(): %i, item.ammo: %i, item.ammoLeft: %i, x: %i, y: %i\n",
308  (item->def() ? item->def()->idx : NONE), (item->ammoDef() ? item->ammoDef()->idx : NONE),
309  item->getAmmoLeft(), item->getX(), item->getY());
310  if (item->def())
311  Com_Printf(".... weapon: %s\n", item->def()->id);
312  if (item->ammoDef())
313  Com_Printf(".... ammo: %s (%i)\n", item->ammoDef()->id, item->getAmmoLeft());
314  }
315  }
316  const float invWeight = actor->chr.inv.getWeight();
317  const int maxWeight = actor->chr.score.skills[ABILITY_POWER];
318  const float penalty = GET_ENCUMBRANCE_PENALTY(invWeight, maxWeight);
319  const int normalTU = GET_TU(actor->chr.score.skills[ABILITY_SPEED], 1.0f - WEIGHT_NORMAL_PENALTY);
320  const int tus = GET_TU(actor->chr.score.skills[ABILITY_SPEED], penalty);
321  const int tuPenalty = tus - normalTU;
322  const char* penaltyStr = 1.0f - penalty < WEIGHT_NORMAL_PENALTY ? "'Light weight'" : (1.0f - penalty < WEIGHT_HEAVY_PENALTY ? "'Normal weight'" : "'Encumbered'");
323  Com_Printf("Weight: %g/%i, Encumbrance: %s (%.0f%%), TU's: %i (normal: %i, penalty/bonus: %+i)\n", invWeight, maxWeight, penaltyStr, invWeight / maxWeight * 100.0f, tus, normalTU, tuPenalty);
324  }
325 }
326 
327 static void G_TouchEdict_f (void)
328 {
329  if (gi.Cmd_Argc() < 2) {
330  gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
331  return;
332  }
333 
334  const int i = atoi(gi.Cmd_Argv(1));
335  if (!G_EdictsIsValidNum(i))
336  return;
337 
338  Edict* e = G_EdictsGetByNum(i);
339  if (!e->hasTouch()) {
340  gi.DPrintf("No touch function for entity %s\n", e->classname);
341  return;
342  }
343 
344  Actor* actor = G_EdictsGetNextLivingActor(nullptr);
345  if (!actor)
346  return; /* didn't find any */
347 
348  gi.DPrintf("Call touch function for %s\n", e->classname);
349  e->callTouch(actor);
350 }
351 
352 static void G_UseEdict_f (void)
353 {
354  if (gi.Cmd_Argc() < 2) {
355  gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
356  return;
357  }
358 
359  const int i = atoi(gi.Cmd_Argv(1));
360  if (!G_EdictsIsValidNum(i)) {
361  gi.DPrintf("No entity with number %i\n", i);
362  return;
363  }
364 
365  Edict* e = G_EdictsGetByNum(i);
366  if (!e->use) {
367  gi.DPrintf("No use function for entity %s\n", e->classname);
368  return;
369  }
370 
371  gi.DPrintf("Call use function for %s\n", e->classname);
372  e->use(e, nullptr);
373 }
374 
375 static void G_DestroyEdict_f (void)
376 {
377  if (gi.Cmd_Argc() < 2) {
378  gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
379  return;
380  }
381 
382  const int i = atoi(gi.Cmd_Argv(1));
383  if (!G_EdictsIsValidNum(i))
384  return;
385 
386  Edict* e = G_EdictsGetByNum(i);
387  if (!e->destroy) {
388  gi.DPrintf("No destroy function for entity %s\n", e->classname);
389  return;
390  }
391 
392  gi.DPrintf("Call destroy function for %s\n", e->classname);
393  e->destroy(e);
394 }
395 
396 static void G_StateChange_f (void)
397 {
398  if (gi.Cmd_Argc() < 3) {
399  gi.DPrintf("Usage: %s <entnum> <state>\n States are: panic, rage, shaken", gi.Cmd_Argv(0));
400  return;
401  }
402 
403  const int entnum = atoi(gi.Cmd_Argv(1));
404  Edict* e = G_EdictsGetByNum(entnum);
405  if (e == nullptr)
406  return;
407 
408  const char* state = gi.Cmd_Argv(2);
409  if (Q_strcasecmp(state, "panic")) {
410  e->setMorale(mor_panic->integer / 2);
411  } else if (Q_strcasecmp(state, "shaken")) {
412  e->setMorale(mor_shaken->integer / 2);
413  } else if (Q_strcasecmp(state, "rage")) {
414  e->setMorale(m_rage->integer / 2);
415  } else {
416  e->setMorale(0);
417  }
418 
419  G_MoraleBehaviour(e->getTeam());
420 }
421 #endif
422 
423 void G_ClientCommand (Player& player)
424 {
425  if (!player.isInUse())
426  return; /* not fully in game yet */
427 
428  const char* cmd = gi.Cmd_Argv(0);
429 
430  if (Q_strcasecmp(cmd, "players") == 0)
431  G_Players_f(player);
432  else if (Q_strcasecmp(cmd, "say") == 0)
433  G_Say_f(player, false, false);
434  else if (Q_strcasecmp(cmd, "say_team") == 0)
435  G_Say_f(player, false, true);
436 #ifdef DEBUG
437  else if (Q_strcasecmp(cmd, "debug_actorinvlist") == 0)
438  G_InvList_f(player);
439  else if (Q_strcasecmp(cmd, "debug_killteam") == 0)
440  G_KillTeam_f();
441  else if (Q_strcasecmp(cmd, "debug_stunteam") == 0)
442  G_StunTeam_f();
443  else if (Q_strcasecmp(cmd, "debug_listscore") == 0)
444  G_ListMissionScore_f();
445  else if (Q_strcasecmp(cmd, "debug_edicttouch") == 0)
446  G_TouchEdict_f();
447  else if (Q_strcasecmp(cmd, "debug_edictuse") == 0)
448  G_UseEdict_f();
449  else if (Q_strcasecmp(cmd, "debug_edictdestroy") == 0)
450  G_DestroyEdict_f();
451  else if (Q_strcasecmp(cmd, "debug_statechange") == 0)
452  G_StateChange_f();
453 #endif
454  else
455  /* anything that doesn't match a command will be a chat */
456  G_Say_f(player, true, false);
457 }
cvar_t * flood_waitdelay
Definition: g_main.cpp:123
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition: g_client.cpp:206
#define WEIGHT_NORMAL_PENALTY
Definition: q_shared.h:283
Player * G_PlayerGetNextActiveHuman(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:110
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
bool hasTouch() const
Definition: g_edict.h:324
Item * getNextItem(const Item *prev) const
Definition: inv_shared.cpp:671
#define TEAM_PHALANX
Definition: q_shared.h:62
cvar_t * m_rage
Definition: g_main.cpp:107
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
#define TEAM_ALIEN
Definition: q_shared.h:63
unsigned num_stuns[MAX_TEAMS+1][MAX_TEAMS]
Definition: g_local.h:114
#define PRINT_CHAT
Definition: defines.h:106
#define _(String)
Definition: cl_shared.h:44
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
character_t chr
Definition: g_edict.h:116
int integer
Definition: cvar.h:81
void G_ClientCommand(Player &player)
Definition: g_cmds.cpp:423
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
bool callTouch(Edict *activator)
Definition: g_edict.h:329
static void G_Say_f(Player &player, bool arg0, bool team)
Definition: g_cmds.cpp:87
const char *IMPORT * Cmd_Args(void)
const char * id
Definition: inv_shared.h:268
void G_MoraleBehaviour(int team)
Applies morale behaviour on actors.
Definition: g_morale.cpp:156
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:82
item instance data, with linked list capability
Definition: inv_shared.h:402
Match related functions.
int getTeam() const
Definition: g_edict.h:269
int hits[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:89
game_import_t gi
Definition: g_main.cpp:39
#define GET_TU(ab, md)
Definition: q_shared.h:291
cvar_t * mor_panic
Definition: g_main.cpp:103
An Edict of type Actor.
Definition: g_edict.h:348
int firedSplash[SKILL_NUM_TYPES]
Definition: chr_shared.h:92
const char *IMPORT * Cmd_Argv(int n)
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
unsigned num_kills[MAX_TEAMS+1][MAX_TEAMS]
Definition: g_local.h:113
#define Q_strcasecmp(a, b)
Definition: shared.h:131
bool(* use)(Edict *self, Edict *activator)
Definition: g_edict.h:154
int skillKills[SKILL_NUM_TYPES]
Definition: chr_shared.h:99
cvar_t * flood_msgs
Definition: g_main.cpp:121
QGL_EXTERN GLuint count
Definition: r_gl.h:99
#define WEIGHT_HEAVY_PENALTY
Definition: q_shared.h:284
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
bool(* destroy)(Edict *self)
Definition: g_edict.h:155
#define GET_ENCUMBRANCE_PENALTY(weight, max)
Definition: q_shared.h:287
int fired[SKILL_NUM_TYPES]
Definition: chr_shared.h:86
bool G_EdictsIsValidNum(const int num)
Check if the given number could point to an existing entity.
Definition: g_edicts.cpp:72
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
QGL_EXTERN GLint i
Definition: r_gl.h:113
cvar_t * sv_dedicated
Definition: common.cpp:51
Local definitions for game module.
chrScoreGlobal_t score
Definition: chr_shared.h:406
functions to handle the storage and lifecycle of all edicts in the game module.
int getWeight() const
Get the weight of the items in the given inventory (excluding those in temp containers).
Definition: inv_shared.cpp:937
int getX() const
Definition: inv_shared.h:454
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
static void G_Players_f(const Player &player)
Definition: g_cmds.cpp:36
float time
Definition: g_local.h:82
int getY() const
Definition: inv_shared.h:457
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
static bool G_CheckFlood(Player &player)
Check whether the user can talk.
Definition: g_cmds.cpp:65
#define PRINT_CONSOLE
Definition: defines.h:108
float value
Definition: cvar.h:80
char name[MAX_VAR]
Definition: chr_shared.h:390
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
const char * classname
Definition: g_edict.h:67
Edict * G_EdictsGetByNum(const int num)
Get an entity by it&#39;s number.
Definition: g_edicts.cpp:83
#define NONE
Definition: defines.h:68
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
Definition: inv_shared.cpp:722
cvar_t * mor_shaken
Definition: g_main.cpp:102
const objDef_t * def(void) const
Definition: inv_shared.h:469
Definition: g_edict.h:45
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:83
int getAmmoLeft() const
Definition: inv_shared.h:466
cvar_t * flood_persecond
Definition: g_main.cpp:122
Interface for g_client.cpp.
chrScoreMission_t * scoreMission
Definition: chr_shared.h:407
#define DEBUG_GAME
Definition: defines.h:61
int hitsSplash[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:95
level_locals_t level
Definition: g_main.cpp:38
int hitsSplashDamage[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:96
int HP
Definition: g_edict.h:89
Inventory inv
Definition: chr_shared.h:411