1 /**
2  * @file
3  * @brief Player commands.
4  */
5 
6 /*
7 All original material Copyright (C) 2002-2013 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 
G_Players_f(const Player & player)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 
62 /**
63  * @brief Check whether the user can talk
64  */
G_CheckFlood(Player & player)65 static bool G_CheckFlood (Player &player)
66 {
67 	int i;
68 
69 	if (flood_msgs->integer) {
70 		if (level.time < player.pers.flood_locktill) {
71 			G_ClientPrintf(player, PRINT_CHAT, _("You can't talk for %d more seconds\n"), (int)(player.pers.flood_locktill - level.time));
72 			return true;
73 		}
74 		i = player.pers.flood_whenhead - flood_msgs->value + 1;
75 		if (i < 0)
76 			i = (sizeof(player.pers.flood_when)/sizeof(player.pers.flood_when[0])) + i;
77 		if (player.pers.flood_when[i] && level.time - player.pers.flood_when[i] < flood_persecond->value) {
78 			player.pers.flood_locktill = level.time + flood_waitdelay->value;
79 			G_ClientPrintf(player, PRINT_CHAT, _("Flood protection: You can't talk for %d seconds.\n"), flood_waitdelay->integer);
80 			return true;
81 		}
82 		player.pers.flood_whenhead = (player.pers.flood_whenhead + 1) %
83 				(sizeof(player.pers.flood_when)/sizeof(player.pers.flood_when[0]));
84 		player.pers.flood_when[player.pers.flood_whenhead] = level.time;
85 	}
86 	return false;
87 }
88 
G_Say_f(Player & player,bool arg0,bool team)89 static void G_Say_f (Player &player, bool arg0, bool team)
90 {
91 	if (gi.Cmd_Argc() < 2 && !arg0)
92 		return;
93 
94 	if (G_CheckFlood(player))
95 		return;
96 
97 	char text[256];
98 	if (arg0) {
99 		Com_sprintf(text, sizeof(text), "%s %s", gi.Cmd_Argv(0), gi.Cmd_Args());
100 	} else {
101 		Com_sprintf(text, sizeof(text), "%s", gi.Cmd_Args());
102 	}
103 
104 	/* strip quotes */
105 	char* s = text;
106 	if (s[0] == '"' && s[strlen(s) - 1] == '"') {
107 		s[strlen(s) - 1] = '\0';
108 		s++;
109 	}
110 
111 	if (sv_dedicated->integer) {
112 		if (!team)
113 			gi.DPrintf("%s: %s\n", player.pers.netname, s);
114 		else
115 			gi.DPrintf("^B%s (team): %s\n", player.pers.netname, s);
116 	}
117 
118 	Player *p = nullptr;
119 	while ((p = G_PlayerGetNextActiveHuman(p))) {
120 		if (team && p->getTeam() != player.getTeam())
121 			continue;
122 		if (!team)
123 			G_ClientPrintf(*p, PRINT_CHAT, "%s: %s\n", player.pers.netname, s);
124 		else
125 			G_ClientPrintf(*p, PRINT_CHAT, "^B%s (team): %s\n", player.pers.netname, s);
126 	}
127 }
128 
129 #ifdef DEBUG
130 /**
131  * @brief This function does not add statistical values. Because there is no attacker.
132  * The same counts for morale states - they are not affected.
133  * @note: This is a debug function to let a whole team die
134  */
G_KillTeam_f(void)135 static void G_KillTeam_f (void)
136 {
137 	/* default is to kill all teams */
138 	int teamToKill = -1;
139 	Edict* ent = nullptr;
140 	int amount = -1;
141 
142 	/* with a parameter we will be able to kill a specific team */
143 	if (gi.Cmd_Argc() >= 2) {
144 		teamToKill = atoi(gi.Cmd_Argv(1));
145 		if (gi.Cmd_Argc() == 3)
146 			amount = atoi(gi.Cmd_Argv(2));
147 	}
148 
149 	Com_DPrintf(DEBUG_GAME, "G_KillTeam: kill team %i\n", teamToKill);
150 
151 	if (teamToKill >= 0) {
152 		while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, teamToKill))) {
153 			if (amount == 0)
154 				break;
155 			/* die */
156 			ent->HP = 0;
157 			G_ActorDieOrStun(ent, nullptr);
158 
159 			if (teamToKill == TEAM_ALIEN)
160 				level.num_kills[TEAM_PHALANX][TEAM_ALIEN]++;
161 			else
162 				level.num_kills[TEAM_ALIEN][teamToKill]++;
163 			amount--;
164 		}
165 	}
166 
167 	/* check for win conditions */
168 	G_MatchEndCheck();
169 }
170 
171 /**
172  * @brief Stun all members of a giben team.
173  */
G_StunTeam_f(void)174 static void G_StunTeam_f (void)
175 {
176 	/* default is to kill all teams */
177 	int teamToKill = -1;
178 	Edict* ent = nullptr;
179 
180 	/* with a parameter we will be able to kill a specific team */
181 	if (gi.Cmd_Argc() == 2)
182 		teamToKill = atoi(gi.Cmd_Argv(1));
183 
184 	if (teamToKill >= 0) {
185 		while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, teamToKill))) {
186 			/* stun */
187 			G_ActorDieOrStun(ent, nullptr);
188 
189 			if (teamToKill == TEAM_ALIEN)
190 				level.num_stuns[TEAM_PHALANX][TEAM_ALIEN]++;
191 			else
192 				level.num_stuns[TEAM_ALIEN][teamToKill]++;
193 		}
194 	}
195 
196 	/* check for win conditions */
197 	G_MatchEndCheck();
198 }
199 
200 /**
201  * @brief Prints all mission-score entries of all team members.
202  * @note Console command: debug_listscore
203  */
G_ListMissionScore_f(void)204 static void G_ListMissionScore_f (void)
205 {
206 	int team = -1;
207 	Edict* ent = nullptr;
208 	int i, j;
209 
210 	/* With a parameter we will be able to get the info for a specific team */
211 	if (gi.Cmd_Argc() == 2) {
212 		team = atoi(gi.Cmd_Argv(1));
213 	} else {
214 		gi.DPrintf("Usage: %s <teamnumber>\n", gi.Cmd_Argv(0));
215 		return;
216 	}
217 
218 	while ((ent = G_EdictsGetNextLivingActor(ent))) {
219 		if (team >= 0 && ent->team != team)
220 			continue;
221 
222 		assert(ent->chr.scoreMission);
223 
224 		gi.DPrintf("Soldier: %s\n", ent->chr.name);
225 
226 		/* ===================== */
227 		gi.DPrintf("  Move: Normal=%i Crouched=%i\n", ent->chr.scoreMission->movedNormal, ent->chr.scoreMission->movedCrouched);
228 
229 		gi.DPrintf("  Kills:");
230 		for (i = 0; i < KILLED_NUM_TYPES; i++) {
231 			gi.DPrintf(" %i", ent->chr.scoreMission->kills[i]);
232 		}
233 		gi.DPrintf("\n");
234 
235 		gi.DPrintf("  Stuns:");
236 		for (i = 0; i < KILLED_NUM_TYPES; i++) {
237 			gi.DPrintf(" %i", ent->chr.scoreMission->stuns[i]);
238 		}
239 		gi.DPrintf("\n");
240 
241 		/* ===================== */
242 		gi.DPrintf("  Fired:");
243 		for (i = 0; i < SKILL_NUM_TYPES; i++) {
244 			gi.DPrintf(" %i", ent->chr.scoreMission->fired[i]);
245 		}
246 		gi.DPrintf("\n");
247 
248 		gi.DPrintf("  Hits:\n");
249 		for (i = 0; i < SKILL_NUM_TYPES; i++) {
250 			gi.DPrintf("    Skill%i: ",i);
251 			for (j = 0; j < KILLED_NUM_TYPES; j++) {
252 				gi.DPrintf(" %i", ent->chr.scoreMission->hits[i][j]);
253 			}
254 			gi.DPrintf("\n");
255 		}
256 
257 		/* ===================== */
258 		gi.DPrintf("  Fired Splash:");
259 		for (i = 0; i < SKILL_NUM_TYPES; i++) {
260 			gi.DPrintf(" %i", ent->chr.scoreMission->firedSplash[i]);
261 		}
262 		gi.DPrintf("\n");
263 
264 		gi.DPrintf("  Hits Splash:\n");
265 		for (i = 0; i < SKILL_NUM_TYPES; i++) {
266 			gi.DPrintf("    Skill%i: ",i);
267 			for (j = 0; j < KILLED_NUM_TYPES; j++) {
268 				gi.DPrintf(" %i", ent->chr.scoreMission->hitsSplash[i][j]);
269 			}
270 			gi.DPrintf("\n");
271 		}
272 
273 		gi.DPrintf("  Splash Damage:\n");
274 		for (i = 0; i < SKILL_NUM_TYPES; i++) {
275 			gi.DPrintf("    Skill%i: ",i);
276 			for (j = 0; j < KILLED_NUM_TYPES; j++) {
277 				gi.DPrintf(" %i", ent->chr.scoreMission->hitsSplashDamage[i][j]);
278 			}
279 			gi.DPrintf("\n");
280 		}
281 
282 		/* ===================== */
283 		gi.DPrintf("  Kills per skill:");
284 		for (i = 0; i < SKILL_NUM_TYPES; i++) {
285 			gi.DPrintf(" %i", ent->chr.scoreMission->skillKills[i]);
286 		}
287 		gi.DPrintf("\n");
288 
289 		/* ===================== */
290 		gi.DPrintf("  Heal (received): %i\n", ent->chr.scoreMission->heal);
291 	}
292 }
293 
294 /**
295  * @brief Debug function to print a player's inventory
296  */
G_InvList_f(const Player & player)297 void G_InvList_f (const Player &player)
298 {
299 	Edict* ent = nullptr;
300 
301 	gi.DPrintf("Print inventory for '%s'\n", player.pers.netname);
302 	while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, player.getTeam()))) {
303 		gi.DPrintf("actor: '%s'\n", ent->chr.name);
304 
305 		const Container* cont = nullptr;
306 		while ((cont = ent->chr.inv.getNextCont(cont, true))) {
307 			Com_Printf("Container: %i\n", cont->id);
308 			Item* item = nullptr;
309 			while ((item = cont->getNextItem(item))) {
310 				Com_Printf(".. item.def(): %i, item.ammo: %i, item.ammoLeft: %i, x: %i, y: %i\n",
311 						(item->def() ? item->def()->idx : NONE), (item->ammoDef() ? item->ammoDef()->idx : NONE),
312 						item->getAmmoLeft(), item->getX(), item->getY());
313 				if (item->def())
314 					Com_Printf(".... weapon: %s\n", item->def()->id);
315 				if (item->ammoDef())
316 					Com_Printf(".... ammo:   %s (%i)\n", item->ammoDef()->id, item->getAmmoLeft());
317 			}
318 		}
319 	}
320 }
321 
G_TouchEdict_f(void)322 static void G_TouchEdict_f (void)
323 {
324 	Edict* e, *ent;
325 	int i;
326 
327 	if (gi.Cmd_Argc() < 2) {
328 		gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
329 		return;
330 	}
331 
332 	i = atoi(gi.Cmd_Argv(1));
333 	if (!G_EdictsIsValidNum(i))
334 		return;
335 
336 	e = G_EdictsGetByNum(i);
337 	if (!e->touch) {
338 		gi.DPrintf("No touch function for entity %s\n", e->classname);
339 		return;
340 	}
341 
342 	ent = G_EdictsGetNextLivingActor(nullptr);
343 	if (!ent)
344 		return;	/* didn't find any */
345 
346 	gi.DPrintf("Call touch function for %s\n", e->classname);
347 	e->touch(e, ent);
348 }
349 
G_UseEdict_f(void)350 static void G_UseEdict_f (void)
351 {
352 	Edict* e;
353 	int i;
354 
355 	if (gi.Cmd_Argc() < 2) {
356 		gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
357 		return;
358 	}
359 
360 	i = atoi(gi.Cmd_Argv(1));
361 	if (!G_EdictsIsValidNum(i)) {
362 		gi.DPrintf("No entity with number %i\n", i);
363 		return;
364 	}
365 
366 	e = G_EdictsGetByNum(i);
367 	if (!e->use) {
368 		gi.DPrintf("No use function for entity %s\n", e->classname);
369 		return;
370 	}
371 
372 	gi.DPrintf("Call use function for %s\n", e->classname);
373 	e->use(e, nullptr);
374 }
375 
G_DestroyEdict_f(void)376 static void G_DestroyEdict_f (void)
377 {
378 	Edict* e;
379 	int i;
380 
381 	if (gi.Cmd_Argc() < 2) {
382 		gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
383 		return;
384 	}
385 
386 	i = atoi(gi.Cmd_Argv(1));
387 	if (!G_EdictsIsValidNum(i))
388 		return;
389 
390 	e = G_EdictsGetByNum(i);
391 	if (!e->destroy) {
392 		gi.DPrintf("No destroy function for entity %s\n", e->classname);
393 		return;
394 	}
395 
396 	gi.DPrintf("Call destroy function for %s\n", e->classname);
397 	e->destroy(e);
398 }
399 
G_StateChange_f(void)400 static void G_StateChange_f (void)
401 {
402 	if (gi.Cmd_Argc() < 3) {
403 		gi.DPrintf("Usage: %s <entnum> <state>\n States are: panic, rage, shaken", gi.Cmd_Argv(0));
404 		return;
405 	}
406 
407 	const int entnum = atoi(gi.Cmd_Argv(1));
408 	Edict* e = G_EdictsGetByNum(entnum);
409 	if (e == nullptr)
410 		return;
411 
412 	const char* state = gi.Cmd_Argv(2);
413 	if (Q_strcasecmp(state, "panic")) {
414 		e->morale = mor_panic->integer / 2;
415 	} else if (Q_strcasecmp(state, "shaken")) {
416 		e->morale = mor_shaken->integer / 2;
417 	} else if (Q_strcasecmp(state, "rage")) {
418 		e->morale = m_rage->integer / 2;
419 	} else {
420 		e->morale = 0;
421 	}
422 
423 	G_MoraleBehaviour(e->team);
424 }
425 #endif
426 
G_ClientCommand(Player & player)427 void G_ClientCommand (Player &player)
428 {
429 	const char* cmd;
430 
431 	if (!player.isInUse())
432 		return;					/* not fully in game yet */
433 
434 	cmd = gi.Cmd_Argv(0);
435 
436 	if (Q_strcasecmp(cmd, "players") == 0)
437 		G_Players_f(player);
438 	else if (Q_strcasecmp(cmd, "say") == 0)
439 		G_Say_f(player, false, false);
440 	else if (Q_strcasecmp(cmd, "say_team") == 0)
441 		G_Say_f(player, false, true);
442 #ifdef DEBUG
443 	else if (Q_strcasecmp(cmd, "debug_actorinvlist") == 0)
444 		G_InvList_f(player);
445 	else if (Q_strcasecmp(cmd, "debug_killteam") == 0)
446 		G_KillTeam_f();
447 	else if (Q_strcasecmp(cmd, "debug_stunteam") == 0)
448 		G_StunTeam_f();
449 	else if (Q_strcasecmp(cmd, "debug_listscore") == 0)
450 		G_ListMissionScore_f();
451 	else if (Q_strcasecmp(cmd, "debug_edicttouch") == 0)
452 		G_TouchEdict_f();
453 	else if (Q_strcasecmp(cmd, "debug_edictuse") == 0)
454 		G_UseEdict_f();
455 	else if (Q_strcasecmp(cmd, "debug_edictdestroy") == 0)
456 		G_DestroyEdict_f();
457 	else if (Q_strcasecmp(cmd, "debug_statechange") == 0)
458 		G_StateChange_f();
459 #endif
460 	else
461 		/* anything that doesn't match a command will be a chat */
462 		G_Say_f(player, true, false);
463 }
464