UFO: Alien Invasion
Loading...
Searching...
No Matches
g_cmds.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Original file from Quake 2 v3.21: quake2-2.31/game/g_cmds.c
10Copyright (C) 1997-2001 Id Software, Inc.
11
12This program is free software; you can redistribute it and/or
13modify it under the terms of the GNU General Public License
14as published by the Free Software Foundation; either version 2
15of the License, or (at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, 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
36static 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
65static 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
87static 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
133static 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)
158 level.num_kills[TEAM_PHALANX][TEAM_ALIEN]++;
159 else
160 level.num_kills[TEAM_ALIEN][teamToKill]++;
161 amount--;
162 }
163 }
164
165 /* check for win conditions */
167}
168
172static 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)
188 level.num_stuns[TEAM_PHALANX][TEAM_ALIEN]++;
189 else
190 level.num_stuns[TEAM_ALIEN][teamToKill]++;
191 }
192 }
193
194 /* check for win conditions */
196}
197
202static 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
294void 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
327static 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
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
352static 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
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
375static 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
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
396static 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
420}
421#endif
422
423void 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}
@ ABILITY_POWER
Definition chr_shared.h:37
@ ABILITY_SPEED
Definition chr_shared.h:38
@ SKILL_NUM_TYPES
Definition chr_shared.h:51
@ KILLED_NUM_TYPES
Definition chr_shared.h:32
#define _(String)
Definition cl_shared.h:44
An Edict of type Actor.
Definition g_edict.h:348
Item * getNextItem(const Item *prev) const
character_t chr
Definition g_edict.h:116
void setMorale(int mor)
Definition g_edict.h:311
const char * classname
Definition g_edict.h:67
int HP
Definition g_edict.h:89
bool(* use)(Edict *self, Edict *activator)
Definition g_edict.h:154
int getTeam() const
Definition g_edict.h:269
bool callTouch(Edict *activator)
Definition g_edict.h:329
bool(* destroy)(Edict *self)
Definition g_edict.h:155
bool hasTouch() const
Definition g_edict.h:324
int getWeight() const
Get the weight of the items in the given inventory (excluding those in temp containers).
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * ammoDef(void) const
Definition inv_shared.h:460
int getAmmoLeft() const
Definition inv_shared.h:466
int getX() const
Definition inv_shared.h:454
const objDef_t * def(void) const
Definition inv_shared.h:469
int getY() const
Definition inv_shared.h:457
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
cvar_t * sv_dedicated
Definition common.cpp:51
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define PRINT_CHAT
Definition defines.h:106
#define PRINT_CONSOLE
Definition defines.h:108
#define NONE
Definition defines.h:68
#define DEBUG_GAME
Definition defines.h:61
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
Player * G_PlayerGetNextActiveHuman(Player *lastPlayer)
Iterate through the list of players.
Definition g_client.cpp:110
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition g_client.cpp:206
Interface for g_client.cpp.
static void G_Say_f(Player &player, bool arg0, bool team)
Definition g_cmds.cpp:87
void G_ClientCommand(Player &player)
Definition g_cmds.cpp:423
static void G_Players_f(const Player &player)
Definition g_cmds.cpp:36
static bool G_CheckFlood(Player &player)
Check whether the user can talk.
Definition g_cmds.cpp:65
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition g_edicts.cpp:216
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition g_edicts.cpp:196
bool G_EdictsIsValidNum(const int num)
Check if the given number could point to an existing entity.
Definition g_edicts.cpp:72
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition g_edicts.cpp:83
functions to handle the storage and lifecycle of all edicts in the game module.
Local definitions for game module.
cvar_t * flood_msgs
Definition g_main.cpp:121
cvar_t * flood_persecond
Definition g_main.cpp:122
cvar_t * mor_shaken
Definition g_main.cpp:102
level_locals_t level
Definition g_main.cpp:38
cvar_t * mor_panic
Definition g_main.cpp:103
cvar_t * m_rage
Definition g_main.cpp:107
game_import_t gi
Definition g_main.cpp:39
void G_MoraleBehaviour(int team)
Applies morale behaviour on actors.
Definition g_morale.cpp:156
cvar_t * flood_waitdelay
Definition g_main.cpp:123
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
Match related functions.
Shared parsing functions.
#define TEAM_PHALANX
Definition q_shared.h:62
#define WEIGHT_NORMAL_PENALTY
Definition q_shared.h:283
#define GET_TU(ab, md)
Definition q_shared.h:291
#define TEAM_ALIEN
Definition q_shared.h:63
#define GET_ENCUMBRANCE_PENALTY(weight, max)
Definition q_shared.h:287
#define WEIGHT_HEAVY_PENALTY
Definition q_shared.h:284
QGL_EXTERN GLuint count
Definition r_gl.h:99
QGL_EXTERN GLint i
Definition r_gl.h:113
#define Q_strcasecmp(a, b)
Definition shared.h:131
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition shared.cpp:475
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
chrScoreMission_t * scoreMission
Definition chr_shared.h:407
chrScoreGlobal_t score
Definition chr_shared.h:406
char name[MAX_VAR]
Definition chr_shared.h:390
Inventory inv
Definition chr_shared.h:411
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
int hitsSplashDamage[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition chr_shared.h:96
int fired[SKILL_NUM_TYPES]
Definition chr_shared.h:86
int firedSplash[SKILL_NUM_TYPES]
Definition chr_shared.h:92
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 stuns[KILLED_NUM_TYPES]
Definition chr_shared.h:83
int skillKills[SKILL_NUM_TYPES]
Definition chr_shared.h:99
int kills[KILLED_NUM_TYPES]
Definition chr_shared.h:82
const char * id
Definition inv_shared.h:268