UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_cmds.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2020 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
chrScoreGlobal_t score
Definition: chr_shared.h:387
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
bool hasTouch() const
Definition: g_edict.h:324
#define TEAM_PHALANX
Definition: q_shared.h:62
cvar_t * m_rage
Definition: g_main.cpp:107
#define TEAM_ALIEN
Definition: q_shared.h:63
int getAmmoLeft() const
Definition: inv_shared.h:466
#define PRINT_CHAT
Definition: defines.h:106
unsigned num_stuns[MAX_TEAMS+1][MAX_TEAMS]
Definition: g_local.h:114
#define _(String)
Definition: cl_shared.h:43
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
int hitsSplash[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:95
character_t chr
Definition: g_edict.h:116
bool(* use)(Edict *self, Edict *activator)
Definition: g_edict.h:154
float value
Definition: cvar.h:80
const objDef_t * def(void) const
Definition: inv_shared.h:469
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
int integer
Definition: cvar.h:81
chrScoreMission_t * scoreMission
Definition: chr_shared.h:388
void G_MoraleBehaviour(int team)
Applies morale behaviour on actors.
Definition: g_morale.cpp:156
int hits[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:89
unsigned num_kills[MAX_TEAMS+1][MAX_TEAMS]
Definition: g_local.h:113
int getTeam() const
Definition: g_edict.h:269
item instance data, with linked list capability
Definition: inv_shared.h:402
Match related functions.
int hitsSplashDamage[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:96
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:82
int firedSplash[SKILL_NUM_TYPES]
Definition: chr_shared.h:92
game_import_t gi
Definition: g_main.cpp:39
#define GET_TU(ab, md)
Definition: q_shared.h:291
int getWeight() const
Get the weight of the items in the given inventory (excluding those in temp containers).
Definition: inv_shared.cpp:937
cvar_t * mor_panic
Definition: g_main.cpp:103
An Edict of type Actor.
Definition: g_edict.h:348
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
#define Q_strcasecmp(a, b)
Definition: shared.h:131
Item * getNextItem(const Item *prev) const
Definition: inv_shared.cpp:671
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
#define GET_ENCUMBRANCE_PENALTY(weight, max)
Definition: q_shared.h:287
int skillKills[SKILL_NUM_TYPES]
Definition: chr_shared.h:99
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
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
Definition: inv_shared.cpp:722
Local definitions for game module.
functions to handle the storage and lifecycle of all edicts in the game module.
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
bool(* destroy)(Edict *self)
Definition: g_edict.h:155
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
const char *IMPORT * Cmd_Args(void)
Inventory inv
Definition: chr_shared.h:392
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 getX() const
Definition: inv_shared.h:454
static bool G_CheckFlood(Player &player)
Check whether the user can talk.
Definition: g_cmds.cpp:65
#define PRINT_CONSOLE
Definition: defines.h:108
char name[MAX_VAR]
Definition: chr_shared.h:371
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's number.
Definition: g_edicts.cpp:83
#define NONE
Definition: defines.h:68
int fired[SKILL_NUM_TYPES]
Definition: chr_shared.h:86
cvar_t * mor_shaken
Definition: g_main.cpp:102
Definition: g_edict.h:45
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:83
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
const char *IMPORT * Cmd_Argv(int n)
cvar_t * flood_persecond
Definition: g_main.cpp:122
const char * id
Definition: inv_shared.h:268
Interface for g_client.cpp.
int getY() const
Definition: inv_shared.h:457
#define DEBUG_GAME
Definition: defines.h:61
level_locals_t level
Definition: g_main.cpp:38
int HP
Definition: g_edict.h:89
float time
Definition: g_local.h:82