1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 /* interface header */
14 #include "commands.h"
15
16 /* system implementation headers */
17 #ifndef _WIN32
18 # include <sys/types.h>
19 # include <dirent.h>
20 #endif
21 #include <string>
22 #include <vector>
23 #include <cstdlib>
24
25 /* common implementation headers */
26 #include "BZDBCache.h"
27 #include "TextUtils.h"
28 #include "FileManager.h"
29 #include "DirectoryNames.h"
30 #include "version.h"
31 #include "SceneRenderer.h"
32 #include "bzglob.h"
33 #include "BzPNG.h"
34
35 /* local implementation headers */
36 #include "LocalPlayer.h"
37 #include "sound.h"
38 #include "ComposeDefaultKey.h"
39 #include "SilenceDefaultKey.h"
40 #include "ServerCommandKey.h"
41 #include "Roaming.h"
42 #include "playing.h"
43 #include "HUDRenderer.h"
44 #include "HUDui.h"
45
46 /** jump
47 */
48 static std::string cmdJump(const std::string&,
49 const CommandManager::ArgList& args, bool*);
50
51 /** fire weapon
52 */
53 static std::string cmdFire(const std::string&,
54 const CommandManager::ArgList& args, bool*);
55
56 /** drop a flag
57 */
58 static std::string cmdDrop(const std::string&,
59 const CommandManager::ArgList& args, bool*);
60
61 /** toggle radar visibility
62 */
63 static std::string cmdToggleRadar(const std::string&,
64 const CommandManager::ArgList& args, bool*);
65
66 /** toggle console visibility
67 */
68 static std::string cmdToggleConsole(const std::string&,
69 const CommandManager::ArgList& args, bool*);
70
71 /** toggle flags
72 */
73 static std::string cmdToggleFlags(const std::string&,
74 const CommandManager::ArgList& args, bool*);
75
76 /** identify to a server
77 */
78 static std::string cmdIdentify(const std::string&,
79 const CommandManager::ArgList& args, bool*);
80
81 /** restart/respawn
82 */
83 static std::string cmdRestart(const std::string&,
84 const CommandManager::ArgList& args, bool*);
85
86 /** self-destruct
87 */
88 static std::string cmdDestruct(const std::string&,
89 const CommandManager::ArgList& args, bool*);
90
91 /** pause
92 */
93 static std::string cmdPause(const std::string&,
94 const CommandManager::ArgList& args, bool*);
95
96 /** select tabbed message
97 */
98 static std::string cmdMessagePanel(const std::string&,
99 const CommandManager::ArgList& args, bool*);
100
101 /** toggle auto-pilot
102 */
103 static std::string cmdAutoPilot(const std::string&,
104 const CommandManager::ArgList& args, bool*);
105
106 /** change radar scale
107 */
108 static std::string cmdRadarZoom(const std::string&,
109 const CommandManager::ArgList& args, bool*);
110
111 /** change view angle
112 */
113 static std::string cmdViewZoom(const std::string&,
114 const CommandManager::ArgList& args, bool*);
115
116 /** send
117 */
118 static std::string cmdSend(const std::string&,
119 const CommandManager::ArgList& args, bool*);
120
121 /** capture a screenshot
122 */
123 static std::string cmdScreenshot(const std::string&,
124 const CommandManager::ArgList& args, bool*);
125
126 /** time
127 */
128 static std::string cmdTime(const std::string&,
129 const CommandManager::ArgList& args, bool*);
130
131 /** roam
132 */
133 static std::string cmdRoam(const std::string&,
134 const CommandManager::ArgList& args, bool*);
135
136 /** silence another player
137 */
138 static std::string cmdSilence(const std::string&,
139 const CommandManager::ArgList& args, bool*);
140
141 /** perform a server command
142 */
143 static std::string cmdServerCommand(const std::string&,
144 const CommandManager::ArgList& args,
145 bool*);
146
147 /** scroll the chat panel
148 */
149 static std::string cmdScrollPanel(const std::string&,
150 const CommandManager::ArgList& args, bool*);
151
152 /** hunt a player
153 */
154 static std::string cmdHunt(const std::string&,
155 const CommandManager::ArgList& args, bool*);
156
157 /** hunt another player
158 */
159 static std::string cmdAddHunt(const std::string&,
160 const CommandManager::ArgList& args, bool*);
161
162 /** iconify window
163 */
164 static std::string cmdIconify(const std::string&,
165 const CommandManager::ArgList& args, bool*);
166
167 /** mouse box size flags
168 */
169 static std::string cmdMouseBox(const std::string&,
170 const CommandManager::ArgList& args, bool*);
171
172 /** toggle mouse capture
173 */
174 static std::string cmdMouseGrab(const std::string&,
175 const CommandManager::ArgList& args, bool*);
176
177 /** toggle Full Screen
178 */
179 static std::string cmdToggleFS(const std::string&,
180 const CommandManager::ArgList& args, bool*);
181
182 /** cycle to the next radar zoom level
183 */
184 static std::string cmdCycleRadar(const std::string&,
185 const CommandManager::ArgList& args, bool*);
186
187 /** cycle to the next panel tab
188 */
189 static std::string cmdCyclePanel(const std::string&,
190 const CommandManager::ArgList& args, bool*);
191
192
193 const struct CommandListItem commandList[] =
194 {
195 { "fire", &cmdFire, "fire: fire a shot" },
196 { "jump", &cmdJump, "jump: make player jump" },
197 { "drop", &cmdDrop, "drop: drop the current flag" },
198 { "identify", &cmdIdentify, "identify: identify/lock-on-to player in view" },
199 { "restart", &cmdRestart, "restart: restart playing" },
200 { "destruct", &cmdDestruct, "destruct: self destruct" },
201 { "pause", &cmdPause, "pause: pause/resume" },
202 { "send", &cmdSend, "send {all|team|nemesis|recipient|admin}: start composing a message" },
203 { "screenshot", &cmdScreenshot, "screenshot: take a screenshot" },
204 { "time", &cmdTime, "time {forward|backward}: adjust the current time" },
205 { "roam", &cmdRoam, "roam {zoom|cycle} <args>: roam around" },
206 { "silence", &cmdSilence, "silence: silence/unsilence a player" },
207 { "servercommand", &cmdServerCommand, "servercommand: quick admin" },
208 { "scrollpanel", &cmdScrollPanel, "scrollpanel {up|down}: scroll message panel" },
209 { "hunt", &cmdHunt, "hunt: hunt a specific player" },
210 { "addhunt", &cmdAddHunt, "addhunt: add/modify hunted player(s)" },
211 { "iconify", &cmdIconify, "iconify: iconify & pause bzflag" },
212 { "mousebox", &cmdMouseBox, "mousebox <size>: change the mousebox size"},
213 { "mousegrab", &cmdMouseGrab, "mousegrab: toggle exclusive mouse mode" },
214 { "fullscreen", &cmdToggleFS, "fullscreen: toggle fullscreen mode" },
215 { "autopilot", &cmdAutoPilot, "autopilot: set/unset autopilot bot code" },
216 { "radarZoom", &cmdRadarZoom, "radarZoom {in/out}: change maxRadar range"},
217 { "viewZoom", &cmdViewZoom, "viewZoom {in/out/toggle}: change view angle" },
218 { "messagepanel", &cmdMessagePanel, "messagepanel {all|chat|server|misc}: set message tab" },
219 { "toggleRadar", &cmdToggleRadar, "toggleRadar: toggle radar visibility" },
220 { "toggleConsole", &cmdToggleConsole, "toggleConsole: toggle console visibility" },
221 { "toggleFlags", &cmdToggleFlags, "toggleFlags {main|radar}: turn off/on field radar flags" },
222 { "cycleRadar", &cmdCycleRadar, "cycleRadar {level1 [level2 ...] [off]}: cycle to the next radar zoom level" },
223 { "cyclePanel", &cmdCyclePanel, "cyclePanel {left[_off]|right[_off]}: cycle to the previous or next message panel tab" }
224 };
225
226
cmdToggleFS(const std::string &,const CommandManager::ArgList & args,bool *)227 static std::string cmdToggleFS(const std::string&,
228 const CommandManager::ArgList& args, bool*)
229 {
230 if (args.size() != 0)
231 return "usage: fullscreen";
232 mainWindow->toggleFullscreen();
233 mainWindow->getWindow()->callResizeCallbacks();
234 return std::string();
235 }
236
cmdMouseBox(const std::string &,const CommandManager::ArgList & args,bool *)237 static std::string cmdMouseBox(const std::string&,
238 const CommandManager::ArgList& args, bool*)
239 {
240 if (args.size() != 1)
241 return "usage: mousebox <size>";
242 const char* start = args[0].c_str();
243 char* end;
244 const int value = (int) strtol(args[0].c_str(), &end, 10);
245 if (start == end)
246 return "bad number";
247 RENDERER.setMaxMotionFactor(value);
248 return std::string();
249 }
250
cmdMouseGrab(const std::string &,const CommandManager::ArgList & args,bool *)251 static std::string cmdMouseGrab(const std::string&,
252 const CommandManager::ArgList& args, bool*)
253 {
254 if (args.size() != 0)
255 return "usage: mousegrab";
256 const bool grabbing = !(BZDB.isTrue("mousegrab"));
257 BZDB.set("mousegrab", grabbing ? "true" : "false");
258 mainWindow->enableGrabMouse(grabbing);
259 return std::string();
260 }
261
cmdIconify(const std::string &,const CommandManager::ArgList & args,bool *)262 static std::string cmdIconify(const std::string&,
263 const CommandManager::ArgList& args, bool*)
264 {
265 if (args.size() != 0)
266 return "usage: iconify";
267
268 if (!BZDB.isTrue("Win32NoMin"))
269 mainWindow->iconify();
270 return std::string();
271 }
272
cmdJump(const std::string &,const CommandManager::ArgList & args,bool *)273 static std::string cmdJump(const std::string&,
274 const CommandManager::ArgList& args, bool*)
275 {
276 if (args.size() != 0)
277 return "usage: jump";
278 LocalPlayer *myTank = LocalPlayer::getMyTank();
279 if (myTank != NULL)
280 myTank->setJump();
281 return std::string();
282 }
283
cmdToggleFlags(const std::string &,const CommandManager::ArgList & args,bool *)284 static std::string cmdToggleFlags(const std::string&, const
285 CommandManager::ArgList& args, bool*)
286 {
287 if (args.size() != 1)
288 return "usage: main|radar";
289 if (args[0] == "main")
290 {
291 CMDMGR.run("toggle displayMainFlags");
292 warnAboutMainFlags();
293 }
294 else if (args[0] == "radar")
295 {
296 CMDMGR.run("toggle displayRadarFlags");
297 warnAboutRadarFlags();
298 }
299 else
300 return "usage: main|radar";
301
302 return std::string();
303 }
304
cmdToggleRadar(const std::string &,const CommandManager::ArgList & args,bool *)305 static std::string cmdToggleRadar(const std::string&,
306 const CommandManager::ArgList& args, bool*)
307 {
308 if (args.size() != 0)
309 return "usage: toggleRadar";
310
311 CMDMGR.run("toggle displayRadar");
312
313 warnAboutRadar();
314
315 return std::string();
316 }
317
cmdToggleConsole(const std::string &,const CommandManager::ArgList & args,bool *)318 static std::string cmdToggleConsole(const std::string&,
319 const CommandManager::ArgList& args, bool*)
320 {
321 if (args.size() != 0)
322 return "usage: toggleConsole";
323
324 CMDMGR.run("toggle displayConsole");
325
326 warnAboutConsole();
327
328 return std::string();
329 }
330
331
cmdFire(const std::string &,const CommandManager::ArgList & args,bool *)332 static std::string cmdFire(const std::string&,
333 const CommandManager::ArgList& args, bool*)
334 {
335 if (args.size() != 0)
336 return "usage: fire";
337 LocalPlayer *myTank = LocalPlayer::getMyTank();
338 if (fireButton && myTank != NULL && myTank->isAlive()
339 && myTank->getTeam() != ObserverTeam)
340 myTank->fireShot();
341 return std::string();
342 }
343
cmdDrop(const std::string &,const CommandManager::ArgList & args,bool *)344 static std::string cmdDrop(const std::string&,
345 const CommandManager::ArgList& args, bool*)
346 {
347 if (args.size() != 0)
348 return "usage: drop";
349 LocalPlayer *myTank = LocalPlayer::getMyTank();
350 if (myTank != NULL)
351 {
352 FlagType* flag = myTank->getFlag();
353 if ((flag != Flags::Null) && !myTank->isPaused() &&
354 (flag->endurance != FlagSticky) && !myTank->isPhantomZoned() &&
355 !(flag == Flags::OscillationOverthruster &&
356 myTank->getLocation() == LocalPlayer::InBuilding))
357 {
358 serverLink->sendDropFlag(myTank->getPosition());
359 // changed: on windows it may happen the MsgDropFlag
360 // never comes back to us, so we drop it right away
361 handleFlagDropped(myTank);
362 }
363 }
364 return std::string();
365 }
366
cmdIdentify(const std::string &,const CommandManager::ArgList & args,bool *)367 static std::string cmdIdentify(const std::string&,
368 const CommandManager::ArgList& args, bool*)
369 {
370 if (args.size() != 0)
371 return "usage: identify";
372 LocalPlayer *myTank = LocalPlayer::getMyTank();
373 if (myTank != NULL)
374 if (myTank->isAlive() && !myTank->isPaused())
375 setTarget();
376 return std::string();
377 }
378
cmdRestart(const std::string &,const CommandManager::ArgList & args,bool *)379 static std::string cmdRestart(const std::string&,
380 const CommandManager::ArgList& args, bool*)
381 {
382 if (args.size() != 0)
383 return "usage: restart";
384 LocalPlayer *myTank = LocalPlayer::getMyTank();
385 if (myTank != NULL)
386 if (!gameOver && !myTank->isSpawning() && (myTank->getTeam() != ObserverTeam) && !myTank->isAlive()
387 && !myTank->isExploding())
388 {
389 serverLink->sendAlive();
390 myTank->setSpawning(true);
391 CommandManager::ArgList zoomArgs;
392 std::string resetArg = "reset";
393 zoomArgs.push_back(resetArg);
394 cmdViewZoom("", zoomArgs,NULL);
395 }
396
397 return std::string();
398 }
399
cmdDestruct(const std::string &,const CommandManager::ArgList & args,bool *)400 static std::string cmdDestruct(const std::string&,
401 const CommandManager::ArgList& args, bool*)
402 {
403 if (args.size() != 0)
404 return "usage: destruct";
405 LocalPlayer *myTank = LocalPlayer::getMyTank();
406 if (myTank != NULL)
407 {
408 if (destructCountdown > 0.0f)
409 {
410 destructCountdown = 0.0f;
411 hud->setAlert(1, "Self Destruct cancelled", 1.5f, true);
412 }
413 else
414 {
415 destructCountdown = 5.0f;
416 char msgBuf[40];
417 sprintf(msgBuf, "Self Destructing in %d", (int)(destructCountdown + 0.99f));
418 hud->setAlert(1, msgBuf, 1.0f, false);
419 }
420 }
421 return std::string();
422 }
423
cmdPause(const std::string &,const CommandManager::ArgList & args,bool *)424 static std::string cmdPause(const std::string&,
425 const CommandManager::ArgList& args, bool*)
426 {
427 if (args.size() != 0)
428 return "usage: pause";
429
430 LocalPlayer *myTank = LocalPlayer::getMyTank();
431 if (!pausedByUnmap && myTank && myTank->isAlive() && !myTank->isAutoPilot())
432 {
433 if (myTank->isPaused())
434 {
435 // already paused, so unpause
436 myTank->setPause(false);
437 controlPanel->addMessage("Resumed");
438
439 // restore the sound
440 if (savedVolume != -1)
441 {
442 setSoundVolume(savedVolume);
443 savedVolume = -1;
444 }
445
446 // grab mouse
447 if (shouldGrabMouse())
448 mainWindow->grabMouse();
449
450 }
451 else if (pauseCountdown > 0.0f)
452 {
453 // player aborted pause
454 pauseCountdown = 0.0f;
455 hud->setAlert(1, "Pause cancelled", 1.5f, true);
456
457 }
458 else if (myTank->getLocation() == LocalPlayer::InBuilding)
459 {
460 // custom message when trying to pause while in a building
461 // (could get stuck on un-pause if flag is taken)
462 hud->setAlert(1, "Can't pause while inside a building", 1.0f, false);
463
464 }
465 else if (myTank->getLocation() == LocalPlayer::InAir)
466 {
467 // custom message when trying to pause when jumping/falling
468 hud->setAlert(1, "Can't pause when you are in the air", 1.0f, false);
469
470 }
471 else if (myTank->getLocation() != LocalPlayer::OnGround &&
472 myTank->getLocation() != LocalPlayer::OnBuilding)
473 {
474 // catch-all message when trying to pause when you should not
475 hud->setAlert(1, "Unable to pause right now", 1.0f, false);
476
477 }
478 else
479 {
480 // update the pause alert message
481 pauseCountdown = 5.0f;
482 char msgBuf[40];
483 sprintf(msgBuf, "Pausing in %d", (int) (pauseCountdown + 0.99f));
484 hud->setAlert(1, msgBuf, 1.0f, false);
485 }
486 }
487 return std::string();
488 }
489
cmdAutoPilot(const std::string &,const CommandManager::ArgList & args,bool *)490 static std::string cmdAutoPilot(const std::string&,
491 const CommandManager::ArgList& args, bool*)
492 {
493 if (args.size() != 0)
494 return "usage: autopilot";
495
496 // don't enable autopilot until we've fully joined and checked the value
497 // of the server-side _disableBots
498 if (! BZDB.isSet(StateDatabase::BZDB_DISABLEBOTS))
499 return std::string();
500
501 LocalPlayer *myTank = LocalPlayer::getMyTank();
502
503 if (!BZDB.isTrue(StateDatabase::BZDB_TANKWIDTH))
504 return std::string();
505
506 if ((myTank == NULL) || (myTank->getTeam() == ObserverTeam))
507 return std::string();
508
509 if (myTank->isAutoPilot())
510 {
511
512 myTank->activateAutoPilot(false);
513 hud->setAlert(0, "autopilot disabled", 1.0f, true);
514
515 // grab mouse
516 if (shouldGrabMouse()) mainWindow->grabMouse();
517
518 }
519 else if (BZDB.isTrue(StateDatabase::BZDB_DISABLEBOTS))
520 hud->setAlert(0, "autopilot not allowed on this server", 1.0f, true);
521 else
522 {
523
524 // don't enable the AutoPilot if you have within the last 5 secs
525 static TimeKeeper LastAutoPilotEnable = TimeKeeper::getSunGenesisTime();
526 if ((TimeKeeper::getCurrent() - LastAutoPilotEnable) > 5)
527 {
528 // reset timer
529 LastAutoPilotEnable = TimeKeeper::getCurrent();
530
531 // enable autopilot
532 myTank->activateAutoPilot();
533 hud->setAlert(0, "autopilot enabled", 1.0f, true);
534
535 // ungrab mouse
536 mainWindow->ungrabMouse();
537 }
538 else
539 controlPanel->addMessage("You may not enable the Autopilot more than once every five seconds.");
540
541 }
542
543 return std::string();
544 }
545
cmdRadarZoom(const std::string &,const CommandManager::ArgList & args,bool *)546 static std::string cmdRadarZoom(const std::string&,
547 const CommandManager::ArgList& args, bool*)
548 {
549 if (args.size() != 1)
550 return "usage: radarZoom {in|out}";
551
552 float range = BZDB.eval("displayRadarRange");
553
554 if (args[0] == "out")
555 {
556 range *= 1.05f;
557 if (range > 2.0f)
558 range = 2.0f;
559 BZDB.setFloat("displayRadarRange", range);
560 }
561 else if (args[0] == "in")
562 {
563 range /= 1.05f;
564 if (range < 0.005f)
565 range = 0.005f;
566 BZDB.setFloat("displayRadarRange", range);
567 }
568 else
569 return "usage: radarZoom {in|out}";
570
571 return std::string();
572 }
573
cmdViewZoom(const std::string &,const CommandManager::ArgList & args,bool *)574 static std::string cmdViewZoom(const std::string&,
575 const CommandManager::ArgList& args, bool*)
576 {
577 if (args.size() != 1)
578 return "usage: viewZoom {in|out|toggle}";
579
580 float fov = BZDB.eval("displayFOV");
581 float defFov = BZDB.eval("defaultFOV");
582
583 if (args[0] == "out")
584 {
585 fov += 1.0f;
586 if (fov > defFov)
587 fov = defFov;
588 BZDB.setFloat("displayFOV", fov);
589 }
590 else if (args[0] == "in")
591 {
592 fov -= 1.0f;
593 if (fov < 15.0f)
594 fov = 15.0f;
595 BZDB.setFloat("displayFOV", fov);
596 }
597 else if (args[0] == "toggle")
598 {
599 if (fov < 15.5f)
600 fov = defFov;
601 else
602 fov = 15.0f;
603 BZDB.setFloat("displayFOV", fov);
604 // also toggle the observer fov
605 if (ROAM.getZoom() != defFov)
606 ROAM.setZoom(defFov);
607 else
608 ROAM.setZoom(15.0f);
609 }
610 else if (args[0] == "reset")
611 {
612 fov = defFov;
613 ROAM.setZoom(defFov);
614 BZDB.setFloat("displayFOV", fov);
615 }
616 else
617 return "usage: viewZoom {in|out|toggle|reset}";
618
619 return std::string();
620 }
621
cmdMessagePanel(const std::string &,const CommandManager::ArgList & args,bool *)622 static std::string cmdMessagePanel(const std::string&,
623 const CommandManager::ArgList& args, bool*)
624 {
625 if (args.size() != 1)
626 return "usage: messagepanel {all|chat|server|misc}";
627
628 int oldMode = controlPanel->getMessagesMode();
629 int newMode;
630 if (args[0] == "all")
631 newMode = 0;
632 else if (args[0] == "chat")
633 newMode = 1;
634 else if (args[0] == "server")
635 newMode = 2;
636 else if (args[0] == "misc")
637 newMode = 3;
638 else
639 return "usage: messagepanel {all|chat|server|misc}";
640
641 if (newMode == oldMode)
642 newMode = -1;
643 controlPanel->setMessagesMode(newMode);
644
645 return std::string();
646 }
647
cmdSend(const std::string &,const CommandManager::ArgList & args,bool *)648 static std::string cmdSend(const std::string&,
649 const CommandManager::ArgList& args, bool*)
650 {
651 static ComposeDefaultKey composeKeyHandler;
652 if (args.size() != 1)
653 return "usage: send {all|team|nemesis|recipient|admin}";
654 LocalPlayer *myTank = LocalPlayer::getMyTank();
655 if (!myTank)
656 return "use send only when connected";
657 std::string composePrompt;
658 if (args[0] == "all")
659 {
660 void* buf = messageMessage;
661 buf = nboPackUByte(buf, AllPlayers);
662 composePrompt = "Send to all: ";
663 }
664 else if (args[0] == "team")
665 {
666 if (World::getWorld()->allowTeams() || myTank->getTeam() == ObserverTeam)
667 {
668 void* buf = messageMessage;
669 buf = nboPackUByte(buf, TeamToPlayerId(myTank->getTeam()));
670 composePrompt = "Send to teammates: ";
671 }
672 else
673 {
674 void* buf = messageMessage;
675 buf = nboPackUByte(buf, AllPlayers);
676 composePrompt = "Send to all: ";
677 }
678 }
679 else if (args[0] == "nemesis")
680 {
681 const Player* nemesis = myTank->getNemesis();
682 if (!nemesis) return std::string();
683
684 void* buf = messageMessage;
685 buf = nboPackUByte(buf, nemesis->getId());
686 composePrompt = "Send to ";
687 composePrompt += nemesis->getCallSign();
688 composePrompt += ": ";
689 }
690 else if (args[0] == "recipient")
691 {
692 const Player* recipient = myTank->getRecipient();
693 if (!recipient)
694 {
695 for (int i = 0; i < curMaxPlayers; i++)
696 {
697 if (remotePlayers[i])
698 {
699 myTank->setRecipient(remotePlayers[i]);
700 break;
701 }
702 }
703 }
704 recipient = myTank->getRecipient();
705 if (recipient)
706 {
707 void* buf = messageMessage;
708 buf = nboPackUByte(buf, recipient->getId());
709 composePrompt = "Send to ";
710 composePrompt += recipient->getCallSign();
711 composePrompt += ": ";
712 }
713 }
714 else if (args[0] == "admin")
715 {
716 void* buf = messageMessage;
717 buf = nboPackUByte(buf, AdminPlayers);
718 composePrompt = "Send to Admin: ";
719
720 }
721 else
722 return "usage: send {all|team|nemesis|recipient|admin}";
723 messageHistoryIndex = 0;
724 hud->setComposing(composePrompt);
725 HUDui::setDefaultKey(&composeKeyHandler);
726 return std::string();
727 }
728
729
730 struct ScreenshotData
731 {
732 std::string renderer;
733 unsigned char* pixels;
734 int xsize;
735 int ysize;
736 int channels;
737 };
738
739 #ifdef _WIN32
writeScreenshot(void * data)740 static DWORD WINAPI writeScreenshot(void* data)
741 #else
742 static void* writeScreenshot(void* data)
743 #endif
744 {
745 ScreenshotData* ssdata = (ScreenshotData*)data;
746
747 const std::string dirname = getScreenShotDirName();
748 const std::string prefix = "bzfi";
749 const std::string ext = ".png";
750
751 // scan the directory and start numbering with the filename
752 // that follows the existing filename with the highest snap number
753 int snap = 0;
754
755 #ifdef _WIN32
756 const std::string pattern = dirname + prefix + "*" + ext;
757 WIN32_FIND_DATA findData;
758 HANDLE h = FindFirstFile(pattern.c_str(), &findData);
759 if (h != INVALID_HANDLE_VALUE)
760 {
761 std::string file = findData.cFileName;
762 snap = atoi((file.substr(file.length() - 8, 4)).c_str());
763 while (FindNextFile(h, &findData))
764 {
765 file = findData.cFileName;
766 const int number = atoi((file.substr(file.length() - 8, 4)).c_str());
767 if (snap < number)
768 snap = number;
769 }
770 }
771 #else
772 const std::string pattern = prefix + "*" + ext;
773 DIR* directory = opendir(dirname.c_str());
774 if (directory)
775 {
776 struct dirent* contents;
777 std::string file;
778 while ((contents = readdir(directory)))
779 {
780 file = contents->d_name;
781 if (glob_match(pattern, file))
782 {
783 const int number = atoi((file.substr(file.length() - 8, 4)).c_str());
784 if (snap < number)
785 snap = number;
786 }
787 }
788 closedir(directory);
789 }
790 #endif // _WIN32
791
792 snap++;
793 std::string filename = dirname + prefix + TextUtils::format("%04d", snap) + ext;
794
795 std::ostream* f = FILEMGR.createDataOutStream(filename.c_str(), true, true);
796
797 if (f != NULL)
798 {
799 delete(f);
800
801 const std::string& renderer = ssdata->renderer;
802 unsigned char* pixels = ssdata->pixels;
803 const int xsize = ssdata->xsize;
804 const int ysize = ssdata->ysize;
805 const int channels = ssdata->channels;
806
807 // Gamma-correction is preapplied by BZFlag's gamma table
808 // This ignores the PNG gAMA chunk, but so do many viewers (including Mozilla)
809 if (BZDB.isSet("gamma"))
810 {
811 const float gamma = BZDB.eval("gamma");
812 if (gamma != 1.0f)
813 {
814 unsigned char gammaTable[256];
815 for (int i = 0; i < 256; i++)
816 {
817 const float lum = float(i) / 256.0f;
818 const float lumadj = pow(lum, 1.0f / gamma);
819 gammaTable[i] = (unsigned char) (lumadj * 256);
820 }
821 const int pixelCount = (xsize * ysize * channels);
822 for (int i = 0; i < pixelCount; i++)
823 pixels[i] = gammaTable[pixels[i]];
824 }
825 }
826
827 const std::string versionStr = std::string("BZFlag") + getAppVersion();
828 std::vector<BzPNG::Chunk> chunks;
829 chunks.push_back(BzPNG::Chunk("tEXt", "Software", versionStr));
830 chunks.push_back(BzPNG::Chunk("tEXt", "GL Renderer", renderer));
831
832 char buf[128];
833 if (BzPNG::save(filename, chunks, xsize, ysize, channels, pixels))
834 snprintf(buf, sizeof(buf), "%s: %dx%d", filename.c_str(), xsize, ysize);
835 else
836 snprintf(buf, sizeof(buf), "%s: failed to save", filename.c_str());
837 ControlPanel::addMutexMessage(buf);
838 }
839
840 delete[] ssdata->pixels;
841 delete ssdata;
842
843 return NULL;
844 }
845
cmdScreenshot(const std::string &,const CommandManager::ArgList & args,bool *)846 static std::string cmdScreenshot(const std::string&, const CommandManager::ArgList& args, bool*)
847 {
848 if (args.size() != 0)
849 return "usage: screenshot";
850
851 ScreenshotData* ssdata = new ScreenshotData;
852 ssdata->renderer += (const char*)(glGetString(GL_VENDOR));
853 ssdata->renderer += ": ";
854 ssdata->renderer += (const char*)(glGetString(GL_RENDERER));
855 ssdata->renderer += " (OpenGL ";
856 ssdata->renderer += (const char*)(glGetString(GL_VERSION));
857 ssdata->renderer += ")";
858 int w = mainWindow->getWidth();
859 int h = mainWindow->getHeight();
860 ssdata->xsize = w;
861 ssdata->ysize = h;
862 ssdata->channels = 3; // GL_RGB
863 ssdata->pixels = new unsigned char[h * w * 3];
864 glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
865 glPixelStorei(GL_PACK_ALIGNMENT, 1);
866 glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, ssdata->pixels);
867 glPopClientAttrib();
868
869 #if defined(HAVE_PTHREADS)
870 pthread_t thread;
871 pthread_attr_t attr;
872
873 pthread_attr_init(&attr);
874 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
875 pthread_create(&thread, &attr, writeScreenshot, (void *) ssdata);
876 pthread_attr_destroy(&attr);
877 #elif defined(_WIN32)
878 CreateThread(
879 NULL, // Security attributes
880 0, // Stack size (0 -> default)
881 writeScreenshot,
882 ssdata,
883 0, // creation flags (0 -> run immediately)
884 NULL); // thread id return value (NULL -> don't care)
885 #else
886 // no threads? sucks to be you, but we'll still write the screenshot
887 writeScreenshot(ssdata);
888 #endif
889
890 return std::string();
891 }
892
cmdTime(const std::string &,const CommandManager::ArgList & args,bool *)893 static std::string cmdTime(const std::string&,
894 const CommandManager::ArgList& args, bool*)
895 {
896 // FIXME - time should be moved into BZDB
897 if (args.size() != 1)
898 return "usage: time {forward|backward}";
899 if (args[0] == "forward")
900 clockAdjust += 5.0f * 60.0f;
901 else if (args[0] == "backward")
902 clockAdjust -= 5.0f * 60.0f;
903 else
904 return "usage: time {forward|backward}";
905 return std::string();
906 }
907
cmdRoam(const std::string &,const CommandManager::ArgList & args,bool *)908 static std::string cmdRoam(const std::string&,
909 const CommandManager::ArgList& args, bool*)
910 {
911 if (args.size() == 0)
912 return "usage: roam {zoom|cycle} <args>";
913 if (!ROAM.isRoaming())
914 return std::string();
915 if (args[0] == "zoom")
916 {
917 if (args.size() != 2)
918 return "usage: roam zoom {in|out|normal|stop}";
919 if (!roamButton || args[1] == "stop")
920 roamDZoom = 0.0f;
921 else if (args[1] == "out")
922 roamDZoom = +2.0f * BZDBCache::tankSpeed;
923 else if (args[1] == "in")
924 roamDZoom = -2.0f * BZDBCache::tankSpeed;
925 else if (args[1] == "normal")
926 ROAM.setZoom(60.0f);
927 else
928 return "usage: roam zoom {in|out|normal|stop}";
929 }
930 else if (args[0] == "cycle")
931 {
932 if (args.size() != 3)
933 return "usage: roam cycle {type|subject} {forward|backward}";
934 if (args[1] == "type")
935 {
936 if (args[2] == "forward")
937 ROAM.setMode(Roaming::RoamingView((ROAM.getMode() + 1) % Roaming::roamViewCount));
938 else if (args[2] == "backward")
939 {
940 int setto = (ROAM.getMode() - 1) % Roaming::roamViewCount;
941 if (setto < 0) setto += Roaming::roamViewCount;
942 ROAM.setMode(Roaming::RoamingView(setto));
943 }
944 else
945 return "usage: roam cycle {type|subject} {forward|backward}";
946 }
947 else if (args[1] == "subject")
948 {
949 if (args[2] == "forward")
950 ROAM.changeTarget(Roaming::next);
951 else if (args[2] == "backward")
952 ROAM.changeTarget(Roaming::previous);
953 else
954 return "usage: roam cycle {type|subject} {forward|backward}";
955 }
956 else
957 return "usage: roam cycle {type|subject} {forward|backward}";
958 }
959 else
960 return "usage: roam {zoom|cycle} <args>";
961 return std::string();
962 }
963
cmdSilence(const std::string &,const CommandManager::ArgList & args,bool *)964 static std::string cmdSilence(const std::string&,
965 const CommandManager::ArgList& args, bool*)
966 {
967 static SilenceDefaultKey silenceKeyHandler;
968 if (args.size() != 0)
969 return "usage: silence";
970 messageHistoryIndex = 0;
971 hud->setComposing("[Un]Silence: ");
972 HUDui::setDefaultKey(&silenceKeyHandler);
973 return std::string();
974 }
975
cmdServerCommand(const std::string &,const CommandManager::ArgList & args,bool *)976 static std::string cmdServerCommand(const std::string&,
977 const CommandManager::ArgList& args, bool*)
978 {
979 static ServerCommandKey serverCommandKeyHandler;
980 if (args.size() != 0)
981 return "usage: servercommand";
982 LocalPlayer *myTank = LocalPlayer::getMyTank();
983 if (!myTank)
984 return "use only when connected";
985 static bool prevAdmin = myTank->isAdmin();
986 if (!prevAdmin && myTank->isAdmin()) serverCommandKeyHandler.adminInit();
987 if (prevAdmin && !myTank->isAdmin()) serverCommandKeyHandler.nonAdminInit();
988 prevAdmin = myTank->isAdmin();
989
990 messageHistoryIndex = 0;
991 serverCommandKeyHandler.init();
992 HUDui::setDefaultKey(&serverCommandKeyHandler);
993 return std::string();
994 }
995
cmdScrollPanel(const std::string &,const CommandManager::ArgList & args,bool *)996 static std::string cmdScrollPanel(const std::string&,
997 const CommandManager::ArgList& args, bool*)
998 {
999 if ((args.size() < 1) || (args.size() > 2))
1000 return "usage: scrollpanel {up|up_page|down|down_page|top|bottom} [count]\n";
1001 int count = 1;
1002 int linecount = 2;
1003 if (args.size() == 2)
1004 {
1005 count = atoi(args[1].c_str());
1006 linecount = count;
1007 }
1008 // whence - (0 = set, 1 = cur, 2 = end)
1009 if (args[0] == "up")
1010 controlPanel->setMessagesOffset(+linecount, 1 /* current */, false);
1011 else if (args[0] == "down")
1012 controlPanel->setMessagesOffset(-linecount, 1 /* current */, false);
1013 else if (args[0] == "up_page")
1014 controlPanel->setMessagesOffset(+count, 1 /* current */, true);
1015 else if (args[0] == "down_page")
1016 controlPanel->setMessagesOffset(-count, 1 /* current */, true);
1017 else if (args[0] == "top")
1018 controlPanel->setMessagesOffset(123456789, 0 /* set */, false);
1019 else if (args[0] == "bottom")
1020 controlPanel->setMessagesOffset(0, 0 /* set */, false);
1021 else if (args[0] == "pause")
1022 controlPanel->togglePaused();
1023 return std::string();
1024 }
1025
1026
cmdHunt(const std::string &,const CommandManager::ArgList & args,bool *)1027 static std::string cmdHunt(const std::string&,
1028 const CommandManager::ArgList& args, bool*)
1029 {
1030 if (args.size() != 0)
1031 return "usage: hunt";
1032 LocalPlayer *myTank = LocalPlayer::getMyTank();
1033 if (!myTank)
1034 return "use only when connected";
1035 hud->getScoreboard()->huntKeyEvent (false);
1036 return std::string();
1037 }
1038
cmdAddHunt(const std::string &,const CommandManager::ArgList & args,bool *)1039 static std::string cmdAddHunt(const std::string&,
1040 const CommandManager::ArgList& args, bool*)
1041 {
1042 if (args.size() != 0)
1043 return "usage: addhunt";
1044 LocalPlayer *myTank = LocalPlayer::getMyTank();
1045 if (!myTank)
1046 return "use only when connected";
1047 hud->getScoreboard()->huntKeyEvent (true);
1048 return std::string();
1049 }
1050
cmdCycleRadar(const std::string &,const CommandManager::ArgList & args,bool *)1051 static std::string cmdCycleRadar(const std::string&,
1052 const CommandManager::ArgList& args, bool*)
1053 {
1054 const std::string usageText = "usage: cycleRadar {level1 [level2 ...] [off]}: cycle to the next radar zoom level";
1055
1056 if (args.size() == 0)
1057 return usageText;
1058
1059 std::vector<float> radarLevels;
1060
1061 for (size_t i = 0; i < args.size(); ++i)
1062 if (args[i] == "off")
1063 radarLevels.push_back(0.0f);
1064 else if (atof(args[i].c_str()) > 0.0f)
1065 radarLevels.push_back((float)atof(args[i].c_str()));
1066 else
1067 return usageText;
1068
1069 if (radarLevels.size() == 0)
1070 return usageText;
1071
1072 if (radarLevels.size() == 1)
1073 {
1074 // only one specified... just set it
1075 BZDB.set("displayRadar", radarLevels[0] > 0.0f ? "1" : "0");
1076
1077 if (radarLevels[0] > 0.0f)
1078 BZDB.setFloat("displayRadarRange", radarLevels[0]);
1079
1080 return std::string();
1081 }
1082
1083 static size_t radarLevelIndex = radarLevels.size() - 1;
1084
1085 // if it's off and the current level is some form of on, turn it back on and set it
1086 if (BZDB.get("displayRadar") == "0" && radarLevels[radarLevelIndex] > 0.0f)
1087 {
1088 BZDB.set("displayRadar", "1");
1089 BZDB.setFloat("displayRadarRange", radarLevels[radarLevelIndex]);
1090
1091 return std::string();
1092 }
1093
1094 ++radarLevelIndex;
1095 if (radarLevelIndex >= radarLevels.size())
1096 radarLevelIndex = 0;
1097
1098 if (radarLevels[radarLevelIndex] == 0.0f)
1099 BZDB.set("displayRadar", "0");
1100 else
1101 {
1102 BZDB.setFloat("displayRadarRange", radarLevels[radarLevelIndex]);
1103 BZDB.set("displayRadar", "1");
1104 }
1105
1106 return std::string();
1107 }
1108
cmdCyclePanel(const std::string &,const CommandManager::ArgList & args,bool *)1109 static std::string cmdCyclePanel(const std::string&,
1110 const CommandManager::ArgList& args, bool*)
1111 {
1112 if (args.size() != 1)
1113 return "usage: cyclePanel {left[_off]|right[_off]}\n";
1114
1115 bool forward = args[0] == "right" || args[0] == "right_off";
1116 bool includeOff = args[0] == "left_off" || args[0] == "right_off";
1117
1118 if (! BZDB.isTrue("displayConsole"))
1119 {
1120 if (forward && includeOff && controlPanel->getMessagesMode() == 3)
1121 // reversed directions... put it back at beginning
1122 controlPanel->setMessagesMode(0);
1123 else if (! forward && includeOff && controlPanel->getMessagesMode() == 0)
1124 // reversed directions... put it back at end
1125 controlPanel->setMessagesMode(3);
1126
1127 BZDB.setBool("displayConsole", true);
1128 }
1129 else
1130 {
1131 if (controlPanel->getMessagesMode() == 0)
1132 {
1133 controlPanel->setMessagesMode(forward ? 1 : 3);
1134
1135 if (! forward && includeOff)
1136 BZDB.setBool("displayConsole", false);
1137 }
1138 else if (controlPanel->getMessagesMode() == 1)
1139 controlPanel->setMessagesMode(forward ? 2 : 0);
1140 else if (controlPanel->getMessagesMode() == 2)
1141 controlPanel->setMessagesMode(forward ? 3 : 1);
1142 else
1143 {
1144 controlPanel->setMessagesMode(forward ? 0 : 2);
1145
1146 if (forward && includeOff)
1147 BZDB.setBool("displayConsole", false);
1148 }
1149 }
1150
1151 return std::string();
1152 }
1153
1154
1155 // Local Variables: ***
1156 // mode: C++ ***
1157 // tab-width: 4 ***
1158 // c-basic-offset: 4 ***
1159 // indent-tabs-mode: nil ***
1160 // End: ***
1161 // ex: shiftwidth=4 tabstop=4
1162