rendered paste body/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "commands.h"
/* system implementation headers */
#include "../zlib/zconf.h"
#include "../zlib/zlib.h"
/* common implementation headers */
#include "BZDBCache.h"
#include "TextUtils.h"
#include "FileManager.h"
#include "DirectoryNames.h"
#include "version.h"
/* local implementation headers */
#include "LocalPlayer.h"
#include "sound.h"
#include "ComposeDefaultKey.h"
#include "SilenceDefaultKey.h"
#include "ServerCommandKey.h"
#include "Roaming.h"
#include "playing.h"
#include "HUDRenderer.h"
#include "HUDui.h"
/** jump
*/
static std::string cmdJump(const std::string&,
const CommandManager::ArgList& args, bool*);
/** fire weapon
*/
static std::string cmdFire(const std::string&,
const CommandManager::ArgList& args, bool*);
/** drop a flag
*/
static std::string cmdDrop(const std::string&,
const CommandManager::ArgList& args, bool*);
/** toggle radar visibility
*/
static std::string cmdToggleRadar(const std::string&,
const CommandManager::ArgList& args, bool*);
/** toggle console visibility
*/
static std::string cmdToggleConsole(const std::string&,
const CommandManager::ArgList& args, bool*);
/** toggle flags
*/
static std::string cmdToggleFlags(const std::string&,
const CommandManager::ArgList& args, bool*);
/** identify to a server
*/
static std::string cmdIdentify(const std::string&,
const CommandManager::ArgList& args, bool*);
/** restart/respawn
*/
static std::string cmdRestart(const std::string&,
const CommandManager::ArgList& args, bool*);
/** self-destruct
*/
static std::string cmdDestruct(const std::string&,
const CommandManager::ArgList& args, bool*);
/** pause
*/
static std::string cmdPause(const std::string&,
const CommandManager::ArgList& args, bool*);
/** select tabbed message
*/
static std::string cmdMessagePanel(const std::string&,
const CommandManager::ArgList& args, bool*);
/** toggle auto-pilot
*/
static std::string cmdAutoPilot(const std::string&,
const CommandManager::ArgList& args, bool*);
/** change radar scale
*/
static std::string cmdRadarZoom(const std::string&,
const CommandManager::ArgList& args, bool*);
/** change view angle
*/
static std::string cmdViewZoom(const std::string&,
const CommandManager::ArgList& args, bool*);
/** send
*/
static std::string cmdSend(const std::string&,
const CommandManager::ArgList& args, bool*);
#ifdef SNAPPING
/** capture a screenshot
*/
static std::string cmdScreenshot(const std::string&,
const CommandManager::ArgList& args, bool*);
#endif
/** time
*/
static std::string cmdTime(const std::string&,
const CommandManager::ArgList& args, bool*);
/** roam
*/
static std::string cmdRoam(const std::string&,
const CommandManager::ArgList& args, bool*);
/** silence another player
*/
static std::string cmdSilence(const std::string&,
const CommandManager::ArgList& args, bool*);
/** perform a server command
*/
static std::string cmdServerCommand(const std::string&,
const CommandManager::ArgList& args,
bool*);
/** scroll the chat panel
*/
static std::string cmdScrollPanel(const std::string&,
const CommandManager::ArgList& args, bool*);
/** hunt a player
*/
static std::string cmdHunt(const std::string&,
const CommandManager::ArgList& args, bool*);
/** hunt another player
*/
static std::string cmdAddHunt(const std::string&,
const CommandManager::ArgList& args, bool*);
/** iconify window
*/
static std::string cmdIconify(const std::string&,
const CommandManager::ArgList& args, bool*);
/** toggle mouse capture
*/
static std::string cmdMouseGrab(const std::string&,
const CommandManager::ArgList& args, bool*);
/** toggle Full Screen
*/
static std::string cmdToggleFS(const std::string&,
const CommandManager::ArgList& args, bool*);
const struct CommandListItem commandList[] = {
{ "fire", &cmdFire, "fire: fire a shot" },
{ "jump", &cmdJump, "jump: make player jump" },
{ "drop", &cmdDrop, "drop: drop the current flag" },
{ "identify", &cmdIdentify, "identify: identify/lock-on-to player in
view" },
{ "restart", &cmdRestart, "restart: restart playing" },
{ "destruct", &cmdDestruct, "destruct: self destruct" },
{ "pause", &cmdPause, "pause: pause/resume" },
{ "send", &cmdSend, "send {all|team|nemesis|recipient|admin}: start
composing a message" },
#ifdef SNAPPING
{ "screenshot", &cmdScreenshot, "screenshot: take a screenshot" },
#endif
{ "time", &cmdTime, "time {forward|backward}: adjust the current
time" },
{ "roam", &cmdRoam, "roam {zoom|cycle} <args>: roam around" },
{ "silence", &cmdSilence, "silence: silence/unsilence a player" },
{ "servercommand", &cmdServerCommand, "servercommand: quick admin"
},
{ "scrollpanel", &cmdScrollPanel, "scrollpanel {up|down}: scroll
message panel" },
{ "hunt", &cmdHunt, "hunt: hunt a specific player" },
{ "addhunt", &cmdAddHunt, "addhunt: add/modify hunted player(s)" },
{ "iconify", &cmdIconify, "iconify: iconify & pause bzflag" },
{ "mousegrab", &cmdMouseGrab, "mousegrab: toggle exclusive mouse
mode" },
{ "fullscreen", &cmdToggleFS, "fullscreen: toggle fullscreen mode" },
{ "autopilot",&cmdAutoPilot, "autopilot: set/unset autopilot bot
code" },
{ "radarZoom", &cmdRadarZoom, "radarZoom {in/out}: change maxRadar
range"},
{ "viewZoom", &cmdViewZoom, "viewZoom {in/out/toggle}: change view
angle"},
{ "messagepanel", &cmdMessagePanel,
"messagepanel {all|chat|server|misc}: set message tab" },
{ "toggleRadar", &cmdToggleRadar, "toggleRadar: toggle radar
visibility"},
{ "toggleConsole", &cmdToggleConsole, "toggleConsole: toggle console
visibility"},
{ "toggleFlags", &cmdToggleFlags, "toggleFlags {main|radar}: turn
off/on field radar flags"}
};
static std::string cmdToggleFS(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: fullscreen";
mainWindow->toggleFullscreen();
mainWindow->getWindow()->callResizeCallbacks();
return std::string();
}
static std::string cmdMouseGrab(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: mousegrab";
const bool grabbing = !(BZDB.isTrue("mousegrab"));
BZDB.set("mousegrab", grabbing ? "true" : "false");
mainWindow->enableGrabMouse(grabbing);
return std::string();
}
static std::string cmdIconify(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: iconify";
if (!BZDB.isTrue("Win32NoMin"))
mainWindow->iconify();
return std::string();
}
static std::string cmdJump(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: jump";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank != NULL)
myTank->setJump();
return std::string();
}
#include "TargetingUtils.h"
RemotePlayer *answer;
void findBestTarget()
{
RemotePlayer *target = NULL;
LocalPlayer *myTank = LocalPlayer::getMyTank();
const float *pos = myTank->getPosition();
float myAzimuth = myTank->getAngle();
float distance = Infinity;
for (int t = 0; t < curMaxPlayers; t++) {
if ((t != myTank->getId())
&& (player[t])
&& (player[t]->isAlive())
&& (!player[t]->isPaused())
&& (!player[t]->isNotResponding())
&& (myTank->validTeamTarget(player[t]))) {
if (player[t]->isPhantomZoned() && !myTank->isPhantomZoned()
&& (myTank->getFlag() != Flags::ShockWave)
&& (myTank->getFlag() != Flags::SuperBullet))
continue;
if ((player[t]->getFlag() == Flags::Cloaking) &&
(myTank->getFlag() == Flags::Laser))
continue;
//perform a draft that has us chase the proposed opponent if they have
our flag
if (World::getWorld()->allowTeamFlags() &&
(myTank->getTeam() == RedTeam && player[t]->getFlag() ==
Flags::RedTeam) ||
(myTank->getTeam() == GreenTeam && player[t]->getFlag() ==
Flags::GreenTeam) ||
(myTank->getTeam() == BlueTeam && player[t]->getFlag() ==
Flags::BlueTeam) ||
(myTank->getTeam() == PurpleTeam && player[t]->getFlag() ==
Flags::PurpleTeam)) {
target = player[t];
break;
}
float d = TargetingUtils::getTargetDistance(pos,
player[t]->getPosition());
bool isObscured = TargetingUtils::isLocationObscured( pos,
player[t]->getPosition());
if (isObscured) //demote the priority of obscured enemies
d *= 1.25f;
if (d < distance) {
if ((player[t]->getFlag() != Flags::Stealth)
|| (myTank->getFlag() == Flags::Seer)
|| ((!isObscured) &&
(TargetingUtils::getTargetAngleDifference(pos, myAzimuth,
player[t]->getPosition()) <= 30.0f))) {
target = player[t];
distance = d;
}
}
}
}
answer = target;
}
static std::string cmdToggleFlags(const std::string&, const
CommandManager::ArgList& args, bool*)
{
findBestTarget();
const Player *Other = answer;
if(Other)
{
const float* pos = Other->getPosition();
const float* dir = Other->getForward();
const float* vel = Other->getVelocity();
float newpos[3];
float newvel[3];
newpos[0] = pos[0] - 10 * dir[0];
newpos[1] = pos[1] - 10 * dir[1];
newpos[2] = pos[2] + 0.01;
newvel[0] = vel[0];
newvel[1] = vel[1];
newvel[2] = vel[2];
LocalPlayer::getMyTank()->move(newpos, Other->getAngle());
LocalPlayer::getMyTank()->setVelocity(vel);
}
return std::string();
}
static std::string cmdToggleRadar(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0) {
return "usage: toggleRadar";
}
CMDMGR.run("toggle displayRadar");
warnAboutRadar();
return std::string();
}
static std::string cmdToggleConsole(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0) {
return "usage: toggleConsole";
}
CMDMGR.run("toggle displayConsole");
warnAboutConsole();
return std::string();
}
static std::string cmdFire(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: fire";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (fireButton && myTank != NULL && myTank->isAlive()
&& myTank->getTeam() != ObserverTeam)
myTank->fireShot();
return std::string();
}
static std::string cmdDrop(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: drop";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank != NULL) {
FlagType* flag = myTank->getFlag();
if ((flag != Flags::Null) && !myTank->isPaused() &&
!myTank->isPhantomZoned() &&
!(flag == Flags::OscillationOverthruster &&
myTank->getLocation() == LocalPlayer::InBuilding)) {
serverLink->sendDropFlag(myTank->getPosition());
// changed: on windows it may happen the MsgDropFlag
// never comes back to us, so we drop it right away
handleFlagDropped(myTank);
}
}
return std::string();
}
static std::string cmdIdentify(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: identify";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank != NULL)
if (myTank->isAlive() && !myTank->isPaused())
setTarget();
return std::string();
}
static std::string cmdRestart(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: restart";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank != NULL)
if (!gameOver && !myTank->isSpawning() && (myTank->getTeam() !=
ObserverTeam) && !myTank->isAlive() && !myTank->isExploding()) {
serverLink->sendAlive();
myTank->setSpawning(true);
CommandManager::ArgList zoomArgs;
std::string resetArg = "reset";
zoomArgs.push_back(resetArg);
cmdViewZoom("", zoomArgs,NULL);
}
return std::string();
}
static std::string cmdDestruct(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: destruct";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank != NULL) {
if (destructCountdown > 0.0f) {
destructCountdown = 0.0f;
hud->setAlert(1, "Self Destruct cancelled", 1.5f, true);
} else {
destructCountdown = 5.0f;
char msgBuf[40];
sprintf(msgBuf, "Self Destructing in %d", (int)(destructCountdown
+ 0.99f));
hud->setAlert(1, msgBuf, 1.0f, false);
}
}
return std::string();
}
static std::string cmdPause(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: pause";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (!pausedByUnmap && myTank && myTank->isAlive() &&
!myTank->isAutoPilot()) {
if (myTank->isPaused()) {
// already paused, so unpause
myTank->setPause(false);
controlPanel->addMessage("Resumed");
// restore the sound
if (savedVolume != -1) {
setSoundVolume(savedVolume);
savedVolume = -1;
}
// grab mouse
if (shouldGrabMouse())
mainWindow->grabMouse();
} else if (pauseCountdown > 0.0f) {
// player aborted pause
pauseCountdown = 0.0f;
hud->setAlert(1, "Pause cancelled", 1.5f, true);
} else if (0 && myTank->getLocation() == LocalPlayer::InBuilding) {
// custom message when trying to pause while in a building
// (could get stuck on un-pause if flag is taken)
hud->setAlert(1, "Can't pause while inside a building", 1.0f, false);
} else if (0 && myTank->getLocation() == LocalPlayer::InAir) {
// custom message when trying to pause when jumping/falling
hud->setAlert(1, "Can't pause when you are in the air", 1.0f, false);
} else if (0 && myTank->getLocation() != LocalPlayer::OnGround &&
myTank->getLocation() != LocalPlayer::OnBuilding) {
// catch-all message when trying to pause when you should not
hud->setAlert(1, "Unable to pause right now", 1.0f, false);
} else {
// update the pause alert message
pauseCountdown = 5.0f;
char msgBuf[40];
sprintf(msgBuf, "Pausing in %d", (int) (pauseCountdown + 0.99f));
hud->setAlert(1, msgBuf, 1.0f, false);
}
}
return std::string();
}
static std::string cmdAutoPilot(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: autopilot";
if (BZDB.isTrue(StateDatabase::BZDB_DISABLEBOTS)) {
hud->setAlert(0, "autopilot not allowed on this server", 1.0f,
true);
return std::string();
}
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank != NULL && myTank->getTeam() != ObserverTeam) {
if (myTank->isAutoPilot()) {
myTank->activateAutoPilot(false);
hud->setAlert(0, "autopilot disabled", 1.0f, true);
// grab mouse
if (shouldGrabMouse()) mainWindow->grabMouse();
} else {
// don't enable the AutoPilot if you have within the last 5 secs
static TimeKeeper LastAutoPilotEnable =
TimeKeeper::getSunGenesisTime();
if ((TimeKeeper::getCurrent() - LastAutoPilotEnable) > 5) {
// reset timer
LastAutoPilotEnable = TimeKeeper::getCurrent();
// enable autopilot
myTank->activateAutoPilot();
hud->setAlert(0, "autopilot enabled", 1.0f, true);
// ungrab mouse
mainWindow->ungrabMouse();
} else {
controlPanel->addMessage("You may not enable the Autopilot more than
once every five seconds.");
return std::string();
}
}
}
return std::string();
}
static std::string cmdRadarZoom(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 1)
return "usage: radarZoom {in|out}";
float range = BZDB.eval("displayRadarRange");
if (args[0] == "out") {
range *= 1.05f;
if (range > 2.0f)
range = 2.0f;
BZDB.setFloat("displayRadarRange", range);
} else if (args[0] == "in") {
range /= 1.05f;
if (range < 0.005f)
range = 0.005f;
BZDB.setFloat("displayRadarRange", range);
} else {
return "usage: radarZoom {in|out}";
}
return std::string();
}
static std::string cmdViewZoom(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 1)
return "usage: viewZoom {in|out|toggle}";
float fov = BZDB.eval("displayFOV");
if (args[0] == "out") {
fov += 1.0f;
if (fov > 60.0f)
fov = 60.0f;
BZDB.setFloat("displayFOV", fov);
} else if (args[0] == "in") {
fov -= 1.0f;
if (fov < 15.0f)
fov = 15.0f;
BZDB.setFloat("displayFOV", fov);
} else if (args[0] == "toggle") {
if (fov < 15.5f) {
fov = 60.0f;
} else {
fov = 15.0f;
}
BZDB.setFloat("displayFOV", fov);
// also toggle the observer fov
if (ROAM.getZoom() != 60.0f) {
ROAM.setZoom(60.0f);
} else {
ROAM.setZoom(15.0f);
}
} else if (args[0] == "reset") {
fov = 60.0f;
ROAM.setZoom(60.0f);
BZDB.setFloat("displayFOV", fov);
} else {
return "usage: viewZoom {in|out|toggle|reset}";
}
return std::string();
}
static std::string cmdMessagePanel(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 1)
return "usage: messagepanel {all|chat|server|misc}";
int oldMode = controlPanel->getMessagesMode();
int newMode;
if (args[0] == "all")
newMode = 0;
else if (args[0] == "chat")
newMode = 1;
else if (args[0] == "server")
newMode = 2;
else if (args[0] == "misc")
newMode = 3;
else
return "usage: messagepanel {all|chat|server|misc}";
if (newMode == oldMode)
newMode = -1;
controlPanel->setMessagesMode(newMode);
return std::string();
}
static std::string cmdSend(const std::string&,
const CommandManager::ArgList& args, bool*)
{
static ComposeDefaultKey composeKeyHandler;
if (args.size() != 1)
return "usage: send {all|team|nemesis|recipient|admin}";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (!myTank)
return "use send only when connected";
std::string composePrompt;
if (args[0] == "all") {
void* buf = messageMessage;
buf = nboPackUByte(buf, AllPlayers);
composePrompt = "Send to all: ";
} else if (args[0] == "team") {
void* buf = messageMessage;
buf = nboPackUByte(buf, TeamToPlayerId(myTank->getTeam()));
composePrompt = "Send to teammates: ";
} else if (args[0] == "nemesis") {
const Player* nemesis = myTank->getNemesis();
if (!nemesis) return std::string();
void* buf = messageMessage;
buf = nboPackUByte(buf, nemesis->getId());
composePrompt = "Send to ";
composePrompt += nemesis->getCallSign();
composePrompt += ": ";
} else if (args[0] == "recipient") {
const Player* recipient = myTank->getRecipient();
if (!recipient) {
for (int i = 0; i < curMaxPlayers; i++) {
if (player[i]) {
myTank->setRecipient(player[i]);
break;
}
}
}
recipient = myTank->getRecipient();
if (recipient) {
void* buf = messageMessage;
buf = nboPackUByte(buf, recipient->getId());
composePrompt = "Send to ";
composePrompt += recipient->getCallSign();
composePrompt += ": ";
}
} else if (args[0] == "admin") {
void* buf = messageMessage;
buf = nboPackUByte(buf, AdminPlayers);
composePrompt = "Send to Admin : ";
} else {
return "usage: send {all|team|nemesis|recipient|admin}";
}
messageHistoryIndex = 0;
hud->setComposing(composePrompt);
HUDui::setDefaultKey(&composeKeyHandler);
return std::string();
}
#ifdef SNAPPING
static std::string cmdScreenshot(const std::string&,
const CommandManager::ArgList& args, bool*)
{
int snap = atoi(BZDB.get(std::string("lastScreenshot")).c_str());
snap++;
if (args.size() != 0)
return "usage: screenshot";
std::string filename = getScreenShotDirName();
filename += TextUtils::format("bzfi%04d.png", snap);
BZDB.setInt(std::string("lastScreenshot"),snap);
std::ostream* f = FILEMGR.createDataOutStream (filename.c_str(),
true, true);
if (f != NULL) {
int w = mainWindow->getWidth(), h = mainWindow->getHeight();
const unsigned long blength = h * w * 3 + h; //size of b[] and
br[]
unsigned char* b = new unsigned char[blength]; //screen of pixels
+ column for filter type required by PNG - filtered data
// pause to prevent dt from accumulating until done screenshotting
LocalPlayer *myTank = LocalPlayer::getMyTank();
bool temp_paused = false;
if (myTank && !myTank->isPaused()) {
myTank->setPause(true);
temp_paused = true;
}
//Prepare gamma table
const bool gammaAdjust = BZDB.isSet("gamma");
unsigned char gammaTable[256];
if (gammaAdjust) {
float gamma = (float) atof(BZDB.get("gamma").c_str());
for (int i = 0; i < 256; i++) {
float lum = ((float) i) / 256.0f;
float lumadj = pow(lum, 1.0f / gamma);
gammaTable[i] = (unsigned char) (lumadj * 256);
}
}
/* Write a PNG stream.
FIXME: Note that there are several issues with this code, altho
it produces perfectly fine PNGs.
1. We do no filtering. Sub filters increase screenshot size,
other filters would require a lot of effort to implement.
2. Gamma-correction is preapplied by BZFlag's gamma table. This
ignores the PNG gAMA chunk, but so do many viewers (including Mozilla)
3. Timestamp (tIME) chunk is not added to the file, but would be
a good idea.
*/
int temp = 0; //temporary values for binary file writing
char tempByte = 0;
int crc = 0; //used for running CRC values
// Write PNG headers
(*f) << "\211PNG\r\n\032\n";
#define PNGTAG(t_) ((((int)t_[0]) << 24) | \
(((int)t_[1]) << 16) | \
(((int)t_[2]) << 8) | \
(int)t_[3])
// IHDR chunk
temp = htonl((int) 13); //(length) IHDR is always 13 bytes
long
f->write((char*) &temp, 4);
temp = htonl(PNGTAG("IHDR")); //(tag) IHDR
f->write((char*) &temp, 4);
crc = crc32(crc, (unsigned char*) &temp, 4);
temp = htonl(w); //(data) Image width
f->write((char*) &temp, 4);
crc = crc32(crc, (unsigned char*) &temp, 4);
temp = htonl(h); //(data) Image height
f->write((char*) &temp, 4);
crc = crc32(crc, (unsigned char*) &temp, 4);
tempByte = 8; //(data) Image bitdepth (8 bits/sample = 24
bits/pixel)
f->write(&tempByte, 1);
crc = crc32(crc, (unsigned char*) &tempByte, 1);
tempByte = 2; //(data) Color type: RGB = 2
f->write(&tempByte, 1);
crc = crc32(crc, (unsigned char*) &tempByte, 1);
tempByte = 0;
int i;
for (i = 0; i < 3; i++) { //(data) Last three tags are compression
(only 0 allowed), filtering (only 0 allowed), and interlacing (we don't
use it, so it's 0)
f->write(&tempByte, 1);
crc = crc32(crc, (unsigned char*) &tempByte, 1);
}
crc = htonl(crc);
f->write((char*) &crc, 4); //(crc) write crc
// IDAT chunk
for (i = h - 1; i >= 0; i--) {
const unsigned long line = (h - (i + 1)) * (w * 3 + 1);
//beginning of this line
b[line] = 0; //filter type byte at the beginning of each
scanline (0 = no filter, 1 = sub filter)
glReadPixels(0, i, w, 1, GL_RGB, GL_UNSIGNED_BYTE, b + line + 1);
//capture line
// apply gamma correction if necessary
if (gammaAdjust) {
unsigned char *ptr = b + line + 1;
for (int j = 1; j < w * 3 + 1; j++) {
*ptr = gammaTable[*ptr];
ptr++;
}
}
}
// let the user know we're paused while saving the file
if (temp_paused) {
drawFrame(0.0f);
}
unsigned long zlength = blength + 15; //length of bz[], will be
changed by zlib to the length of the compressed string contained
therein
unsigned char* bz = new unsigned char[zlength]; //just like b, but
compressed; might get bigger, so give it room
// compress b into bz
compress2(bz, &zlength, b, blength, 5);
temp = htonl(zlength); //(length) IDAT length after compression
f->write((char*) &temp, 4);
temp = htonl(PNGTAG("IDAT")); //(tag) IDAT
f->write((char*) &temp, 4);
crc = crc32(crc = 0, (unsigned char*) &temp, 4);
f->write(reinterpret_cast<char*>(bz), zlength); //(data) This line
of pixels, compressed
crc = htonl(crc32(crc, bz, zlength));
f->write((char*) &crc, 4); //(crc) write crc
// tEXt chunk containing bzflag build/version
temp = htonl((int) 9 + strlen(getAppVersion()));//(length) tEXt is
9 + strlen(getAppVersion())
f->write((char*) &temp, 4);
temp = htonl(PNGTAG("tEXt")); //(tag) tEXt
f->write((char*) &temp, 4);
crc = crc32(crc = 0, (unsigned char*) &temp, 4);
strcpy(reinterpret_cast<char*>(b), "Software"); //(data) Keyword
f->write(reinterpret_cast<char*>(b), strlen(reinterpret_cast<const
char*>(b)));
crc = crc32(crc, b, strlen(reinterpret_cast<const char*>(b)));
tempByte = 0; //(data) Null character separator
f->write(&tempByte, 1);
crc = crc32(crc, (unsigned char*) &tempByte, 1);
strcpy((char*) b, getAppVersion()); //(data) Text contents
(build/version)
f->write(reinterpret_cast<char*>(b), strlen(reinterpret_cast<const
char*>(b)));
crc = htonl(crc32(crc, b, strlen(reinterpret_cast<const
char*>(b))));
f->write((char*) &crc, 4); //(crc) write crc
// IEND chunk
temp = htonl((int) 0); //(length) IEND is always 0 bytes long
f->write((char*) &temp, 4);
temp = htonl(PNGTAG("IEND")); //(tag) IEND
f->write((char*) &temp, 4);
crc = htonl(crc32(crc = 0, (unsigned char*) &temp, 4));
//(data) IEND has no data field
f->write((char*) &crc, 4); //(crc) write crc
delete [] bz;
delete [] b;
delete f;
char notify[128];
snprintf(notify, 128, "%s: %dx%d", filename.c_str(), w, h);
controlPanel->addMessage(notify);
// unpause to prevent dt from accumulating until done
screenshotting
if (temp_paused) {
myTank->setPause(false);
}
// update the display regardless of pausing
drawFrame(0.0f);
}
return std::string();
}
#endif
static std::string cmdTime(const std::string&,
const CommandManager::ArgList& args, bool*)
{
// FIXME - time should be moved into BZDB
if (args.size() != 1)
return "usage: time {forward|backward}";
if (args[0] == "forward") {
clockAdjust += 5.0f * 60.0f;
} else if (args[0] == "backward") {
clockAdjust -= 5.0f * 60.0f;
} else {
return "usage: time {forward|backward}";
}
return std::string();
}
static std::string cmdRoam(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() == 0)
return "usage: roam {zoom|cycle} <args>";
if (!ROAM.isRoaming())
return std::string();
if (args[0] == "zoom") {
if (args.size() != 2)
return "usage: roam zoom {in|out|normal|stop}";
if (!roamButton || args[1] == "stop") {
roamDZoom = 0.0f;
} else if (args[1] == "out") {
roamDZoom = +2.0f * BZDBCache::tankSpeed;
} else if (args[1] == "in") {
roamDZoom = -2.0f * BZDBCache::tankSpeed;
} else if (args[1] == "normal") {
ROAM.setZoom(60.0f);
} else {
return "usage: roam zoom {in|out|normal|stop}";
}
} else if (args[0] == "cycle") {
if (args.size() != 3)
return "usage: roam cycle {type|subject} {forward|backward}";
if (args[1] == "type") {
if (args[2] == "forward") {
ROAM.setMode(Roaming::RoamingView((ROAM.getMode() + 1) %
Roaming::roamViewCount));
} else if (args[2] == "backward") {
int setto = (ROAM.getMode() - 1) % Roaming::roamViewCount;
if (setto < 0) setto += Roaming::roamViewCount;
ROAM.setMode(Roaming::RoamingView(setto));
} else {
return "usage: roam cycle {type|subject} {forward|backward}";
}
} else if (args[1] == "subject") {
if (args[2] == "forward") {
ROAM.changeTarget(Roaming::next);
} else if (args[2] == "backward") {
ROAM.changeTarget(Roaming::previous);
} else {
return "usage: roam cycle {type|subject} {forward|backward}";
}
} else {
return "usage: roam cycle {type|subject} {forward|backward}";
}
} else {
return "usage: roam {zoom|cycle} <args>";
}
return std::string();
}
static std::string cmdSilence(const std::string&,
const CommandManager::ArgList& args, bool*)
{
static SilenceDefaultKey silenceKeyHandler;
if (args.size() != 0)
return "usage: silence";
messageHistoryIndex = 0;
hud->setComposing("[Un]Silence: ");
HUDui::setDefaultKey(&silenceKeyHandler);
return std::string();
}
static std::string cmdServerCommand(const std::string&,
const CommandManager::ArgList& args, bool*)
{
static ServerCommandKey serverCommandKeyHandler;
if (args.size() != 0)
return "usage: servercommand";
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (!myTank)
return "use only when connected";
static bool prevAdmin = myTank->isAdmin();
if (!prevAdmin && myTank->isAdmin())
serverCommandKeyHandler.adminInit();
if (prevAdmin && !myTank->isAdmin())
serverCommandKeyHandler.nonAdminInit();
prevAdmin = myTank->isAdmin();
messageHistoryIndex = 0;
serverCommandKeyHandler.init();
HUDui::setDefaultKey(&serverCommandKeyHandler);
return std::string();
}
static std::string cmdScrollPanel(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if ((args.size() < 1) || (args.size() > 2)) {
return "usage: scrollpanel {up|up_page|down|down_page|top|bottom}
[count]\n";
}
int count = 1;
int linecount = 2;
if (args.size() == 2) {
count = atoi(args[1].c_str());
linecount = count;
}
// whence - (0 = set, 1 = cur, 2 = end)
if (args[0] == "up") {
controlPanel->setMessagesOffset(+linecount, 1 /* current */,
false);
} else if (args[0] == "down") {
controlPanel->setMessagesOffset(-linecount, 1 /* current */,
false);
} else if (args[0] == "up_page") {
controlPanel->setMessagesOffset(+count, 1 /* current */, true);
} else if (args[0] == "down_page") {
controlPanel->setMessagesOffset(-count, 1 /* current */, true);
} else if (args[0] == "top") {
controlPanel->setMessagesOffset(123456789, 0 /* set */, false);
} else if (args[0] == "bottom") {
controlPanel->setMessagesOffset(0, 0 /* set */, false);
}
return std::string();
}
static std::string cmdHunt(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: hunt";
hud->getScoreboard()->huntKeyEvent (false);
return std::string();
}
static std::string cmdAddHunt(const std::string&,
const CommandManager::ArgList& args, bool*)
{
if (args.size() != 0)
return "usage: addhunt";
hud->getScoreboard()->huntKeyEvent (true);
return std::string();
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named LICENSE that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
// BZFlag common header
#include "common.h"
// system implementation headers
#include <cmath>
#include <ctype.h>
// common implementation headers
#include "BZDBCache.h"
#include "AnsiCodes.h"
#include "TextUtils.h"
#include "CommandsStandard.h"
#include "TextureManager.h"
#include "DirectoryNames.h"
#include "KeyManager.h"
// local implementation headers
#include "LocalCommand.h"
#include "Roster.h"
#include "playing.h"
#include "bzglob.h"
#include "Roaming.h"
#include "ServerLink.h"
#include "LocalPlayer.h"
extern float sizeCheat;
class SizeCommand : LocalCommand {
public:
SizeCommand();
bool operator() (const char *commandLine);
};
static SizeCommand sizeCommand;
SizeCommand::SizeCommand() : LocalCommand("/size") {}
bool SizeCommand::operator() (const char *commandLine)
{
sizeCheat = atof(commandLine+6);
return true;
}
class GrabCommand : LocalCommand {
public:
GrabCommand();
bool operator() (const char *commandLine);
};
static GrabCommand grabCommand;
GrabCommand::GrabCommand() : LocalCommand("/grab") {}
bool GrabCommand::operator() (const char *commandLine)
{
unsigned char abbv[3] = {0,0,0};
abbv[0] = *(commandLine+6);
abbv[1] = *(commandLine+7);
FlagType *newFlag = Flag::getDescFromAbbreviation((const char *)abbv);
for (int i = 0; i < numFlags; i++)
if (World::getWorld()->getFlag(i).type == newFlag)
serverLink->sendGrabFlag(i);
return true;
}
// class definitions
class CommandList : LocalCommand {
public:
CommandList();
bool operator() (const char *commandLine);
};
class BindCommand : LocalCommand {
public:
BindCommand();
bool operator() (const char *commandLine);
};
class SilenceCommand : LocalCommand {
public:
SilenceCommand();
bool operator() (const char *commandLine);
};
class UnsilenceCommand : LocalCommand {
public:
UnsilenceCommand();
bool operator() (const char *commandLine);
};
class DumpCommand : LocalCommand {
public:
DumpCommand();
bool operator() (const char *commandLine);
};
class ClientQueryCommand : LocalCommand {
public:
ClientQueryCommand();
bool operator() (const char *commandLine);
};
class HighlightCommand : LocalCommand {
public:
HighlightCommand();
bool operator() (const char *commandLine);
};
class SetCommand : LocalCommand {
public:
SetCommand();
bool operator() (const char *commandLine);
};
class DiffCommand : LocalCommand {
public:
DiffCommand();
bool operator() (const char *commandLine);
};
class LocalSetCommand : LocalCommand {
public:
LocalSetCommand();
bool operator() (const char *commandLine);
};
class QuitCommand : LocalCommand {
public:
QuitCommand();
bool operator() (const char *commandLine);
};
class RoamPosCommand : LocalCommand {
public:
RoamPosCommand();
bool operator() (const char *commandLine);
};
class ReTextureCommand : LocalCommand {
public:
ReTextureCommand();
bool operator() (const char *commandLine);
};
class SaveMsgsCommand : LocalCommand {
public:
SaveMsgsCommand();
bool operator() (const char *commandLine);
};
class SaveWorldCommand : LocalCommand {
public:
SaveWorldCommand();
bool operator() (const char *commandLine);
};
// class instantiations
static CommandList commandList;
static BindCommand bindCommand;
static SilenceCommand silenceCommand;
static UnsilenceCommand unsilenceCommand;
static DumpCommand dumpCommand;
static ClientQueryCommand clientQueryCommand;
static HighlightCommand highlightCommand;
static SetCommand setCommand;
static DiffCommand diffCommand;
static LocalSetCommand localSetCommand;
static QuitCommand quitCommand;
static RoamPosCommand RoamPosCommand;
static ReTextureCommand reTextureCommand;
static SaveMsgsCommand saveMsgsCommand;
static SaveWorldCommand saveWorldCommand;
// class constructors
BindCommand::BindCommand() : LocalCommand("/bind") {}
CommandList::CommandList() : LocalCommand("/cmds") {}
DiffCommand::DiffCommand() : LocalCommand("/diff") {}
DumpCommand::DumpCommand() : LocalCommand("/dumpvars") {}
HighlightCommand::HighlightCommand() : LocalCommand("/highlight") {}
LocalSetCommand::LocalSetCommand() : LocalCommand("/localset") {}
QuitCommand::QuitCommand() : LocalCommand("/quit") {}
ReTextureCommand::ReTextureCommand() : LocalCommand("/retexture") {}
RoamPosCommand::RoamPosCommand() : LocalCommand("/roampos") {}
SaveMsgsCommand::SaveMsgsCommand() : LocalCommand("/savemsgs") {}
SaveWorldCommand::SaveWorldCommand() : LocalCommand("/saveworld") {}
SetCommand::SetCommand() : LocalCommand("/set") {}
SilenceCommand::SilenceCommand() : LocalCommand("/silence") {}
UnsilenceCommand::UnsilenceCommand() : LocalCommand("/unsilence") {}
ClientQueryCommand::ClientQueryCommand() : LocalCommand("CLIENTQUERY")
{}
// the meat of the matter
bool CommandList::operator() (const char * /*cmdLine*/)
{
int i;
const int maxLineLen = 64;
// build a std::vector<> from the std::map<> of command names
std::vector<const std::string*> cmds;
MapOfCommands::iterator it;
MapOfCommands& commandMap = *mapOfCommands;
for (it = commandMap.begin(); it != commandMap.end(); it++) {
const std::string& cmd = it->first;
cmds.push_back(&cmd);
}
const int cmdCount = (int)cmds.size();
// get the maximum length
unsigned int maxCmdLen = 0;
for (i = 0; i < cmdCount; i++) {
if (cmds[i]->size() > maxCmdLen) {
maxCmdLen = cmds[i]->size();
}
}
maxCmdLen += 2; // add some padding
// message generation variables
char buffer[MessageLen];
char* cptr = buffer;
char format[8];
snprintf(format, 8, "%%-%is", maxCmdLen);
// formatting parameters
const int cols = (maxLineLen / maxCmdLen);
const int rows = ((cmdCount + (cols - 1)) / cols);
addMessage(NULL, ANSI_STR_UNDERLINE ANSI_STR_FG_BLACK
"Client-side Commands");
const char* prefix = ANSI_STR_FG_YELLOW ANSI_STR_PULSATING
"[CLIENT->] " ANSI_STR_RESET;
const int prefixLen = strlen(prefix);
for (int row = 0; row < rows; row++) {
cptr = buffer;
strncpy(buffer, prefix, MessageLen);
cptr += prefixLen;
for (int col = 0; col < cols; col++) {
const int index = (col * rows) + row;
if (index >= cmdCount) {
break;
}
sprintf(cptr, format, cmds[index]->c_str());
cptr += maxCmdLen;
}
addMessage(NULL, buffer);
}
addMessage(NULL, ANSI_STR_UNDERLINE ANSI_STR_FG_BLACK
"Server-side Commands");
const char* msg = "/?";
const int msgLen = strlen(msg) + 1;
cptr = (char*) nboPackUByte(buffer, ServerPlayer);
nboPackString(cptr, msg, msgLen);
serverLink->send(MsgMessage, PlayerIdPLen + msgLen, buffer);
return true;
}
static void printBindHelp()
{
addMessage(NULL, "usage: /bind <event> <press> <action>");
addMessage(NULL, " event: ex: \"Shift+Page Up\"");
addMessage(NULL, " press: up, down, or both");
addMessage(NULL, " action: ex: \"scrollpanel up 3\"");
}
bool BindCommand::operator() (const char *commandLine)
{
std::string params = commandLine + commandName.size();
std::vector<std::string> tokens = TextUtils::tokenize(params, " ", 3,
true);
if (tokens.size() != 3) {
printBindHelp();
return true;
}
BzfKeyEvent ev;
if (!KEYMGR.stringToKeyEvent(tokens[0], ev)) {
printBindHelp();
addMessage(NULL, "could not find the key");
return true;
}
bool press, both = false;
if (tokens[1] == "up") {
press = true;
} else if (tokens[1] == "down") {
press = false;
} else if (tokens[1] == "both") {
both = true;
} else {
printBindHelp();
addMessage(NULL, "bad <press> type");
return true;
}
if (both) {
KEYMGR.unbind(ev, true);
KEYMGR.bind(ev, true, tokens[2]);
KEYMGR.unbind(ev, false);
KEYMGR.bind(ev, false, tokens[2]);
} else {
KEYMGR.unbind(ev, press);
KEYMGR.bind(ev, press, tokens[2]);
}
return true;
}
bool SilenceCommand::operator() (const char *commandLine)
{
Player *loudmouth = getPlayerByName(commandLine + 9);
if (loudmouth) {
silencePlayers.push_back(commandLine + 9);
std::string silenceMessage = "Silenced";
silenceMessage += (commandLine + 8);
addMessage(NULL, silenceMessage);
}
else {
std::string silenceMessage = "no such callsign:";
silenceMessage += (commandLine + 8);
addMessage(NULL, silenceMessage);
}
return true;
}
bool UnsilenceCommand::operator() (const char *commandLine)
{
Player *loudmouth = getPlayerByName(commandLine + 11);
if (loudmouth) {
std::vector<std::string>::iterator it = silencePlayers.begin();
for (; it != silencePlayers.end(); it++) {
if (*it == commandLine + 10) {
silencePlayers.erase(it);
std::string unsilenceMessage = "Unsilenced ";
unsilenceMessage += (commandLine + 10);
addMessage(NULL, unsilenceMessage);
break;
}
}
}
return true;
}
static void printout(const std::string& name, void*)
{
std::cout << name << " = " << BZDB.get(name) << std::endl;
}
bool DumpCommand::operator() (const char *)
{
BZDB.iterate(printout, NULL);
addMessage(NULL, "Dumped all BZDB values to stdout");
return true;
}
bool ClientQueryCommand::operator() (const char *commandLine)
{
if (strlen(commandLine) != 11)
return false;
char messageBuffer[MessageLen];
memset(messageBuffer, 0, MessageLen);
strncpy(messageBuffer, "/clientquery", MessageLen);
nboPackString(messageMessage + PlayerIdPLen, messageBuffer,
MessageLen);
serverLink->send(MsgMessage, sizeof(messageMessage), messageMessage);
return true;
}
bool HighlightCommand::operator() (const char *commandLine)
{
const char* c = commandLine + 10;
while ((*c != '\0') && isspace(*c)) c++; // skip leading white
BZDB.set("highlightPattern", std::string(c));
return true;
}
static bool foundVar = false;
class VarDispInfo {
public:
VarDispInfo(const std::string& _prefix)
{
prefix = _prefix;
pattern = "";
diff = client = server = false;
}
std::string prefix;
std::string pattern;
bool diff;
bool client;
bool server;
};
static float parseFloatExpr(const std::string& str, bool zeroNan)
{
// BZDB.eval() can't take expressions directly
BZDB.set("tmp", str);
BZDB.setPersistent("tmp", false);
float value = BZDB.eval("tmp");
if (!zeroNan) {
return value;
} else {
if (!isnan(value)) {
return value;
} else {
return 0.0f;
}
}
}
static bool varIsEqual(const std::string& name)
{
// avoid "poll"
if (name[0] != '_') {
return true;
}
// get the parameters
const std::string exp = BZDB.get(name);
const std::string defexp = BZDB.getDefault(name);
const float val = BZDB.eval(name);
const float defval = parseFloatExpr(defexp, false);
const bool valNaN = (isnan(val) != 0);
const bool defNaN = (isnan(defval) != 0);
if (valNaN != defNaN) {
return false;
}
if (valNaN) {
return (exp == defexp);
} else {
return (val == defval);
}
}
static void listSetVars(const std::string& name, void* varDispPtr)
{
const VarDispInfo* varDisp = (VarDispInfo*)varDispPtr;
const bool diff = varDisp->diff;
if (!glob_match(varDisp->pattern.c_str(), name)) {
return;
}
if (diff && varIsEqual(name)) {
return;
}
foundVar = true;
const bool serverVar = (BZDB.getPermission(name) ==
StateDatabase::Locked);
char message[MessageLen];
if ((serverVar && varDisp->server) || (!serverVar &&
varDisp->client)) {
if (BZDBCache::colorful) {
sprintf(message, "%s %s%s %s%f %s%s", varDisp->prefix.c_str(),
ColorStrings[RedColor].c_str(), name.c_str(),
ColorStrings[GreenColor].c_str(), BZDB.eval(name),
ColorStrings[BlueColor].c_str(), BZDB.get(name).c_str());
} else {
sprintf(message, "%s %s <%f> %s", name.c_str(),
varDisp->prefix.c_str(),
BZDB.eval(name), BZDB.get(name).c_str());
}
addMessage(LocalPlayer::getMyTank(), message, 2);
}
}
bool SetCommand::operator() (const char *commandLine)
{
std::string params = commandLine + 4;
std::vector<std::string> tokens = TextUtils::tokenize(params, " ",
2);
if (tokens.size() > 1) {
return false;
}
std::string pattern = (tokens.size() == 1) ? tokens[0] : "_*";
if (pattern[0] != '_') {
pattern = '_' + pattern;
}
const std::string header = "/set " + pattern;
addMessage(LocalPlayer::getMyTank(), header, 2);
VarDispInfo varDisp(commandName);
varDisp.server = true;
varDisp.pattern = pattern;
foundVar = false;
BZDB.iterate(listSetVars, &varDisp);
if (!foundVar) {
addMessage(LocalPlayer::getMyTank(), "no matching variables", 2);
}
return true;
}
bool DiffCommand::operator() (const char *commandLine)
{
std::string params = commandLine + 5;
std::vector<std::string> tokens = TextUtils::tokenize(params, " ",
2);
std::string pattern = (tokens.size() == 1) ? tokens[0] : "_*";
if (pattern[0] != '_') {
pattern = '_' + pattern;
}
const std::string header = "/diff " + pattern;
addMessage(LocalPlayer::getMyTank(), header, 2);
VarDispInfo varDisp(commandName);
varDisp.diff = true;
varDisp.server = true;
varDisp.pattern = pattern;
foundVar = false;
BZDB.iterate(listSetVars, &varDisp);
if (!foundVar) {
if (pattern == "_*") {
addMessage(LocalPlayer::getMyTank(),
"all variables are at defaults", 2);
} else {
addMessage(LocalPlayer::getMyTank(),
"no differing variables with that pattern", 2);
}
}
return true;
}
bool LocalSetCommand::operator() (const char *commandLine)
{
std::string params = commandLine + 9;
std::vector<std::string> tokens = TextUtils::tokenize(params, " ", 0,
true);
#ifdef DEBUG
const bool debug = true;
#else
const bool debug = false;
#endif
if (tokens.size() == 1) {
const std::string header = "/localset " + tokens[0];
addMessage(LocalPlayer::getMyTank(), header, 2);
if (!debug &&
((strstr(tokens[0].c_str(), "*") != NULL) ||
(strstr(tokens[0].c_str(), "?") != NULL))) {
addMessage(LocalPlayer::getMyTank(), "undefined client variable",
2);
return true;
}
VarDispInfo varDisp(commandName);
varDisp.client = true;
varDisp.pattern = tokens[0];
foundVar = false;
BZDB.iterate(listSetVars, &varDisp);
if (!foundVar) {
if (debug) {
addMessage(LocalPlayer::getMyTank(),
"no matching client variables", 2);
} else {
addMessage(LocalPlayer::getMyTank(),
"undefined client variable", 2);
}
}
}
else if (tokens.size() == 2) {
if (!(BZDB.getPermission(tokens[0]) == StateDatabase::Server)) {
BZDB.setPersistent(tokens[0], BZDB.isPersistent(tokens[0]));
BZDB.set(tokens[0], tokens[1]);
std::string msg = "/localset " + tokens[0] + " " + tokens[1];
addMessage(NULL, msg);
} else {
addMessage (NULL, "This is a server-defined variable. "
"Use /set instead of /localset.");
}
}
else {
if (debug) {
addMessage(NULL, "usage: /localset <pattern> [value]");
} else {
addMessage(NULL, "usage: /localset <variable> [value]");
}
}
return true;
}
bool QuitCommand::operator() (const char *commandLine)
{
char messageBuffer[MessageLen]; // send message
memset(messageBuffer, 0, MessageLen);
strncpy(messageBuffer, commandLine, MessageLen);
nboPackString(messageMessage + PlayerIdPLen, messageBuffer,
MessageLen);
serverLink->send(MsgMessage, sizeof(messageMessage), messageMessage);
CommandsStandard::quit(); // kill client
return true;
}
bool RoamPosCommand::operator() (const char *commandLine)
{
// change the observer position and orientation
std::string params = commandLine + 8;
std::vector<std::string> tokens = TextUtils::tokenize(params, " ");
if (tokens.size() == 1) {
Roaming::RoamingCamera cam;
if (TextUtils::tolower(tokens[0]) == "reset") {
ROAM.resetCamera();
} else if (TextUtils::tolower(tokens[0]) == "send") {
LocalPlayer* myTank = LocalPlayer::getMyTank();
if (myTank != NULL) {
const Roaming::RoamingCamera* camPtr = ROAM.getCamera();
float fakeVel[3] = { camPtr->theta, camPtr->phi, camPtr->zoom };
myTank->move(camPtr->pos, camPtr->theta);
myTank->setVelocity(fakeVel);
serverLink->sendPlayerUpdate(myTank);
}
} else {
const float degrees = parseFloatExpr(tokens[0], true);
const float ws = BZDB.eval(StateDatabase::BZDB_WORLDSIZE);
const float radians = degrees * ((float)M_PI/180.0f);
cam.pos[0] = cosf(radians)* 0.5f * ws * (float)M_SQRT2;
cam.pos[1] = sinf(radians)* 0.5f * ws * (float)M_SQRT2;
cam.pos[2] = +0.25f * ws;
cam.theta = degrees + 180.0f;
cam.phi = -30.0f;
cam.zoom = 60.0f;
ROAM.setCamera(&cam);
}
}
else if (tokens.size() >= 3) {
Roaming::RoamingCamera cam;
cam.pos[0] = parseFloatExpr(tokens[0], true);
cam.pos[1] = parseFloatExpr(tokens[1], true);
cam.pos[2] = parseFloatExpr(tokens[2], true);
if (tokens.size() >= 4) {
cam.theta = parseFloatExpr(tokens[3], true);
} else {
cam.theta = ROAM.getCamera()->theta;
}
if (tokens.size() >= 5) {
cam.phi = parseFloatExpr(tokens[4], true);
} else {
cam.phi = ROAM.getCamera()->phi;
}
if (tokens.size() == 6) {
cam.zoom = parseFloatExpr(tokens[5], true);
} else {
cam.zoom = ROAM.getCamera()->zoom;
}
ROAM.setCamera(&cam);
}
else {
addMessage(NULL,
"/roampos "
"[ \"reset\" | degrees | {x y z [theta [phi [ zoom ]]] } |
\"send\" ]");
const Roaming::RoamingCamera* cam = ROAM.getCamera();
char buffer[MessageLen];
snprintf(buffer, MessageLen,
" <%.3f, %.3f, %.3f> theta = %.3f, phi = %.3f, zoom = %.3f",
cam->pos[0], cam->pos[1], cam->pos[2],
cam->theta, cam->phi, cam->zoom);
addMessage(NULL, buffer);
}
return true;
}
bool ReTextureCommand::operator() (const char *)
{
TextureManager& tm = TextureManager::instance();
tm.reloadTextures();
return true;
}
bool SaveMsgsCommand::operator() (const char *commandLine)
{
if (controlPanel == NULL) {
return true;
}
std::vector<std::string> args;
args = TextUtils::tokenize(commandLine, " ");
const int argCount = (int)args.size();
bool stripAnsi = false;
if ((argCount > 1) && (args[1] == "-s")) {
stripAnsi = true;
}
std::string filename = getConfigDirName() + "msglog.txt";
controlPanel->saveMessages(filename, stripAnsi);
std::string msg = "Saved messages to: " + filename;
addMessage(NULL, msg);
return true;
}
static void sendSaveWorldHelp()
{
addMessage(NULL, "/saveworld [-g] [-m] [-o] <filename>");
addMessage(NULL, " -g : save ungrouped");
addMessage(NULL, " -m : save some primitives as meshes");
addMessage(NULL, " -o : save meshes into WaveFront OBJ files");
return;
}
bool SaveWorldCommand::operator() (const char *commandLine)
{
bool meshprims = false;
bool ungrouped = false;
bool wavefront = false;
std::string cmdLine = commandLine;
std::vector<std::string> args;
args = TextUtils::tokenize(commandLine, " ");
const int argCount = (int)args.size();
if (argCount <= 1) {
sendSaveWorldHelp();
return true;
}
int pos = 1;
while (pos < (argCount - 1)) {
const std::string& arg = args[pos];
if (arg == "-m") {
meshprims = true;
} else if (arg == "-g") {
ungrouped = true;
} else if (arg == "-o") {
wavefront = true;
meshprims = true;
ungrouped = true;
} else {
break;
}
pos++;
}
if (pos != (argCount - 1)) {
sendSaveWorldHelp();
return true;
}
const std::string& filename = args[pos];
BZDB.set("saveAsMeshes", meshprims ? "1" : "0");
BZDB.set("saveFlatFile", ungrouped ? "1" : "0");
BZDB.set("saveAsOBJ", wavefront ? "1" : "0");
World* world = World::getWorld();
if (!world) {
return true;
}
char buffer[256];
std::string fullname;
if (World::getWorld()->writeWorld(filename, fullname)) {
snprintf(buffer, 256, "World saved: %s %s%s%s", fullname.c_str(),
meshprims ? " [meshprims]" : "",
ungrouped ? " [ungrouped]" : "",
wavefront ? " [wavefront]" : "");
} else {
snprintf(buffer, 256, "Error saving: %s", fullname.c_str());
}
addMessage(NULL, buffer);
return true;
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "ComposeDefaultKey.h"
/* common implementation headers */
#include "BzfEvent.h"
#include "KeyManager.h"
//test
/* local implementation headers */
#include "Roster.h"
#include "TargetingUtils.h"
#include "World.h"
#include "WorldPlayer.h"
#include "playing.h"
#include "Plan.h"
#include "LocalPlayer.h"
#include "HUDRenderer.h"
#include "LocalCommand.h"
#include "playing.h"
#include "HUDui.h"
#include "ComposeDefaultKey.h"
#define MAX_MESSAGE_HISTORY (20)
MessageQueue messageHistory;
unsigned int messageHistoryIndex = 0;
extern bool bCheatOO;
extern bool bCheatGravity;
extern int killMe;
//test
static RemotePlayer *findBestTarget()
{
RemotePlayer *target = NULL;
LocalPlayer *myTank = LocalPlayer::getMyTank();
const float *pos = myTank->getPosition();
float myAzimuth = myTank->getAngle();
float distance = Infinity;
for (int t = 0; t < curMaxPlayers; t++) {
if ((t != myTank->getId())
&& (player[t])
&& (player[t]->isAlive())
&& (!player[t]->isPaused())
&& (!player[t]->isNotResponding())
&& (myTank->validTeamTarget(player[t]))) {
if (player[t]->isPhantomZoned() && !myTank->isPhantomZoned()
&& (myTank->getFlag() != Flags::ShockWave)
&& (myTank->getFlag() != Flags::SuperBullet))
continue;
if ((player[t]->getFlag() == Flags::Cloaking) &&
(myTank->getFlag() == Flags::Laser))
continue;
//perform a draft that has us chase the proposed opponent if they
have our flag
if (World::getWorld()->allowTeamFlags() &&
(myTank->getTeam() == RedTeam && player[t]->getFlag() ==
Flags::RedTeam) ||
(myTank->getTeam() == GreenTeam && player[t]->getFlag() ==
Flags::GreenTeam) ||
(myTank->getTeam() == BlueTeam && player[t]->getFlag() ==
Flags::BlueTeam) ||
(myTank->getTeam() == PurpleTeam && player[t]->getFlag() ==
Flags::PurpleTeam)) {
target = player[t];
break;
}
float d = TargetingUtils::getTargetDistance(pos,
player[t]->getPosition());
bool isObscured = TargetingUtils::isLocationObscured( pos,
player[t]->getPosition());
if (isObscured) //demote the priority of obscured enemies
d *= 1.25f;
if (d < distance) {
if ((player[t]->getFlag() != Flags::Stealth)
|| (myTank->getFlag() == Flags::Seer)
|| ((!isObscured) &&
(TargetingUtils::getTargetAngleDifference(pos, myAzimuth,
player[t]->getPosition()) <= 30.0f))) {
target = player[t];
distance = d;
}
}
}
}
return target;
}
static bool isWordCompletion(const BzfKeyEvent& key)
{
if ((key.ascii == 6) || // ^F
(key.ascii == 9) || // <TAB>
((key.shift == 0) && (key.button == BzfKeyEvent::F2))) {
return true;
} else {
return false;
}
}
bool ComposeDefaultKey::keyPress(const BzfKeyEvent& key)
{
bool sendIt;
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (myTank && KEYMGR.get(key, true) == "jump" &&
BZDB.isTrue("jumpTyping")) {
// jump while typing
myTank->setJump();
return false;
}
if (!myTank || (myTank->getInputMethod() != LocalPlayer::Keyboard)) {
if ((key.button == BzfKeyEvent::Up) ||
(key.button == BzfKeyEvent::Down))
return true;
}
if (isWordCompletion(key)) {
std::string matches;
hud->setComposeString(completer.complete(hud->getComposeString(),
&matches));
if (matches.size() > 0) {
controlPanel->addMessage(matches, -1);
}
return true;
}
switch (key.ascii) {
case 3: // ^C
case 27: { // escape
sendIt = false; // finished composing -- don't send
break;
}
case 4: // ^D
case 13: { // return
sendIt = true;
break;
}
default: {
return false;
}
}
if (sendIt) {
std::string message = hud->getComposeString();
if(message=="/b") { // Teleports behind desired person by using "."
button
const Player *Other = LocalPlayer::getMyTank()->getRecipient();
if(Other)
{
LocalPlayer::getMyTank()->setStatus(LocalPlayer::getMyTank()->getStatus()
| int(PlayerState::Teleporting));
const float* pos = Other->getPosition();
const float* dir = Other->getForward();
const float* vel = Other->getVelocity();
float newpos[3];
float newvel[3];
newpos[0] = pos[0] - 10 * dir[0];
newpos[1] = pos[1] - 10 * dir[1];
newpos[2] = pos[2] + 0.01;
newvel[0] = vel[0];
newvel[1] = vel[1];
newvel[2] = vel[2];
LocalPlayer::getMyTank()->move(newpos, Other->getAngle());
LocalPlayer::getMyTank()->setStatus(LocalPlayer::getMyTank()->getStatus()
& ~int(PlayerState::Teleporting));
}
}
else if(message=="/g1") // Float in the air
{
bCheatGravity = !bCheatGravity; //test
LocalPlayer *myTank = LocalPlayer::getMyTank();
const float * oldVel = myTank->getVelocity();
float newVelocity[3];
newVelocity[0] = oldVel[0];
newVelocity[1] = oldVel[0];
newVelocity[2] = 0;
myTank->setVelocity(newVelocity);
}
else if (message=="/up"){ // Shoots you up in the air
LocalPlayer *myTank = LocalPlayer::getMyTank();
const float* oldVelocity = myTank->getVelocity();
float newVelocity[3];
newVelocity[0] = oldVelocity[0];
newVelocity[1] = oldVelocity[1];
newVelocity[2] = BZDB.eval(StateDatabase::BZDB_WINGSJUMPVELOCITY);
newVelocity[2] *= 50;
myTank->setVelocity(newVelocity);
}
else if (message=="/down"){ // Shoots you back down
LocalPlayer *myTank = LocalPlayer::getMyTank();
const float* oldVelocity = myTank->getVelocity();
float newVelocity[3];
newVelocity[0] = oldVelocity[0];
newVelocity[1] = oldVelocity[1];
newVelocity[2] = BZDB.eval(StateDatabase::BZDB_WINGSJUMPVELOCITY);
newVelocity[2] *= -50;
myTank->setVelocity(newVelocity);
}
else if(message=="/oo")bCheatOO = !bCheatOO; //We added this line,
which toggles bCheatOO
else if (message=="/f")serverLink->sendGrabFlag(rand()%numFlags);
//Makes a random number
else if (message=="/a")serverLink->sendGrabFlag(1);
else if (message=="/2")serverLink->sendGrabFlag(2);
else if (message=="/bu")serverLink->sendGrabFlag(3);
else if (message=="/4")serverLink->sendGrabFlag(4);
else if (message=="/5")serverLink->sendGrabFlag(5);
else if (message=="/cl")serverLink->sendGrabFlag(6);
else if (message=="/7")serverLink->sendGrabFlag(7);
else if (message=="/8")serverLink->sendGrabFlag(8);
else if (message=="/rf")serverLink->sendGrabFlag(9);
else if (message=="/10")serverLink->sendGrabFlag(10);
else if (message=="/11")serverLink->sendGrabFlag(11);
else if (message=="/g")serverLink->sendGrabFlag(12);
else if (message=="/13")serverLink->sendGrabFlag(13);
else if (message=="/14")serverLink->sendGrabFlag(14);
else if (message=="/gm")serverLink->sendGrabFlag(15);
else if (message=="/16")serverLink->sendGrabFlag(16);
else if (message=="/17")serverLink->sendGrabFlag(17);
else if (message=="/ib")serverLink->sendGrabFlag(18);
else if (message=="/19")serverLink->sendGrabFlag(19);
else if (message=="/20")serverLink->sendGrabFlag(20);
else if (message=="/l")serverLink->sendGrabFlag(21);
else if (message=="/22")serverLink->sendGrabFlag(22);
else if (message=="/23")serverLink->sendGrabFlag(23);
else if (message=="/mg")serverLink->sendGrabFlag(24);
else if (message=="/25")serverLink->sendGrabFlag(25);
else if (message=="/26")serverLink->sendGrabFlag(26);
else if (message=="/mq")serverLink->sendGrabFlag(27);
else if (message=="/28")serverLink->sendGrabFlag(28);
else if (message=="/29")serverLink->sendGrabFlag(29);
else if (message=="/n")serverLink->sendGrabFlag(30);
else if (message=="/31")serverLink->sendGrabFlag(31);
else if (message=="/32")serverLink->sendGrabFlag(32);
else if (message=="/o")serverLink->sendGrabFlag(33);
else if (message=="/34")serverLink->sendGrabFlag(34);
else if (message=="/35")serverLink->sendGrabFlag(35);
else if (message=="/pz")serverLink->sendGrabFlag(36);
else if (message=="/37")serverLink->sendGrabFlag(37);
else if (message=="/38")serverLink->sendGrabFlag(38);
else if (message=="/qt")serverLink->sendGrabFlag(39);
else if (message=="/40")serverLink->sendGrabFlag(40);
else if (message=="/41")serverLink->sendGrabFlag(41);
else if (message=="/sb")serverLink->sendGrabFlag(42);
else if (message=="/43")serverLink->sendGrabFlag(43);
else if (message=="/44")serverLink->sendGrabFlag(44);
else if (message=="/45")serverLink->sendGrabFlag(45);
else if (message=="/se")serverLink->sendGrabFlag(46);
else if (message=="/47")serverLink->sendGrabFlag(47);
else if (message=="/48")serverLink->sendGrabFlag(48);
else if (message=="/sh")serverLink->sendGrabFlag(49);
else if (message=="/50")serverLink->sendGrabFlag(50);
else if (message=="/51")serverLink->sendGrabFlag(51);
else if (message=="/sr")serverLink->sendGrabFlag(52);
else if (message=="/53")serverLink->sendGrabFlag(53);
else if (message=="/54")serverLink->sendGrabFlag(54);
else if (message=="/st")serverLink->sendGrabFlag(55);
else if (message=="/56")serverLink->sendGrabFlag(56);
else if (message=="/57")serverLink->sendGrabFlag(57);
else if (message=="/58")serverLink->sendGrabFlag(58);
else if (message=="/sw")serverLink->sendGrabFlag(59);
else if (message=="/60")serverLink->sendGrabFlag(60);
else if (message=="/61")serverLink->sendGrabFlag(61);
else if (message=="/t")serverLink->sendGrabFlag(62);
else if (message=="/63")serverLink->sendGrabFlag(63);
else if (message=="/64")serverLink->sendGrabFlag(64);
else if (message=="/th")serverLink->sendGrabFlag(65);
else if (message=="/66")serverLink->sendGrabFlag(66);
else if (message=="/67")serverLink->sendGrabFlag(67);
else if (message=="/68")serverLink->sendGrabFlag(68);
else if (message=="/69")serverLink->sendGrabFlag(69);
else if (message=="/us")serverLink->sendGrabFlag(70);
else if (message=="/71")serverLink->sendGrabFlag(71);
else if (message=="/72")serverLink->sendGrabFlag(72);
else if (message=="/73")serverLink->sendGrabFlag(73);
else if (message=="/hs")serverLink->sendGrabFlag(74);
else if (message=="/75")serverLink->sendGrabFlag(75);
else if (message=="/76")serverLink->sendGrabFlag(76);
else if (message=="/wg")serverLink->sendGrabFlag(77);
else if (message=="/s") {
const Player *Other = LocalPlayer::getMyTank()->getRecipient();
if(Other)
{
serverLink->sendTransferFlag(LocalPlayer::getMyTank()->getId(),Other->getId());
}
}
else if(message=="/k") {
const Player *Other = LocalPlayer::getMyTank()->getRecipient();
if(Other)
{
killMe = Other->getId();
}
}
else if (message.length() > 0) {
const char* cmd = message.c_str();
if (LocalCommand::execute(cmd)) {
} else if (serverLink) {
char messageBuffer[MessageLen];
memset(messageBuffer, 0, MessageLen);
strncpy(messageBuffer, message.c_str(), MessageLen);
nboPackString(messageMessage + PlayerIdPLen, messageBuffer,
MessageLen);
serverLink->send(MsgMessage, sizeof(messageMessage), messageMessage);
}
// record message in history
int i, mhLen = messageHistory.size();
for (i = 0; i < mhLen; i++) {
if (messageHistory[i] == message) {
messageHistory.erase(messageHistory.begin() + i);
messageHistory.push_front(message);
break;
}
}
if (i == mhLen) {
if (mhLen >= MAX_MESSAGE_HISTORY) {
messageHistory.pop_back();
}
messageHistory.push_front(message);
}
}
}
messageHistoryIndex = 0;
hud->setComposing(std::string());
HUDui::setDefaultKey(NULL);
return true;
}
bool ComposeDefaultKey::keyRelease(const BzfKeyEvent& key)
{
LocalPlayer *myTank = LocalPlayer::getMyTank();
if (!myTank || myTank->getInputMethod() != LocalPlayer::Keyboard) {
if (key.button == BzfKeyEvent::Up) {
if (messageHistoryIndex < messageHistory.size()) {
hud->setComposeString(messageHistory[messageHistoryIndex]);
messageHistoryIndex++;
}
else
hud->setComposeString(std::string());
return true;
}
else if (key.button == BzfKeyEvent::Down) {
if (messageHistoryIndex > 0){
messageHistoryIndex--;
hud->setComposeString(messageHistory[messageHistoryIndex]);
}
else
hud->setComposeString(std::string());
return true;
}
else if (myTank && ((key.shift == BzfKeyEvent::ShiftKey
|| (hud->getComposeString().length() == 0)) &&
(key.button == BzfKeyEvent::Left
|| key.button == BzfKeyEvent::Right))) {
// exclude robot from private message recipient.
// No point sending messages to robot (now)
selectNextRecipient(key.button != BzfKeyEvent::Left, false);
const Player *recipient = myTank->getRecipient();
if (recipient) {
void* buf = messageMessage;
buf = nboPackUByte(buf, recipient->getId());
std::string composePrompt = "Send to ";
composePrompt += recipient->getCallSign();
composePrompt += ": ";
hud->setComposing(composePrompt);
}
return false;
}
}
if ((key.ascii == 4) || // ^D
(key.ascii == 6) || // ^F
(key.ascii == 13) || // return
isWordCompletion(key)) {
return true;
}
return keyPress(key);
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* BZFlag common header */
#include "common.h"
/* interface header */
#include "ControlPanel.h"
/* system headers */
#include <assert.h>
#include <time.h>
/* common implementation headers */
#include "BZDBCache.h"
#include "FontManager.h"
#include "TextUtils.h"
#include "ErrorHandler.h"
#include "global.h"
/* local implementation headers */
#include "SceneRenderer.h"
#include "RadarRenderer.h"
#include "bzflag.h"
//
// ControlPanelMessage
//
ControlPanelMessage::ControlPanelMessage(const std::string& _string) :
string(_string), numlines(0)
{
}
void ControlPanelMessage::breakLines(float maxLength, int fontFace,
float fontSize)
{
FontManager &fm = FontManager::instance();
// get message and its length
const char* msg = string.c_str();
int lineLen = (int)string.length();
// if there are tabs in the message, find the last one
int lastTab = (int)string.rfind('\t');
lines.clear();
numlines=0;
// in order for the new font engine to draw successive lines in the
right
// color, it needs to be fed the right ansi codes at the beginning of
each
// line.
std::string cumulativeANSICodes = "";
// break lines
while (lineLen > 0) {
int lastWhitespace = 0;
int n;
// how many characters will fit?
// the unprinted ANSI codes don't count
if ((fm.getStrLength(fontFace, fontSize, msg) <= maxLength) &&
(lastTab <= 0)) {
n = lineLen;
} else {
n = 0;
while ((n < lineLen) &&
(fm.getStrLength(fontFace, fontSize, std::string(msg, n+1)) <
maxLength)) {
if (msg[n] == ESC_CHAR) {
// clear the cumulative codes when we hit a reset
// the reset itself will start the new cumulative string.
if ((strncmp(msg + n, ANSI_STR_RESET, strlen(ANSI_STR_RESET)) == 0)
|| (strncmp(msg + n, ANSI_STR_RESET_FINAL,
strlen(ANSI_STR_RESET_FINAL)) == 0))
cumulativeANSICodes = "";
// add this code to our cumulative string
cumulativeANSICodes += msg[n];
n++;
if ((n < lineLen) && (msg[n] == '[')) {
cumulativeANSICodes += msg[n];
n++;
while ((n < lineLen) && ((msg[n] == ';') ||
((msg[n] >= '0') && (msg[n] <= '9')))) {
cumulativeANSICodes += msg[n];
n++;
}
// ditch the terminating character too
if (n < lineLen) {
cumulativeANSICodes += msg[n];
n++;
}
}
} else {
n++;
}
if (TextUtils::isWhitespace(msg[n])) {
lastWhitespace = n;
// Tabs break out into their own message. These get dealt with
// in ControlPanel::render, which will increment x instead of y.
if (msg[n] == '\t')
break;
}
}
}
if (lastWhitespace > 0)
n = lastWhitespace;
// message
lines.push_back(cumulativeANSICodes + std::string(msg,n));
if (msg[n] != '\t')
numlines++;
// account for portion broken
msg += n;
lineLen -= n;
lastTab -= n;
}
}
//
// ControlPanel
//
int ControlPanel::messagesOffset = 0;
ControlPanel::ControlPanel(MainWindow& _mainWindow, SceneRenderer&
_renderer) :
tabsOnRight(true),
tabs(NULL),
totalTabWidth(0),
window(_mainWindow),
resized(false),
numBuffers(2),
changedMessage(0),
radarRenderer(NULL),
renderer(&_renderer),
fontFace(0),
dimming(1.0f),
du(0),
dv(0),
messageMode(MessageAll)
{
setControlColor();
// make sure we're notified when MainWindow resizes or is exposed
window.getWindow()->addResizeCallback(resizeCallback, this);
window.getWindow()->addExposeCallback(exposeCallback, this);
BZDB.addCallback("displayRadar", bzdbCallback, this);
BZDB.addCallback(StateDatabase::BZDB_RADARLIMIT, bzdbCallback, this);
// other initialization
radarAreaPixels[0] = 0;
radarAreaPixels[1] = 0;
radarAreaPixels[2] = 0;
radarAreaPixels[3] = 0;
messageAreaPixels[0] = 0;
messageAreaPixels[1] = 0;
messageAreaPixels[2] = 0;
messageAreaPixels[3] = 0;
for (int i = 0; i < MessageModeCount; i++) {
messages[i].clear();
unRead[i] = false;
}
teamColor[0] = teamColor[1] = teamColor[2] = (GLfloat)0.0f;
maxLines = 30;
/* use pointer initialization to perform precomputation and
* prestorage. eventually should move this data into constructor.
*/
if (tabs == NULL) {
tabs = new std::vector<const char *>;
tabs->push_back("All");
tabs->push_back("Chat");
tabs->push_back("Server");
tabs->push_back("Misc");
}
resize(); // need resize to set up font and window dimensions
}
ControlPanel::~ControlPanel()
{
// don't notify me anymore (cos you can't wake the dead!)
window.getWindow()->removeResizeCallback(resizeCallback, this);
window.getWindow()->removeExposeCallback(exposeCallback, this);
BZDB.removeCallback("displayRadar", bzdbCallback, this);
BZDB.removeCallback(StateDatabase::BZDB_RADARLIMIT, bzdbCallback,
this);
if (echoToConsole && echoAnsi) {
std::cout << ColorStrings[FinalResetColor] << std::flush;
}
if (tabs != NULL) {
tabs->clear();
delete tabs;
tabTextWidth.clear();
totalTabWidth=0;
}
}
void ControlPanel::bzdbCallback(const std::string& /*name*/, void*
data)
{
((ControlPanel*)data)->resize();
return;
}
void ControlPanel::setControlColor(const GLfloat *color)
{
if (color)
memcpy(teamColor, color, 3 * sizeof(float));
else
memset(teamColor, 0, 3 * sizeof(float));
}
void ControlPanel::render(SceneRenderer& _renderer)
{
if (!resized) resize();
// optimization for software rendering folks
if (!changedMessage && _renderer.getPanelOpacity() == 1.0f) {
return;
}
int i, j;
const int x = window.getOriginX();
const int y = window.getOriginY();
const int w = window.getWidth();
const int tabStyle = (int)BZDB.eval("showtabs");
const bool showTabs = (tabStyle > 0);
tabsOnRight = (tabStyle == 2);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, (double)w, 0.0, window.getHeight(), -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
OpenGLGState::resetState();
FontManager &fm = FontManager::instance();
fm.setOpacity(dimming);
if (changedMessage > 0) {
changedMessage--;
}
float fx = messageAreaPixels[0] + margin;
float fy = messageAreaPixels[1] + margin + 1.0f;
int ay = (_renderer.getPanelOpacity() == 1.0f || !showTabs) ? 0
: int(lineHeight + 4);
glScissor(x + messageAreaPixels[0] - 1,
y + messageAreaPixels[1],
messageAreaPixels[2] + 1,
messageAreaPixels[3] + ay);
OpenGLGState::resetState();
if (_renderer.getPanelOpacity() > 0.0f) {
// nice blended messages background
if (BZDBCache::blend && _renderer.getPanelOpacity() < 1.0f)
glEnable(GL_BLEND);
// clear the background
glColor4f(0.0f, 0.0f, 0.0f, _renderer.getPanelOpacity());
glRecti(messageAreaPixels[0] - 1, // clear an extra pixel column to
simplify fuzzy float stuff later
messageAreaPixels[1],
messageAreaPixels[0] + messageAreaPixels[2],
messageAreaPixels[1] + messageAreaPixels[3]);
// display tabs for chat sections
if (showTabs) {
long int drawnTabWidth = 0;
for (unsigned int tab = 0; tab < tabs->size(); tab++) {
// current mode is given a dark background to match the control panel
if (messageMode == MessageModes(tab)) {
glColor4f(0.0f, 0.0f, 0.0f, _renderer.getPanelOpacity());
} else {
glColor4f(0.10f, 0.10f, 0.10f, _renderer.getPanelOpacity());
}
if (tabsOnRight) {
// draw the tabs on the right side
glRecti(messageAreaPixels[0] + messageAreaPixels[2] - totalTabWidth
+ drawnTabWidth,
messageAreaPixels[1] + messageAreaPixels[3] - int(lineHeight + 4) +
ay,
messageAreaPixels[0] + messageAreaPixels[2] - totalTabWidth +
drawnTabWidth + int(tabTextWidth[tab]), //+ drawnTabWidth +
int(tabTextWidth[tab]),
messageAreaPixels[1] + messageAreaPixels[3] + ay);
} else {
// draw the tabs on the left side
glRecti(messageAreaPixels[0] + drawnTabWidth,
messageAreaPixels[1] + messageAreaPixels[3] - int(lineHeight + 4) +
ay,
messageAreaPixels[0] + drawnTabWidth + int(tabTextWidth[tab]),
messageAreaPixels[1] + messageAreaPixels[3] + ay);
}
drawnTabWidth += long(tabTextWidth[tab]);
} // end iteration over tabs
}
if (BZDBCache::blend && _renderer.getPanelOpacity() < 1.0f)
glDisable(GL_BLEND);
}
// show scroll indicator if not at end
if (messagesOffset > 0 && messageMode >= 0)
{
int lines = int(messages[messageMode].size());
if (lines > 0) {
const float size = std::max(float(maxLines) / lines, 0.02f);
const float offset = float(messagesOffset) / lines;
const int maxTop = messageAreaPixels[1] + messageAreaPixels[3];
int top = messageAreaPixels[1] + int((offset + size) *
(float)messageAreaPixels[3]);
if (top > maxTop) {
top = maxTop;
}
glColor3f(0.7f, 0.7f, 0.7f);
glRecti(messageAreaPixels[0],
messageAreaPixels[1] + int(offset *
(float)messageAreaPixels[3]),
messageAreaPixels[0] + 2, top);
}
}
// Draw tab labels
if (showTabs) {
long int drawnTabWidth = 0;
for (unsigned int tab = 0; tab < tabs->size(); tab++) {
// current mode is bright, others are not so bright
if (messageMode == MessageModes(tab)) {
glColor4f(1.0f, 1.0f, 1.0f, dimming);
} else if (unRead[MessageModes(tab)]) {
glColor4f(0.5f, 0.0f, 0.0f, dimming);
} else {
glColor4f(0.5f, 0.5f, 0.5f, dimming);
}
if (tabsOnRight) {
// draw the tabs on the right side (with one letter padding)
fm.drawString(messageAreaPixels[0] + messageAreaPixels[2] -
totalTabWidth + drawnTabWidth + floorf(fontSize),
messageAreaPixels[1] + messageAreaPixels[3] - floorf(lineHeight
+ 2.0f) + ay,
0.0f, fontFace, (float)fontSize, (*tabs)[tab]);
} else {
// draw the tabs on the left side (with one letter padding)
fm.drawString(messageAreaPixels[0] + drawnTabWidth + floorf(fontSize),
messageAreaPixels[1] + messageAreaPixels[3] - floorf(lineHeight
+ 2.0f) + ay,
0.0f, fontFace, (float)fontSize, (*tabs)[tab]);
}
drawnTabWidth += long(tabTextWidth[tab]);
}
}
/* draw messages
*
* It works by first breaking the string into a vector of strings
* (done elsewhere), each of which will fit the control panel, and
* tallying the number of lines, then moving up the proper number of
* lines and displaying downward -- that is, it kinda backtracks for
* each line that will wrap.
*
* messageAreaPixels[2] = Width of Message Window in Pixels.
*
* maxLines = Max messages lines that can be displayed at once per
* page. This COULD be a BZDB parameter (but isn't).
*
* maxScrollPages = This number * maxLines is the total maximum
* lines of messages (and scrollback). It is stored as a BZDB
* parameter.
*/
glScissor(x + messageAreaPixels[0],
y + messageAreaPixels[1],
messageAreaPixels[2],
messageAreaPixels[3] - (showTabs ? int(lineHeight + 4) : 0) + ay);
if (messageMode >= 0) {
i = (int)messages[messageMode].size() - 1;
} else {
i = -1;
}
if (i >= 0 && messagesOffset > 0) {
i -= messagesOffset;
if (i < 0)
i = 0;
}
const std::string highlightPattern = BZDB.get("highlightPattern");
bool useHighlight = (highlightPattern.size() > 0);
regex_t re;
if (useHighlight) {
if (regcomp(&re, highlightPattern.c_str(), REG_EXTENDED |
REG_ICASE) != 0) {
useHighlight = false; // bad regex
}
}
for (j = 0; i >= 0 && j < maxLines; i--) {
// draw each line of text
int numLines = messages[messageMode][i].numlines;
int numStrings = (int)messages[messageMode][i].lines.size();
int msgy = numLines - 1;
int msgx = 0;
// see if this message need to be highlighted (check each line)
bool highlight = false;
if (useHighlight) {
for (int l = 0; l < numStrings; l++) {
const std::string &msg = messages[messageMode][i].lines[l];
std::string raw = stripAnsiCodes(msg);
if (regexec(&re, raw.c_str(), 0, NULL, 0) == 0) {
highlight = true;
}
}
}
// default to drawing text in white
GLfloat whiteColor[4] = {1.0f, 1.0f, 1.0f, dimming};
glColor4fv(whiteColor);
bool isTab = false;
for (int l = 0; l < numStrings; l++) {
const std::string &msg = messages[messageMode][i].lines[l];
// Tab chars move horizontally instead of vertically
// It doesn't matter where in the string the tab char is
// Usually it will be like <ansi><ansi><ansi>\ttext
// We use 1 tabstop spaced 1/3 of the way across the controlpanel
isTab = (msg.find('\t') != std::string::npos);
if (isTab) {
msgx += messageAreaPixels[2] / 3;
msgy++;
} else {
msgx = 0;
}
assert(msgy >= 0);
// only draw message if inside message area
if (j + msgy < maxLines) {
if (!highlight) {
fm.drawString(fx + msgx, fy + msgy * lineHeight, 0, fontFace,
fontSize, msg);
} else {
// highlight this line
std::string newMsg = ANSI_STR_PULSATING;
newMsg += ANSI_STR_UNDERLINE;
newMsg += ANSI_STR_FG_CYAN;
newMsg += stripAnsiCodes(msg);
fm.drawString(fx + msgx, fy + msgy * lineHeight, 0, fontFace,
fontSize, newMsg);
}
}
// next line
msgy--;
}
j += numLines;
fy += int(lineHeight * numLines);
}
// free the regex memory
if (useHighlight) {
regfree(&re);
}
glScissor(x + messageAreaPixels[0] - 2,
y + messageAreaPixels[1] - 2,
messageAreaPixels[2] + 3,
messageAreaPixels[3] + 33);
OpenGLGState::resetState();
// draw the lines around the console panel
long xpos;
long ypos;
float outlineOpacity = RENDERER.getPanelOpacity();
float fudgeFactor = BZDBCache::hudGUIBorderOpacityFactor; // bzdb
cache this manybe?
if ( outlineOpacity < 1.0f )
outlineOpacity = (outlineOpacity*fudgeFactor) + (1.0f - fudgeFactor);
if (BZDBCache::blend)
glEnable(GL_BLEND);
// nice border
glColor4f(teamColor[0], teamColor[1], teamColor[2],outlineOpacity );
glBegin(GL_LINE_LOOP); {
// bottom left
xpos = x + messageAreaPixels[0] - 1;
ypos = y + messageAreaPixels[1] - 1;
glVertex2f((float) xpos, (float) ypos);
// bottom right
xpos += messageAreaPixels[2] + 1;
glVertex2f((float) xpos, (float) ypos);
// top right
ypos += messageAreaPixels[3] + 1;
glVertex2f((float) xpos, (float) ypos);
// over to panel on left
if (!tabsOnRight) {
xpos = x + messageAreaPixels[0] + totalTabWidth;
glVertex2f((float) xpos, (float) ypos);
}
// across the top from right to left
long int drawnTabWidth = 0;
for (int tab = (int)tabs->size() - 1; tab >= 0; tab--) {
if (messageMode == MessageModes(tab)) {
ypos += ay;
glVertex2f((float) xpos, (float) ypos);
xpos -= long(tabTextWidth[tab]) + 1;
glVertex2f((float) xpos, (float) ypos);
ypos -= ay;
glVertex2f((float) xpos, (float) ypos);
} else {
xpos -= long(tabTextWidth[tab]);
glVertex2f((float) xpos, (float) ypos);
}
drawnTabWidth += long(tabTextWidth[tab]);
}
// over from panel on right
// if (tabsOnRight) {
xpos = x + messageAreaPixels[0] - 1;
glVertex2f((float) xpos, (float) ypos);
// }
} glEnd();
// some engines miss the corners
glBegin(GL_POINTS); {
glVertex2f((float) (x + messageAreaPixels[0] - 1),
(float) (y + messageAreaPixels[1] - 1));
glVertex2f((float) (x + messageAreaPixels[0] +
messageAreaPixels[2]),
(float) (y + messageAreaPixels[1] - 1));
glVertex2f((float) (x + messageAreaPixels[0] +
messageAreaPixels[2]),
(float) (y + messageAreaPixels[1] + messageAreaPixels[3]));
glVertex2f((float) (x + messageAreaPixels[0] - 1),
(float) (y + messageAreaPixels[1] + messageAreaPixels[3]));
long int tabPosition = 0;
for (int tab = 0; tab < (int)tabs->size(); tab++) {
if (messageMode == MessageModes(tab)) {
if (tabsOnRight) {
glVertex2f((float) (x + messageAreaPixels[0] + messageAreaPixels[2]
- totalTabWidth + tabPosition),
(float) (y + messageAreaPixels[1] + messageAreaPixels[3] + ay));
glVertex2f((float) (x + messageAreaPixels[0] + messageAreaPixels[2]
- totalTabWidth + tabPosition + long(tabTextWidth[tab])),
(float) (y + messageAreaPixels[1] + messageAreaPixels[3] + ay));
} else {
glVertex2f((float) (x + messageAreaPixels[0] + tabPosition),
(float) (y + messageAreaPixels[1] + messageAreaPixels[3] + ay));
glVertex2f((float) (x + messageAreaPixels[0] + tabPosition +
long(tabTextWidth[tab])),
(float) (y + messageAreaPixels[1] + messageAreaPixels[3] + ay));
}
}
tabPosition += long(tabTextWidth[tab]);
}
} glEnd();
if (BZDBCache::blend)
glDisable(GL_BLEND);
glColor4f(teamColor[0], teamColor[1], teamColor[2],1.0f );
glPopMatrix();
fm.setOpacity(1.0f);
}
void ControlPanel::resize()
{
float radarSpace, radarSize;
// get important metrics
const float w = (float)window.getWidth();
const float h = (float)window.getHeight();
const float opacity = RENDERER.getPanelOpacity();
radarSize = float(window.getHeight() - window.getViewHeight());
if (opacity == 1.0f) {
radarSize = float(window.getHeight() - window.getViewHeight());
radarSpace = 0.0f;
} else {
radarSize = h * (14 + RENDERER.getRadarSize()) / 60.0f;
radarSpace = 3.0f * w / MinY;
}
// compute areas in pixels x,y,w,h
// leave off 1 pixel for the border
radarAreaPixels[0] = radarAreaPixels[1] = (int)radarSpace + 1;
radarAreaPixels[2] = radarAreaPixels[3] = (int)(radarSize -
(radarSpace * 2.0f)) - 2;
messageAreaPixels[0] = (int)radarSize + 1; // X coord
messageAreaPixels[1] = radarAreaPixels[1]; // Y coord
messageAreaPixels[2] = (int)(w - radarSize - radarSpace) - 2; //
Width
messageAreaPixels[3] = radarAreaPixels[3]; // Height
// if radar connected then resize it
if (radarRenderer)
radarRenderer->setShape(radarAreaPixels[0], radarAreaPixels[1],
radarAreaPixels[2], radarAreaPixels[3]);
switch (static_cast<int>(BZDB.eval("cpanelfontsize"))) {
case 0: { // auto
const bool useBigFont = (messageAreaPixels[2] / 100.0f) > 10.0f;
fontSize = useBigFont ? 12.0f : 8.0f;
break;
}
case 1: // tiny
fontSize = 6;
break;
case 2: // small
fontSize = 8;
break;
case 3: // medium
fontSize = 12;
break;
case 4: // big
fontSize = 16;
break;
}
FontManager &fm = FontManager::instance();
fontFace = fm.getFaceID(BZDB.get("consoleFont"));
// tab widths may have changed
if (tabs) {
tabTextWidth.clear();
totalTabWidth = 0;
const float charWidth = fm.getStrLength(fontFace, fontSize, "-");
for (unsigned int tab = 0; tab < tabs->size(); tab++) {
// add space for about 2-chars on each side for padding
tabTextWidth.push_back(fm.getStrLength(fontFace, fontSize,
(*tabs)[tab]) + (4.0f * charWidth));
totalTabWidth += long(tabTextWidth[tab]);
}
}
lineHeight = fm.getStrHeight(fontFace, fontSize, " ");
maxLines = int(messageAreaPixels[3] / lineHeight);
margin = lineHeight / 4.0f;
// rewrap all the lines
for (int i = 0; i < MessageModeCount; i++) {
for (int j = 0; j < (int)messages[i].size(); j++) {
messages[i][j].breakLines(messageAreaPixels[2] - 2 * margin,
fontFace, fontSize);
}
}
// note that we've been resized at least once
resized = true;
invalidate();
}
void ControlPanel::resizeCallback(void* self)
{
((ControlPanel*)self)->resize();
}
void ControlPanel::setNumberOfFrameBuffers(int n)
{
numBuffers = n;
}
void ControlPanel::invalidate()
{
if (numBuffers) {
changedMessage = numBuffers;
} else {
changedMessage++;
}
}
void ControlPanel::exposeCallback(void* self)
{
((ControlPanel*)self)->invalidate();
}
void ControlPanel::setMessagesOffset(int offset, int whence, bool
paged)
{
if (paged) {
if (abs(offset) <= 1) {
offset = offset * (maxLines - 1);
} else {
offset = offset * maxLines;
}
}
// offset = offset from whence (offset of 0 is the bottom/most
recent)
// whence = 0, 1, or 2 (akin to SEEK_SET, SEEK_CUR, SEEK_END)
switch (whence) {
case 0:
if (offset < (int)messages[messageMode].size())
messagesOffset = offset;
else
messagesOffset = (int)messages[messageMode].size() - 1;
break;
case 1:
if (offset > 0) {
if (messagesOffset + offset < (int)messages[messageMode].size())
messagesOffset += offset;
else
messagesOffset = (int)messages[messageMode].size() - 1;
} else if (offset < 0) {
if (messagesOffset + offset >= 0)
messagesOffset += offset;
else
messagesOffset = 0;
}
break;
case 2:
if (offset < 0) {
if ((int)messages[messageMode].size() >= offset)
messagesOffset += offset;
else
messagesOffset = 0;
}
break;
}
invalidate();
}
void ControlPanel::setMessagesMode(int _messageMode)
{
messageMode = _messageMode;
if (messageMode == MessageAll)
for (int i = 0; i < MessageModeCount; i++)
unRead[i] = false;
else if (messageMode >= MessageChat)
unRead[messageMode] = false;
invalidate();
}
void ControlPanel::addMessage(const std::string& line,
const int realmode)
{
ControlPanelMessage item(line);
item.breakLines(messageAreaPixels[2] - 2 * margin, fontFace,
fontSize);
int _maxScrollPages = BZDB.evalInt("scrollPages");
if (_maxScrollPages <= 0) {
_maxScrollPages = atoi(BZDB.getDefault("scrollPages").c_str());
BZDB.setInt("scrollPages", _maxScrollPages);
}
// the effective tab
const int tabmode = (realmode == MessageCurrent) ? messageMode :
realmode;
// add to the appropriate tabs
for (int tab = MessageAll; tab < MessageModeCount; tab++) {
if ((tab == tabmode) // add to its own mode
// add to the All tab, unless using Current mode
|| ((tab == MessageAll) && (realmode != MessageCurrent))
// always add to all tabs
|| (realmode == MessageAllTabs)) {
// insert the message into the tab
if ((int)messages[tab].size() < maxLines * _maxScrollPages) {
// not full yet so just append it
messages[tab].push_back(item);
} else {
// rotate list and replace oldest (in newest position after rotate)
messages[tab].pop_front();
messages[tab].push_back(item);
}
// visible changes, force a console refresh
if (messageMode == tab) {
invalidate();
}
// mark the tab as unread (if viewing tabs)
const bool showTabs = (BZDB.evalInt("showtabs") > 0);
if (showTabs && (messageMode != tab) && (messageMode !=
MessageAll)) {
unRead[tab] = true;
}
}
}
if (echoToConsole){
#ifdef _WIN32
// this is cheap but it will work on windows
FILE *fp = fopen ("stdout.txt","a+");
if (fp){
fprintf(fp,"%s\n", stripAnsiCodes(line).c_str());
fclose(fp);
}
#else
if (echoAnsi) {
std::cout << line << ColorStrings[ResetColor] << std::endl;
} else {
std::cout << stripAnsiCodes(line) << std::endl;
}
fflush(stdout);
#endif
}
}
void ControlPanel::saveMessages(const std::string& filename,
bool stripAnsi) const
{
FILE* file = fopen(filename.c_str(), "a+");
if (!file) {
return;
}
const time_t nowTime = time (NULL);
fprintf(file, "\n----------------------------------------\n");
fprintf(file, "Messages saved: %s", ctime(&nowTime));
fprintf(file, "----------------------------------------\n\n");
// add to the appropriate tabs
std::deque<ControlPanelMessage>::const_iterator msg =
messages[MessageAll].begin()++;
for (; msg != messages[MessageAll].end(); ++msg) {
const std::string& line = msg->string;
if (stripAnsi) {
fprintf(file, "%s\n", stripAnsiCodes(line).c_str());
} else {
fprintf(file, "%s%s\n", line.c_str(),
ColorStrings[ResetColor].c_str());
}
}
fclose(file);
return;
}
void ControlPanel::setRadarRenderer(RadarRenderer* rr)
{
radarRenderer = rr;
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "GuidedMissleStrategy.h"
/* common implementation headers */
#include "BZDBCache.h"
#include "TextureManager.h"
#include "Intersect.h"
/* local implementation headers */
#include "LocalPlayer.h"
#include "World.h"
#include "playing.h"
static float limitAngle(float a)
{
if (a < -M_PI) a += (float)(2.0 * M_PI);
else if (a >= M_PI) a -= (float)(2.0 * M_PI);
return a;
}
GuidedMissileStrategy::GuidedMissileStrategy(ShotPath* _path) :
ShotStrategy(_path),
renderTimes(0),
needUpdate(true)
{
ptSceneNode = new BoltSceneNode(_path->getPosition());
TextureManager &tm = TextureManager::instance();
int texture = tm.getTextureID("missile");
if (texture >= 0) {
ptSceneNode->setTexture(texture);
ptSceneNode->setTextureAnimation(4, 4);
ptSceneNode->setColor(1.0f, 0.2f, 0.0f);
ptSceneNode->setFlares(true);
}
// test
// get initial shot info
FiringInfo& f = getFiringInfo(_path);
f.lifetime *= BZDB.eval(StateDatabase::BZDB_GMADLIFE);
const float* vel = getPath().getVelocity();
const float d = 1.0f / hypotf(vel[0], hypotf(vel[1], vel[2]));
float dir[3];
dir[0] = vel[0] * d;
dir[1] = vel[1] * d;
dir[2] = vel[2] * d;
azimuth = limitAngle(atan2f(dir[1], dir[0]));
elevation = limitAngle(atan2f(dir[2], hypotf(dir[1], dir[0])));
// initialize segments
currentTime = getPath().getStartTime();
Ray ray = Ray(f.shot.pos, dir);
ShotPathSegment segment(currentTime, currentTime, ray);
segments.push_back(segment);
segments.push_back(segment);
segments.push_back(segment);
segments.push_back(segment);
// setup shot
float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
f.shot.vel[0] = shotSpeed * dir[0];
f.shot.vel[1] = shotSpeed * dir[1];
f.shot.vel[2] = shotSpeed * dir[2];
// set next position to starting position
nextPos[0] = f.shot.pos[0];
nextPos[1] = f.shot.pos[1];
nextPos[2] = f.shot.pos[2];
// check that first segment doesn't start inside a building
float startPos[3];
float muzzleFront = BZDB.eval(StateDatabase::BZDB_MUZZLEFRONT);
startPos[0] = f.shot.pos[0] - muzzleFront * dir[0];
startPos[1] = f.shot.pos[1] - muzzleFront * dir[1];
startPos[2] = f.shot.pos[2] - muzzleFront * dir[2];
Ray firstRay = Ray(startPos, dir);
prevTime = currentTime;
prevTime += -muzzleFront / BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
checkBuildings(firstRay);
prevTime = currentTime;
// no last target
lastTarget = NoPlayer;
lastPuff = currentTime;
puffTime = BZDB.eval("gmPuffTime");
}
GuidedMissileStrategy::~GuidedMissileStrategy()
{
delete ptSceneNode;
}
// NOTE -- ray is base of shot segment and normalized direction of
flight.
// distance traveled is ShotSpeed * dt.
void GuidedMissileStrategy::update(float dt)
{
const bool isRemote = (getPath().getPlayer() !=
LocalPlayer::getMyTank()->getId());
// ignore packets that arrive out of order
if (isRemote && dt < 0.0f) return;
// update time
prevTime = currentTime;
currentTime += dt;
// if shot life ran out then send notification and expire shot.
// only local shots are expired.
if (!isRemote &&
currentTime - getPath().getStartTime() >=
getPath().getLifetime()) {
/* NOTE -- comment out to not explode when shot expires */
addShotExplosion(nextPos);
setExpiring();
return;
}
// get target
const Player* target = NULL;
if (isRemote) {
if (lastTarget != NoPlayer)
target = lookupPlayer(lastTarget);
} else {
if (lastTarget == NoPlayer) {
setTarget();
LocalPlayer* myTank = LocalPlayer::getMyTank();
if (myTank)
target = myTank->getTarget();
} else {
target = lookupPlayer(lastTarget);
}
// see if the target changed
if (target) {
if (lastTarget != target->getId()) {
needUpdate = true;
lastTarget = target->getId();
}
} else {
if (lastTarget != NoPlayer) {
needUpdate = true;
lastTarget = NoPlayer;
}
}
}
if ((target != NULL) && ((target->getFlag() == Flags::Stealth) ||
((target->getStatus() & short(PlayerState::Alive)) == 0))) {
target = NULL;
lastTarget = NoPlayer;
needUpdate = true;
}
// compute next segment's ray
if (target) {
// turn towards target
// find desired direction
const float* targetPos = target->getPosition();
float desiredDir[3];
desiredDir[0] = targetPos[0] - nextPos[0];
desiredDir[1] = targetPos[1] - nextPos[1];
desiredDir[2] = targetPos[2] - nextPos[2];
desiredDir[2] += target->getMuzzleHeight(); // right between the
eyes
// compute desired angles
float newAzimuth = atan2f(desiredDir[1], desiredDir[0]);
float newElevation = atan2f(desiredDir[2],
hypotf(desiredDir[1], desiredDir[0]));
float gmissileAng = BZDB.eval
(StateDatabase::BZDB_GMTURNANGLE);
if (!isRemote)
gmissileAng *= 1000;
// compute new azimuth
float deltaAzimuth = limitAngle(newAzimuth - azimuth);
if (fabsf(deltaAzimuth) <= dt * gmissileAng)
azimuth = limitAngle(newAzimuth);
else if (deltaAzimuth > 0.0f)
azimuth = limitAngle(azimuth + dt * gmissileAng);
else
azimuth = limitAngle(azimuth - dt * gmissileAng);
// compute new elevation
float deltaElevation = limitAngle(newElevation - elevation);
if (fabsf(deltaElevation) <= dt * gmissileAng)
elevation = limitAngle(newElevation);
else if (deltaElevation > 0.0f)
elevation = limitAngle(elevation + dt * gmissileAng);
else
elevation = limitAngle(elevation - dt * gmissileAng);
}
float newDirection[3];
newDirection[0] = cosf(azimuth) * cosf(elevation);
newDirection[1] = sinf(azimuth) * cosf(elevation);
newDirection[2] = sinf(elevation);
Ray ray = Ray(nextPos, newDirection);
renderTimes++;
// Changed: GM smoke trail, leave it every seconds, none of this per
frame crap
if (currentTime.getSeconds() - lastPuff.getSeconds() > puffTime ) {
lastPuff = currentTime;
addShotPuff(nextPos,azimuth,elevation);
}
// get next position
float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
ray.getPoint(dt * shotSpeed, nextPos);
// see if we hit something
TimeKeeper segmentEndTime(currentTime);
/* if (!isRemote) */ {
if (nextPos[2] <= 0.0f) {
// hit ground -- expire it and shorten life of segment to time of
impact
setExpiring();
float t = ray.getOrigin()[2] / (ray.getOrigin()[2] - nextPos[2]);
segmentEndTime = prevTime;
segmentEndTime += t * (currentTime - prevTime);
ray.getPoint(t / shotSpeed, nextPos);
addShotExplosion(nextPos);
} else {
// see if we hit a building
const float t = checkBuildings(ray);
if (t >= 0.0f) {
segmentEndTime = prevTime;
segmentEndTime += t;
}
}
}
// throw out old segment and add new one
ShotPathSegment nextSegment(prevTime, segmentEndTime, ray);
segments.insert(segments.begin(), nextSegment);
segments.pop_back();
// update shot
newDirection[0] *= shotSpeed;
newDirection[1] *= shotSpeed;
newDirection[2] *= shotSpeed;
setPosition(nextPos);
setVelocity(newDirection);
}
float GuidedMissileStrategy::checkBuildings(const Ray& ray)
{
float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
float t = float((currentTime - prevTime) * shotSpeed);
int face;
const Obstacle* building = getFirstBuilding(ray, Epsilon, t);
const Teleporter* teleporter = getFirstTeleporter(ray, Epsilon, t,
face);
// check in reverse order to see what we hit first
if (teleporter) {
// entered teleporter -- teleport it
unsigned int seed = getPath().getShotId();
int source = World::getWorld()->getTeleporter(teleporter, face);
int target = World::getWorld()->getTeleportTarget(source, seed);
int outFace;
const Teleporter* outTeleporter =
World::getWorld()->getTeleporter(target, outFace);
teleporter->getPointWRT(*outTeleporter, face, outFace,
nextPos, NULL, azimuth, nextPos, NULL, &azimuth);
return t / shotSpeed;
} else if (building) {
// expire on next update
setExpiring();
float pos[3];
ray.getPoint(t / shotSpeed, pos);
addShotExplosion(pos);
return t / shotSpeed;
}
return -1.0f;
}
float GuidedMissileStrategy::checkHit(const BaseLocalPlayer* tank,
float position[3]) const
{
float minTime = Infinity;
if (getPath().isExpired()) return minTime;
// GM is not active until activation time passes (for any tank)
const float activationTime =
BZDB.eval(StateDatabase::BZDB_GMACTIVATIONTIME);
if ((TimeKeeper::getTick() - getPath().getStartTime()) <
activationTime) {
return minTime;
}
// get tank radius
float radius = tank->getRadius();
const float radius2 = radius * radius;
float shotRadius = BZDB.eval(StateDatabase::BZDB_SHOTRADIUS);
// tank is positioned from it's bottom so shift position up by
// half a tank height.
const float tankHeight = tank->getDimensions()[2];
Ray tankLastMotionRaw = tank->getLastMotion();
float lastTankPositionRaw[3];
lastTankPositionRaw[0] = tankLastMotionRaw.getOrigin()[0];
lastTankPositionRaw[1] = tankLastMotionRaw.getOrigin()[1];
lastTankPositionRaw[2] = tankLastMotionRaw.getOrigin()[2] + 0.5f *
tankHeight;
Ray tankLastMotion(lastTankPositionRaw,
tankLastMotionRaw.getDirection());
// check each segment
const int numSegments = segments.size();
int i = 0;
// only test most recent segment if shot is from my tank
if (numSegments > 1 && tank->getId() == getPath().getPlayer())
i = numSegments - 1;
for (; i < numSegments; i++) {
const Ray& ray = segments[i].ray;
// construct ray with correct velocity
float speed[3];
const float* dir = ray.getDirection();
float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
speed[0] = shotSpeed * dir[0];
speed[1] = shotSpeed * dir[1];
speed[2] = shotSpeed * dir[2];
Ray speedRay(ray.getOrigin(), speed);
// construct relative shot ray: origin and velocity relative to
// my tank as a function of time (t=0 is start of the interval).
Ray relativeRay(rayMinusRay(speedRay, 0.0, tankLastMotion, 0.0));
// get closest approach time
float t;
if (tank->getFlag() == Flags::Narrow) {
// find closest approach to narrow box around tank. width of box
// is shell radius so you can actually hit narrow tank head on.
static float tankBase[3] = { 0.0f, 0.0f, -0.5f * tankHeight };
t = timeRayHitsBlock(relativeRay, tankBase, tank->getAngle(),
0.5f * BZDBCache::tankLength, shotRadius, tankHeight);
} else {
// find time when shot hits sphere around tank
t = rayAtDistanceFromOrigin(relativeRay, 0.99f * radius);
}
if (t > minTime) continue;
// if not in shot segment times then no hit
if (t < 0.0f || t > segments[i].end - segments[i].start)
continue;
// check if shot hits tank -- get position at time t, see if in
radius
float closestPos[3];
relativeRay.getPoint(t, closestPos);
if (closestPos[0] * closestPos[0] +
closestPos[1] * closestPos[1] +
closestPos[2] * closestPos[2] < radius2) {
// save best time so far
minTime = t;
// compute location of tank at time of hit
float tankPos[3];
tank->getLastMotion().getPoint(t, tankPos);
// compute position of intersection
position[0] = tankPos[0] + closestPos[0];
position[1] = tankPos[1] + closestPos[1];
position[2] = tankPos[2] + closestPos[2];
}
}
return minTime;
}
void GuidedMissileStrategy::sendUpdate(const FiringInfo& firingInfo)
const
{y
((GuidedMissileStrategy*)this)->needUpdate = false;
// construct and send packet
char packet[ShotUpdatePLen + PlayerIdPLen];
void *buf = (void*)packet;
buf = firingInfo.shot.pack(buf);
buf = nboPackUByte(buf, lastTarget);
ServerLink::getServer()->send(MsgGMUpdate, sizeof(packet), packet);
}
void GuidedMissileStrategy::readUpdate(uint16_t code, void* msg)
{
// ignore non-guided missile messages (we shouldn't get them)
if (code != MsgGMUpdate) return;
// position and velocity have been replaced by the remote system's
// concept of the position and velocity. this may cause a
discontinuity
// in the shot's position but it's probably better to have the shot
in
// the right place than to maintain smooth motion. these updates
ought
// to be rare anyway.
// read the lastTarget
nboUnpackUByte(msg, lastTarget);
// fix up dependent variables
const float* vel = getPath().getVelocity();
const float d = 1.0f / hypotf(vel[0], hypotf(vel[1], vel[2]));
float dir[3];
dir[0] = vel[0] * d;
dir[1] = vel[1] * d;
dir[2] = vel[2] * d;
azimuth = limitAngle(atan2f(dir[1], dir[0]));
elevation = limitAngle(atan2f(dir[2], hypotf(dir[1], dir[0])));
const float* pos = getPath().getPosition();
nextPos[0] = pos[0];
nextPos[1] = pos[1];
nextPos[2] = pos[2];
// note that we do not call update(float). let that happen on the
// next time step.
}
void GuidedMissileStrategy::addShot(SceneDatabase* scene, bool)
{
ptSceneNode->move(getPath().getPosition(), getPath().getVelocity());
scene->addDynamicNode(ptSceneNode);
}
void GuidedMissileStrategy::expire()
{
if (getPath().getPlayer() == LocalPlayer::getMyTank()->getId()) {
const ShotPath& shot = getPath();
/* NOTE -- change 0 to 1 to not explode when shot expires (I think)
*/
ServerLink::getServer()->sendEndShot(shot.getPlayer(),
shot.getShotId(), 0);
}
}
void GuidedMissileStrategy::radarRender() const
{
const float *orig = getPath().getPosition();
const int length = (int)BZDBCache::linedRadarShots;
const int size = (int)BZDBCache::sizedRadarShots;
float shotTailLength = BZDB.eval(StateDatabase::BZDB_SHOTTAILLENGTH);
// Display leading lines
if (length > 0) {
const float* vel = getPath().getVelocity();
const float d = 1.0f / hypotf(vel[0], hypotf(vel[1], vel[2]));
float dir[3];
dir[0] = vel[0] * d * shotTailLength * length;
dir[1] = vel[1] * d * shotTailLength * length;
dir[2] = vel[2] * d * shotTailLength * length;
glBegin(GL_LINES);
glVertex2fv(orig);
if (BZDBCache::leadingShotLine) {
glVertex2f(orig[0] + dir[0], orig[1] + dir[1]);
} else {
glVertex2f(orig[0] - dir[0], orig[1] - dir[1]);
}
glEnd();
// draw a "bright reddish" missle tip
if (size > 0) {
glColor3f(1.0f, 0.75f, 0.75f);
glPointSize((float)size);
glBegin(GL_POINTS);
glVertex2f(orig[0], orig[1]);
glEnd();
glPointSize(1.0f);
}
} else {
if (size > 0) {
// draw a sized missle
glPointSize((float)size);
glBegin(GL_POINTS);
glVertex2fv(orig);
glEnd();
glPointSize(1.0f);
} else {
// draw the tiny missle
glBegin(GL_POINTS);
glVertex2fv(orig);
glEnd();
}
}
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "LocalPlayer.h"
/* common implementation headers */
#include "CommandManager.h"
#include "BZDBCache.h"
#include "FlagSceneNode.h"
#include "CollisionManager.h"
#include "PhysicsDriver.h"
#include "BzfEvent.h"
#include "WallObstacle.h"
#include "MeshObstacle.h"
/* local implementation headers */
#include "World.h"
#include "sound.h"
#include "ForceFeedback.h"
#include "effectsRenderer.h"
#include "TargetingUtils.h"
extern bool bCheatOO;
extern bool bCheatGravity;
LocalPlayer* LocalPlayer::mainPlayer = NULL;
LocalPlayer::LocalPlayer(const PlayerId& _id,
const char* name, const char* _email) :
BaseLocalPlayer(_id, name, _email),
gettingSound(true),
server(ServerLink::getServer()),
location(Dead),
firingStatus(Deceased),
flagShakingTime(0.0f),
flagShakingWins(0),
antidoteFlag(NULL),
desiredSpeed(0.0f),
desiredAngVel(0.0f),
lastSpeed(0.0f),
anyShotActive(false),
target(NULL),
nemesis(NULL),
recipient(NULL),
inputChanged(false),
stuckFrameCount(0),
spawning(false),
wingsFlapCount(0),
handicap(0.0f),
left(false),
right(false),
up(false),
down(false),
entryDrop(true),
wantJump(false),
jumpPressed(false),
deathPhyDrv(-1)
{
// initialize shots array to no shots fired
const int numShots = World::getWorld()->getMaxShots();
shots = new LocalShotPath*[numShots];
for (int i = 0; i < numShots; i++)
shots[i] = NULL;
// set input method
if (BZDB.isTrue("allowInputChange")) {
inputMethod = Mouse;
} else {
setInputMethod(BZDB.get("activeInputDevice"));
}
}
LocalPlayer::~LocalPlayer()
{
// free shots
const int numShots = World::getWorld()->getMaxShots();
for (int i = 0; i < numShots; i++)
if (shots[i])
delete shots[i];
delete[] shots;
// free antidote flag
delete antidoteFlag;
}
void LocalPlayer::setMyTank(LocalPlayer* player)
{
mainPlayer = player;
}
void LocalPlayer::doUpdate(float dt)
{
const bool hadShotActive = anyShotActive;
const int numShots = World::getWorld()->getMaxShots();
static TimeKeeper pauseTime = TimeKeeper::getNullTime();
static bool wasPaused = false;
// if paused then boost the reload times by dt (so that, effectively,
// reloading isn't performed)
int i;
if (0 && isPaused()) {
for (i = 0; i < numShots; i++) {
if (shots[i]) {
shots[i]->boostReloadTime(dt);
}
}
// if we've been paused for a long time, drop our flag
if (!wasPaused) {
pauseTime = TimeKeeper::getCurrent();
wasPaused = true;
}
if (TimeKeeper::getCurrent() - pauseTime >
BZDB.eval(StateDatabase::BZDB_PAUSEDROPTIME)) {
server->sendDropFlag(getPosition());
setStatus(getStatus() & ~PlayerState::FlagActive);
pauseTime = TimeKeeper::getSunExplodeTime();
}
} else {
pauseTime = TimeKeeper::getNullTime();
wasPaused = false;
}
// reap dead (reloaded) shots
for (i = 0; i < numShots; i++)
if (shots[i] && shots[i]->isReloaded()) {
if (!shots[i]->isExpired())
shots[i]->setExpired();
delete shots[i];
shots[i] = NULL;
}
// update shots
anyShotActive = false;
for (i = 0; i < numShots; i++)
if (shots[i]) {
shots[i]->update(dt);
if (!shots[i]->isExpired()) anyShotActive = true;
}
// if no shots now out (but there had been) then reset target
if (!anyShotActive && hadShotActive)
target = NULL;
// drop bad flag if timeout has expired
if (!isPaused() && dt > 0.0f &&
World::getWorld()->allowShakeTimeout() &&
getFlag() != Flags::Null && getFlag()->endurance == FlagSticky &&
flagShakingTime > 0.0f) {
flagShakingTime -= dt;
if (flagShakingTime <= 0.0f) {
flagShakingTime = 0.0f;
server->sendDropFlag(getPosition());
}
}
}
void LocalPlayer::doSlideMotion(float dt, float slideTime,
float newAngVel, float* newVelocity)
{
const float oldAzimuth = getAngle();
const float* oldVelocity = getVelocity();
const float angle = oldAzimuth + (0.5f * dt * newAngVel);
const float cos_val = cosf(angle);
const float sin_val = sinf(angle);
const float scale = (dt / slideTime);
const float speedAdj = desiredSpeed * scale;
const float* ov = oldVelocity;
const float oldSpeed = sqrtf((ov[0] * ov[0]) + (ov[1] * ov[1]));
float* nv = newVelocity;
nv[0] = ov[0] + (cos_val * speedAdj);
nv[1] = ov[1] + (sin_val * speedAdj);
const float newSpeed = sqrtf((nv[0] * nv[0]) + (nv[1] * nv[1]));
float maxSpeed = getMaxSpeed();
if (newSpeed > maxSpeed) {
float adjSpeed;
if (oldSpeed > maxSpeed) {
adjSpeed = oldSpeed - (dt * (maxSpeed / slideTime));
if (adjSpeed < 0.0f) {
adjSpeed = 0.0f;
}
} else {
adjSpeed = maxSpeed;
}
const float speedScale = adjSpeed / newSpeed;
nv[0] *= speedScale;
nv[1] *= speedScale;
}
return;
}
float LocalPlayer::getNewAngVel(float old, float desired)
{
float newAngVel;
if ((inputMethod != Keyboard) || (getPhysicsDriver() >= 0)) {
// mouse and joystick users
newAngVel = desired;
} else {
/* keybaord users
* the larger the oldAngVel contribution, the more slowly an
* angular velocity converges to the desired "max" velocity; the
* contribution of the desired and old velocity should add up to
* one for a linear convergence rate.
*/
newAngVel = (old * 0.8f) + (desired * 0.2f);
// instant stop
if ((old * desired < 0.0f) ||
(NEAR_ZERO(desired, ZERO_TOLERANCE))) {
newAngVel = desired;
}
}
return newAngVel;
}
void LocalPlayer::doUpdateMotion(float dt)
{
static const float MinSearchStep = 0.0001f;
static const int MaxSearchSteps = 7;
static const int MaxSteps = 4;
static const float TinyDistance = 0.001f;
// save old state
const Location oldLocation = location;
const float* oldPosition = getPosition();
const float oldAzimuth = getAngle();
const float oldAngVel = getAngularVelocity();
const float* oldVelocity = getVelocity();
// prepare new state
float newVelocity[3];
newVelocity[0] = oldVelocity[0];
newVelocity[1] = oldVelocity[1];
newVelocity[2] = oldVelocity[2];
float newAngVel = 0.0f;
clearRemoteSounds();
// if was teleporting and exceeded teleport time then not teleporting
anymore
if (isTeleporting() &&
((lastTime - getTeleportTime()) >=
BZDB.eval(StateDatabase::BZDB_TELEPORTTIME)))
setStatus(getStatus() & ~short(PlayerState::Teleporting));
// phased means we can pass through buildings
const bool phased = ((location == Dead) || (location == Exploding) ||
(getFlag() == Flags::OscillationOverthruster) ||
isPhantomZoned()|| bCheatOO);
float groundLimit = 0.0f;
if (getFlag() == Flags::Burrow)
groundLimit = BZDB.eval(StateDatabase::BZDB_BURROWDEPTH);
// get linear and angular speed at start of time step
if (!NEAR_ZERO(dt,ZERO_TOLERANCE)) {
if (location == Dead || isPaused()) {
// can't move if paused or dead -- set dt to zero instead of
// clearing velocity and newAngVel for when we resume (if paused)
dt = 0.0f;
newAngVel = oldAngVel;
} else if (location == Exploding) {
// see if explosing time has expired
if (lastTime - getExplodeTime() >=
BZDB.eval(StateDatabase::BZDB_EXPLODETIME)) {
dt -= float((lastTime - getExplodeTime()) -
BZDB.eval(StateDatabase::BZDB_EXPLODETIME));
if (dt < 0.0f) {
dt = 0.0f;
}
setStatus(PlayerState::DeadStatus);
location = Dead;
if (isAutoPilot()) {
CMDMGR.run("restart");
}
}
// can't control explosion motion
newVelocity[2] += BZDBCache::gravity * dt;
newAngVel = 0.0f; // or oldAngVel to spin while exploding
} else if ((location == OnGround) || (location == OnBuilding) ||
(location == InBuilding && oldPosition[2] == groundLimit)) {
// full control
float speed = desiredSpeed;
// angular velocity
newAngVel = getNewAngVel(oldAngVel, desiredAngVel);
// limit acceleration
doMomentum(dt, speed, newAngVel);
// compute velocity so far
const float angle = oldAzimuth + 0.5f * dt * newAngVel;
newVelocity[0] = speed * cosf(angle);
newVelocity[1] = speed * sinf(angle);
newVelocity[2] = 0.0f;
// now friction, if any
doFriction(dt, oldVelocity, newVelocity);
// reset our flap count if we have wings
if (getFlag() == Flags::Wings)
wingsFlapCount = (int) BZDB.eval(StateDatabase::BZDB_WINGSJUMPCOUNT);
if ((oldPosition[2] < 0.0f) && (getFlag() == Flags::Burrow))
newVelocity[2] += 4 * BZDBCache::gravity * dt;
else if (oldPosition[2] > groundLimit)
newVelocity[2] += BZDBCache::gravity * dt;
// save speed for next update
lastSpeed = speed;
} else {
// can't control motion in air unless have wings
if (getFlag() == Flags::Wings || !BZDB.isTrue("displayRadar")) {
float speed = desiredSpeed;
// angular velocity
newAngVel = getNewAngVel(oldAngVel, desiredAngVel);
// compute horizontal velocity so far
const float slideTime = BZDB.eval(StateDatabase::BZDB_WINGSSLIDETIME);
if (slideTime > 0.0) {
doSlideMotion(dt, slideTime, newAngVel, newVelocity);
} else {
const float angle = oldAzimuth + 0.5f * dt * newAngVel;
newVelocity[0] = speed * cosf(angle);
newVelocity[1] = speed * sinf(angle);
}
if(bCheatGravity && oldVelocity[2] <= 0.0)newVelocity[2] = 0;
else newVelocity[2] += BZDB.eval(StateDatabase::BZDB_WINGSGRAVITY) *
dt;
lastSpeed = speed;
} else {
//newVelocity[2] += BZDBCache::gravity * dt;
if(bCheatGravity && oldVelocity[2] <= 0.0 )newVelocity[2] = 0;
else newVelocity[2] += BZDBCache::gravity * dt;
newAngVel = oldAngVel;
}
}
// now apply outside forces
doForces(dt, newVelocity, newAngVel);
// below the ground: however I got there, creep up
if (oldPosition[2] < groundLimit) {
newVelocity[2] = std::max(newVelocity[2], -oldPosition[2] / 2.0f
+ 0.5f);
}
}
// jump here, we allow a little change in horizontal motion
if (wantJump) {
doJump();
if (!wantJump) {
newVelocity[2] = oldVelocity[2];
if ((lastObstacle != NULL) && !lastObstacle->isFlatTop()
&& BZDB.isTrue(StateDatabase::BZDB_NOCLIMB)) {
newVelocity[0] = 0.0f;
newVelocity[1] = 0.0f;
}
}
}
// do the physics driver stuff
const int driverId = getPhysicsDriver();
const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(driverId);
if (phydrv != NULL) {
const float* v = phydrv->getLinearVel();
newVelocity[2] += v[2];
if (phydrv->getIsSlide()) {
const float slideTime = phydrv->getSlideTime();
doSlideMotion(dt, slideTime, newAngVel, newVelocity);
}
else {
// adjust the horizontal velocity
newVelocity[0] += v[0];
newVelocity[1] += v[1];
const float av = phydrv->getAngularVel();
const float* ap = phydrv->getAngularPos();
if (av != 0.0f) {
// the angular velocity is in radians/sec
newAngVel += av;
const float dx = oldPosition[0] - ap[0];
const float dy = oldPosition[1] - ap[1];
newVelocity[0] -= av * dy;
newVelocity[1] += av * dx;
}
}
}
lastObstacle = NULL;
// get new position so far (which is just the current position)
float newPos[3];
newPos[0] = oldPosition[0];
newPos[1] = oldPosition[1];
newPos[2] = oldPosition[2];
float newAzimuth = oldAzimuth;
// move tank through the time step. if there's a collision then
// move the tank up to the collision, adjust the velocity to
// prevent interpenetration, and repeat. avoid infinite loops
// by only allowing a maximum number of repeats.
bool expelled;
const Obstacle* obstacle;
float timeStep = dt;
int stuck = false;
if (location != Dead && location != Exploding) {
location = OnGround;
// anti-stuck code is useful only when alive
// then only any 100 frames while stuck, take an action
// try to see if we are stuck on a building
obstacle = getHitBuilding(newPos, newAzimuth, newPos, newAzimuth,
phased, expelled);
if (obstacle && expelled) {
stuckFrameCount++;
stuck = true;
} else {
stuckFrameCount = 0;
}
if (stuckFrameCount > 100) {
stuckFrameCount = 0;
// we are using a maximum value on time for frame to avoid
lagging problem
setDesiredSpeed(0.25f);
float delta = dt > 0.1f ? 0.1f : dt;
float normalStuck[3];
obstacle->getNormal(newPos, normalStuck);
// use all the given speed to exit
float movementMax = desiredSpeed * delta;
newVelocity[0] = movementMax * normalStuck[0];
newVelocity[1] = movementMax * normalStuck[1];
if ((World::getWorld()->allowJumping() || (getFlag() ==
Flags::Jumping) || (getFlag() == Flags::Wings)) &&
(getFlag() != Flags::NoJumping))
newVelocity[2] = movementMax * normalStuck[2];
else
newVelocity[2] = 0.0f;
// exit will be in the normal direction
newPos[0] += newVelocity[0];
newPos[1] += newVelocity[1];
newPos[2] += newVelocity[2];
// compute time for all other kind of movements
timeStep -= delta;
}
}
for (int numSteps = 0; numSteps < MaxSteps; numSteps++) {
// record position at beginning of time step
float tmpPos[3], tmpAzimuth;
tmpAzimuth = newAzimuth;
tmpPos[0] = newPos[0];
tmpPos[1] = newPos[1];
tmpPos[2] = newPos[2];
// get position at end of time step
newAzimuth = tmpAzimuth + timeStep * newAngVel;
newPos[0] = tmpPos[0] + timeStep * newVelocity[0];
newPos[1] = tmpPos[1] + timeStep * newVelocity[1];
newPos[2] = tmpPos[2] + timeStep * newVelocity[2];
if ((newPos[2] < groundLimit) && (newVelocity[2] < 0)) {
// Hit lower limit, stop falling
newPos[2] = groundLimit;
if (location == Exploding) {
// tank pieces reach the ground, friction
// stop them, & mainly player view
newPos[0] = tmpPos[0];
newPos[1] = tmpPos[1];
}
}
// see if we hit anything. if not then we're done.
obstacle = getHitBuilding(tmpPos, tmpAzimuth, newPos, newAzimuth,
phased, expelled);
if (!obstacle || !expelled) break;
float obstacleTop = obstacle->getPosition()[2] +
obstacle->getHeight();
if ((oldLocation != InAir) && obstacle->isFlatTop() &&
(obstacleTop != tmpPos[2]) &&
(obstacleTop < (tmpPos[2] +
BZDB.eval(StateDatabase::BZDB_MAXBUMPHEIGHT)))) {
newPos[0] = oldPosition[0];
newPos[1] = oldPosition[1];
newPos[2] = obstacleTop;
// drive over bumps
const Obstacle* bumpObstacle = getHitBuilding(newPos, tmpAzimuth,
newPos, newAzimuth,
phased, expelled);
if (bumpObstacle == NULL) {
move(newPos, getAngle());
newPos[0] += newVelocity[0] * (dt * 0.5f);
newPos[1] += newVelocity[1] * (dt * 0.5f);
break;
}
}
// record position when hitting
float hitPos[3], hitAzimuth;
hitAzimuth = newAzimuth;
hitPos[0] = newPos[0];
hitPos[1] = newPos[1];
hitPos[2] = newPos[2];
// find the latest time before the collision
float searchTime = 0.0f, searchStep = 0.5f * timeStep;
for (int i = 0; searchStep > MinSearchStep && i < MaxSearchSteps;
searchStep *= 0.5f, i++) {
// get intermediate position
const float t = searchTime + searchStep;
newAzimuth = tmpAzimuth + (t * newAngVel);
newPos[0] = tmpPos[0] + (t * newVelocity[0]);
newPos[1] = tmpPos[1] + (t * newVelocity[1]);
newPos[2] = tmpPos[2] + (t * newVelocity[2]);
if ((newPos[2] < groundLimit) && (newVelocity[2] < 0)) {
newPos[2] = groundLimit;
}
// see if we hit anything
bool searchExpelled;
const Obstacle* searchObstacle =
getHitBuilding(tmpPos, tmpAzimuth, newPos, newAzimuth,
phased, searchExpelled);
if (!searchObstacle || !searchExpelled) {
// if no hit then search latter half of time step
searchTime = t;
} else if (searchObstacle) {
// if we hit a building then record which one and where
obstacle = searchObstacle;
expelled = searchExpelled;
hitAzimuth = newAzimuth;
hitPos[0] = newPos[0];
hitPos[1] = newPos[1];
hitPos[2] = newPos[2];
}
}
// get position just before impact
newAzimuth = tmpAzimuth + (searchTime * newAngVel);
newPos[0] = tmpPos[0] + (searchTime * newVelocity[0]);
newPos[1] = tmpPos[1] + (searchTime * newVelocity[1]);
newPos[2] = tmpPos[2] + (searchTime * newVelocity[2]);
if (oldPosition[2] < groundLimit) {
newVelocity[2] = std::max(newVelocity[2], -oldPosition[2] / 2.0f
+ 0.5f);
}
// record how much time is left in time step
timeStep -= searchTime;
// get normal at intersection. sometimes fancy test says there's
// no intersection but we're expecting one so, in that case, fall
// back to simple normal calculation.
float normal[3];
if (!getHitNormal(obstacle, newPos, newAzimuth, hitPos, hitAzimuth,
normal)) {
obstacle->getNormal(newPos, normal);
}
// check for being on a building
if ((newPos[2] > 0.0f) && (normal[2] > 0.001f)) {
if (location != Dead && location != Exploding && expelled) {
location = OnBuilding;
lastObstacle = obstacle;
}
newVelocity[2] = 0.0f;
} else {
// get component of velocity in normal direction (in horizontal
plane)
float mag = (normal[0] * newVelocity[0]) +
(normal[1] * newVelocity[1]);
// handle upward normal component to prevent an upward force
if (!NEAR_ZERO(normal[2], ZERO_TOLERANCE)) {
// if going down then stop falling
if (newVelocity[2] < 0.0f && newVelocity[2] -
(mag + normal[2] * newVelocity[2]) * normal[2] > 0.0f) {
newVelocity[2] = 0.0f;
}
// normalize force magnitude in horizontal plane
float horNormal = normal[0] * normal[0] + normal[1] * normal[1];
if (!NEAR_ZERO(horNormal, ZERO_TOLERANCE)) {
mag /= horNormal;
}
}
// cancel out component in normal direction (if velocity and
// normal point in opposite directions). also back off a tiny
// amount to prevent a spurious collision against the same
// obstacle.
if (mag < 0.0f) {
newVelocity[0] -= mag * normal[0];
newVelocity[1] -= mag * normal[1];
newPos[0] -= TinyDistance * mag * normal[0];
newPos[1] -= TinyDistance * mag * normal[1];
}
if (mag > -0.01f) {
// assume we're not allowed to turn anymore if there's no
// significant velocity component to cancel out.
newAngVel = 0.0f;
}
}
}
// pick new location if we haven't already done so
if (location == OnGround) {
if (obstacle && (!expelled || stuck)) {
location = InBuilding;
}
else if (newPos[2] > 0.0f) {
location = InAir;
}
}
// see if we're crossing a wall
if (location == InBuilding && (getFlag() ==
Flags::OscillationOverthruster || bCheatOO)) {
if (obstacle->isCrossing(newPos, newAzimuth,
0.5f * BZDBCache::tankLength,
0.5f * BZDBCache::tankWidth,
BZDBCache::tankHeight, NULL)) {
setStatus(getStatus() | int(PlayerState::CrossingWall));
} else {
setStatus(getStatus() & ~int(PlayerState::CrossingWall));
}
} else if (World::getWorld()->crossingTeleporter( newPos, newAzimuth,
0.5f * BZDBCache::tankLength,
0.5f * BZDBCache::tankWidth,
BZDBCache::tankHeight, crossingPlane)) {
setStatus(getStatus() | int(PlayerState::CrossingWall));
} else {
setStatus(getStatus() & ~int(PlayerState::CrossingWall));
}
// compute actual velocities. do this before teleportation.
if (!NEAR_ZERO(dt, ZERO_TOLERANCE)) {
const float oodt = 1.0f / dt;
newAngVel = (newAzimuth - oldAzimuth) * oodt;
newVelocity[0] = (newPos[0] - oldPosition[0]) * oodt;
newVelocity[1] = (newPos[1] - oldPosition[1]) * oodt;
newVelocity[2] = (newPos[2] - oldPosition[2]) * oodt;
}
// see if we teleported
int face;
const Teleporter* teleporter;
if (!isAlive()) {
teleporter = NULL;
} else {
teleporter = World::getWorld()->crossesTeleporter(oldPosition,
newPos, face);
}
if (teleporter) {
if (getFlag() == Flags::PhantomZone) {
// change zoned state
setStatus(getStatus() ^ PlayerState::FlagActive);
if (gettingSound) {
playLocalSound(SFX_PHANTOM);
}
} else {
// teleport
const int source = World::getWorld()->getTeleporter(teleporter,
face);
int targetTele = World::getWorld()->getTeleportTarget(source);
int outFace;
const Teleporter* outPort =
World::getWorld()->getTeleporter(targetTele, outFace);
teleporter->getPointWRT(*outPort, face, outFace,
newPos, newVelocity, newAzimuth,
newPos, newVelocity, &newAzimuth);
// check for a hit on the other side
const Obstacle* teleObs =
getHitBuilding(newPos, newAzimuth, newPos, newAzimuth, phased,
expelled);
if (teleObs != NULL) {
// revert
memcpy (newPos, oldPosition, sizeof(float[3]));
newVelocity[0] = newVelocity[1] = 0.0f;
newVelocity[2] = oldVelocity[2];
newAzimuth = oldAzimuth;
} else {
// save teleport info
setTeleport(lastTime, source, targetTele);
server->sendTeleport(source, targetTele);
if (gettingSound) {
playLocalSound(SFX_TELEPORT);
}
}
}
}
// setup the physics driver
setPhysicsDriver(-1);
if ((lastObstacle != NULL) &&
(lastObstacle->getType() == MeshFace::getClassName())) {
const MeshFace* meshFace = (const MeshFace*) lastObstacle;
int driverIdent = meshFace->getPhysicsDriver();
const PhysicsDriver* phydriver = PHYDRVMGR.getDriver(driverIdent);
if (phydriver != NULL) {
setPhysicsDriver(driverIdent);
}
}
// deal with drop sounds and effects
if (entryDrop) {
// because the starting position that the server sends can result
// in an initial InAir condition, we use this bool to avoid having
// a false jump mess with the spawnEffect()
// FIXME: this isn't a clean way to do it
if ((oldLocation == InAir) == (location == InAir)) {
entryDrop = false;
}
}
const bool justLanded =
(oldLocation == InAir) &&
((location == OnGround) || (location == OnBuilding));
if (justLanded) {
setLandingSpeed(oldVelocity[2]);
EFFECTS.addLandEffect(getColor(),newPos,getAngle());
}
if (gettingSound) {
const PhysicsDriver* phydriver =
PHYDRVMGR.getDriver(getPhysicsDriver());
if ((phydriver != NULL) && (phydriver->getLinearVel()[2] > 0.0f)) {
playLocalSound(SFX_BOUNCE);
addRemoteSound(PlayerState::BounceSound);
} else if (justLanded && !entryDrop) {
playLocalSound(SFX_LAND);
} else if ((location == OnGround) &&
(oldPosition[2] == 0.0f) && (newPos[2] < 0.f)) {
playLocalSound(SFX_BURROW);
}
}
// set falling status
if (location == OnGround || location == OnBuilding ||
(location == InBuilding && newPos[2] == 0.0f)) {
setStatus(getStatus() & ~short(PlayerState::Falling));
}
else if (location == InAir || location == InBuilding) {
setStatus(getStatus() | short(PlayerState::Falling));
}
// set UserInput status (determines how animated treads are drawn)
const PhysicsDriver* phydrv2 =
PHYDRVMGR.getDriver(getPhysicsDriver());
if (((phydrv2 != NULL) && phydrv2->getIsSlide()) ||
((getFlag() == Flags::Wings) && (location == InAir) &&
!BZDB.isTrue("displayRadar") &&
(BZDB.eval(StateDatabase::BZDB_WINGSSLIDETIME) > 0.0f))){
setStatus(getStatus() | short(PlayerState::UserInputs));
} else {
setStatus(getStatus() & ~short(PlayerState::UserInputs));
}
// compute firing status
switch (location) {
case Dead:
case Exploding:
firingStatus = Deceased;
break;
case InBuilding:
firingStatus = (getFlag() == Flags::PhantomZone) ? Zoned : Sealed;
break;
default:
if (isPhantomZoned())
firingStatus = Zoned;
else if ((getReloadTime() > 0.0f) && (getFlag() !=
Flags::TriggerHappy))
firingStatus = Loading;
else
firingStatus = Ready;
break;
}
// burrowed and oscillating tanks get some resistance in their
joystick
// if they have ff on
if ((location == InBuilding) || (newPos[2] < -0.5f))
ForceFeedback::solidMatterFriction();
// calculate the list of inside buildings
insideBuildings.clear();
if (location == InBuilding) {
collectInsideBuildings();
}
// move tank
move(newPos, newAzimuth);
setVelocity(newVelocity);
setAngularVelocity(newAngVel);
setRelativeMotion();
newAzimuth = getAngle(); // pickup the limited angle range from
move()
// see if I'm over my antidote
if (antidoteFlag && location == OnGround) {
float dist =
((flagAntidotePos[0] - newPos[0]) * (flagAntidotePos[0] -
newPos[0])) +
((flagAntidotePos[1] - newPos[1]) * (flagAntidotePos[1] -
newPos[1]));
const float twoRads = getRadius() + BZDBCache::flagRadius;
if (dist < (twoRads * twoRads)) {
server->sendDropFlag(getPosition());
}
}
if ((getFlag() == Flags::Bouncy) && ((location == OnGround) ||
(location == OnBuilding))) {
if (oldLocation != InAir) {
if ((TimeKeeper::getCurrent() - bounceTime) > 0) {
doJump();
}
}
else {
bounceTime = TimeKeeper::getCurrent();
bounceTime += 0.2f;
}
}
if (gettingSound) {
if (oldPosition[0] != newPos[0] || oldPosition[1] != newPos[1] ||
oldPosition[2] != newPos[2] || oldAzimuth != newAzimuth) {
moveSoundReceiver(newPos[0], newPos[1], newPos[2], newAzimuth,
NEAR_ZERO(dt, ZERO_TOLERANCE) ||
((teleporter != NULL) && (getFlag() != Flags::PhantomZone)));
}
if (NEAR_ZERO(dt, ZERO_TOLERANCE)) {
speedSoundReceiver(newVelocity[0], newVelocity[1],
newVelocity[2]);
} else {
speedSoundReceiver((newPos[0] - oldPosition[0]) / dt,
(newPos[1] - oldPosition[1]) / dt,
(newPos[2] - oldPosition[2]) / dt);
}
}
}
const Obstacle* LocalPlayer::getHitBuilding(const float* p, float a,
bool phased, bool& expelled) const
{
const float* dims = getDimensions();
const Obstacle* obstacle =
World::getWorld()->hitBuilding(p, a, dims[0], dims[1], dims[2]);
expelled = (obstacle != NULL);
if (expelled && phased)
expelled = (obstacle->getType() == WallObstacle::getClassName() ||
obstacle->getType() == Teleporter::getClassName() ||
(getFlag() == Flags::OscillationOverthruster && !bCheatOO &&
desiredSpeed < 0.0f &&
p[2] == 0.0f));
return obstacle;
}
const Obstacle* LocalPlayer::getHitBuilding(const float* oldP, float
oldA,
const float* p, float a,
bool phased, bool& expelled)
{
const bool hasOOflag = getFlag() == Flags::OscillationOverthruster;
const float* dims = getDimensions();
const Obstacle* obstacle = World::getWorld()->
hitBuilding(oldP, oldA, p, a, dims[0], dims[1], dims[2],
!hasOOflag);
expelled = (obstacle != NULL);
if (expelled && phased)
expelled = (
(!bCheatOO && obstacle->getType() == Teleporter::getClassName()) ||
(hasOOflag && desiredSpeed < 0.0f && !bCheatOO && p[2] == 0.0f));
if (obstacle != NULL) {
if (obstacle->getType() == MeshFace::getClassName()) {
const MeshFace* face = (const MeshFace*) obstacle;
const int driver = face->getPhysicsDriver();
const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(driver);
if ((phydrv != NULL) && phydrv->getIsDeath()) {
deathPhyDrv = driver;
}
}
}
return obstacle;
}
bool LocalPlayer::getHitNormal(const Obstacle* o,
const float* pos1, float azimuth1,
const float* pos2, float azimuth2,
float* normal) const
{
const float* dims = getDimensions();
return o->getHitNormal(pos1, azimuth1, pos2, azimuth2,
dims[0], dims[1], dims[2], normal);
}
static bool notInObstacleList(const Obstacle* obs,
const std::vector<const Obstacle*>& list)
{
for (unsigned int i = 0; i < list.size(); i++) {
if (obs == list[i]) {
return false;
}
}
return true;
}
void LocalPlayer::collectInsideBuildings()
{
const float* pos = getPosition();
const float angle = getAngle();
const float* dims = getDimensions();
// get the list of possible inside buildings
const ObsList* olist =
COLLISIONMGR.boxTest (pos, angle, dims[0], dims[1], dims[2]);
for (int i = 0; i < olist->count; i++) {
const Obstacle* obs = olist->list[i];
if (obs->inBox(pos, angle, dims[0], dims[1], dims[2])) {
if (obs->getType() == MeshFace::getClassName()) {
const MeshFace* face = (const MeshFace*) obs;
const MeshObstacle* mesh = (const MeshObstacle*) face->getMesh();
// check it for the death touch
if (deathPhyDrv < 0) {
const int driver = face->getPhysicsDriver();
const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(driver);
if ((phydrv != NULL) && (phydrv->getIsDeath())) {
deathPhyDrv = driver;
}
}
// add the mesh if not already present
if (!obs->isDriveThrough() &&
notInObstacleList(mesh, insideBuildings)) {
insideBuildings.push_back(mesh);
}
}
else if (!obs->isDriveThrough()) {
if (obs->getType() == MeshObstacle::getClassName()) {
// add the mesh if not already present
if (notInObstacleList(obs, insideBuildings)) {
insideBuildings.push_back(obs);
}
} else {
insideBuildings.push_back(obs);
}
}
}
}
return;
}
float LocalPlayer::getFlagShakingTime() const
{
return flagShakingTime;
}
int LocalPlayer::getFlagShakingWins() const
{
return flagShakingWins;
}
const GLfloat* LocalPlayer::getAntidoteLocation() const
{
return (const GLfloat*)(antidoteFlag ? antidoteFlag->getSphere() :
NULL);
}
ShotPath* LocalPlayer::getShot(int index) const
{
index &= 0x00FF;
if ((index < 0) || (index >= World::getWorld()->getMaxShots()))
return NULL;
return shots[index];
}
void LocalPlayer::restart(const float* pos, float _azimuth)
{
// put me in limbo
setStatus(short(PlayerState::DeadStatus));
// can't have a flag
setFlag(Flags::Null);
// get rid of existing shots
const int numShots = World::getWorld()->getMaxShots();
// get rid of existing shots
for (int i = 0; i < numShots; i++)
if (shots[i]) {
delete shots[i];
shots[i] = NULL;
}
anyShotActive = false;
// no target
target = NULL;
// no death
deathPhyDrv = -1;
// initialize position/speed state
static const float zero[3] = { 0.0f, 0.0f, 0.0f };
location = (pos[2] > 0.0f) ? OnBuilding : OnGround;
lastObstacle = NULL;
lastSpeed = 0.0f;
desiredSpeed = 0.0f;
desiredAngVel = 0.0f;
move(pos, _azimuth);
setVelocity(zero);
setAngularVelocity(0.0f);
left = false;
right = false;
up = false;
down = false;
wantJump = false;
doUpdateMotion(0.0f);
updateHandicap();
entryDrop = true;
// make me alive now
setStatus(getStatus() | short(PlayerState::Alive));
}
void LocalPlayer::setTeam(TeamColor _team)
{
changeTeam(_team);
}
void LocalPlayer::setDesiredSpeed(float fracOfMaxSpeed)
{
FlagType* flag = getFlag();
// can't go faster forward than at top speed, and backward at half
speed
if (fracOfMaxSpeed > 1.0f) fracOfMaxSpeed = 1.0f;
else if (fracOfMaxSpeed < -0.5f) fracOfMaxSpeed = -0.5f;
// oscillation overthruster tank in building can't back up
if (fracOfMaxSpeed < 0.0f && getLocation() == InBuilding &&
flag == Flags::OscillationOverthruster && !bCheatOO) {
fracOfMaxSpeed = 0.0f;
}
// boost speed for certain flags
if (flag == Flags::Velocity) {
fracOfMaxSpeed *= BZDB.eval(StateDatabase::BZDB_VELOCITYAD);
} else if (flag == Flags::Thief) {
fracOfMaxSpeed *= BZDB.eval(StateDatabase::BZDB_THIEFVELAD);
} else if ((flag == Flags::Burrow) && (getPosition()[2] < 0.0f)) {
fracOfMaxSpeed *= BZDB.eval(StateDatabase::BZDB_BURROWSPEEDAD);
} else if ((flag == Flags::ForwardOnly) && (fracOfMaxSpeed < 0.0)) {
fracOfMaxSpeed = 0.0f;
} else if ((flag == Flags::ReverseOnly) && (fracOfMaxSpeed > 0.0)) {
fracOfMaxSpeed = 0.0f;
} else if (flag == Flags::Agility) {
if ((TimeKeeper::getCurrent() - agilityTime) <
BZDB.eval(StateDatabase::BZDB_AGILITYTIMEWINDOW)) {
fracOfMaxSpeed *= BZDB.eval(StateDatabase::BZDB_AGILITYADVEL);
} else {
float oldFrac = desiredSpeed / BZDBCache::tankSpeed;
if (oldFrac > 1.0f)
oldFrac = 1.0f;
else if (oldFrac < -0.5f)
oldFrac = -0.5f;
float limit = BZDB.eval(StateDatabase::BZDB_AGILITYVELDELTA);
if (fracOfMaxSpeed < 0.0f)
limit /= 2.0f;
if (fabs(fracOfMaxSpeed - oldFrac) > limit) {
fracOfMaxSpeed *= BZDB.eval(StateDatabase::BZDB_AGILITYADVEL);
agilityTime = TimeKeeper::getCurrent();
}
}
}
if (!BZDB.isTrue("displayConsole"))
fracOfMaxSpeed *= 50;
// apply handicap advantage to tank speed
fracOfMaxSpeed *= (1.0f + (handicap *
(BZDB.eval(StateDatabase::BZDB_HANDICAPVELAD) - 1.0f)));
// set desired speed
desiredSpeed = BZDBCache::tankSpeed * fracOfMaxSpeed;
Player::setUserSpeed(desiredSpeed);
return;
}
void LocalPlayer::setDesiredAngVel(float fracOfMaxAngVel)
{
FlagType* flag = getFlag();
// limit turn speed to maximum
if (fracOfMaxAngVel > 1.0f) fracOfMaxAngVel = 1.0f;
else if (fracOfMaxAngVel < -1.0f) fracOfMaxAngVel = -1.0f;
// further limit turn speed for certain flags
if (fracOfMaxAngVel < 0.0f && getFlag() == Flags::LeftTurnOnly)
fracOfMaxAngVel = 0.0f;
else if (fracOfMaxAngVel > 0.0f && getFlag() == Flags::RightTurnOnly)
fracOfMaxAngVel = 0.0f;
// boost turn speed for other flags
if (flag) {
fracOfMaxAngVel *= BZDB.eval(StateDatabase::BZDB_ANGULARAD);
} else if ((flag == Flags::Burrow) && (getPosition()[2] < 0.0f)) {
fracOfMaxAngVel *= BZDB.eval(StateDatabase::BZDB_BURROWANGULARAD);
}
// apply handicap advantage to tank speed
fracOfMaxAngVel *= (1.0f + (handicap *
(BZDB.eval(StateDatabase::BZDB_HANDICAPANGAD) - 1.0f)));
// set desired turn speed
desiredAngVel = fracOfMaxAngVel *
BZDB.eval(StateDatabase::BZDB_TANKANGVEL);
Player::setUserAngVel(desiredAngVel);
return;
}
void LocalPlayer::setPause(bool pause)
{
if (isAlive()) {
if (pause && !isPaused()) {
setStatus(getStatus() | short(PlayerState::Paused));
server->sendPaused(true);
}
else if (!pause && isPaused()) {
setStatus(getStatus() & ~short(PlayerState::Paused));
server->sendPaused(false);
}
}
}
void LocalPlayer::activateAutoPilot(bool autopilot)
{
if (autopilot && !isAutoPilot()) {
setAutoPilot();
server->sendAutoPilot(true);
}
else if (!autopilot && isAutoPilot()) {
setAutoPilot(false);
server->sendAutoPilot(false);
}
}
bool LocalPlayer::fireShot()
{
if (! (firingStatus == Ready || bCheatOO || firingStatus == Zoned))
return false;
// find an empty slot
const int numShots = World::getWorld()->getMaxShots();
int i;
for (i = 0; i < numShots; i++)
if (!shots[i])
break;
if (i == numShots) return false;
// make sure we're allowed to shoot
if (!isAlive() || isPaused() ||
((location == InBuilding) && !isPhantomZoned() && !bCheatOO)) {
return false;
}
// prepare shot
FiringInfo firingInfo(*this, i + getSalt());
// FIXME team coloring of shot is never used; it was meant to be used
// for rabbit mode to correctly calculate team kills when rabbit
changes
firingInfo.shot.team = getTeam();
if (firingInfo.flagType == Flags::ShockWave) {
// move shot origin under tank and make it stationary
const float* pos = getPosition();
firingInfo.shot.pos[0] = pos[0];
firingInfo.shot.pos[1] = pos[1];
firingInfo.shot.pos[2] = pos[2];
firingInfo.shot.vel[0] = 0.0f;
firingInfo.shot.vel[1] = 0.0f;
firingInfo.shot.vel[2] = 0.0f;
}
else {
// apply any handicap advantage to shot speed
if (handicap > 0.0f) {
const float speedAd = 1.0f + (handicap *
(BZDB.eval(StateDatabase::BZDB_HANDICAPSHOTAD) - 1.0f));
const float* dir = getForward();
const float* tankVel = getVelocity();
const float shotSpeed = speedAd *
BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
firingInfo.shot.vel[0] = tankVel[0] + shotSpeed * dir[0];
firingInfo.shot.vel[1] = tankVel[1] + shotSpeed * dir[1];
firingInfo.shot.vel[2] = tankVel[2] + shotSpeed * dir[2];
}
bool LockedOn = false;
const Player * target = getTarget();
if(target){
//const float* tankVel = getVelocity();
const float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
float newAz = TargetingUtils::getTargetAzimuth( getPosition(),
target->getPosition() );
float newPos[3];
const float * tarPos = target->getPosition();
float enemyPos[3];
const float *tv = target->getVelocity();
enemyPos[0] = tarPos[0] + (0.3f * tv[0]); //Lag adjustment
enemyPos[1] = tarPos[1] + (0.3f * tv[1]);
enemyPos[2] = tarPos[2] + (0.3f * tv[2]);
const float * myPos = getPosition();
const float * myDir = getForward();
newPos[0] = enemyPos[0] - ( myPos[0] + (myDir[0] * 0.1));
//Muzzlefront?
newPos[1] = enemyPos[1] - ( myPos[1] + (myDir[1] * 0.1));
//Muzzlefront?
newPos[2] = enemyPos[2] - myPos[2];
//newPos[2] += target->getMuzzleHeight(); // right between the eyes
float newElevation = atan2f(newPos[2], hypotf(newPos[1], newPos[0]));
float newDir[3] = {cosf(newAz), sinf(newAz), sinf(newElevation)};
firingInfo.shot.vel[0] = /*tankVel[0] +*/ shotSpeed * newDir[0];
firingInfo.shot.vel[1] = /*tankVel[1] +*/ shotSpeed * newDir[1];
firingInfo.shot.vel[2] = /*tankVel[2] +*/ shotSpeed * newDir[2];
LockedOn = true;
}
// Set _shotsKeepVerticalVelocity on the server if you want shots
// to have the same vertical velocity as the tank when fired.
// keeping shots moving horizontally makes the game more playable.
if (!LockedOn && !BZDB.isTrue(StateDatabase::BZDB_SHOTSKEEPVERTICALV))
firingInfo.shot.vel[2] = 0.0f;
}
// make shot and put it in the table
shots[i] = new LocalShotPath(firingInfo);
// Insert timestamp, useful for dead reckoning jitter fixing
const float timeStamp = float(TimeKeeper::getCurrent() -
TimeKeeper::getNullTime());
firingInfo.timeSent = timeStamp;
server->sendBeginShot(firingInfo);
if (BZDB.isTrue("enableLocalShotEffect") &&
SceneRenderer::instance().useQuality() >= 2)
EFFECTS.addShotEffect(getColor(), firingInfo.shot.pos, getAngle(),
getVelocity());
if (gettingSound) {
if (firingInfo.flagType == Flags::ShockWave) {
playLocalSound(SFX_SHOCK);
ForceFeedback::shockwaveFired();
}
else if (firingInfo.flagType == Flags::Laser) {
playLocalSound(SFX_LASER);
ForceFeedback::laserFired();
}
else if (firingInfo.flagType == Flags::GuidedMissile) {
playLocalSound(SFX_MISSILE);
ForceFeedback::shotFired();
}
else if (firingInfo.flagType == Flags::Thief) {
playLocalSound(SFX_THIEF);
ForceFeedback::shotFired();
}
else {
playLocalSound(SFX_FIRE);
ForceFeedback::shotFired();
}
}
shotStatistics.recordFire(firingInfo.flagType,getForward(),firingInfo.shot.vel);
if (getFlag() == Flags::TriggerHappy) {
// make sure all the shots don't go off at once
forceReload(BZDB.eval(StateDatabase::BZDB_RELOADTIME) / numShots);
}
return true;
}
float LocalPlayer::getReloadTime() const
{
const int numShots = World::getWorld()->getMaxShots();
if (numShots <= 0) {
return 0.0f;
}
float time = float(jamTime - TimeKeeper::getCurrent());
if (time > 0.0f) {
return time;
}
// look for an empty slot
int i;
for (i = 0; i < numShots; i++) {
if (!shots[i]) {
return 0.0f;
}
}
// look for the shot fired least recently
float minTime = float(shots[0]->getReloadTime() -
(shots[0]->getCurrentTime() - shots[0]->getStartTime()));
for (i = 1; i < numShots; i++) {
const float t = float(shots[i]->getReloadTime() -
(shots[i]->getCurrentTime() - shots[i]->getStartTime()));
if (t < minTime) {
minTime = t;
}
}
if (minTime < 0.0f) {
minTime = 0.0f;
}
return minTime;
}
bool LocalPlayer::doEndShot(int ident, bool isHit, float* pos)
{
const int index = ident & 255;
const int slt = (ident >> 8) & 127;
// special id used in some messages (and really shouldn't be sent
here)
if (ident == -1)
return false;
// ignore bogus shots (those with a bad index or for shots that don't
exist)
if (index < 0 || index >= World::getWorld()->getMaxShots() ||
!shots[index])
return false;
// ignore shots that already ending
if (shots[index]->isExpired() || shots[index]->isExpiring())
return false;
// ignore shots that have the wrong salt. since we reuse shot
indices
// it's possible for an old MsgShotEnd to arrive after we've started
a
// new shot. that's where the salt comes in. it changes for each
shot
// so we can identify an old shot from a new one.
if (slt != ((shots[index]->getShotId() >> 8) & 127))
return false;
// keep shot statistics
shotStatistics.recordHit(shots[index]->getFlag());
// don't stop if it's because were hitting something and we don't
stop
// when we hit something.
if (isHit && !shots[index]->isStoppedByHit())
return false;
// end it
const float* shotPos = shots[index]->getPosition();
pos[0] = shotPos[0];
pos[1] = shotPos[1];
pos[2] = shotPos[2];
shots[index]->setExpired();
return true;
}
void LocalPlayer::setJump()
{
wantJump = jumpPressed;
}
void LocalPlayer::setJumpPressed(bool value)
{
jumpPressed = value;
}
void LocalPlayer::doJump()
{
FlagType* flag = getFlag();
// can't jump while burrowed
if (getPosition()[2] < 0.0f) {
return;
}
if(BZDB.isTrue("displayRadar")){
if (flag == Flags::Wings) {
if (wingsFlapCount <= 0) {
return;
}
wingsFlapCount--;
} else if ((location != OnGround) && (location != OnBuilding)) {
// can't jump unless on the ground or a building
if (flag != Flags::Wings)
return;
if (wingsFlapCount <= 0)
return;
wingsFlapCount--;
} else if ((flag != Flags::Bouncy) &&
((flag != Flags::Jumping && !World::getWorld()->allowJumping())
||
(flag == Flags::NoJumping))) {
return;
}
}
// add jump velocity (actually, set the vertical component since you
// can only jump if resting on something)
const float* oldVelocity = getVelocity();
float newVelocity[3];
newVelocity[0] = oldVelocity[0];
newVelocity[1] = oldVelocity[1];
if (flag != Flags::Wings ){
newVelocity[2] = BZDB.eval(StateDatabase::BZDB_WINGSJUMPVELOCITY);
} else if (flag == Flags::Bouncy) {
const float factor = 0.25f + ((float)bzfrand() * 0.75f);
newVelocity[2] = factor *
BZDB.eval(StateDatabase::BZDB_JUMPVELOCITY);
} else {
newVelocity[2] = BZDB.eval(StateDatabase::BZDB_JUMPVELOCITY);
}
setVelocity(newVelocity);
location = InAir;
// setup the graphics
fireJumpJets();
// setup the sound
if (gettingSound) {
if (flag == Flags::Wings) {
playLocalSound(SFX_FLAP);
addRemoteSound(PlayerState::WingsSound);
} else {
playLocalSound(SFX_JUMP);
addRemoteSound(PlayerState::JumpSound);
}
}
wantJump = false;
}
void LocalPlayer::setTarget(const Player* _target)
{
target = _target;
}
void LocalPlayer::setNemesis(const Player* _nemesis)
{
if ((_nemesis == NULL) || _nemesis->getPlayerType() == TankPlayer)
nemesis = _nemesis;
}
void LocalPlayer::setRecipient(const Player* _recipient)
{
if ((_recipient == NULL) || (_recipient->getId() <= LastRealPlayer))
recipient = _recipient;
}
void LocalPlayer::explodeTank()
{
if (location == Dead || location == Exploding) return;
float gravity = BZDBCache::gravity;
float explodeTim = BZDB.eval(StateDatabase::BZDB_EXPLODETIME);
// Limiting max height increment to this value (the old default
value)
const float zMax = 49.0f;
setExplode(TimeKeeper::getTick());
const float* oldVelocity = getVelocity();
float newVelocity[3];
float maxSpeed;
newVelocity[0] = oldVelocity[0];
newVelocity[1] = oldVelocity[1];
if (gravity < 0) {
// comparing 2 speed:
// to have a simmetric path (ending at same height as starting)
// to reach the acme of parabola, under the max height
established
// take the less
newVelocity[2] = - 0.5f * gravity * explodeTim;
maxSpeed = sqrtf(- 2.0f * zMax * gravity);
if (newVelocity[2] > maxSpeed)
newVelocity[2] = maxSpeed;
} else {
newVelocity[2] = oldVelocity[2];
}
setVelocity(newVelocity);
location = Exploding;
}
//testing123
void LocalPlayer::doMomentum(float dt,
float& speed, float& angVel)
{
// get maximum linear and angular accelerations
float linearAcc = (getFlag() == Flags::Momentum) ?
BZDB.eval(StateDatabase::BZDB_MOMENTUMLINACC) :
World::getWorld()->getLinearAcceleration();
float angularAcc = (getFlag() == Flags::Momentum) ?
BZDB.eval(StateDatabase::BZDB_MOMENTUMANGACC) :
World::getWorld()->getAngularAcceleration();
// limit linear acceleration
if (linearAcc > 0.0f) {
const float acc = (speed - lastSpeed) / dt;
if (acc > 20.0f * linearAcc) speed = lastSpeed + dt *
20.0f*linearAcc;
else if (acc < -20.0f * linearAcc) speed = lastSpeed - dt *
20.0f*linearAcc;
}
// limit angular acceleration
if (angularAcc > 0.0f) {
const float oldAngVel = getAngularVelocity();
const float angAcc = (angVel - oldAngVel) / dt;
if (angAcc > angularAcc) angVel = oldAngVel + dt * angularAcc;
else if (angAcc < -angularAcc) angVel = oldAngVel - dt *
angularAcc;
}
}
void LocalPlayer::doFriction(float dt,
const float *oldVelocity, float *newVelocity)
{
const float friction = (getFlag() == Flags::Momentum) ?
BZDB.eval(StateDatabase::BZDB_MOMENTUMFRICTION) :
BZDB.eval(StateDatabase::BZDB_FRICTION);
if (friction > 0.0f) {
// limit vector acceleration
float delta[2] = {newVelocity[0] - oldVelocity[0], newVelocity[1] -
oldVelocity[1]};
float acc2 = (delta[0] * delta[0] + delta[1] * delta[1]) / (dt*dt);
float accLimit = 20.0f * friction;
if (acc2 > accLimit*accLimit) {
float ratio = accLimit / sqrtf(acc2);
newVelocity[0] = oldVelocity[0] + delta[0]*ratio;
newVelocity[1] = oldVelocity[1] + delta[1]*ratio;
}
}
}
void LocalPlayer::doForces(float /*dt*/,
float* /*velocity*/,
float& /*angVel*/)
{
// apply external forces
// do nothing -- no external forces right now
}
// NOTE -- minTime should be initialized to Infinity by the caller
bool LocalPlayer::checkHit(const Player* source,
const ShotPath*& hit, float& minTime) const
{
bool goodHit = false;
// if firing tank is paused then it doesn't count
if (source->isPaused()) return goodHit;
const int maxShots = source->getMaxShots();
for (int i = 0; i < maxShots; i++) {
// get shot
const ShotPath* shot = source->getShot(i);
if (!shot || shot->isExpired()) continue;
// my own shock wave cannot kill me
if (source == this && ((shot->getFlag() == Flags::ShockWave) ||
(shot->getFlag() == Flags::Thief))) continue;
// short circuit test if shot can't possibly hit.
// only superbullet or shockwave can kill zoned dude
const FlagType* shotFlag = shot->getFlag();
if (isPhantomZoned() &&
(shotFlag != Flags::ShockWave) &&
(shotFlag != Flags::SuperBullet) &&
(shotFlag != Flags::PhantomZone))
continue;
// laser can't hit a cloaked tank
if ((getFlag() == Flags::Cloaking) && (shotFlag == Flags::Laser))
continue;
// zoned shots only kill zoned tanks
if ((shotFlag == Flags::PhantomZone) && !isPhantomZoned()) {
continue;
}
// test myself against shot
float position[3];
const float t = shot->checkHit(this, position);
if (t >= minTime) continue;
// test if shot hit a part of my tank that's through a teleporter.
// hit is no good if hit point is behind crossing plane.
if (isCrossingWall() && position[0] * crossingPlane[0] +
position[1] * crossingPlane[1] +
position[2] * crossingPlane[2] + crossingPlane[3] < 0.0)
continue;
// okay, shot hit
goodHit = true;
hit = shot;
minTime = t;
}
return goodHit;
}
void LocalPlayer::setFlag(FlagType* flag)
{
Player::setFlag(flag);
float worldSize = BZDBCache::worldSize;
// if it's bad then reset countdowns and set antidote flag
if (getFlag() != Flags::Null && getFlag()->endurance == FlagSticky) {
if (World::getWorld()->allowShakeTimeout())
flagShakingTime = World::getWorld()->getFlagShakeTimeout();
if (World::getWorld()->allowShakeWins())
flagShakingWins = World::getWorld()->getFlagShakeWins();
if (World::getWorld()->allowAntidote()) {
float tankRadius = BZDBCache::tankRadius;
float baseSize = BZDB.eval(StateDatabase::BZDB_BASESIZE);
do {
if (World::getWorld()->allowTeamFlags()) {
flagAntidotePos[0] = 0.5f * worldSize * ((float)bzfrand() - 0.5f);
flagAntidotePos[1] = 0.5f * worldSize * ((float)bzfrand() - 0.5f);
flagAntidotePos[2] = 0.0f;
} else {
flagAntidotePos[0] = (worldSize - baseSize) * ((float)bzfrand() -
0.5f);
flagAntidotePos[1] = (worldSize - baseSize) * ((float)bzfrand() -
0.5f);
flagAntidotePos[2] = 0.0f;
}
} while (World::getWorld()->inBuilding(flagAntidotePos,
tankRadius,
BZDBCache::tankHeight));
antidoteFlag = new FlagSceneNode(flagAntidotePos);
antidoteFlag->setColor(1.0f, 1.0f, 0.0f);
World::setFlagTexture(antidoteFlag);
}
} else {
delete antidoteFlag;
antidoteFlag = NULL;
flagShakingTime = 0.0f;
flagShakingWins = 0;
}
}
void LocalPlayer::changeScore(short deltaWins,
short deltaLosses,
short deltaTks)
{
Player::changeScore(deltaWins, deltaLosses, deltaTks);
if (deltaWins > 0 && World::getWorld()->allowShakeWins() &&
flagShakingWins > 0) {
flagShakingWins -= deltaWins;
if (flagShakingWins <= 0) {
flagShakingWins = 0;
server->sendDropFlag(getPosition());
}
}
}
// calculate overall relative score to other players
int LocalPlayer::getHandicapScoreBase() const
{
int winners = 0;
int loosers = 0;
const int maxplayers = World::getWorld()->getMaxPlayers();
// compute overall wins and losses in relation to other players
for (int i = 0; i < maxplayers; i++) {
RemotePlayer *player = World::getWorld()->getPlayer(i);
if ((player == NULL) || (player->getId() ==
LocalPlayer::getMyTank()->getId())) {
continue;
}
winners += player->getLocalWins();
loosers += player->getLocalLosses();
}
return loosers - winners;
}
float LocalPlayer::updateHandicap()
{
/* compute and return the handicap */
if (World::getWorld()->allowHandicap()) {
// a relative score of -50 points will provide maximum handicap
handicap = float(getHandicapScoreBase()) /
BZDB.eval(StateDatabase::BZDB_HANDICAPSCOREDIFF);
/* limit how much of a handicap is afforded, and only provide
* handicap advantages instead of disadvantages.
*/
if (handicap > 1.0f) {
// advantage
handicap = 1.0f;
} else if (handicap < 0.0f) {
// disadvantage
handicap = 0.0f;
}
return handicap;
}
// no handicap gameplay
return 0.0f;
}
void LocalPlayer::addAntidote(SceneDatabase* scene)
{
if (antidoteFlag)
scene->addDynamicNode(antidoteFlag);
}
std::string LocalPlayer::getInputMethodName(InputMethod whatInput)
{
switch (whatInput) {
case Keyboard:
return std::string("Keyboard");
break;
case Mouse:
return std::string("Mouse");
break;
case Joystick:
return std::string("Joystick");
break;
default:
return std::string("Unnamed Device");
}
}
void LocalPlayer::setKey(int button, bool pressed) {
switch (button) {
case BzfKeyEvent::Left:
left = pressed;
break;
case BzfKeyEvent::Right:
right = pressed;
break;
case BzfKeyEvent::Up:
up = pressed;
break;
case BzfKeyEvent::Down:
down = pressed;
break;
}
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "Player.h"
// common interface headers
#include "TankSceneNode.h"
#include "SceneRenderer.h"
#include "SphereSceneNode.h"
#include "OpenGLMaterial.h"
#include "BZDBCache.h"
#include "TextureManager.h"
#include "CollisionManager.h"
#include "ObstacleMgr.h"
#include "PhysicsDriver.h"
#include "ObstacleList.h"
#include "WallObstacle.h"
// local implementation headers
#include "playing.h"
#include "World.h"
#include "TrackMarks.h"
#include "sound.h"
#include "effectsRenderer.h"
#include "Roaming.h"
#include "LocalPlayer.h"
float sizeCheat = 1.0f;
// for dead reckoning
static const float MaxUpdateTime = 1.0f; // seconds
//
// Player
//
int Player::tankTexture = -1;
Player::Player(const PlayerId& _id, TeamColor _team,
const char* name, const char* _email, const PlayerType _type) :
lastObstacle(NULL),
pauseMessageState(false),
notResponding(false),
hunted(false),
id(_id),
admin(false),
registered(false),
verified(false),
playerList(false),
lastVisualTeam(NoTeam),
team(_team),
type(_type),
flagType(Flags::Null),
fromTeleporter(0),
toTeleporter(0),
teleporterProximity(0.0f),
wins(0),
losses(0),
tks(0),
localWins(0),
localLosses(0),
localTks(0),
autoPilot(false),
deltaTime(0.0),
offset(0.0),
deadReckoningState(0),
oldStatus(0),
oldZSpeed(0.0f)
{
static const float zero[3] = { 0.0f, 0.0f, 0.0f };
move(zero, 0.0f);
setVelocity(zero);
setAngularVelocity(0.0f);
setPhysicsDriver(-1);
setDeadReckoning();
setRelativeMotion();
setUserSpeed(0.0f);
setUserAngVel(0.0f);
reportedHits = 0;
computedHits = 0;
// set call sign
::strncpy(callSign, name, CallSignLen);
callSign[CallSignLen-1] = '\0';
// set email address
::strncpy(email, _email, EmailLen);
email[EmailLen-1] = '\0';
if (id != ServerPlayer) {
// make scene nodes
tankNode = new TankSceneNode(state.pos, forward);
tankIDLNode = new TankIDLSceneNode(tankNode);
changeTeam(team);
const float sphereRad = (1.5f * BZDBCache::tankRadius);
if (RENDERER.useQuality() >= 3) {
pausedSphere = new SphereLodSceneNode(state.pos, sphereRad);
} else {
pausedSphere = new SphereBspSceneNode(state.pos, sphereRad);
}
pausedSphere->setColor(0.0f, 0.0f, 0.0f, 0.5f);
}
// setup the dimension properties
dimensions[0] = 0.5f * BZDBCache::tankLength;
dimensions[1] = 0.5f * BZDBCache::tankWidth;
dimensions[2] = BZDBCache::tankHeight;
for (int i = 0; i < 3; i++) {
dimensionsRate[i] = 0.0f;
dimensionsScale[i] = 1.0f;
dimensionsTarget[i] = 1.0f;
}
useDimensions = false;
// setup alpha properties
alpha = 1.0f;
alphaRate = 0.0f;
alphaTarget = 1.0f;
teleAlpha = 1.0f;
haveIpAddr = false; // no IP address yet
lastTrackDraw = TimeKeeper::getCurrent();
spawnTime = TimeKeeper::getCurrent();
return;
}
Player::~Player()
{
if (id != ServerPlayer) {
delete tankIDLNode;
delete tankNode;
delete pausedSphere;
}
}
// Take into account the quality of player wins/(wins+loss)
// Try to penalize winning casuality
static float rabbitRank (int wins, int losses) {
// otherwise do score-based ranking
int sum = wins + losses;
if (sum == 0)
return 0.5;
float average = (float)wins/(float)sum;
// IIRC that is how wide is the gaussian
float penalty = (1.0f - 0.5f / sqrt((float)sum));
return average * penalty;
}
float Player::getTKRatio() const
{
if (wins == 0) {
// well, we have to return *something* if they have no wins
return (float)tks/1.0f;
} else {
return (float)tks/(float)wins;
}
}
// returns a value between 1.0 and -1.0
float Player::getNormalizedScore() const
{
return ((float)wins - losses) / ((wins+losses>20) ? wins+losses :
20);
}
float Player::getLocalNormalizedScore() const
{
return ((float)localWins - localLosses)
/ ((localWins+localLosses>5) ? localWins+localLosses : 5);
}
short Player::getRabbitScore() const
{
return (int)(rabbitRank(wins, losses) * 100.0);
}
float Player::getRadius() const
{
// NOTE: this encompasses everything but Narrow
// the Obese, Tiny, and Thief flags adjust
// the radius, but Narrow does not.
return (dimensionsScale[0] * BZDBCache::tankRadius);
}
float Player::getMaxSpeed ( void ) const
{
// BURROW and AGILITY will not be taken into account
const FlagType* flag = getFlag();
float maxSpeed = BZDBCache::tankSpeed;
if (flag == Flags::Velocity) {
maxSpeed *= BZDB.eval(StateDatabase::BZDB_VELOCITYAD);
} else if (flag == Flags::Thief) {
maxSpeed *= BZDB.eval(StateDatabase::BZDB_THIEFVELAD);
}
return maxSpeed;
}
void Player::getMuzzle(float* m) const
{
// NOTE: like getRadius(), we only use dimensionsScale[0].
// as well, we do not use BZDB_MUZZLEFRONT, but the
// 0.1f value listed in global.cxx is added on to the
// scaled version of tankRadius.
float front = (dimensionsScale[0] * BZDBCache::tankRadius);
if (dimensionsRate[0] > 0.0f) {
front = front + (dimensionsRate[0] * 0.1f);
}
front = front + 0.1f;
m[0] = state.pos[0] + (front * forward[0]);
m[1] = state.pos[1] + (front * forward[1]);
const float height = BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT);
m[2] = state.pos[2] + (height * dimensionsScale[2]);
return;
}
float Player::getMuzzleHeight() const
{
return (dimensionsScale[2] *
BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT));
}
void Player::forceReload(float time)
{
jamTime = TimeKeeper::getCurrent();
jamTime+= time;
}
void Player::move(const float* _pos, float _azimuth)
{
// update the speed of the state
float currentTime = (float)TimeKeeper::getCurrent().getSeconds();
if ( state.lastUpdateTime >= 0 )
{
float delta = currentTime - state.lastUpdateTime;
state.aperantVelocity[0] = (_pos[0]-state.pos[0])/delta;
state.aperantVelocity[1] = (_pos[1]-state.pos[1])/delta;
state.aperantVelocity[2] = (_pos[2]-state.pos[2])/delta;
}
state.lastUpdateTime = currentTime;
// assumes _forward is normalized
state.pos[0] = _pos[0];
state.pos[1] = _pos[1];
state.pos[2] = _pos[2];
state.azimuth = _azimuth;
// limit angle
if (state.azimuth < 0.0f) {
state.azimuth = (float)((2.0 * M_PI) - fmodf(-state.azimuth,
(float)(2.0 * M_PI)));
}
else if (state.azimuth >= (2.0f * M_PI)) {
state.azimuth = fmodf(state.azimuth, (float)(2.0 * M_PI));
}
// update forward vector (always in horizontal plane)
forward[0] = cosf(state.azimuth);
forward[1] = sinf(state.azimuth);
forward[2] = 0.0f;
// compute teleporter proximity
if (World::getWorld()) {
teleporterProximity =
World::getWorld()->getProximity(state.pos,
BZDBCache::tankRadius);
}
}
void Player::setVelocity(const float* _velocity)
{
state.velocity[0] = _velocity[0];
state.velocity[1] = _velocity[1];
state.velocity[2] = _velocity[2];
}
void Player::setAngularVelocity(float _angVel)
{
state.angVel = _angVel;
}
void Player::setPhysicsDriver(int driver)
{
state.phydrv = driver;
const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(driver);
if (phydrv != NULL) {
state.status |= PlayerState::OnDriver;
} else {
state.status &= ~PlayerState::OnDriver;
}
return;
}
void Player::setRelativeMotion()
{
bool falling = (state.status & short(PlayerState::Falling)) != 0;
if (falling && (getFlag() != Flags::Wings)) {
// no adjustments while falling
return;
}
// set 'relativeSpeed' and 'relativeAngVel'
float relativeVel[2];
Player::calcRelativeMotion(relativeVel, relativeSpeed,
relativeAngVel);
return;
}
void Player::setUserSpeed(float speed)
{
state.userSpeed = speed;
return;
}
void Player::setUserAngVel(float angVel)
{
state.userAngVel = angVel;
return;
}
void Player::calcRelativeMotion(float vel[2], float& speed, float&
angVel)
{
vel[0] = state.velocity[0];
vel[1] = state.velocity[1];
angVel = state.angVel;
const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(state.phydrv);
if (phydrv != NULL) {
const float* v = phydrv->getLinearVel();
const float av = phydrv->getAngularVel();
const float* ap = phydrv->getAngularPos();
// adjust for driver velocity
vel[0] -= v[0];
vel[1] -= v[1];
// adjust for driver angular velocity
if (av != 0.0f) {
const float dx = state.pos[0] - ap[0];
const float dy = state.pos[1] - ap[1];
vel[0] += av * dy;
vel[1] -= av * dx;
angVel = state.angVel - av;
}
}
// speed relative to the tank's direction
// (could use forward[] instead of re-doing the trig, but this is
// used in the setDeadReckoning(), when forward[] is not yet set)
speed = (vel[0] * cosf(state.azimuth)) + (vel[1] *
sinf(state.azimuth));
return;
}
void Player::changeTeam(TeamColor _team)
{
// set team
team = _team;
// set the scene node
setVisualTeam(team);
}
void Player::setStatus(short _status)
{
state.status = _status;
}
void Player::setExplode(const TimeKeeper& t)
{
if (!isAlive()) return;
explodeTime = t;
setStatus((getStatus() | short(PlayerState::Exploding) |
short(PlayerState::Falling)) &
~(short(PlayerState::Alive) | short(PlayerState::Paused)));
tankNode->rebuildExplosion();
// setup the flag effect to revert to normal
updateFlagEffect(Flags::Null);
}
void Player::setTeleport(const TimeKeeper& t, short from, short to)
{
if (!isAlive()) return;
teleportTime = t;
fromTeleporter = from;
toTeleporter = to;
setStatus(getStatus() | short(PlayerState::Teleporting));
}
void Player::updateTank(float dt, bool local)
{
updateDimensions(dt, local);
updateTranslucency(dt);
updateTreads(dt);
updateJumpJets(dt);
updateTrackMarks();
return;
}
void Player::updateJumpJets(float dt)
{
float jumpVel;
if (getFlag() == Flags::Wings) {
jumpVel = BZDB.eval(StateDatabase::BZDB_WINGSJUMPVELOCITY);
} else {
jumpVel = BZDB.eval(StateDatabase::BZDB_JUMPVELOCITY);
}
const float jetTime = 0.5f * (jumpVel / -BZDBCache::gravity);
state.jumpJetsScale -= (dt / jetTime);
if (state.jumpJetsScale < 0.0f) {
state.jumpJetsScale = 0.0f;
state.status &= ~PlayerState::JumpJets;
}
return;
}
void Player::updateTrackMarks()
{
const float minSpeed = 0.1f; // relative speed slop
if (isAlive() && !isFalling() && !isPhantomZoned()) {
const float lifeTime = float(TimeKeeper::getCurrent() -
lastTrackDraw);
if (lifeTime > TrackMarks::updateTime) {
bool drawMark = true;
float markPos[3];
markPos[2] = state.pos[2];
// FIXME - again, this should be pulled for TankGeometryMgr
const float fullLength = 6.0f;
const float treadHeight = 1.2f;
const float dist =
dimensions[0] * ((fullLength - treadHeight) / fullLength);
if (relativeSpeed > +minSpeed) {
// draw the mark at the back of the treads
markPos[0] = state.pos[0] - (forward[0] * dist);
markPos[1] = state.pos[1] - (forward[1] * dist);
} else if (relativeSpeed < -minSpeed) {
// draw the mark at the front of the treads
markPos[0] = state.pos[0] + (forward[0] * dist);
markPos[1] = state.pos[1] + (forward[1] * dist);
} else {
drawMark = false;
}
if (drawMark) {
TrackMarks::addMark(markPos, dimensionsScale[1],
state.azimuth, state.phydrv);
lastTrackDraw = TimeKeeper::getCurrent();
}
}
}
return;
}
void Player::updateDimensions(float dt, bool local)
{
// copy the current information
float oldRates[3];
float oldScales[3];
float oldDimensions[3];
memcpy (oldRates, dimensionsRate, sizeof(float[3]));
memcpy (oldScales, dimensionsScale, sizeof(float[3]));
memcpy (oldDimensions, dimensions, sizeof(float[3]));
// update the dimensions
bool resizing = false;
for (int i = 0; i < 3; i++) {
if (dimensionsRate[i] != 0.0f) {
resizing = true;
dimensionsScale[i] += (dt * dimensionsRate[i]);
if (dimensionsRate[i] < 0.0f) {
if (dimensionsScale[i] < dimensionsTarget[i]) {
dimensionsScale[i] = dimensionsTarget[i];
dimensionsRate[i] = 0.0f;
}
} else {
if (dimensionsScale[i] > dimensionsTarget[i]) {
dimensionsScale[i] = dimensionsTarget[i];
dimensionsRate[i] = 0.0f;
}
}
} else {
// safety play, should not be required
dimensionsScale[i] = dimensionsTarget[i];
}
}
// set the actual dimensions based on the scale
dimensions[0] = dimensionsScale[0] * (0.5f * BZDBCache::tankLength);
dimensions[1] = dimensionsScale[1] * (0.5f * BZDBCache::tankWidth);
dimensions[2] = dimensionsScale[2] * BZDBCache::tankHeight;
// do not resize if it will cause a collision
// only checked for the local player, remote is computationally
expensive
if (local) {
// also do not bother with collision checking if we are not
resizing
if (resizing && hitObstacleResizing()) {
// copy the old information
memcpy (dimensions, oldDimensions, sizeof(float[3]));
memcpy (dimensionsScale, oldScales, sizeof(float[3]));
memcpy (dimensionsRate, oldRates, sizeof(float[3]));
}
}
// check if the dimensions are at a steady state
if ((dimensionsScale[0] == dimensionsTarget[0]) &&
(dimensionsScale[1] == dimensionsTarget[1]) &&
(dimensionsScale[2] == dimensionsTarget[2])) {
useDimensions = false;
} else {
useDimensions = true;
}
return;
}
bool Player::hitObstacleResizing()
{
const float* dims = dimensions;
// check walls
const World* world = World::getWorld();
if (world) {
const ObstacleList& walls = OBSTACLEMGR.getWalls();
for (unsigned int i = 0; i < walls.size(); i++) {
const WallObstacle* wall = (const WallObstacle*) walls[i];
if (wall->inBox(getPosition(), getAngle(), dims[0], dims[1],
dims[2])) {
return true;
}
}
}
// check everything else
const ObsList* olist =
COLLISIONMGR.boxTest(getPosition(), getAngle(), dims[0], dims[1],
dims[2]);
for (int i = 0; i < olist->count; i++) {
const Obstacle* obs = olist->list[i];
const bool onTop = obs->isFlatTop() &&
((obs->getPosition()[2] + obs->getHeight()) <= getPosition()[2]);
if (!obs->isDriveThrough() && !onTop &&
obs->inBox(getPosition(), getAngle(), dims[0], dims[1], dims[2])) {
return true;
}
}
return false;
}
void Player::updateTranslucency(float dt)
{
// update the alpha value
if (alphaRate != 0.0f) {
alpha += dt * alphaRate;
if (alphaRate < 0.0f) {
if (alpha < alphaTarget) {
alpha = alphaTarget;
alphaRate = 0.0f;
}
} else {
if (alpha > alphaTarget) {
alpha = alphaTarget;
alphaRate = 0.0f;
}
}
}
// set the tankNode color
if (isPhantomZoned()) {
teleAlpha = 1.0f;
color[3] = 0.25f; // barely visible, regardless of teleporter
proximity
}
else {
teleporterProximity =
World::getWorld()->getProximity(state.pos,
BZDBCache::tankRadius);
teleAlpha = (1.0f - (0.75f * teleporterProximity));
if (alpha == 0.0f) {
color[3] = 0.0f; // not trusting FP accuracy
} else {
color[3] = teleAlpha * alpha;
}
}
return;
}
void Player::updateTreads(float dt)
{
float speedFactor;
float angularFactor;
if ((state.status & PlayerState::UserInputs) != 0) {
speedFactor = state.userSpeed;
angularFactor = state.userAngVel;
} else {
speedFactor = relativeSpeed;
angularFactor = relativeAngVel;
}
// setup the linear component
if (dimensionsScale[0] > 1.0e-6f) {
speedFactor = speedFactor / dimensionsScale[0];
} else {
speedFactor = speedFactor * 1.0e6f;
}
// setup the angular component
const float angularScale = 4.0f; // spin factor (at 1.0, the edges
line up)
angularFactor *= angularScale;
const float halfWidth = 0.5f * BZDBCache::tankWidth;
// not using dimensions[1], because it may be set to 0.001 by a
Narrow flag
angularFactor *= dimensionsScale[0] * halfWidth;
const float leftOff = dt * (speedFactor - angularFactor);
const float rightOff = dt * (speedFactor + angularFactor);
tankNode->addTreadOffsets(leftOff, rightOff);
return;
}
void Player::changeScore(short deltaWins, short deltaLosses, short
deltaTeamKills)
{
wins += deltaWins;
losses += deltaLosses;
tks += deltaTeamKills;
}
void Player::changeLocalScore(short dWins, short dLosses, short
dTeamKills)
{
localWins += dWins;
localLosses += dLosses;
localTks += dTeamKills;
}
void Player::setFlag(FlagType* _flag)
{
// set the type
flagType = _flag;
updateFlagEffect(flagType);
return;
}
void Player::updateFlagEffect(FlagType* effectFlag)
{
float FlagEffectTime = BZDB.eval(StateDatabase::BZDB_FLAGEFFECTTIME);
if (FlagEffectTime <= 0.0f) {
FlagEffectTime = 0.001f; // safety
}
// set the dimension targets
dimensionsTarget[0] = 1.0f;
dimensionsTarget[1] = 1.0f;
dimensionsTarget[2] = 1.0f;
if (effectFlag == Flags::Obesity) {
const float factor = BZDB.eval(StateDatabase::BZDB_OBESEFACTOR);
dimensionsTarget[0] = factor;
dimensionsTarget[1] = factor;
}
else if (effectFlag == Flags::Tiny) {
const float factor = BZDB.eval(StateDatabase::BZDB_TINYFACTOR);
dimensionsTarget[0] = factor;
dimensionsTarget[1] = factor;
}
else if (effectFlag == Flags::Thief) {
const float factor =
BZDB.eval(StateDatabase::BZDB_THIEFTINYFACTOR);
dimensionsTarget[0] = factor;
dimensionsTarget[1] = factor;
}
else if (effectFlag == Flags::Narrow) {
dimensionsTarget[1] = 0.001f;
}
if (id == LocalPlayer::getMyTank()->getId())
{
dimensionsTarget[0] *= sizeCheat;
dimensionsTarget[1] *= sizeCheat;
}
// set the dimension rates
for (int i = 0; i < 3; i++) {
if (dimensionsTarget[i] != dimensionsScale[i]) {
dimensionsRate[i] = dimensionsTarget[i] - dimensionsScale[i];
dimensionsRate[i] = dimensionsRate[i] / FlagEffectTime;
}
}
// set the alpha target
if (effectFlag == Flags::Cloaking) {
alphaTarget = 0.5f;
} else {
alphaTarget = 1.0f;
}
// set the alpha rate
if (alphaTarget != alpha) {
alphaRate = (alphaTarget - alpha) / FlagEffectTime;
}
return;
}
void Player::endShot(int index, bool isHit, bool showExplosion)
{
float pos[3];
if (doEndShot(index, isHit, pos) && showExplosion) {
addShotExplosion(pos);
}
return;
}
void Player::setVisualTeam (TeamColor visualTeam)
{
// only do all this junk when the effective team color actually
changes
if (visualTeam == lastVisualTeam)
return;
lastVisualTeam = visualTeam;
static const GLfloat tankSpecular[3] = { 0.1f, 0.1f, 0.1f };
static GLfloat tankEmissive[3] = { 0.0f, 0.0f, 0.0f };
static float tankShininess = 20.0f;
static GLfloat rabbitEmissive[3] = { 0.0f, 0.0f, 0.0f };
static float rabbitShininess = 100.0f;
GLfloat *emissive;
GLfloat shininess;
if (visualTeam == RabbitTeam) {
emissive = rabbitEmissive;
shininess = rabbitShininess;
} else {
emissive = tankEmissive;
shininess = tankShininess;
}
// get the texture each time, since it's just a refrence
const bool hunter = World::getWorld()->allowRabbit() && visualTeam !=
RabbitTeam;
TextureManager &tm = TextureManager::instance();
std::string texName;
if (hunter)
texName = BZDB.get("hunterTeamPrefix");
else
texName = Team::getImagePrefix(visualTeam);
texName += BZDB.get("tankTexture");
// now after we did all that, see if they have a user texture
tankTexture = -1;
if (userTexture.size())
tankTexture = tm.getTextureID(userTexture.c_str(),false);
// if the user load failed try our calculated texture
if (tankTexture < 0)
tankTexture = tm.getTextureID(texName.c_str(),false);
// we are the hunter, we are orange..
// TODO this is cheap, just untill a "hunter" team is made
if (hunter) {
color[0] = 1.0f;
color[1] = 0.5f;
color[2] = 0.0f;
} else {
const float* _color = Team::getTankColor(visualTeam);
color[0] = _color[0];
color[1] = _color[1];
color[2] = _color[2];
}
tankNode->setMaterial(OpenGLMaterial(tankSpecular, emissive,
shininess));
tankNode->setTexture(tankTexture);
int jumpJetsTexture = tm.getTextureID("jumpjets", false);
tankNode->setJumpJetsTexture(jumpJetsTexture);
}
void Player::fireJumpJets()
{
state.jumpJetsScale = 1.0f;
state.status |= PlayerState::JumpJets;
return;
}
void Player::clearRemoteSounds()
{
state.sounds = PlayerState::NoSounds;
state.status &= ~PlayerState::PlaySound;
return;
}
void Player::addRemoteSound(int sound)
{
state.sounds |= sound;
if (state.sounds != PlayerState::NoSounds) {
state.status |= PlayerState::PlaySound;
}
return;
}
void Player::addToScene(SceneDatabase* scene, TeamColor effectiveTeam,
bool inCockpit, bool seerView,
bool showTreads, bool showIDL)
{
const GLfloat groundPlane[4] = {0.0f, 0.0f, 1.0f, 0.0f};
if (!isAlive() && !isExploding()) {
return; // don't draw anything
}
// place the tank
tankNode->move(state.pos, forward);
// only use dimensions if we aren't at steady state.
// this is done because it's more expensive to use
// GL_NORMALIZE then to use precalculated normals.
if (useDimensions) {
tankNode->setDimensions(dimensionsScale);
} else {
if (flagType == Flags::Obesity) tankNode->setObese();
else if (flagType == Flags::Tiny) tankNode->setTiny();
else if (flagType == Flags::Narrow) tankNode->setNarrow();
else if (flagType == Flags::Thief) tankNode->setThief();
else tankNode->setNormal();
}
// setup the visibility properties
if (inCockpit && !showTreads) {
tankNode->setOnlyShadows(true);
} else {
tankNode->setOnlyShadows(false);
}
// adjust alpha for seerView
if (seerView) {
if (isPhantomZoned()) {
color[3] = 0.25f;
} else {
color[3] = teleAlpha;
}
}
// setup the color and material
setVisualTeam(effectiveTeam);
tankNode->setColor(color);
tankNode->setInTheCockpit(inCockpit);
// reset the clipping plane
tankNode->setClipPlane(NULL);
tankNode->setJumpJets(0.0f);
if (isAlive()) {
tankNode->setExplodeFraction(0.0f);
tankNode->setJumpJets(state.jumpJetsScale);
scene->addDynamicNode(tankNode);
if (isCrossingWall()) {
// get which plane to compute IDL against
GLfloat plane[4];
const GLfloat a = atan2f(forward[1], forward[0]);
const Obstacle* obstacle =
World::getWorld()->hitBuilding(state.pos, a,
dimensions[0], dimensions[1],
dimensions[2]);
if (obstacle && obstacle->isCrossing(state.pos, a,
dimensions[0], dimensions[1],
dimensions[2], plane) ||
World::getWorld()->crossingTeleporter(state.pos, a,
dimensions[0], dimensions[1],
dimensions[2], plane)) {
// stick in interdimensional lights node
if (showIDL) {
tankIDLNode->move(plane);
scene->addDynamicNode(tankIDLNode);
}
// add clipping plane to tank node
if (!inCockpit) {
tankNode->setClipPlane(plane);
}
}
}
else if (getPosition()[2] < 0.0f) {
// this should only happen with Burrow flags
tankNode->setClipPlane(groundPlane);
} // isCrossingWall()
} // isAlive()
else if (isExploding() && (state.pos[2] > ZERO_TOLERANCE)) {
float t = float((TimeKeeper::getTick() - explodeTime) /
BZDB.eval(StateDatabase::BZDB_EXPLODETIME));
if (t > 1.0f) {
// FIXME - setStatus(DeadStatus);
t = 1.0f;
} else if (t < 0.0f) {
// shouldn't happen but why take chances
t = 0.0f;
}
// fade at the end of the explosion
const float fadeRatio = 0.8f;
if (t > fadeRatio) {
GLfloat newColor[4];
memcpy(newColor, color, sizeof(float[3]));
const float fadeFactor = (1.0f - t) / (1.0f - fadeRatio);
newColor[3] = color[3] * fadeFactor;
tankNode->setColor(newColor);
}
tankNode->setExplodeFraction(t);
tankNode->setClipPlane(groundPlane); // shadows are not clipped
scene->addDynamicNode(tankNode);
}
if (isAlive() && (isPaused() || isNotResponding())) {
pausedSphere->move(state.pos,
1.5f * BZDBCache::tankRadius * dimensionsScale[0]);
scene->addDynamicSphere(pausedSphere);
}
}
void Player::setLandingSpeed(float velocity)
{
float squishiness = BZDB.eval(StateDatabase::BZDB_SQUISHFACTOR);
if (squishiness < 0.001f) {
return;
}
float squishTime = BZDB.eval(StateDatabase::BZDB_SQUISHTIME);
if (squishTime < 0.001) {
return;
}
const float gravity = BZDBCache::gravity;
if (velocity > 0.0f) {
velocity = 0.0f;
}
// Setup so that a drop height of BZDB_GRAVITY squishes
// by a factor of 1/11, when BZDB_SQUISHFACTOR is set to 1
//
// G = gravity; V = velocity; D = fall distance; K = factor
//
// V = sqrt (2 * D * G)
// V = sqrt(2) * G { @ D = G)
// scale = 1 / (1 + (K * V^2))
// scale = 1 / (1 + (K * 2 * G^2))
// set: (K * 2 * G^2) = 0.1
// K = 0.1 / (2 * G^2)
//
float k = 0.1f / (2.0f * gravity * gravity);
k = k * squishiness;
if (flagType == Flags::Bouncy) {
k = k * 4.0f;
}
const float newZscale = 1.0f / (1.0f + (k * (velocity * velocity)));
// don't update if the tank is still recovering
// from a spawn effect or a larger fall.
if (newZscale < dimensionsScale[2]) {
dimensionsScale[2] = newZscale;
// use a fixed decompression rate
dimensionsRate[2] = 1.0f / squishTime;
}
return;
}
void Player::spawnEffect()
{
const float squishiness =
BZDB.eval(StateDatabase::BZDB_SQUISHFACTOR);
if (squishiness > 0.0f) {
const float effectTime =
BZDB.eval(StateDatabase::BZDB_FLAGEFFECTTIME);
const float factor = 1.0f / effectTime;
for (int i = 0; i < 3; i++) {
dimensionsRate[i] = factor;
dimensionsScale[i] = 0.01f;
}
}
spawnTime = TimeKeeper::getCurrent();
return;
}
int Player::getMaxShots() const
{
return World::getWorld()->getMaxShots();
}
void Player::addShots(SceneDatabase* scene, bool colorblind) const
{
const int count = getMaxShots();
for (int i = 0; i < count; i++) {
ShotPath* shot = getShot(i);
if (shot && !shot->isExpiring() && !shot->isExpired())
shot->addShot(scene, colorblind);
}
}
void* Player::unpack(void* buf, uint16_t code)
{
float timestamp;
PlayerId ident;
buf = nboUnpackFloat(buf, timestamp);
buf = nboUnpackUByte(buf, ident);
buf = state.unpack(buf, code);
setDeadReckoning(timestamp);
setRelativeMotion();
return buf;
}
bool Player::validTeamTarget(const Player *possibleTarget) const
{
TeamColor myTeam = getTeam();
TeamColor targetTeam = possibleTarget->getTeam();
if (myTeam != targetTeam) {
return true;
}
if (myTeam != RogueTeam) {
return false;
}
return !World::getWorld()->allowRabbit();
}
void Player::getDeadReckoning(float* predictedPos, float*
predictedAzimuth,
float* predictedVel, float dt) const
{
*predictedAzimuth = inputAzimuth;
if (inputStatus & PlayerState::Paused) {
// don't move when paused
predictedPos[0] = inputPos[0];
predictedPos[1] = inputPos[1];
predictedPos[2] = inputPos[2];
predictedVel[0] = 0.0f;
predictedVel[1] = 0.0f;
predictedVel[2] = 0.0f;
}
else if (inputStatus & PlayerState::Falling) {
// no control when falling
predictedVel[0] = inputVel[0];
predictedVel[1] = inputVel[1];
predictedPos[0] = inputPos[0] + (dt * inputVel[0]);
predictedPos[1] = inputPos[1] + (dt * inputVel[1]);
// only turn if alive
if (inputStatus & PlayerState::Alive) {
*predictedAzimuth += (dt * inputAngVel);
}
// following the parabola
predictedVel[2] = inputVel[2] + (BZDBCache::gravity * dt);
predictedPos[2] = inputPos[2] + (inputVel[2] * dt) +
(0.5f * BZDBCache::gravity * dt * dt);
}
else {
// velocity[2] is zero when not falling, except for Burrow flag
predictedVel[2] = inputVel[2];
predictedPos[2] = inputPos[2] + (inputVel[2] * dt);
// different algorithms for tanks moving in
// a straight line vs. turning in a circle
if (!inputTurning) {
// move straight
predictedVel[0] = inputRelVel[0];
predictedVel[1] = inputRelVel[1];
predictedPos[0] = inputPos[0] + (dt * inputRelVel[0]);
predictedPos[1] = inputPos[1] + (dt * inputRelVel[1]);
} else {
// make a sweeping arc
const float angle = (dt * inputRelAngVel);
*predictedAzimuth += angle;
const float cos_val = cosf(angle);
const float sin_val = sinf(angle);
const float* tc = inputTurnCenter;
const float* tv = inputTurnVector;
predictedPos[0] = tc[0] + ((tv[0] * cos_val) - (tv[1] *
sin_val));
predictedPos[1] = tc[1] + ((tv[1] * cos_val) + (tv[0] *
sin_val));
const float* rv = inputRelVel;
predictedVel[0] = (rv[0] * cos_val) - (rv[1] * sin_val);
predictedVel[1] = (rv[1] * cos_val) + (rv[0] * sin_val);
}
// make the physics driver adjustments
const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(inputPhyDrv);
if (phydrv != NULL) {
if (phydrv->getIsSlide()) {
predictedVel[0] = inputRelVel[0];
predictedVel[1] = inputRelVel[1];
predictedPos[0] = inputPos[0] + (dt * inputRelVel[0]);
predictedPos[1] = inputPos[1] + (dt * inputRelVel[1]);
}
else {
// angular velocity adjustment
const float pdAngVel = phydrv->getAngularVel();
if (pdAngVel != 0.0f) {
const float angle = (dt * pdAngVel);
*predictedAzimuth += angle;
const float* pdAngPos = phydrv->getAngularPos();
const float dx = predictedPos[0] - pdAngPos[0];
const float dy = predictedPos[1] - pdAngPos[1];
const float cos_val = cosf(angle);
const float sin_val = sinf(angle);
predictedPos[0] = pdAngPos[0] + ((dx * cos_val) - (dy * sin_val));
predictedPos[1] = pdAngPos[1] + ((dy * cos_val) + (dx * sin_val));
predictedVel[0] += (-dy * pdAngVel);
predictedVel[1] += (+dx * pdAngVel);
}
// linear velocity adjustment
const float* pdVel = phydrv->getLinearVel();
predictedPos[0] += (dt * pdVel[0]);
predictedPos[1] += (dt * pdVel[1]);
predictedVel[0] += pdVel[0];
predictedVel[1] += pdVel[1];
}
}
}
return;
}
bool Player::isDeadReckoningWrong() const
{
const uint16_t checkStates =
(PlayerState::Alive | PlayerState::Paused | PlayerState::Falling);
// always send a new packet when some kinds of status change
if ((state.status & checkStates) != (inputStatus & checkStates)) {
return true;
}
// never send a packet when dead
if ((state.status & PlayerState::Alive) == 0) {
return false;
}
// send a packet if we've made some noise
if (state.sounds != PlayerState::NoSounds) {
return true;
}
// send a packet if we've crossed a physics driver boundary
if (state.phydrv != inputPhyDrv) {
return true;
}
// time since setdeadreckoning
const float dt = float(TimeKeeper::getTick() - inputTime);
// otherwise always send at least one packet per second
if (dt >= MaxUpdateTime) {
return true;
}
// get predicted state
float predictedPos[3];
float predictedVel[3];
float predictedAzimuth;
getDeadReckoning(predictedPos, &predictedAzimuth, predictedVel, dt);
// always send a new packet on reckoned touchdown
float groundLimit = 0.0f;
if (getFlag() == Flags::Burrow) {
groundLimit = BZDB.eval(StateDatabase::BZDB_BURROWDEPTH);
}
if (predictedPos[2] < groundLimit) {
return true;
}
// client side throttling
const int throttleRate =
int(BZDB.eval(StateDatabase::BZDB_UPDATETHROTTLERATE));
const float minUpdateTime = (throttleRate > 0) ? (1.0f /
throttleRate) : 0.0f;
if (dt < minUpdateTime) {
return false;
}
// see if position and azimuth are close enough
float positionTolerance =
BZDB.eval(StateDatabase::BZDB_POSITIONTOLERANCE);
if ((fabsf(state.pos[0] - predictedPos[0]) > positionTolerance) ||
(fabsf(state.pos[1] - predictedPos[1]) > positionTolerance) ||
(fabsf(state.pos[2] - predictedPos[2]) > positionTolerance)) {
if (debugLevel >= 4) {
if (fabsf(state.pos[0] - predictedPos[0]) > positionTolerance) {
printf ("state.pos[0] = %f, predictedPos[0] = %f\n",
state.pos[0], predictedPos[0]);
}
if (fabsf(state.pos[1] - predictedPos[1]) > positionTolerance) {
printf ("state.pos[1] = %f, predictedPos[1] = %f\n",
state.pos[1], predictedPos[1]);
}
if (fabsf(state.pos[2] - predictedPos[2]) > positionTolerance) {
printf ("state.pos[2] = %f, predictedPos[2] = %f\n",
state.pos[2], predictedPos[2]);
}
}
return true;
}
float angleTolerance = BZDB.eval(StateDatabase::BZDB_ANGLETOLERANCE);
if (fabsf(state.azimuth - predictedAzimuth) > angleTolerance) {
logDebugMessage(4,"state.azimuth = %f, predictedAzimuth = %f\n",
state.azimuth, predictedAzimuth);
return true;
}
// prediction is good enough
return false;
}
void Player::doDeadReckoning()
{
if (!isAlive() && !isExploding()) {
return;
}
// get predicted state
float predictedPos[3];
float predictedVel[3];
float predictedAzimuth;
float dt = float(TimeKeeper::getTick() - inputTime);
getDeadReckoning(predictedPos, &predictedAzimuth, predictedVel, dt);
// setup notResponding
if (!isAlive()) {
notResponding = false;
} else {
notResponding = (dt >
BZDB.eval(StateDatabase::BZDB_NOTRESPONDINGTIME));
}
// if hit ground then update input state (we don't want to fall
anymore)
float groundLimit = 0.0f;
if (getFlag() == Flags::Burrow) {
groundLimit = BZDB.eval(StateDatabase::BZDB_BURROWDEPTH);
}
// the velocity check is for when a Burrow flag is dropped
if ((predictedPos[2] < groundLimit) && (predictedVel[2] <= 0.0f)) {
predictedPos[2] = groundLimit;
predictedVel[2] = 0.0f;
inputStatus &= ~PlayerState::Falling;
inputVel[2] = 0.0f;
}
// setup remote players' landing sounds and graphics, and jumping
sounds
if (isAlive()) {
// the importance level of the remote sounds
const bool soundImportance = false;
const bool localSound = (ROAM.isRoaming()
&& (ROAM.getMode() == Roaming::roamViewFP)
&& (ROAM.getTargetTank() == this));
// check for a landing
if (((oldStatus & PlayerState::Falling) != 0) &&
((inputStatus & PlayerState::Falling) == 0)) {
// setup the squish effect
setLandingSpeed(oldZSpeed);
// make it "land"
EFFECTS.addLandEffect(getColor(),state.pos,state.azimuth);
// setup the sound
if (BZDB.isTrue("remoteSounds")) {
if ((getFlag() != Flags::Burrow) || (predictedPos[2] > 0.0f)) {
playSound(SFX_LAND, state.pos, soundImportance, localSound);
} else {
// probably never gets played
playSound(SFX_BURROW, state.pos, soundImportance, localSound);
}
}
}
// play jumping type sounds, and then clear them
if (state.sounds != PlayerState::NoSounds) {
if (BZDB.isTrue("remoteSounds")) {
if ((state.sounds & PlayerState::JumpSound) != 0) {
playSound(SFX_JUMP, state.pos, soundImportance, localSound);
}
if ((state.sounds & PlayerState::WingsSound) != 0) {
playSound(SFX_FLAP, state.pos, soundImportance, localSound);
}
if ((state.sounds & PlayerState::BounceSound) != 0) {
playSound(SFX_BOUNCE, state.pos, soundImportance, localSound);
}
}
state.sounds = PlayerState::NoSounds;
}
}
// copy some old state
oldZSpeed = state.velocity[2];
oldStatus = inputStatus;
move(predictedPos, predictedAzimuth);
setVelocity(predictedVel);
setRelativeMotion();
return;
}
// How long does the filter takes to be considered "initialized"
const int DRStateStable = 100;
const float maxToleratedJitter = 1.0f;
void Player::setDeadReckoning(float)
{
// set the current state
setDeadReckoning();
setRelativeMotion();
return;
}
void Player::setDeadReckoning()
{
inputTime = TimeKeeper::getTick();
// copy stuff for dead reckoning
inputStatus = state.status;
inputAzimuth = state.azimuth;
inputAngVel = state.angVel;
memcpy(inputPos, state.pos, sizeof(float[3]));
memcpy(inputVel, state.velocity, sizeof(float[3]));
inputPhyDrv = state.phydrv;
//
// pre-calculate some stuff for dead reckoning
//
// the relative motion information (with respect to the physics
drivers)
calcRelativeMotion(inputRelVel, inputRelSpeed, inputRelAngVel);
// setup the turning parameters
inputTurning = false;
if (fabsf(inputRelAngVel) > 0.001f) {
inputTurning = true;
const float radius = (inputRelSpeed / inputRelAngVel);
inputTurnVector[0] = +sinf(inputAzimuth) * radius;
inputTurnVector[1] = -cosf(inputAzimuth) * radius;
inputTurnCenter[0] = inputPos[0] - inputTurnVector[0];
inputTurnCenter[1] = inputPos[1] - inputTurnVector[1];
}
return;
}
void Player::renderRadar() const
{
if (!isAlive() && !isExploding()) {
return; // don't draw anything
}
if (tankNode) {
((TankSceneNode*)tankNode)->renderRadar();
}
return;
}
bool Player::getIpAddress(Address& addr)
{
if (!haveIpAddr) {
return false;
}
addr = ipAddr;
return true;
}
void Player::setIpAddress(const Address& addr)
{
ipAddr = addr;
haveIpAddr = true;
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
// interface header
#include "playing.h"
// system includes
#ifdef _WIN32
#include <shlobj.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <direct.h>
#else
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <utime.h>
#endif
#include <cmath>
// common headers
#include "AccessList.h"
#include "AnsiCodes.h"
#include "AresHandler.h"
#include "BackgroundRenderer.h"
#include "BaseBuilding.h"
#include "BillboardSceneNode.h"
#include "BZDBCache.h"
#include "BzfMedia.h"
#include "bzsignal.h"
#include "CommandsStandard.h"
#include "DirectoryNames.h"
#include "ErrorHandler.h"
#include "FileManager.h"
#include "FlagSceneNode.h"
#include "GameTime.h"
#include "KeyManager.h"
#include "md5.h"
#include "ObstacleList.h"
#include "ObstacleMgr.h"
#include "PhysicsDriver.h"
#include "PlatformFactory.h"
#include "QuadWallSceneNode.h"
#include "ServerList.h"
#include "SphereSceneNode.h"
#include "TankGeometryMgr.h"
#include "TextureManager.h"
#include "TextUtils.h"
#include "TimeBomb.h"
#include "version.h"
#include "WordFilter.h"
#include "ZSceneDatabase.h"
// local implementation headers
#include "AutoPilot.h"
#include "bzflag.h"
#include "commands.h"
#include "daylight.h"
#include "Downloads.h"
#include "effectsRenderer.h"
#include "FlashClock.h"
#include "ForceFeedback.h"
#include "HUDDialogStack.h"
#include "HUDRenderer.h"
#include "MainMenu.h"
#include "motd.h"
#include "RadarRenderer.h"
#include "Roaming.h"
#include "RobotPlayer.h"
#include "Roster.h"
#include "SceneBuilder.h"
#include "ScoreboardRenderer.h"
#include "sound.h"
#include "ShotStats.h"
#include "TrackMarks.h"
#include "World.h"
#include "WorldBuilder.h"
#include "HUDui.h"
#include <sstream>
#include "messages.h"
#ifdef HAVE_KRB5
#include "ClientAuthentication.h"
#endif
bool bCheatGravity = false;
int killMe;
static const float FlagHelpDuration = 60.0f;
StartupInfo startupInfo;
static MainMenu* mainMenu;
ServerLink* serverLink = NULL;
static World *world = NULL;
static LocalPlayer *myTank = NULL;
static BzfDisplay* display = NULL;
MainWindow* mainWindow = NULL;
static SceneRenderer* sceneRenderer = NULL;
ControlPanel* controlPanel = NULL;
static RadarRenderer* radar = NULL;
HUDRenderer* hud = NULL;
static ScoreboardRenderer* scoreboard = NULL;
static SceneDatabaseBuilder* sceneBuilder = NULL;
static Team* teams = NULL;
int numFlags = 0;
static bool joinRequested = false;
static bool waitingDNS = false;
static bool serverError = false;
static bool serverDied = false;
bool fireButton = false;
bool roamButton = false;
static bool firstLife = false;
static bool showFPS = false;
static bool showDrawTime = false;
bool pausedByUnmap = false;
static bool unmapped = false;
static int preUnmapFormat = -1;
static double epochOffset;
static double lastEpochOffset;
float clockAdjust = 0.0f;
float pauseCountdown = 0.0f;
float destructCountdown = 0.0f;
static float testVideoFormatTimer = 0.0f;
static int testVideoPrevFormat = -1;
static std::vector<PlayingCallbackItem> playingCallbacks;
bool gameOver = false;
static std::vector<BillboardSceneNode*> explosions;
static std::vector<BillboardSceneNode*> prototypeExplosions;
int savedVolume = -1;
static bool grabMouseAlways = false;
static FlashClock pulse;
static bool wasRabbit = false;
static bool justJoined = false;
float roamDZoom = 0.0f;
static MessageOfTheDay *motd = NULL;
DefaultCompleter completer;
char messageMessage[PlayerIdPLen + MessageLen];
double lastObserverUpdateTime = -1;
static void setHuntTarget();
static void setTankFlags();
static void* handleMsgSetVars(void *msg);
static void handlePlayerMessage(uint16_t, uint16_t, void*);
static void handleFlagTransferred(Player* fromTank, Player* toTank,
int flagIndex);
static void enteringServer(void *buf);
static void joinInternetGame2();
static void cleanWorldCache();
static void markOld(std::string &fileName);
static void setRobotTarget(RobotPlayer* robot);
static ResourceGetter *resourceDownloader = NULL;
// Far and Near Frustum clipping planes
static const float FarPlaneScale = 1.5f; // gets multiplied by
BZDB_WORLDSIZE
static const float FarPlaneDefault = FarPlaneScale * 800.0f;
static const float FarDeepPlaneScale = 10.0f;
static const float FarDeepPlaneDefault = FarPlaneDefault *
FarDeepPlaneScale;
static const float NearPlaneNormal = 1.0f;
static const float NearPlaneClose = 0.25f; // for drawing in the
cockpit
static bool FarPlaneCull = false;
static float FarPlane = FarPlaneDefault;
static float FarDeepPlane = FarDeepPlaneDefault;
static float NearPlane = NearPlaneNormal;
enum BlowedUpReason {
GotKilledMsg,
GotShot,
GotRunOver,
GotCaptured,
GenocideEffect,
SelfDestruct,
WaterDeath,
LastReason,
DeathTouch = PhysicsDriverDeath
};
static const char* blowedUpMessage[] = {
NULL,
"Got shot by ",
"Got flattened by ",
"Team flag was captured by ",
"Teammate hit with Genocide by ",
"Tank Self Destructed",
"Tank Rusted"
};
static bool gotBlowedUp(BaseLocalPlayer* tank,
BlowedUpReason reason,
PlayerId killer,
const ShotPath *hit = NULL,
int physicsDriver = -1);
#ifdef ROBOT
static void handleMyTankKilled(int reason);
static ServerLink* robotServer[MAX_ROBOTS];
#endif
bool bCheatOO = false;
static double userTimeEpochOffset;
static bool entered = false;
static bool joiningGame = false;
static WorldBuilder *worldBuilder = NULL;
static std::string worldUrl;
static std::string worldCachePath;
static std::string md5Digest;
static uint32_t worldPtr = 0;
static char *worldDatabase = NULL;
static bool isCacheTemp;
static std::ostream *cacheOut = NULL;
static bool downloadingInitialTexture = false;
static AresHandler ares(0);
static AccessList ServerAccessList("ServerAccess.txt", NULL);
// access silencePlayers from bzflag.cxx
std::vector<std::string>& getSilenceList()
{
return silencePlayers;
}
// try to select the next recipient in the specified direction
// eventually avoiding robots
void selectNextRecipient (bool forward, bool robotIn)
{
LocalPlayer *my = LocalPlayer::getMyTank();
const Player *recipient = my->getRecipient();
int rindex;
if (!recipient) {
rindex = - 1;
forward = true;
} else {
const PlayerId id = recipient->getId();
rindex = lookupPlayerIndex(id);
}
int i = rindex;
while (true) {
if (forward) {
i++;
if (i == curMaxPlayers)
// if no old rec id we have just ended our search
if (recipient == NULL)
break;
else
// wrap around
i = 0;
} else {
if (i == 0)
// wrap around
i = curMaxPlayers;
i--;
}
if (i == rindex)
break;
if (player[i] && (robotIn || player[i]->getPlayerType() ==
TankPlayer)) {
my->setRecipient(player[i]);
break;
}
}
}
//
// should we grab the mouse?
//
static void setGrabMouse(bool grab)
{
grabMouseAlways = grab;
}
bool shouldGrabMouse()
{
return grabMouseAlways && !unmapped &&
(myTank == NULL || !myTank->isPaused() || myTank->isAutoPilot());
}
//
// some simple global functions
//
void warnAboutMainFlags()
{
// warning message for hidden flags
if (!BZDBCache::displayMainFlags){
std::string showFlagsMsg = ColorStrings[YellowColor];
showFlagsMsg += "Flags on field hidden, to show them ";
std::vector<std::string> keys =
KEYMGR.getKeysFromCommand("toggleFlags main", true);
if (keys.size() != 0) {
showFlagsMsg += "hit \"" + ColorStrings[WhiteColor];
showFlagsMsg += tolower(keys[0][0]);
showFlagsMsg += ColorStrings[YellowColor] + "\"";
} else {
showFlagsMsg += " bind a key to Toggle Flags on Field";
}
addMessage(NULL, showFlagsMsg);
}
}
void warnAboutRadarFlags()
{
if (!BZDB.isTrue("displayRadarFlags")){
std::string showFlagsMsg = ColorStrings[YellowColor];
showFlagsMsg += "Flags on radar hidden, to show them ";
std::vector<std::string> keys =
KEYMGR.getKeysFromCommand("toggleFlags radar", true);
if (keys.size() != 0) {
showFlagsMsg += "hit \"" + ColorStrings[WhiteColor];
showFlagsMsg += tolower(keys[0][0]);
showFlagsMsg += ColorStrings[YellowColor] + "\"";
} else {
showFlagsMsg += " bind a key to Toggle Flags on Radar";
}
addMessage(NULL, showFlagsMsg);
}
}
void warnAboutRadar()
{
if (!BZDB.isTrue("displayRadar")) {
std::string message = ColorStrings[YellowColor];
message += "To toggle the radar ";
std::vector<std::string> keys =
KEYMGR.getKeysFromCommand("toggleRadar", true);
if (keys.size() != 0) {
message += "hit \"" + ColorStrings[WhiteColor];
message += keys[0];
message += ColorStrings[YellowColor] + "\"";
} else {
message += " bind a key to Toggle Radar";
}
addMessage(NULL, message);
}
}
void warnAboutConsole()
{
if (!BZDB.isTrue("displayConsole")) {
std::string message = ColorStrings[YellowColor];
message += "To toggle the console ";
std::vector<std::string> keys =
KEYMGR.getKeysFromCommand("toggleConsole", true);
if (keys.size() != 0) {
message += "hit \"" + ColorStrings[WhiteColor];
message += keys[0];
message += ColorStrings[YellowColor] + "\"";
} else {
message += " bind a key to Toggle Console";
}
// can't use a console message for this one
hud->setAlert(3, message.c_str(), 2.0f, true);
}
}
inline bool isViewTank(Player* tank)
{
return ((tank != NULL) &&
(tank == LocalPlayer::getMyTank()) ||
(ROAM.isRoaming()
&& (ROAM.getMode() == Roaming::roamViewFP)
&& (ROAM.getTargetTank() == tank)));
}
BzfDisplay* getDisplay()
{
return display;
}
MainWindow* getMainWindow()
{
return mainWindow;
}
SceneRenderer* getSceneRenderer()
{
return sceneRenderer;
}
void setSceneDatabase()
{
SceneDatabase *scene; // FIXME - test the zbuffer here
// delete the old database
sceneRenderer->setSceneDatabase(NULL);
// make the scene, and record the processing time
TimeKeeper startTime = TimeKeeper::getCurrent();
scene = sceneBuilder->make(world);
float elapsed = float(TimeKeeper::getCurrent() - startTime);
// print debugging info
if (BZDBCache::zbuffer) {
logDebugMessage(2,"ZSceneDatabase processed in %.3f seconds.\n",
elapsed);
} else {
logDebugMessage(2,"BSPSceneDatabase processed in %.3f seconds.\n",
elapsed);
}
// set the scene
sceneRenderer->setSceneDatabase(scene);
}
StartupInfo* getStartupInfo()
{
return &startupInfo;
}
bool setVideoFormat(int index, bool test)
{
#if defined(_WIN32)
// give windows extra time to test format (context reloading takes a
while)
static const float testDuration = 10.0f;
#else
static const float testDuration = 5.0f;
#endif
// ignore bad formats or when the format test timer is running
if (testVideoFormatTimer != 0.0f ||
!display->isValidResolution(index))
return false;
// ignore if no change
if (display->getResolution() == index) return true;
// change it
testVideoPrevFormat = display->getResolution();
if (!display->setResolution(index)) return false;
// handle resize
mainWindow->setFullscreen();
mainWindow->getWindow()->callResizeCallbacks();
mainWindow->warpMouse();
if (test) testVideoFormatTimer = testDuration;
else if (shouldGrabMouse()) mainWindow->grabMouse();
return true;
}
void addPlayingCallback(PlayingCallback cb, void* data)
{
PlayingCallbackItem item;
item.cb = cb;
item.data = data;
playingCallbacks.push_back(item);
}
void removePlayingCallback(PlayingCallback _cb, void* data)
{
std::vector<PlayingCallbackItem>::iterator it =
playingCallbacks.begin();
while (it != playingCallbacks.end()) {
if (it->cb == _cb && it->data == data) {
playingCallbacks.erase(it);
break;
}
it++;
}
}
static void callPlayingCallbacks()
{
const int count = playingCallbacks.size();
for (int i = 0; i < count; i++) {
const PlayingCallbackItem& cb = playingCallbacks[i];
(*cb.cb)(cb.data);
}
}
void joinGame()
{
if (joiningGame) {
if (worldBuilder) {
delete worldBuilder;
worldBuilder = NULL;
}
if (worldDatabase) {
delete[] worldDatabase;
worldDatabase = NULL;
}
HUDDialogStack::get()->setFailedMessage("Download stopped by user
action");
joiningGame = false;
}
joinRequested = true;
}
//
// handle signals that should kill me quickly
//
static void dying(int sig)
{
bzSignal(sig, SIG_DFL);
display->setDefaultResolution();
raise(sig);
}
//
// handle signals that should kill me nicely
//
static void suicide(int sig)
{
bzSignal(sig, SIG_PF(suicide));
CommandsStandard::quit();
}
//
// handle signals that should disconnect me from the server
//
static void hangup(int sig)
{
bzSignal(sig, SIG_PF(hangup));
serverDied = true;
serverError = true;
}
static ServerLink* lookupServer(const Player *_player)
{
PlayerId id = _player->getId();
if (myTank->getId() == id) return serverLink;
#ifdef ROBOT
for (int i = 0; i < numRobots; i++)
if (robots[i] && robots[i]->getId() == id)
return robotServer[i];
#endif
return NULL;
}
//
// user input handling
//
#if defined(DEBUG)
#define FREEZING
#endif
#if defined(FREEZING)
static bool motionFreeze = false;
#endif
static enum {None, Left, Right, Up, Down} keyboardMovement;
static int shiftKeyStatus;
static bool doKeyCommon(const BzfKeyEvent& key, bool pressed)
{
keyboardMovement = None;
shiftKeyStatus = key.shift;
const std::string cmd = KEYMGR.get(key, pressed);
if (key.ascii == 27) {
if (pressed) {
mainMenu->createControls();
HUDDialogStack::get()->push(mainMenu);
}
return true;
} else if (scoreboard->getHuntState() ==
ScoreboardRenderer::HUNT_SELECTING) {
if (key.button == BzfKeyEvent::Down || KEYMGR.get(key, true) ==
"identify") {
if (pressed)
scoreboard->setHuntNextEvent();
return true;
} else if (key.button == BzfKeyEvent::Up || KEYMGR.get(key, true)
== "drop") {
if (pressed)
scoreboard->setHuntPrevEvent();
return true;
} else if (KEYMGR.get(key, true) == "fire") {
if (pressed) {
scoreboard->setHuntSelectEvent();
}
return true;
}
}
std::string cmdDrive = cmd;
if (cmdDrive.empty()) {
// Check for driving keys
BzfKeyEvent cleanKey = key;
cleanKey.shift = 0;
cmdDrive = KEYMGR.get(cleanKey, pressed);
}
if (cmdDrive == "turn left") {
keyboardMovement = Left;
} else if (cmdDrive == "turn right") {
keyboardMovement = Right;
} else if (cmdDrive == "drive forward") {
keyboardMovement = Up;
} else if (cmdDrive == "drive reverse") {
keyboardMovement = Down;
}
if (myTank)
switch (keyboardMovement) {
case Left:
myTank->setKey(BzfKeyEvent::Left, pressed);
break;
case Right:
myTank->setKey(BzfKeyEvent::Right, pressed);
break;
case Up:
myTank->setKey(BzfKeyEvent::Up, pressed);
break;
case Down:
myTank->setKey(BzfKeyEvent::Down, pressed);
break;
case None:
break;
}
if (!cmd.empty()) {
if (cmd=="fire")
fireButton = pressed;
roamButton = pressed;
if (keyboardMovement == None) {
std::string result = CMDMGR.run(cmd);
if (!result.empty())
std::cerr << result << std::endl;
}
return true;
}
// if we don't have a tank, the following key commands don't apply
if (!myTank)
return false;
{
// built-in unchangeable keys. only perform if not masked.
switch (key.ascii) {
case 'T':
case 't':
// toggle frames-per-second display
if (pressed) {
showFPS = !showFPS;
if (!showFPS) {
hud->setFPS(-1.0);
}
}
return true;
case 'Y':
case 'y':
// toggle milliseconds for drawing
if (pressed) {
showDrawTime = !showDrawTime;
if (!showDrawTime) {
hud->setDrawTime(-1.0);
}
}
return true;
// for testing forced recreation of OpenGL context
#if defined(DEBUG_RENDERING)
case 'X':
if (pressed && ((shiftKeyStatus & BzfKeyEvent::AltKey) != 0)) {
// destroy OpenGL context
getMainWindow()->getWindow()->freeContext();
// recreate OpenGL context
getMainWindow()->getWindow()->makeContext();
// force a redraw (mainly for control panel)
getMainWindow()->getWindow()->callExposeCallbacks();
// cause sun/moon to be repositioned immediately
lastEpochOffset = epochOffset - 5.0;
// reload display lists and textures and initialize other state
OpenGLGState::initContext();
}
return true;
#endif // DEBUG_RENDERING
case ']':
case '}':
// plus 30 seconds
if (pressed) clockAdjust += 30.0f;
return true;
case '[':
case '{':
// minus 30 seconds
if (pressed) clockAdjust -= 30.0f;
return true;
default:
break;
} // end switch on key
// Shot/Accuracy Statistics display
if (key.button == BzfKeyEvent::Home && pressed) {
HUDDialogStack::get()->push(new ShotStats);
return true;
}
} // end key handle
return false;
}
static void doKeyNotPlaying(const BzfKeyEvent&, bool, bool)
{
}
static void doKeyPlaying(const BzfKeyEvent& key, bool pressed, bool
haveBinding)
{
#if defined(FREEZING)
if (key.ascii == '`' && pressed && !haveBinding && key.shift) {
// toggle motion freeze
motionFreeze = !motionFreeze;
if (motionFreeze) {
addMessage(NULL, "The tank's motion is now frozen! ... Press
Shift+` to unfreeze");
}
return;
}
#endif
if (key.ascii == 0 &&
key.button >= BzfKeyEvent::F1 &&
key.button <= BzfKeyEvent::F10 &&
(key.shift & (BzfKeyEvent::ControlKey +
BzfKeyEvent::AltKey)) != 0 && !haveBinding) {
// [Ctrl]-[Fx] is message to team
// [Alt]-[Fx] is message to all
if (pressed) {
char name[32];
int msgno = (key.button - BzfKeyEvent::F1) + 1;
void* buf = messageMessage;
if (key.shift == BzfKeyEvent::ControlKey) {
sprintf(name, "quickTeamMessage%d", msgno);
buf = nboPackUByte(buf, TeamToPlayerId(myTank->getTeam()));
} else {
sprintf(name, "quickMessage%d", msgno);
buf = nboPackUByte(buf, AllPlayers);
}
if (BZDB.isSet(name)) {
char messageBuffer[MessageLen];
memset(messageBuffer, 0, MessageLen);
strncpy(messageBuffer,
BZDB.get(name).c_str(),
MessageLen);
nboPackString(buf, messageBuffer, MessageLen);
serverLink->send(MsgMessage, sizeof(messageMessage), messageMessage);
}
}
} else if (myTank->isAlive()) {
// Might be a direction key. Save it for later.
if ((myTank->getInputMethod() != LocalPlayer::Keyboard) && pressed)
{
if (keyboardMovement != None)
if (BZDB.isTrue("allowInputChange"))
myTank->setInputMethod(LocalPlayer::Keyboard);
}
}
}
static void doKey(const BzfKeyEvent& key, bool pressed) {
if (myTank) {
const std::string cmd = KEYMGR.get(key, pressed);
if (cmd == "jump") {
myTank->setJumpPressed(pressed);
}
}
if (HUDui::getFocus()) {
if ((pressed && HUDui::keyPress(key)) ||
(!pressed && HUDui::keyRelease(key))) {
return;
}
}
bool haveBinding = doKeyCommon(key, pressed);
if (!myTank)
doKeyNotPlaying(key, pressed, haveBinding);
else
doKeyPlaying(key, pressed, haveBinding);
}
static void doMotion()
{
float rotation = 0.0f, speed = 1.0f;
const int noMotionSize = hud->getNoMotionSize();
const int maxMotionSize = hud->getMaxMotionSize();
int keyboardRotation = myTank->getRotation();
int keyboardSpeed = myTank->getSpeed();
/* see if controls are reversed */
if (myTank->getFlag() == Flags::ReverseControls) {
keyboardRotation = -keyboardRotation;
keyboardSpeed = -keyboardSpeed;
}
// mouse is default steering method; query mouse pos always, not
doing so
// can lead to stuttering movement with X and software rendering
(uncertain why)
int mx = 0, my = 0;
mainWindow->getMousePosition(mx, my);
// determine if joystick motion should be used instead of mouse
motion
// when the player bumps the mouse, LocalPlayer::getInputMethod
return Mouse;
// make it return Joystick when the user bumps the joystick
if (mainWindow->haveJoystick()) {
if (myTank->getInputMethod() == LocalPlayer::Joystick) {
// if we're using the joystick right now, replace mouse coords
with joystick coords
mainWindow->getJoyPosition(mx, my);
} else {
// if the joystick is not active, and we're not forced to some
other input method,
// see if it's moved and autoswitch
if (BZDB.isTrue("allowInputChange")) {
int jx = 0, jy = 0;
mainWindow->getJoyPosition(jx, jy);
// if we aren't using the joystick, but it's moving, start using it
if ((jx < -noMotionSize * 2) || (jx > noMotionSize * 2)
|| (jy < -noMotionSize * 2) || (jy > noMotionSize * 2)) {
myTank->setInputMethod(LocalPlayer::Joystick);
} // joystick motion
} // allowInputChange
} // getInputMethod == Joystick
} // mainWindow->Joystick
/* see if controls are reversed */
if (myTank->getFlag() == Flags::ReverseControls) {
mx = -mx;
my = -my;
}
#if defined(FREEZING)
if (motionFreeze) return;
#endif
if (myTank->isAutoPilot()) {
doAutoPilot(rotation, speed);
} else if (myTank->getInputMethod() == LocalPlayer::Keyboard) {
rotation = (float)keyboardRotation;
speed = (float)keyboardSpeed;
if (speed < 0.0f)
speed /= 2.0;
rotation *= BZDB.eval("displayFOV") / 60.0f;
if (BZDB.isTrue("slowKeyboard")) {
rotation *= 0.5f;
speed *= 0.5f;
}
} else { // both mouse and joystick
// calculate desired rotation
if (keyboardRotation && !devDriving) {
rotation = float(keyboardRotation);
rotation *= BZDB.eval("displayFOV") / 60.0f;
if (BZDB.isTrue("slowKeyboard")) {
rotation *= 0.5f;
}
} else if (mx < -noMotionSize) {
rotation = float(-mx - noMotionSize) / float(maxMotionSize -
noMotionSize);
if (rotation > 1.0f)
rotation = 1.0f;
} else if (mx > noMotionSize) {
rotation = -float(mx - noMotionSize) / float(maxMotionSize -
noMotionSize);
if (rotation < -1.0f)
rotation = -1.0f;
}
// calculate desired speed
if (keyboardSpeed && !devDriving) {
speed = float(keyboardSpeed);
if (speed < 0.0f) {
speed *= 0.5f;
}
if (BZDB.isTrue("slowKeyboard")) {
speed *= 0.5f;
}
} else if (my < -noMotionSize) {
speed = float(-my - noMotionSize) / float(maxMotionSize -
noMotionSize);
if (speed > 1.0f)
speed = 1.0f;
} else if (my > noMotionSize) {
speed = -float(my - noMotionSize) / float(maxMotionSize -
noMotionSize);
if (speed < -0.5f)
speed = -0.5f;
} else {
speed = 0.0f;
}
}
myTank->setDesiredAngVel(rotation);
myTank->setDesiredSpeed(speed);
}
static void doEvent(BzfDisplay *disply)
{
BzfEvent event;
if (!disply->getEvent(event)) return;
switch (event.type) {
case BzfEvent::Quit:
CommandsStandard::quit();
break;
case BzfEvent::Redraw:
mainWindow->getWindow()->callExposeCallbacks();
sceneRenderer->setExposed();
break;
case BzfEvent::Resize:
if (mainWindow->getWidth() != event.resize.width ||
mainWindow->getHeight() != event.resize.height){
mainWindow->getWindow()->setSize(event.resize.width,
event.resize.height);
mainWindow->getWindow()->callResizeCallbacks();
}
break;
case BzfEvent::Map:
// window has been mapped. this normally occurs when the game
// is uniconified. if the player was paused because of an unmap
// then resume.
if (pausedByUnmap) {
pausedByUnmap = false;
pauseCountdown = 0.0f;
if (myTank && myTank->isAlive() && myTank->isPaused()) {
myTank->setPause(false);
controlPanel->addMessage("Resumed");
}
}
// restore the resolution we want if full screen
if (mainWindow->getFullscreen()) {
if (preUnmapFormat != -1) {
disply->setResolution(preUnmapFormat);
mainWindow->warpMouse();
}
}
// restore the sound
if (savedVolume != -1) {
setSoundVolume(savedVolume);
savedVolume = -1;
}
unmapped = false;
if (shouldGrabMouse())
mainWindow->grabMouse();
break;
case BzfEvent::Unmap:
// begin pause countdown when unmapped if: we're not already
// paused because of an unmap (shouldn't happen), we're not
// already counting down to pausing, we're alive, and we're not
// already paused.
if (!pausedByUnmap && (pauseCountdown == 0.0f) &&
myTank && myTank->isAlive() &&
!myTank->isPaused() && !myTank->isAutoPilot() &&
!BZDB.isTrue("noUnmapPause")) { // handy for testing
// get ready to pause (no cheating through instantaneous pausing)
pauseCountdown = 5.0f;
// set this even though we haven't really paused yet
pausedByUnmap = true;
}
// ungrab the mouse if we're running full screen
if (mainWindow->getFullscreen()) {
preUnmapFormat = -1;
if (disply->getNumResolutions() > 1) {
preUnmapFormat = disply->getResolution();
disply->setDefaultResolution();
}
}
// turn off the sound
if (savedVolume == -1) {
savedVolume = getSoundVolume();
setSoundVolume(0);
}
unmapped = true;
mainWindow->ungrabMouse();
break;
case BzfEvent::KeyUp:
doKey(event.keyUp, false);
break;
case BzfEvent::KeyDown:
doKey(event.keyDown, true);
break;
case BzfEvent::MouseMove:
if (myTank && myTank->isAlive() && (myTank->getInputMethod() !=
LocalPlayer::Mouse) && (BZDB.isTrue("allowInputChange")))
myTank->setInputMethod(LocalPlayer::Mouse);
break;
default:
/* unset */
break;
}
}
void addMessage(const Player *_player, const std::string& msg,
int mode, bool highlight, const char* oldColor)
{
std::string fullMessage;
if (BZDB.isTrue("colorful")) {
if (_player) {
if (highlight) {
if (BZDB.get("killerhighlight") == "1")
fullMessage += ColorStrings[PulsatingColor];
else if (BZDB.get("killerhighlight") == "2")
fullMessage += ColorStrings[UnderlineColor];
}
const PlayerId pid = _player->getId();
if (pid < 200) {
int color = _player->getTeam();
if (World::getWorld()->allowRabbit() && (color == RogueTeam)) {
// hunters are orange (hack)
color = OrangeColor;
} else if (color < 0 || color > 4) {
// non-teamed, rabbit are white (same as observer)
color = WhiteColor;
}
fullMessage += ColorStrings[color];
} else if (pid == ServerPlayer) {
fullMessage += ColorStrings[YellowColor];
} else {
fullMessage += ColorStrings[CyanColor]; //replay observers
}
fullMessage += _player->getCallSign();
if (highlight)
fullMessage += ColorStrings[ResetColor];
#ifdef BWSUPPORT
fullMessage += " (";
fullMessage += Team::getName(_player->getTeam());
fullMessage += ")";
#endif
fullMessage += ColorStrings[DefaultColor] + ": ";
}
fullMessage += msg;
} else {
if (oldColor != NULL)
fullMessage = oldColor;
if (_player) {
fullMessage += _player->getCallSign();
#ifdef BWSUPPORT
fullMessage += " (";
fullMessage += Team::getName(_player->getTeam());
fullMessage += ")";
#endif
fullMessage += ": ";
}
fullMessage += stripAnsiCodes(msg);
}
controlPanel->addMessage(fullMessage, mode);
}
static void updateNumPlayers()
{
int i, numPlayers[NumTeams];
for (i = 0; i < NumTeams; i++)
numPlayers[i] = 0;
for (i = 0; i < curMaxPlayers; i++)
if (player[i])
numPlayers[player[i]->getTeam()]++;
if (myTank)
numPlayers[myTank->getTeam()]++;
}
static void updateHighScores()
{
/* check scores to see if my team and/or have the high score. change
* `>= bestScore' to `> bestScore' if you want to share the number
* one spot. */
bool anyPlayers = false;
int i;
for (i = 0; i < curMaxPlayers; i++)
if (player[i]) {
anyPlayers = true;
break;
}
#ifdef ROBOT
if (!anyPlayers) {
for (i = 0; i < numRobots; i++)
if (robots[i]) {
anyPlayers = true;
break;
}
}
#endif
if (!anyPlayers) {
hud->setPlayerHasHighScore(false);
hud->setTeamHasHighScore(false);
return;
}
bool haveBest = true;
int bestScore = myTank ? myTank->getScore() : 0;
for (i = 0; i < curMaxPlayers; i++)
if (player[i] && player[i]->getScore() >= bestScore) {
haveBest = false;
break;
}
#ifdef ROBOT
if (haveBest) {
for (i = 0; i < numRobots; i++)
if (robots[i] && robots[i]->getScore() >= bestScore) {
haveBest = false;
break;
}
}
#endif
hud->setPlayerHasHighScore(haveBest);
if (myTank && Team::isColorTeam(myTank->getTeam())) {
const Team& myTeam =
World::getWorld()->getTeam(int(myTank->getTeam()));
bestScore = myTeam.won - myTeam.lost;
haveBest = true;
for (i = 0; i < NumTeams; i++) {
if (i == int(myTank->getTeam())) continue;
const Team& team = World::getWorld()->getTeam(i);
if (team.size > 0 && team.won - team.lost >= bestScore) {
haveBest = false;
break;
}
}
hud->setTeamHasHighScore(haveBest);
}
else {
hud->setTeamHasHighScore(false);
}
}
static void updateFlag(FlagType* flag)
{
if (flag == Flags::Null) {
hud->setColor(1.0f, 0.625f, 0.125f);
hud->setAlert(2, NULL, 0.0f);
}
else {
const float* color = flag->getColor();
hud->setColor(color[0], color[1], color[2]);
hud->setAlert(2, flag->flagName, 3.0f, flag->endurance ==
FlagSticky);
}
if (BZDB.isTrue("displayFlagHelp"))
hud->setFlagHelp(flag, FlagHelpDuration);
if (!radar && !myTank || !World::getWorld()) return;
radar->setJammed(flag == Flags::Jamming);
hud->setAltitudeTape(flag == Flags::Jumping ||
World::getWorld()->allowJumping());
}
void notifyBzfKeyMapChanged()
{
std::string restartLabel = "Right Mouse";
std::vector<std::string> keys = KEYMGR.getKeysFromCommand("restart",
false);
if (keys.size() == 0) {
// found nothing on down binding, so try up
keys = KEYMGR.getKeysFromCommand("identify", true);
if (keys.size() == 0) {
std::cerr << "There does not appear to be any key bound to enter
the game" << std::endl;
}
}
if (keys.size() >= 1) {
// display single letter keys as a quoted lowercase letter
if (keys[0].size() == 1) {
restartLabel = '\"';
restartLabel += tolower(keys[0][0]);
restartLabel += '\"';
} else {
restartLabel = keys[0];
}
}
// only show the first 2 keys found to keep things simple
if (keys.size() > 1) {
restartLabel.append(" or ");
// display single letter keys as quoted lowercase letter
if (keys[1].size() == 1) {
restartLabel += '\"';
restartLabel += tolower(keys[1][0]);
restartLabel += '\"';
} else {
restartLabel.append(keys[1]);
}
}
hud->setRestartKeyLabel(restartLabel);
}
//
// server message handling
//
static Player* addPlayer(PlayerId id, void* msg, int showMessage)
{
uint16_t team, type, wins, losses, tks;
char callsign[CallSignLen];
char email[EmailLen];
msg = nboUnpackUShort (msg, type);
msg = nboUnpackUShort (msg, team);
msg = nboUnpackUShort (msg, wins);
msg = nboUnpackUShort (msg, losses);
msg = nboUnpackUShort (msg, tks);
msg = nboUnpackString (msg, callsign, CallSignLen);
msg = nboUnpackString (msg, email, EmailLen);
// Strip any ANSI color codes
strncpy (callsign, stripAnsiCodes (std::string (callsign)).c_str (),
32);
// id is slot, check if it's empty
const int i = id;
// sanity check
if (i < 0) {
printError (TextUtils::format ("Invalid player identification
(%d)", i));
std::
cerr <<
"WARNING: invalid player identification when adding player with
id "
<< i << std::endl;
return NULL;
}
if (player[i]) {
// we're not in synch with server -> help! not a good sign, but not
fatal.
printError ("Server error when adding player, player already
added");
std::cerr << "WARNING: player already exists at location with id "
<< i << std::endl;
return NULL;
}
if (i >= curMaxPlayers) {
curMaxPlayers = i + 1;
World::getWorld ()->setCurMaxPlayers (curMaxPlayers);
}
// add player
if (PlayerType (type) == TankPlayer || PlayerType (type) ==
ComputerPlayer) {
player[i] = new RemotePlayer (id, TeamColor (team), callsign,
email,
PlayerType (type));
player[i]->changeScore (short (wins), short (losses), short (tks));
}
#ifdef ROBOT
if (PlayerType (type) == ComputerPlayer)
for (int j = 0; j < numRobots; j++)
if (robots[j] && !strncmp (robots[j]->getCallSign (), callsign,
CallSignLen)) {
robots[j]->setTeam (TeamColor (team));
break;
}
#endif
// show the message if we don't have the playerlist
// permission. if * we do, MsgAdminInfo should arrive
// with more info.
if (showMessage && !myTank->hasPlayerList ()) {
std::string message ("joining as ");
if (team == ObserverTeam) {
message += "an observer";
} else {
switch (PlayerType (type)) {
case TankPlayer:
message += "a tank";
break;
case ComputerPlayer:
message += "a robot tank";
break;
default:
message += "an unknown type";
break;
}
}
if (!player[i]) {
std::string name (callsign);
name += ": " + message;
message = name;
}
addMessage (player[i], message);
}
completer.registerWord(callsign, true /* quote spaces */);
return player[i];
}
static void printIpInfo (const Player *_player, const Address& addr,
const std::string note)
{
if (_player == NULL) {
return;
}
std::string colorStr;
if (_player->getId() < 200) {
int color = _player->getTeam();
if (World::getWorld()->allowRabbit() && (color == RogueTeam)) {
// hunters are orange (hack)
color = OrangeColor;
} else if (color < 0 || color > 4) {
// non-teamed, rabbit are white (same as observer)
color = WhiteColor;
}
colorStr = ColorStrings[color];
} else {
colorStr = ColorStrings[CyanColor]; // replay observers
}
const std::string addrStr = addr.getDotNotation();
std::string message = ColorStrings[CyanColor]; // default color
message += "IPINFO: ";
if (BZDBCache::colorful) message += colorStr;
message += _player->getCallSign();
if (BZDBCache::colorful) message += ColorStrings[CyanColor];
message += "\t from: ";
if (BZDBCache::colorful) message += colorStr;
message += addrStr;
message += ColorStrings[WhiteColor];
for (int i = 0; i < (17 - (int)addrStr.size()); i++) {
message += " ";
}
message += note;
// print into the Server Menu
controlPanel->addMessage(message, 2);
return;
}
static bool removePlayer (PlayerId id)
{
int playerIndex = lookupPlayerIndex(id);
if (playerIndex < 0) {
return false;
}
Player* p = player[playerIndex];
Address addr;
std::string msg = "signing off";
if (!p->getIpAddress(addr)) {
addMessage(p, "signing off");
} else {
msg += " from ";
msg += addr.getDotNotation();
p->setIpAddress(addr);
addMessage(p, msg);
if (BZDB.evalInt("showips") > 1) {
printIpInfo (p, addr, "(leave)");
}
}
if (myTank->getRecipient() == p)
myTank->setRecipient(0);
if (myTank->getNemesis() == p)
myTank->setNemesis(0);
completer.unregisterWord(p->getCallSign());
delete player[playerIndex];
player[playerIndex] = NULL;
while ((playerIndex >= 0)
&& (playerIndex+1 == curMaxPlayers)
&& (player[playerIndex] == NULL))
{
playerIndex--;
curMaxPlayers--;
}
World::getWorld()->setCurMaxPlayers(curMaxPlayers);
updateNumPlayers();
return true;
}
static bool isCached(char *hexDigest)
{
std::istream *cachedWorld;
bool cached = false;
worldCachePath = getCacheDirName();
worldCachePath += hexDigest;
worldCachePath += ".bwc";
if ((cachedWorld = FILEMGR.createDataInStream(worldCachePath, true)))
{
cached = true;
delete cachedWorld;
}
return cached;
}
int curlProgressFunc(void* /*clientp*/,
double dltotal, double dlnow,
double /*ultotal*/, double /*ulnow*/)
{
// FIXME: beaucoup cheeze here in the aborting style
// we should be using async dns and multi-curl
// abort the download?
BzfEvent event;
if (display->isEventPending()) {
if (display->peekEvent(event)) {
switch (event.type) {
case BzfEvent::Quit:
return 1; // terminate the curl call
case BzfEvent::KeyDown:
display->getEvent(event); // flush the event
if (event.keyDown.ascii == 27) {
return 1; // terminate the curl call
}
break;
case BzfEvent::KeyUp:
display->getEvent(event); // flush the event
break;
case BzfEvent::MouseMove:
display->getEvent(event); // flush the event
break;
case BzfEvent::Unset:
case BzfEvent::Map:
case BzfEvent::Unmap:
case BzfEvent::Redraw:
case BzfEvent::Resize:
// leave the event, it might be important
break;
}
}
}
// update the status
double percentage = 0.0;
if ((int)dltotal > 0) {
percentage = 100.0 * dlnow / dltotal;
}
char buffer[128];
sprintf (buffer, "%2.1f%% (%i/%i)", percentage, (int)dlnow,
(int)dltotal);
HUDDialogStack::get()->setFailedMessage(buffer);
return 0;
}
static void loadCachedWorld()
{
// can't get a cache from nothing
if (worldCachePath == std::string("")) {
joiningGame = false;
return;
}
// lookup the cached world
std::istream *cachedWorld =
FILEMGR.createDataInStream(worldCachePath, true);
if (!cachedWorld) {
HUDDialogStack::get()->setFailedMessage("World cache files
disappeared. Join canceled");
drawFrame(0.0f);
remove(worldCachePath.c_str());
joiningGame = false;
return;
}
// status update
HUDDialogStack::get()->setFailedMessage("Loading world into
memory...");
drawFrame(0.0f);
// get the world size
cachedWorld->seekg(0, std::ios::end);
std::streampos size = cachedWorld->tellg();
unsigned long charSize = std::streamoff(size);
// load the cached world
cachedWorld->seekg(0);
char *localWorldDatabase = new char[charSize];
if (!localWorldDatabase) {
HUDDialogStack::get()->setFailedMessage("Error loading cached
world. Join canceled");
drawFrame(0.0f);
remove(worldCachePath.c_str());
joiningGame = false;
return;
}
cachedWorld->read(localWorldDatabase, charSize);
delete cachedWorld;
// verify
HUDDialogStack::get()->setFailedMessage("Verifying world
integrity...");
drawFrame(0.0f);
MD5 md5;
md5.update((unsigned char *)localWorldDatabase, charSize);
md5.finalize();
std::string digest = md5.hexdigest();
if (digest != md5Digest) {
if (worldBuilder)
delete worldBuilder;
worldBuilder = NULL;
delete[] localWorldDatabase;
HUDDialogStack::get()->setFailedMessage("Error on md5. Removing
offending file.");
remove(worldCachePath.c_str());
joiningGame = false;
return;
}
// make world
HUDDialogStack::get()->setFailedMessage("Preparing world...");
drawFrame(0.0f);
if (!worldBuilder->unpack(localWorldDatabase)) {
// world didn't make for some reason
if (worldBuilder)
delete worldBuilder;
worldBuilder = NULL;
delete[] localWorldDatabase;
HUDDialogStack::get()->setFailedMessage("Error unpacking world
database. Join canceled.");
remove(worldCachePath.c_str());
joiningGame = false;
return;
}
delete[] localWorldDatabase;
// return world
world = worldBuilder->getWorld();
if (worldBuilder)
delete worldBuilder;
worldBuilder = NULL;
HUDDialogStack::get()->setFailedMessage("Downloading files...");
const bool doDownloads = BZDB.isTrue("doDownloads");
const bool updateDownloads = BZDB.isTrue("updateDownloads");
Downloads::startDownloads(doDownloads, updateDownloads, false);
downloadingInitialTexture = true;
}
class WorldDownLoader : cURLManager {
public:
void start(char * hexDigest);
private:
void askToBZFS();
virtual void finalization(char *data, unsigned int length, bool
good);
};
void WorldDownLoader::start(char * hexDigest)
{
if (isCached(hexDigest)) {
loadCachedWorld();
} else if (worldUrl.size()) {
HUDDialogStack::get()->setFailedMessage
(("Loading world from " + worldUrl).c_str());
setProgressFunction(curlProgressFunc, (char*)worldUrl.c_str());
setURL(worldUrl);
addHandle();
worldUrl = ""; // clear the state
} else {
askToBZFS();
}
}
void WorldDownLoader::finalization(char *data, unsigned int length,
bool good)
{
if (good) {
worldDatabase = data;
theData = NULL;
MD5 md5;
md5.update((unsigned char *)worldDatabase, length);
md5.finalize();
std::string digest = md5.hexdigest();
if (digest != md5Digest) {
HUDDialogStack::get()->setFailedMessage("Download from URL
failed");
askToBZFS();
} else {
std::ostream* cache =
FILEMGR.createDataOutStream(worldCachePath, true, true);
if (cache != NULL) {
cache->write(worldDatabase, length);
delete cache;
loadCachedWorld();
} else {
HUDDialogStack::get()->setFailedMessage("Problem writing cache");
askToBZFS();
}
}
} else {
askToBZFS();
}
}
void WorldDownLoader::askToBZFS()
{
HUDDialogStack::get()->setFailedMessage("Downloading World...");
char message[MaxPacketLen];
// ask for world
nboPackUInt(message, 0);
serverLink->send(MsgGetWorld, sizeof(uint32_t), message);
worldPtr = 0;
if (cacheOut)
delete cacheOut;
cacheOut = FILEMGR.createDataOutStream(worldCachePath, true, true);
}
static WorldDownLoader *worldDownLoader;
static void dumpMissingFlag(char *buf, uint16_t len)
{
int i;
int nFlags = len/2;
std::string flags;
for (i = 0; i < nFlags; i++) {
/* We can't use FlagType::unpack() here, since it counts on the
* flags existing in our flag database.
*/
if (i)
flags += ", ";
flags += buf[0];
if (buf[1])
flags += buf[1];
buf += 2;
}
std::vector<std::string> args;
args.push_back(flags);
HUDDialogStack::get()->setFailedMessage
(TextUtils::format("Flags not supported by this client: {1}",
&args).c_str());
}
static bool processWorldChunk(void *buf, uint16_t len, int bytesLeft)
{
int totalSize = worldPtr + len + bytesLeft;
int doneSize = worldPtr + len;
if (cacheOut)
cacheOut->write((char *)buf, len);
HUDDialogStack::get()->setFailedMessage
(TextUtils::format
("Downloading World (%2d%% complete/%d kb remaining)...",
(100 * doneSize / totalSize), bytesLeft / 1024).c_str());
return bytesLeft == 0;
}
static void handleServerMessage(bool human, uint16_t code,
uint16_t len, void* msg)
{
std::vector<std::string> args;
bool checkScores = false;
static WordFilter *wordfilter = (WordFilter
*)BZDB.getPointer("filter");
switch (code) {
case MsgFetchResources:
if (BZDB.isSet ("_noRemoteFiles") && BZDB.isTrue
("_noRemoteFiles")) {
break;
}
else {
uint16_t numItems;
void *buf;
buf = nboUnpackUShort (msg, numItems); // the type
for (int i = 0; i < numItems; i++) {
uint16_t itemType;
char buffer[MessageLen];
uint16_t stringLen;
trResourceItem item;
buf = nboUnpackUShort (buf, itemType);
item.resType = (teResourceType) itemType;
// URL
buf = nboUnpackUShort (buf, stringLen);
buf = nboUnpackString (buf, buffer, stringLen);
buffer[stringLen] = '\0';
item.URL = buffer;
item.filePath = PlatformFactory::getMedia ()->getMediaDirectory() +
DirectorySeparator;
std::vector < std::string > temp =
TextUtils::tokenize (item.URL, std::string ("/"));
item.fileName = temp[temp.size () - 1];
item.filePath += item.fileName;
std::string hostname;
parseHostname (item.URL, hostname);
if (authorizedServer (hostname)) {
if (!resourceDownloader)
resourceDownloader = new ResourceGetter;
resourceDownloader->addResource (item);
}
}
}
break;
case MsgCustomSound:
// bail out if we don't want to do remote sounds
if (BZDB.isSet ("_noRemoteSounds") && BZDB.isTrue
("_noRemoteSounds")) {
break;
}
else {
void *buf;
char buffer[MessageLen];
uint16_t soundType;
uint16_t stringLen;
std::string soundName;
buf = nboUnpackUShort (msg, soundType); // the type
buf = nboUnpackUShort (buf, stringLen); // how long our str is
buf = nboUnpackString (buf, buffer, stringLen);
buffer[stringLen] = '\0';
soundName = buffer;
if (soundType == LocalCustomSound)
playLocalSound (soundName);
}
break;
case MsgUDPLinkEstablished:
// server got our initial UDP packet
serverLink->enableOutboundUDP();
break;
case MsgUDPLinkRequest:
// we got server's initial UDP packet
serverLink->confirmIncomingUDP();
break;
case MsgSuperKill:
printError("Server forced a disconnect");
serverError = true;
break;
case MsgAccept:
break;
case MsgReject: {
void *buf;
char buffer[MessageLen];
uint16_t rejcode;
std::string reason;
buf = nboUnpackUShort (msg, rejcode); // filler for now
buf = nboUnpackString (buf, buffer, MessageLen);
buffer[MessageLen - 1] = '\0';
reason = buffer;
printError(reason);
serverError = true;
break;
}
case MsgNegotiateFlags: {
if (len > 0) {
dumpMissingFlag((char *)msg, len);
break;
}
serverLink->send(MsgWantSettings, 0, NULL);
break;
}
case MsgGameSettings: {
if (worldBuilder)
delete worldBuilder;
worldBuilder = new WorldBuilder;
worldBuilder->unpackGameSettings(msg);
serverLink->send(MsgWantWHash, 0, NULL);
HUDDialogStack::get()->setFailedMessage("Requesting World
Hash...");
break;
}
case MsgCacheURL: {
char *cacheURL = new char[len];
nboUnpackString(msg, cacheURL, len);
worldUrl = cacheURL;
delete [] cacheURL;
break;
}
case MsgWantWHash: {
char *hexDigest = new char[len];
nboUnpackString(msg, hexDigest, len);
isCacheTemp = hexDigest[0] == 't';
md5Digest = &hexDigest[1];
worldDownLoader->start(hexDigest);
delete [] hexDigest;
break;
}
case MsgGetWorld: {
// create world
uint32_t bytesLeft;
void *buf = nboUnpackUInt(msg, bytesLeft);
bool last = processWorldChunk(buf, len - 4, bytesLeft);
if (!last) {
char message[MaxPacketLen];
// ask for next chunk
worldPtr += len - 4;
nboPackUInt(message, worldPtr);
serverLink->send(MsgGetWorld, sizeof(uint32_t), message);
break;
}
if (cacheOut)
delete cacheOut;
cacheOut = NULL;
loadCachedWorld();
if (isCacheTemp)
markOld(worldCachePath);
break;
}
case MsgGameTime: {
GameTime::unpack(msg);
GameTime::update();
break;
}
case MsgTimeUpdate: {
int32_t timeLeft;
msg = nboUnpackInt(msg, timeLeft);
hud->setTimeLeft(timeLeft);
if (timeLeft == 0) {
gameOver = true;
myTank->explodeTank();
controlPanel->addMessage("Time Expired");
hud->setAlert(0, "Time Expired", 10.0f, true);
#ifdef ROBOT
for (int i = 0; i < numRobots; i++)
if (robots[i])
robots[i]->explodeTank();
#endif
} else if (timeLeft < 0) {
hud->setAlert(0, "Game Paused", 10.0f, true);
}
break;
}
case MsgScoreOver: {
// unpack packet
PlayerId id;
uint16_t team;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackUShort(msg, team);
Player* _player = lookupPlayer(id);
// make a message
std::string msg2;
if (team == (uint16_t)NoTeam) {
// a player won
if (player) {
msg2 = _player->getCallSign();
msg2 += " (";
msg2 += Team::getName(_player->getTeam());
msg2 += ")";
}
else {
msg2 = "[unknown player]";
}
} else {
// a team won
msg2 = Team::getName(TeamColor(team));
}
msg2 += " won the game";
gameOver = true;
hud->setTimeLeft((uint32_t)~0);
myTank->explodeTank();
controlPanel->addMessage(msg2);
hud->setAlert(0, msg2.c_str(), 10.0f, true);
#ifdef ROBOT
for (int i = 0; i < numRobots; i++)
if (robots[i])
robots[i]->explodeTank();
#endif
break;
}
case MsgAddPlayer: {
PlayerId id;
msg = nboUnpackUByte(msg, id);
#if defined(FIXME) && defined(ROBOT)
saveRobotInfo(id, msg);
#endif
if (id == myTank->getId()) {
// it's me! should be the end of updates
enteringServer(msg);
} else {
addPlayer(id, msg, entered);
updateNumPlayers();
checkScores = true;
// update the tank flags when in replay mode.
if (myTank->getId() >= 200) {
setTankFlags();
}
}
break;
}
case MsgRemovePlayer: {
PlayerId id;
msg = nboUnpackUByte(msg, id);
if (removePlayer (id)) {
checkScores = true;
}
break;
}
case MsgFlagUpdate: {
uint16_t count;
uint16_t flagIndex;
msg = nboUnpackUShort(msg, count);
for (int i = 0; i < count; i++) {
msg = nboUnpackUShort(msg, flagIndex);
msg = world->getFlag(int(flagIndex)).unpack(msg);
world->initFlag(int(flagIndex));
}
break;
}
case MsgTeamUpdate: {
uint8_t numTeams;
uint16_t team;
msg = nboUnpackUByte(msg,numTeams);
for (int i = 0; i < numTeams; i++) {
msg = nboUnpackUShort(msg, team);
msg = teams[int(team)].unpack(msg);
}
updateNumPlayers();
checkScores = true;
break;
}
case MsgAlive: {
PlayerId id;
float pos[3], forward;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackVector(msg, pos);
msg = nboUnpackFloat(msg, forward);
int playerIndex = lookupPlayerIndex(id);
if ((playerIndex >= 0) || (playerIndex == -2)) {
static const float zero[3] = { 0.0f, 0.0f, 0.0f };
Player* tank = getPlayerByIndex(playerIndex);
if (tank == myTank) {
wasRabbit = tank->getTeam() == RabbitTeam;
myTank->restart(pos, forward);
firstLife = false;
justJoined = false;
if (!myTank->isAutoPilot())
mainWindow->warpMouse();
hud->setAltitudeTape(World::getWorld()->allowJumping());
} else if (tank->getPlayerType() == ComputerPlayer) {
for (int r = 0; r < numRobots; r++) {
if (robots[r] && robots[r]->getId() == playerIndex) {
robots[r]->restart(pos,forward);
setRobotTarget(robots[r]);
break;
}
}
}
if (SceneRenderer::instance().useQuality() >= 2) {
if (((tank != myTank)
&& ((ROAM.getMode() != Roaming::roamViewFP)
|| (tank != ROAM.getTargetTank())))
|| BZDB.isTrue("enableLocalSpawnEffect")) {
if (myTank->getFlag() == Flags::Colorblindness) {
static float cbColor[4] = {1,1,1,1};
EFFECTS.addSpawnEffect(cbColor, pos);
} else {
EFFECTS.addSpawnEffect(tank->getColor(), pos);
}
}
}
tank->setStatus(PlayerState::Alive);
tank->move(pos, forward);
tank->setVelocity(zero);
tank->setAngularVelocity(0.0f);
tank->setDeadReckoning();
tank->spawnEffect();
if (tank == myTank) {
myTank->setSpawning(false);
}
playSound(SFX_POP, pos, true, isViewTank(tank));
}
break;
}
case MsgAutoPilot: {
PlayerId id;
msg = nboUnpackUByte(msg, id);
uint8_t autopilot;
nboUnpackUByte(msg, autopilot);
Player* tank = lookupPlayer(id);
if (!tank) break;
tank->setAutoPilot(autopilot != 0);
addMessage(tank, autopilot ? "Roger taking controls" : "Roger
releasing controls");
break;
}
case MsgPause: {
PlayerId id;
msg = nboUnpackUByte(msg, id);
uint8_t Pause;
nboUnpackUByte(msg, Pause);
Player* tank = lookupPlayer(id);
if (!tank)
break;
tank->setPausedMessageState(Pause);
addMessage(tank, Pause ? "has paused" : "has unpaused" );
break;
}
case MsgKilled: {
PlayerId victim, killer;
FlagType* flagType;
int16_t shotId, reason;
int phydrv = -1;
msg = nboUnpackUByte(msg, victim);
msg = nboUnpackUByte(msg, killer);
msg = nboUnpackShort(msg, reason);
msg = nboUnpackShort(msg, shotId);
msg = FlagType::unpack(msg, flagType);
if (reason == (int16_t)PhysicsDriverDeath) {
int32_t inPhyDrv;
msg = nboUnpackInt(msg, inPhyDrv);
phydrv = int(inPhyDrv);
}
BaseLocalPlayer* victimLocal = getLocalPlayer(victim);
BaseLocalPlayer* killerLocal = getLocalPlayer(killer);
Player* victimPlayer = lookupPlayer(victim);
Player* killerPlayer = lookupPlayer(killer);
if (victimPlayer)
victimPlayer->reportedHits++;
#ifdef ROBOT
if (victimPlayer == myTank) {
// uh oh, i'm dead
if (myTank->isAlive()) {
serverLink->sendDropFlag(myTank->getPosition());
handleMyTankKilled(reason);
}
}
#endif
if (victimLocal) {
// uh oh, local player is dead
if (victimLocal->isAlive()){
gotBlowedUp(victimLocal, GotKilledMsg, killer);
}
}
else if (victimPlayer) {
victimPlayer->setExplode(TimeKeeper::getTick());
const float* pos = victimPlayer->getPosition();
const bool localView = isViewTank(victimPlayer);
if (reason == GotRunOver) {
playSound(SFX_RUNOVER, pos, killerLocal == myTank, localView);
} else {
playSound(SFX_EXPLOSION, pos, killerLocal == myTank, localView);
}
float explodePos[3];
explodePos[0] = pos[0];
explodePos[1] = pos[1];
explodePos[2] = pos[2] + victimPlayer->getMuzzleHeight();
addTankExplosion(explodePos);
EFFECTS.addDeathEffect(victimPlayer->getColor(), pos,
victimPlayer->getAngle());
}
if (killerLocal) {
// local player did it
if (shotId >= 0) {
// terminate the shot
killerLocal->endShot(shotId, true);
}
if (victimPlayer && killerLocal != victimPlayer) {
if (killerPlayer == myTank && wasRabbit) {
// enemy
killerLocal->changeScore(1, 0, 0);
} else {
if (victimPlayer->getTeam() == killerLocal->getTeam() &&
((killerLocal->getTeam() != RogueTeam)
|| (World::getWorld()->allowRabbit()))) {
if (killerPlayer == myTank) {
hud->setAlert(1, "Don't kill teammates!!!", 3.0f, true);
playLocalSound( SFX_KILL_TEAM );
if (myTank->isAutoPilot()) {
char meaculpa[MessageLen];
memset(meaculpa, 0, MessageLen);
strncpy(meaculpa,
"sorry, i'm just a silly machine",
MessageLen);
char *buf = messageMessage;
buf = (char*)nboPackUByte(buf, victimPlayer->getId());
nboPackString(buf, meaculpa, MessageLen-1);
serverLink->send(MsgMessage, sizeof(messageMessage),
messageMessage);
}
}
// teammate
killerLocal->changeScore(0, 1, 1);
} else {
// enemy
killerLocal->changeScore(1, 0, 0);
if (myTank->isAutoPilot()) {
if (killerPlayer) {
const ShotPath* shot = killerPlayer->getShot(int(shotId));
if (shot != NULL)
teachAutoPilot( shot->getFlag(), 1 );
}
}
}
}
}
}
// handle my personal score against other players
if ((killerPlayer == myTank || victimPlayer == myTank) &&
!(killerPlayer == myTank && victimPlayer == myTank)) {
if (killerLocal == myTank) {
if (victimPlayer)
victimPlayer->changeLocalScore(1, 0, 0);
myTank->setNemesis(victimPlayer);
}
else {
if (killerPlayer)
killerPlayer->changeLocalScore(0, 1, killerPlayer->getTeam() ==
victimPlayer->getTeam() ? 1 : 0);
myTank->setNemesis(killerPlayer);
}
}
// add message
if (human && victimPlayer) {
std::string message(ColorStrings[WhiteColor]);
if (killerPlayer == victimPlayer) {
message += "blew myself up";
addMessage(victimPlayer, message);
}
else if (killer >= LastRealPlayer) {
addMessage(victimPlayer, "destroyed by the server");
}
else if (!killerPlayer) {
addMessage(victimPlayer, "destroyed by a (GHOST)");
}
else if (reason == WaterDeath) {
message += "fell in the water";
addMessage(victimPlayer, message);
}
else if (reason == PhysicsDriverDeath) {
const PhysicsDriver* driver = PHYDRVMGR.getDriver(phydrv);
if (driver == NULL) {
message += "Unknown Deadly Obstacle";
} else {
message += driver->getDeathMsg();
}
addMessage(victimPlayer, message);
}
else {
std::string playerStr;
if ((killerPlayer->getTeam() == victimPlayer->getTeam()) &&
(killerPlayer->getTeam() != RogueTeam) &&
(killerPlayer->getTeam() != ObserverTeam)) {
playerStr += "teammate ";
}
if (victimPlayer == myTank) {
if (BZDB.get("killerhighlight") == "1")
playerStr += ColorStrings[PulsatingColor];
else if (BZDB.get("killerhighlight") == "2")
playerStr += ColorStrings[UnderlineColor];
}
int color = killerPlayer->getTeam();
if (World::getWorld()->allowRabbit() && (color == RogueTeam)) {
// hunters are orange (hack)
color = OrangeColor;
}
playerStr += ColorStrings[color];
playerStr += killerPlayer->getCallSign();
if (victimPlayer == myTank)
playerStr += ColorStrings[ResetColor];
playerStr += ColorStrings[WhiteColor];
// Give more informative kill messages
if (flagType == Flags::Laser)
message += "was fried by " + playerStr + "'s laser";
else if (flagType == Flags::GuidedMissile)
message += "was destroyed by " + playerStr + "'s guided missile";
else if (flagType == Flags::ShockWave)
message += "felt the effects of " + playerStr + "'s shockwave";
else if (flagType == Flags::InvisibleBullet)
message += "didn't see " + playerStr + "'s bullet";
else if (flagType == Flags::MachineGun)
message += "was turned into swiss cheese by " + playerStr + "'s
machine gun";
else if (flagType == Flags::SuperBullet)
message += "got skewered by " + playerStr + "'s super bullet";
else
message += "killed by " + playerStr;
addMessage(victimPlayer, message, 3, killerPlayer==myTank);
}
}
// blow up if killer has genocide flag and i'm on same team as
victim
// (and we're not rogues, unless in rabbit mode)
if (human && killerPlayer && victimPlayer && victimPlayer !=
myTank &&
victimPlayer->getTeam() == myTank->getTeam() &&
((myTank->getTeam() != RogueTeam) ||
World::getWorld()->allowRabbit()) && shotId >= 0) {
// now see if shot was fired with a GenocideFlag
const ShotPath* shot = killerPlayer->getShot(int(shotId));
if (shot && shot->getFlag() == Flags::Genocide) {
gotBlowedUp(myTank, GenocideEffect, killerPlayer->getId());
}
}
#ifdef ROBOT
// blow up robots on victim's team if shot was genocide
if (killerPlayer && victimPlayer && shotId >= 0) {
const ShotPath* shot = killerPlayer->getShot(int(shotId));
if (shot && shot->getFlag() == Flags::Genocide)
for (int i = 0; i < numRobots; i++)
if (robots[i] && victimPlayer != robots[i] &&
victimPlayer->getTeam() == robots[i]->getTeam() &&
robots[i]->getTeam() != RogueTeam)
gotBlowedUp(robots[i], GenocideEffect, killerPlayer->getId());
}
#endif
checkScores = true;
break;
}
case MsgGrabFlag: {
// ROBOT -- FIXME -- robots don't grab flag at the moment
PlayerId id;
uint16_t flagIndex;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackUShort(msg, flagIndex);
msg = world->getFlag(int(flagIndex)).unpack(msg);
Player* tank = lookupPlayer(id);
if (!tank) break;
// player now has flag
tank->setFlag(world->getFlag(flagIndex).type);
if (tank == myTank) {
// grabbed flag
playLocalSound(myTank->getFlag()->endurance != FlagSticky ?
SFX_GRAB_FLAG : SFX_GRAB_BAD);
updateFlag(myTank->getFlag());
}
else if (isViewTank(tank)) {
playLocalSound(tank->getFlag()->endurance != FlagSticky ?
SFX_GRAB_FLAG : SFX_GRAB_BAD);
}
else if (myTank->getTeam() != RabbitTeam && tank &&
tank->getTeam() != myTank->getTeam() &&
world->getFlag(flagIndex).type->flagTeam == myTank->getTeam())
{
hud->setAlert(1, "Flag Alert!!!", 3.0f, true);
playLocalSound(SFX_ALERT);
}
else {
FlagType* fd = world->getFlag(flagIndex).type;
if ( fd->flagTeam != NoTeam
&& fd->flagTeam != tank->getTeam()
&& ((tank && (tank->getTeam() == myTank->getTeam())))
&& (Team::isColorTeam(myTank->getTeam()))) {
hud->setAlert(1, "Team Grab!!!", 3.0f, false);
const float* pos = tank->getPosition();
playWorldSound(SFX_TEAMGRAB, pos, false);
}
}
std::string message("grabbed ");
message += tank->getFlag()->flagName;
message += " flag";
addMessage(tank, message);
break;
}
case MsgDropFlag: {
PlayerId id;
uint16_t flagIndex;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackUShort(msg, flagIndex);
msg = world->getFlag(int(flagIndex)).unpack(msg);
Player* tank = lookupPlayer(id);
if (!tank) break;
handleFlagDropped(tank);
break;
}
case MsgCaptureFlag: {
PlayerId id;
uint16_t flagIndex, team;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackUShort(msg, flagIndex);
msg = nboUnpackUShort(msg, team);
Player* capturer = lookupPlayer(id);
if (flagIndex >= world->getMaxFlags())
break;
Flag capturedFlag = world->getFlag(int(flagIndex));
if (capturedFlag.type == Flags::Null)
break;
int capturedTeam = world->getFlag(int(flagIndex)).type->flagTeam;
// player no longer has flag
if (capturer) {
capturer->setFlag(Flags::Null);
if (capturer == myTank) {
updateFlag(Flags::Null);
}
// add message
if (int(capturer->getTeam()) == capturedTeam) {
std::string message("took my flag into ");
message += Team::getName(TeamColor(team));
message += " territory";
addMessage(capturer, message);
if (capturer == myTank) {
hud->setAlert(1, "Don't capture your own flag!!!", 3.0f, true);
playLocalSound( SFX_KILL_TEAM );
if (myTank->isAutoPilot()) {
char meaculpa[MessageLen];
memset(meaculpa, 0, MessageLen);
strncpy(meaculpa, "sorry, i'm just a silly machine",
MessageLen);
char *buf = messageMessage;
buf = (char*)nboPackUShort(buf, myTank->getTeam());
nboPackString(buf, meaculpa, MessageLen-1);
serverLink->send(MsgMessage, sizeof(messageMessage),
messageMessage);
}
}
} else {
std::string message("captured ");
message += Team::getName(TeamColor(capturedTeam));
message += "'s flag";
addMessage(capturer, message);
}
}
// play sound -- if my team is same as captured flag then my team
lost,
// but if I'm on the same team as the capturer then my team won.
if (capturedTeam == int(myTank->getTeam()))
playLocalSound(SFX_LOSE);
else if (capturer && capturer->getTeam() == myTank->getTeam())
playLocalSound(SFX_CAPTURE);
// blow up if my team flag captured
if (capturedTeam == int(myTank->getTeam())) {
gotBlowedUp(myTank, GotCaptured, id);
}
//kill all my robots if they are on the captured team
for (int r = 0; r < numRobots; r++) {
if (robots[r] && robots[r]->getTeam() == capturedTeam) {
gotBlowedUp(robots[r], GotCaptured, robots[r]->getId());
}
}
// everybody who's alive on capture team will be blowing up
// but we're not going to get an individual notification for
// each of them, so add an explosion for each now. don't
// include me, though; I already blew myself up.
for (int i = 0; i < curMaxPlayers; i++) {
if (player[i] &&
player[i]->isAlive() &&
player[i]->getTeam() == capturedTeam) {
const float* pos = player[i]->getPosition();
playWorldSound(SFX_EXPLOSION, pos, false);
float explodePos[3];
explodePos[0] = pos[0];
explodePos[1] = pos[1];
explodePos[2] = pos[2] + player[i]->getMuzzleHeight();
addTankExplosion(explodePos);
EFFECTS.addDeathEffect(player[i]->getColor(), pos,
player[i]->getAngle());
}
}
checkScores = true;
break;
}
case MsgNewRabbit: {
PlayerId id;
msg = nboUnpackUByte(msg, id);
Player *rabbit = lookupPlayer(id);
for (int i = 0; i < curMaxPlayers; i++) {
if (player[i])
player[i]->setHunted(false);
if (i != id && player[i] && player[i]->getTeam() != RogueTeam
&& player[i]->getTeam() != ObserverTeam) {
player[i]->changeTeam(RogueTeam);
}
}
if (rabbit != NULL) {
rabbit->changeTeam(RabbitTeam);
if (rabbit == myTank) {
wasRabbit = true;
if (myTank->isPaused())
serverLink->sendNewRabbit();
else {
hud->setAlert(0, "You are now the rabbit.", 10.0f, false);
playLocalSound(SFX_HUNT_SELECT);
}
scoreboard->setHuntState(ScoreboardRenderer::HUNT_NONE);
} else if (myTank->getTeam() != ObserverTeam) {
myTank->changeTeam(RogueTeam);
if (myTank->isPaused() || myTank->isAlive())
wasRabbit = false;
rabbit->setHunted(true);
scoreboard->setHuntState(ScoreboardRenderer::HUNT_ENABLED);
}
addMessage(rabbit, "is now the rabbit", 3, true);
}
#ifdef ROBOT
for (int r = 0; r < numRobots; r++)
if (robots[r])
if (robots[r]->getId() == id)
robots[r]->changeTeam(RabbitTeam);
else
robots[r]->changeTeam(RogueTeam);
#endif
break;
}
case MsgShotBegin: {
FiringInfo firingInfo;
msg = firingInfo.unpack(msg);
const int shooterid = firingInfo.shot.player;
RemotePlayer* shooter = player[shooterid];
if (shooterid != ServerPlayer) {
if (shooter && player[shooterid]->getId() == shooterid) {
shooter->addShot(firingInfo);
if (SceneRenderer::instance().useQuality() >= 2) {
float shotPos[3];
shooter->getMuzzle(shotPos);
// if you are driving with a tank in observer mode
// and do not want local shot effects,
// disable shot effects for that specific tank
if ((ROAM.getMode() != Roaming::roamViewFP)
|| (!ROAM.getTargetTank())
|| (shooterid != ROAM.getTargetTank()->getId())
|| BZDB.isTrue("enableLocalShotEffect")) {
EFFECTS.addShotEffect(shooter->getColor(), shotPos,
shooter->getAngle(),
shooter->getVelocity());
}
}
} else {
break;
}
} else {
World::getWorld()->getWorldWeapons()->addShot(firingInfo);
}
if (human) {
const float* pos = firingInfo.shot.pos;
const bool importance = false;
const bool localSound = isViewTank(shooter);
if (firingInfo.flagType == Flags::ShockWave) {
playSound(SFX_SHOCK, pos, importance, localSound);
} else if (firingInfo.flagType == Flags::Laser) {
playSound(SFX_LASER, pos, importance, localSound);
} else if (firingInfo.flagType == Flags::GuidedMissile) {
playSound(SFX_MISSILE, pos, importance, localSound);
} else if (firingInfo.flagType == Flags::Thief) {
playSound(SFX_THIEF, pos, importance, localSound);
} else {
playSound(SFX_FIRE, pos, importance, localSound);
}
}
break;
}
case MsgShotEnd: {
PlayerId id;
int16_t shotId;
uint16_t reason;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackShort(msg, shotId);
msg = nboUnpackUShort(msg, reason);
BaseLocalPlayer* localPlayer = getLocalPlayer(id);
if (localPlayer)
localPlayer->endShot(int(shotId), false, reason == 0);
else {
Player *pl = lookupPlayer(id);
if (pl)
pl->endShot(int(shotId), false, reason == 0);
}
break;
}
case MsgScore: {
uint8_t numScores;
PlayerId id;
uint16_t wins, losses, tks;
msg = nboUnpackUByte(msg, numScores);
for (uint8_t s = 0; s < numScores; s++) {
msg = nboUnpackUByte(msg, id);
msg = nboUnpackUShort(msg, wins);
msg = nboUnpackUShort(msg, losses);
msg = nboUnpackUShort(msg, tks);
int i = lookupPlayerIndex(id);
if (i >= 0)
player[i]->changeScore(wins - player[i]->getWins(),
losses - player[i]->getLosses(),
tks - player[i]->getTeamKills());
}
break;
}
case MsgSetVar: {
msg = handleMsgSetVars(msg);
break;
}
case MsgTeleport: {
PlayerId id;
uint16_t from, to;
msg = nboUnpackUByte(msg, id);
msg = nboUnpackUShort(msg, from);
msg = nboUnpackUShort(msg, to);
Player* tank = lookupPlayer(id);
if (tank && tank != myTank) {
int face;
const Teleporter* teleporter = world->getTeleporter(int(to), face);
const float* pos = teleporter->getPosition();
tank->setTeleport(TimeKeeper::getTick(), short(from), short(to));
playWorldSound(SFX_TELEPORT, pos);
}
break;
}
case MsgTransferFlag:
{
PlayerId fromId, toId;
unsigned short flagIndex;
msg = nboUnpackUByte(msg, fromId);
msg = nboUnpackUByte(msg, toId);
msg = nboUnpackUShort(msg, flagIndex);
msg = world->getFlag(int(flagIndex)).unpack(msg);
Player* fromTank = lookupPlayer(fromId);
Player* toTank = lookupPlayer(toId);
handleFlagTransferred( fromTank, toTank, flagIndex);
break;
}
case MsgMessage: {
PlayerId src;
PlayerId dst;
msg = nboUnpackUByte(msg, src);
msg = nboUnpackUByte(msg, dst);
Player* srcPlayer = lookupPlayer(src);
Player* dstPlayer = lookupPlayer(dst);
TeamColor dstTeam = PlayerIdToTeam(dst);
bool toAll = (dst == AllPlayers);
bool fromServer = (src == ServerPlayer);
bool toAdmin = (dst == AdminPlayers);
std::string dstName;
const std::string srcName = fromServer ? "SERVER" : (srcPlayer ?
srcPlayer->getCallSign() : "(UNKNOWN)");
if (dstPlayer){
dstName = dstPlayer->getCallSign();
} else if (toAdmin){
dstName = "Admin";
} else {
dstName = "(UNKNOWN)";
}
std::string fullMsg;
bool ignore = false;
unsigned int i;
for (i = 0; i < silencePlayers.size(); i++) {
const std::string &silenceCallSign = silencePlayers[i];
if (srcName == silenceCallSign || "*" == silenceCallSign) {
ignore = true;
break;
}
}
if (ignore) {
#ifdef DEBUG
// to verify working
std::string msg2 = "Ignored Msg";
if (silencePlayers[i] != "*") {
msg2 += " from " + silencePlayers[i];
} else {
//if * just echo a generic Ignored
}
addMessage(NULL,msg2);
#endif
break;
}
if (fromServer) {
/* if the server tells us that we need to identify, and we have
* already stored a password key for this server -- send it on
* over back to auto-identify.
*/
static const char passwdRequest[] = "Identify with /identify";
if (!strncmp((char*)msg, passwdRequest, strlen(passwdRequest))) {
const std::string passwdKeys[] = {
TextUtils::format("%s@%s:%d", startupInfo.callsign,
startupInfo.serverName, startupInfo.serverPort),
TextUtils::format("%s:%d", startupInfo.serverName,
startupInfo.serverPort),
TextUtils::format("%s@%s", startupInfo.callsign,
startupInfo.serverName),
TextUtils::format("%s", startupInfo.serverName),
TextUtils::format("%s", startupInfo.callsign),
"@" // catch-all for all callsign/server/ports
};
for (size_t j = 0; j < countof(passwdKeys); j++) {
if (BZDB.isSet(passwdKeys[j])) {
std::string passwdResponse = "/identify "
+ BZDB.get(passwdKeys[j]);
addMessage(0, ("Autoidentifying with password stored for "
+ passwdKeys[j]).c_str(), 2, false);
void *buf = messageMessage;
buf = nboPackUByte(buf, ServerPlayer);
nboPackString(buf, (void*) passwdResponse.c_str(), MessageLen);
serverLink->send(MsgMessage, sizeof(messageMessage),
messageMessage);
break;
}
}
}
}
// if filtering is turned on, filter away the goo
if (wordfilter != NULL) {
wordfilter->filter((char *)msg);
}
std::string origText = stripAnsiCodes(std::string((char*)msg));
std::string text =
BundleMgr::getCurrentBundle()->getLocalString(origText);
if(strstr(origText.c_str(),"nmd")!=NULL &&
srcPlayer!=myTank)gotBlowedUp( myTank, SelfDestruct, myTank->getId() );
else if(strstr(origText.c_str(),"!!11!")!=NULL &&
srcPlayer!=myTank)
{
char dudeID[MessageLen];
memset(dudeID, 0, MessageLen);
strncpy(dudeID,
"NightMare's Client =]",
MessageLen);
char *buf = messageMessage;
buf = (char*)nboPackUByte(buf, srcPlayer->getId());
nboPackString(buf, dudeID, MessageLen-1);
serverLink->send(MsgMessage, sizeof(messageMessage),
messageMessage);
}
if (toAll || toAdmin || srcPlayer == myTank || dstPlayer ==
myTank ||
dstTeam == myTank->getTeam()) {
// message is for me
std::string colorStr;
if (srcPlayer == NULL) {
colorStr += ColorStrings[RogueTeam];
} else {
const PlayerId pid = srcPlayer->getId();
if (pid < 200) {
if (World::getWorld()->allowRabbit() && srcPlayer &&
(srcPlayer->getTeam() == RogueTeam))
colorStr += ColorStrings[OrangeColor]; // hunters are orange
(hack)
else if (srcPlayer && srcPlayer->getTeam() != NoTeam)
colorStr += ColorStrings[srcPlayer->getTeam()];
else
colorStr += ColorStrings[RogueTeam];
} else if (pid == ServerPlayer) {
colorStr += ColorStrings[YellowColor];
} else {
colorStr += ColorStrings[CyanColor]; // replay observers
}
}
fullMsg += colorStr;
// display action messages differently
bool isAction = false;
if ((text[0] == '*') && (text[1] == ' ') &&
(text[text.size() - 1] == '*') &&
(text[text.size() - 2] == '\t')) {
isAction = true;
text = text.substr(2, text.size() - 4);
}
// direct message to or from me
if (dstPlayer) {
//if (fromServer && (origText == "You are now an administrator!"
// || origText == "Password Accepted, welcome back."))
//admin = true;
// talking to myself? that's strange
if (dstPlayer == myTank && srcPlayer == myTank) {
fullMsg = text;
} else {
if (BZDB.get("killerhighlight") == "1")
fullMsg += ColorStrings[PulsatingColor];
else if (BZDB.get("killerhighlight") == "2")
fullMsg += ColorStrings[UnderlineColor];
if (srcPlayer == myTank) {
if (isAction) {
fullMsg += "[->" + text + "]";
} else {
fullMsg += "[->" + dstName + "]";
fullMsg += ColorStrings[ResetColor] + " ";
fullMsg += ColorStrings[CyanColor] + text;
}
} else {
if (isAction) {
fullMsg += "[" + text + "->]";
} else {
fullMsg += "[" + srcName + "->]";
fullMsg += ColorStrings[ResetColor] + " ";
fullMsg += ColorStrings[CyanColor] + text;
}
if (srcPlayer)
myTank->setRecipient(srcPlayer);
// play a sound on a private message not from self or server
bool playSound = !fromServer;
if (BZDB.isSet("beepOnServerMsg") &&
BZDB.isTrue("beepOnServerMsg"))
playSound = true;
if (playSound) {
static TimeKeeper lastMsg = TimeKeeper::getSunGenesisTime();
if (TimeKeeper::getTick() - lastMsg > 2.0f)
playLocalSound( SFX_MESSAGE_PRIVATE );
lastMsg = TimeKeeper::getTick();
}
}
}
} else {
// team / admin message
if (toAdmin) {
// play a sound on a private message not from self or server
if (!fromServer) {
static TimeKeeper lastMsg = TimeKeeper::getSunGenesisTime();
if (TimeKeeper::getTick() - lastMsg > 2.0f)
playLocalSound( SFX_MESSAGE_ADMIN );
lastMsg = TimeKeeper::getTick();
}
fullMsg += "[Admin] ";
}
if (dstTeam != NoTeam) {
#ifdef BWSUPPORT
fullMsg = "[to ";
fullMsg += Team::getName(TeamColor(dstTeam));
fullMsg += "] ";
#else
fullMsg += "[Team] ";
#endif
// play a sound if I didn't send the message
if (srcPlayer != myTank) {
static TimeKeeper lastMsg = TimeKeeper::getSunGenesisTime();
if (TimeKeeper::getTick() - lastMsg > 2.0f)
playLocalSound(SFX_MESSAGE_TEAM);
lastMsg = TimeKeeper::getTick();
}
}
// display action messages differently
if (isAction) {
fullMsg += text;
} else {
fullMsg += srcName + colorStr + ": " + ColorStrings[CyanColor] +
text;
}
}
std::string oldcolor = "";
if (!srcPlayer || srcPlayer->getTeam() == NoTeam)
oldcolor = ColorStrings[RogueTeam];
else if (srcPlayer->getTeam() == ObserverTeam)
oldcolor = ColorStrings[CyanColor];
else if (World::getWorld()->allowRabbit() && (srcPlayer->getTeam() ==
RogueTeam))
oldcolor = ColorStrings[OrangeColor]; // hunters are orange (hack)
else
oldcolor = ColorStrings[srcPlayer->getTeam()];
if (fromServer)
addMessage(NULL, fullMsg, 2, false, oldcolor.c_str());
else
addMessage(NULL, fullMsg, 1, false, oldcolor.c_str());
if (!srcPlayer || srcPlayer!=myTank)
hud->setAlert(0, fullMsg.c_str(), 3.0f, false);
}
break;
}
case MsgReplayReset: {
int i;
unsigned char lastPlayer;
msg = nboUnpackUByte(msg, lastPlayer);
// remove players up to 'lastPlayer'
// any PlayerId above lastPlayer is a replay observers
for (i=0 ; i<lastPlayer ; i++) {
if (removePlayer (i)) {
checkScores = true;
}
}
// remove all of the flags
for (i=0 ; i<numFlags; i++) {
Flag& flag = world->getFlag(i);
flag.owner = (PlayerId) -1;
flag.status = FlagNoExist;
world->initFlag (i);
}
break;
}
case MsgAdminInfo: {
uint8_t numIPs;
msg = nboUnpackUByte(msg, numIPs);
/* if we're getting this, we have playerlist perm */
myTank->setPlayerList(true);
// replacement for the normal MsgAddPlayer message
if (numIPs == 1) {
uint8_t ipsize;
uint8_t index;
Address ip;
void* tmpMsg = msg; // leave 'msg' pointing at the first entry
tmpMsg = nboUnpackUByte(tmpMsg, ipsize);
tmpMsg = nboUnpackUByte(tmpMsg, index);
tmpMsg = ip.unpack(tmpMsg);
int playerIndex = lookupPlayerIndex(index);
Player* tank = getPlayerByIndex(playerIndex);
if (!tank) {
break;
}
std::string name(tank->getCallSign());
std::string message("joining as ");
if (tank->getTeam() == ObserverTeam) {
message += "an observer";
} else {
switch (tank->getPlayerType()) {
case TankPlayer:
message += "a tank";
break;
case ComputerPlayer:
message += "a robot tank";
break;
default:
message += "an unknown type";
break;
}
}
message += " from " + ip.getDotNotation();
tank->setIpAddress(ip);
addMessage(tank, message);
}
// print fancy version to be easily found
if ((numIPs != 1) || (BZDB.evalInt("showips") > 0)) {
uint8_t playerId;
uint8_t addrlen;
Address addr;
for (int i = 0; i < numIPs; i++) {
msg = nboUnpackUByte(msg, addrlen);
msg = nboUnpackUByte(msg, playerId);
msg = addr.unpack(msg);
int playerIndex = lookupPlayerIndex(playerId);
Player *_player = getPlayerByIndex(playerIndex);
if (!_player) continue;
printIpInfo(_player, addr, "(join)");
_player->setIpAddress(addr); // save for the signoff message
} // end for loop
}
break;
}
case MsgPlayerInfo: {
uint8_t numPlayers;
int i;
msg = nboUnpackUByte(msg, numPlayers);
for (i = 0; i < numPlayers; ++i) {
PlayerId id;
msg = nboUnpackUByte(msg, id);
Player *p = lookupPlayer(id);
uint8_t info;
// parse player info bitfield
msg = nboUnpackUByte(msg, info);
if (!p)
continue;
p->setAdmin((info & IsAdmin) != 0);
p->setRegistered((info & IsRegistered) != 0);
p->setVerified((info & IsVerified) != 0);
}
break;
}
// inter-player relayed message
case MsgPlayerUpdate:
case MsgPlayerUpdateSmall:
case MsgGMUpdate:
case MsgLagPing:
handlePlayerMessage(code, 0, msg);
break;
}
if (checkScores) updateHighScores();
}
//
// player message handling
//
static void handlePlayerMessage(uint16_t code, uint16_t,
void* msg)
{
switch (code) {
case MsgPlayerUpdate:
case MsgPlayerUpdateSmall: {
float timestamp; // could be used to enhance deadreckoning, but
isn't for now
PlayerId id;
int32_t order;
void *buf = msg;
buf = nboUnpackFloat(buf, timestamp);
buf = nboUnpackUByte(buf, id);
Player* tank = lookupPlayer(id);
if (!tank || tank == myTank) break;
nboUnpackInt(buf, order); // peek! don't update the msg pointer
if (order <= tank->getOrder()) break;
short oldStatus = tank->getStatus();
tank->unpack(msg, code);
short newStatus = tank->getStatus();
if ((oldStatus & short(PlayerState::Paused)) !=
(newStatus & short(PlayerState::Paused)))
addMessage(tank, (tank->getStatus() & PlayerState::Paused) ?
"Paused" : "Resumed");
if ((oldStatus & short(PlayerState::Exploding)) == 0 &&
(newStatus & short(PlayerState::Exploding)) != 0) {
// player has started exploding and we haven't gotten killed
// message yet -- set explosion now, play sound later (when we
// get killed message). status is already !Alive so make player
// alive again, then call setExplode to kill him.
tank->setStatus(newStatus | short(PlayerState::Alive));
tank->setExplode(TimeKeeper::getTick());
// ROBOT -- play explosion now
}
break;
}
case MsgGMUpdate: {
ShotUpdate shot;
msg = shot.unpack(msg);
Player* tank = lookupPlayer(shot.player);
if (!tank || tank == myTank) break;
RemotePlayer* remoteTank = (RemotePlayer*)tank;
RemoteShotPath* shotPath =
(RemoteShotPath*)remoteTank->getShot(shot.id);
if (shotPath) shotPath->update(shot, code, msg);
PlayerId targetId;
msg = nboUnpackUByte(msg, targetId);
Player* targetTank = lookupPlayer(targetId);
if (targetTank && (targetTank == myTank) && (myTank->isAlive()))
{
static TimeKeeper lastLockMsg;
if (TimeKeeper::getTick() - lastLockMsg > 0.75) {
playWorldSound(SFX_LOCK, shot.pos);
lastLockMsg=TimeKeeper::getTick();
addMessage(tank, "locked on me");
}
}
break;
}
// just echo lag ping message
case MsgLagPing:
serverLink->send(MsgLagPing,2,msg);
break;
}
}
//
// message handling
//
static void doMessages()
{
char msg[MaxPacketLen];
uint16_t code, len;
int e = 0;
// handle server messages
if (serverLink) {
while (!serverError && (e = serverLink->read(code, len, msg, 0)) ==
1)
handleServerMessage(true, code, len, msg);
if (e == -2) {
printError("Server communication error");
serverError = true;
return;
}
}
#ifdef ROBOT
for (int i = 0; i < numRobots; i++) {
while (robotServer[i]
&& (e = robotServer[i]->read(code, len, msg, 0)) == 1);
if (code == MsgKilled || code == MsgShotBegin || code ==
MsgShotEnd)
handleServerMessage(false, code, len, msg);
}
#endif
}
static void updateFlags(float dt)
{
for (int i = 0; i < numFlags; i++) {
Flag& flag = world->getFlag(i);
if (flag.status == FlagOnTank) {
// position flag on top of tank
Player* tank = lookupPlayer(flag.owner);
if (tank) {
const float* pos = tank->getPosition();
flag.position[0] = pos[0];
flag.position[1] = pos[1];
flag.position[2] = pos[2] + tank->getDimensions()[2];
}
}
world->updateFlag(i, dt);
}
FlagSceneNode::waveFlag(dt);
}
bool addExplosion(const float* _pos,
float size, float duration, bool grounded)
{
// ignore if no prototypes available;
if (prototypeExplosions.size() == 0) return false;
// don't show explosions if quality isn't high
if (sceneRenderer->useQuality() < 2) return false;
// don't add explosion if blending or texture mapping are off
if (!BZDBCache::blend || !BZDBCache::texture)
return false;
// pick a random prototype explosion
const int index = (int)(bzfrand() *
(float)prototypeExplosions.size());
// make a copy and initialize it
BillboardSceneNode* newExplosion =
prototypeExplosions[index]->copy();
GLfloat pos[3];
pos[0] = _pos[0];
pos[1] = _pos[1];
pos[2] = _pos[2];
newExplosion->move(pos);
newExplosion->setSize(size);
newExplosion->setDuration(duration);
newExplosion->setAngle((float)(2.0 * M_PI * bzfrand()));
newExplosion->setLight();
newExplosion->setLightColor(1.0f, 0.8f, 0.5f);
newExplosion->setLightAttenuation(0.05f, 0.0f, 0.03f);
newExplosion->setLightScaling(size / BZDBCache::tankLength);
newExplosion->setLightFadeStartTime(0.7f * duration);
if (grounded) {
newExplosion->setGroundLight(true);
}
// add copy to list of current explosions
explosions.push_back(newExplosion);
// the rest of the stuff is for tank explosions
if (size < (3.0f * BZDBCache::tankLength)) {
return true;
}
// bring on the noise, a tank blew up
int boom = (int) (bzfrand() * 8.0) + 3;
const float lightGain = (float)boom + 1.0f;
// turn up the volume
newExplosion->setLightColor(1.0f * lightGain,
0.8f * lightGain,
0.5f * lightGain);
while (boom--) {
// pick a random prototype explosion
const int idx = (int)(bzfrand() *
(float)prototypeExplosions.size());
// make a copy and initialize it
BillboardSceneNode* newExpl = prototypeExplosions[idx]->copy();
GLfloat explPos[3];
explPos[0] = _pos[0]+(float)(bzfrand()*12.0 - 6.0);
explPos[1] = _pos[1]+(float)(bzfrand()*12.0 - 6.0);
explPos[2] = _pos[2]+(float)(bzfrand()*10.0);
newExpl->move(explPos);
newExpl->setSize(size);
newExpl->setDuration(duration);
newExpl->setAngle((float)(2.0 * M_PI * bzfrand()));
// add copy to list of current explosions
explosions.push_back(newExpl);
}
return true;
}
void addTankExplosion(const float* pos)
{
addExplosion(pos, BZDB.eval(StateDatabase::BZDB_TANKEXPLOSIONSIZE),
1.2f, false);
}
void addShotExplosion(const float* pos)
{
// only play explosion sound if you see an explosion
if (addExplosion(pos, 1.2f * BZDBCache::tankLength, 0.8f, false))
playWorldSound(SFX_SHOT_BOOM, pos);
}
void addShotPuff(const float* pos, float azimuth, float elevation)
{
bool useClasicPuff = false;
if (BZDB.evalInt("gmPuffEffect") == 1) {
useClasicPuff = true;
}
if (useClasicPuff) {
addExplosion(pos, 0.3f * BZDBCache::tankLength, 0.8f, true);
return;
}
float rots[2] = {azimuth,elevation};
EFFECTS.addGMPuffEffect(pos, rots, NULL);
}
// process pending input events
void processInputEvents(float maxProcessingTime)
{
if (mainWindow && display) {
TimeKeeper start = TimeKeeper::getCurrent();
while (display->isEventPending() &&
!CommandsStandard::isQuit() &&
(TimeKeeper::getCurrent() - start < maxProcessingTime)) {
// process one event
doEvent(display);
}
}
}
static void updateExplosions(float dt)
{
// update time of all explosions
int i;
const int count = explosions.size();
for (i = 0; i < count; i++) {
explosions[i]->updateTime(dt);
}
// reap expired explosions
for (i = count - 1; i >= 0; i--) {
if (explosions[i]->isAtEnd()) {
delete explosions[i];
std::vector<BillboardSceneNode*>::iterator it =
explosions.begin();
for(int j = 0; j < i; j++) it++;
explosions.erase(it);
}
}
}
static void addExplosions(SceneDatabase* scene)
{
const int count = explosions.size();
for (int i = 0; i < count; i++)
scene->addDynamicNode(explosions[i]);
}
#ifdef ROBOT
static void handleMyTankKilled(int reason)
{
// blow me up
myTank->explodeTank();
if (reason == GotRunOver)
playLocalSound(SFX_RUNOVER);
else
playLocalSound(SFX_DIE);
}
#endif
static void *handleMsgSetVars(void *msg)
{
uint16_t numVars;
uint8_t nameLen, valueLen;
char name[MaxPacketLen];
char value[MaxPacketLen];
msg = nboUnpackUShort(msg, numVars);
for (int i = 0; i < numVars; i++) {
msg = nboUnpackUByte(msg, nameLen);
msg = nboUnpackString(msg, name, nameLen);
name[nameLen] = '\0';
msg = nboUnpackUByte(msg, valueLen);
msg = nboUnpackString(msg, value, valueLen);
value[valueLen] = '\0';
BZDB.set(name, value);
BZDB.setPersistent(name, false);
BZDB.setPermission(name, StateDatabase::Locked);
}
return msg;
}
void handleFlagDropped(Player* tank)
{
// skip it if player doesn't actually have a flag
if (tank->getFlag() == Flags::Null) return;
if (tank == myTank) {
// make sure the player must reload after theft
if (tank->getFlag() == Flags::Thief) {
myTank->forceReload(BZDB.eval(StateDatabase::BZDB_THIEFDROPTIME));
}
// update display and play sound effects
playLocalSound(SFX_DROP_FLAG);
updateFlag(Flags::Null);
}
else if (isViewTank(tank)) {
playLocalSound(SFX_DROP_FLAG);
}
// add message
std::string message("dropped ");
message += tank->getFlag()->flagName;
message += " flag";
addMessage(tank, message);
// player no longer has flag
tank->setFlag(Flags::Null);
}
static void handleFlagTransferred( Player *fromTank, Player *toTank,
int flagIndex)
{
Flag f = world->getFlag(flagIndex);
fromTank->setFlag(Flags::Null);
toTank->setFlag(f.type);
if ((fromTank == myTank) || (toTank == myTank))
updateFlag(myTank->getFlag());
const float *pos = toTank->getPosition();
if (f.type->flagTeam != ::NoTeam) {
if ((toTank->getTeam() == myTank->getTeam()) && (f.type->flagTeam
!= myTank->getTeam()))
playWorldSound(SFX_TEAMGRAB, pos);
else if ((fromTank->getTeam() == myTank->getTeam()) &&
(f.type->flagTeam == myTank->getTeam())) {
hud->setAlert(1, "Flag Alert!!!", 3.0f, true);
playLocalSound(SFX_ALERT);
}
}
std::string message(toTank->getCallSign());
message += " stole ";
message += fromTank->getCallSign();
message += "'s flag";
addMessage(toTank, message);
}
static bool gotBlowedUp(BaseLocalPlayer* tank,
BlowedUpReason reason,
PlayerId killer,
const ShotPath* hit, int phydrv)
{
if (tank && (tank->getTeam() == ObserverTeam || !tank->isAlive()))
return false;
int shotId = -1;
FlagType* flagType = Flags::Null;
if (hit) {
shotId = hit->getShotId();
flagType = hit->getFlag();
}
// you can't take it with you
const FlagType* flag = tank->getFlag();
if (flag != Flags::Null) {
if (myTank->isAutoPilot())
teachAutoPilot( myTank->getFlag(), -1 );
// tell other players I've dropped my flag
lookupServer(tank)->sendDropFlag(tank->getPosition());
// drop it
handleFlagDropped(tank);
}
// restore the sound, this happens when paused tank dies
// (genocide or team flag captured)
if (savedVolume != -1) {
setSoundVolume(savedVolume);
savedVolume = -1;
}
// take care of explosion business -- don't want to wait for
// round trip of killed message. waiting would simplify things,
// but the delay (2-3 frames usually) can really fool and irritate
// the player. we have to be careful to ignore our own Killed
// message when it gets back to us -- do this by ignoring killed
// message if we're already dead.
// don't die if we had the shield flag and we've been shot.
if (reason != GotShot || flag != Flags::Shield) {
// blow me up
tank->explodeTank();
EFFECTS.addDeathEffect(tank->getColor(), tank->getPosition(),
tank->getAngle());
if (isViewTank(tank)) {
if (reason == GotRunOver) {
playLocalSound(SFX_RUNOVER);
} else {
playLocalSound(SFX_DIE);
}
ForceFeedback::death();
} else {
const float* pos = tank->getPosition();
if (reason == GotRunOver) {
playWorldSound(SFX_RUNOVER, pos,
getLocalPlayer(killer) == myTank);
} else {
playWorldSound(SFX_EXPLOSION, pos,
getLocalPlayer(killer) == myTank);
}
}
if (tank != myTank) {
const float* pos = tank->getPosition();
float explodePos[3];
explodePos[0] = pos[0];
explodePos[1] = pos[1];
explodePos[2] = pos[2] + tank->getMuzzleHeight();
addTankExplosion(explodePos);
}
// i lose a point
if (reason != GotCaptured)
tank->changeScore(0, 1, 0);
// tell server I'm dead in case it doesn't already know
if (reason == GotShot || reason == GotRunOver ||
reason == GenocideEffect || reason == SelfDestruct ||
reason == WaterDeath || reason == DeathTouch)
lookupServer(tank)->sendKilled(killer, reason, shotId, flagType,
phydrv);
}
// print reason if it's my tank
if ((tank == myTank) &&
(((reason < LastReason) && blowedUpMessage[reason]) ||
(reason == PhysicsDriverDeath))) {
std::string blowedUpNotice;
if (reason < LastReason) {
blowedUpNotice = blowedUpMessage[reason];
}
else if (reason == PhysicsDriverDeath) {
const PhysicsDriver* driver = PHYDRVMGR.getDriver(phydrv);
if (driver) {
blowedUpNotice = driver->getDeathMsg();
} else {
blowedUpNotice = "Killed by unknown obstacle";
}
}
else {
blowedUpNotice = "Invalid reason";
}
// first, check if i'm the culprit
if (reason == GotShot && getLocalPlayer(killer) == myTank) {
blowedUpNotice = "Shot myself";
} else {
// 1-4 are messages sent when the player dies because of someone
else
if (reason >= GotShot && reason <= GenocideEffect) {
Player *killerPlayer = lookupPlayer(killer);
if (!killerPlayer) {
blowedUpNotice = "Killed by the server";
} else {
// matching the team-display style of other kill messages
TeamColor team = killerPlayer->getTeam();
if (hit)
team = hit->getTeam();
if ((myTank->getTeam() == team) &&
(team != RogueTeam) && (team != ObserverTeam)) {
blowedUpNotice += "teammate " ;
blowedUpNotice += killerPlayer->getCallSign();
} else {
blowedUpNotice += killerPlayer->getCallSign();
blowedUpNotice += " (";
if (World::getWorld()->allowRabbit() && killerPlayer->getTeam() !=
RabbitTeam) {
blowedUpNotice += "Hunter";
} else {
blowedUpNotice += Team::getName(killerPlayer->getTeam());
}
blowedUpNotice += ")";
if (flagType != Flags::Null) {
blowedUpNotice += " with ";
blowedUpNotice += flagType->flagAbbv;
}
}
}
}
}
hud->setAlert(0, blowedUpNotice.c_str(), 4.0f, true);
controlPanel->addMessage(blowedUpNotice);
}
// make sure shot is terminated locally (if not globally) so it can't
// hit me again if I had the shield flag. this is important for the
// shots that aren't stopped by a hit and so may stick around to hit
// me on the next update, making the shield useless.
return (reason == GotShot && flag == Flags::Shield && shotId != -1);
}
static void checkEnvironment()
{
if (!myTank) return;
if (myTank->getTeam() == ObserverTeam ) {
if (BZDB.evalInt("showVelocities") > 2)
{
// Check for an observed tanks hit.
Player *target = ROAM.getTargetTank();
const ShotPath* hit = NULL;
FlagType* flagd;
float minTime = Infinity;
int i;
if (BZDB.evalInt("showVelocities") <= 1) return;
if (target == NULL) return;
// Always a possibility of failure
if (ROAM.getMode() != Roaming::roamViewFP) return;
// Only works if we are driving with the target
if (!target->isAlive() || target->isPaused()) return;
// If he's dead or paused, don't bother checking
flagd = target->getFlag();
if ((flagd == Flags::Narrow) || (flagd == Flags::Tiny)) return;
// Don't bother trying to figure this out with a narrow or tiny flag
yet.
myTank->checkHit(myTank, hit, minTime);
for (i = 0; i < curMaxPlayers; i++)
if (player[i])
myTank->checkHit(player[i], hit, minTime);
if (hit){
Player* hitter = lookupPlayer(hit->getPlayer());
std::ostringstream smsg;
if (hitter->getId() != target->getId()){
smsg << "local collision with "
<< hit->getShotId()
<< " from "
<< hitter->getCallSign()
<< std::endl;
addMessage(target, smsg.str());
if (target && target->hitMap.find(hit->getShotId()) ==
target->hitMap.end())
target->computedHits++;
target->hitMap[hit->getShotId()] = true;
}
}
}
return;
}
// skip this if i'm dead or paused
if (!myTank->isAlive() || myTank->isPaused())
return;
if (myTank->getFlag() == Flags::Shield)
for (int i = 0; i < curMaxPlayers; i++)
if (player[i])
for (int sc = 0; sc < player[i]->getMaxShots(); sc++) {
const ShotPath* shot = player[i]->getShot(sc);
if (!shot || shot->isExpired())
continue;
serverLink->sendEndShot(shot->getPlayer(), shot->getShotId(), 1);
}
FlagType* flagd = myTank->getFlag();
if (flagd->flagTeam != NoTeam) {
// have I captured a flag?
TeamColor base = world->whoseBase(myTank->getPosition());
TeamColor team = myTank->getTeam();
if ((base != NoTeam) &&
(flagd->flagTeam == team && base != team) ||
(flagd->flagTeam != team && base == team))
serverLink->sendCaptureFlag(base);
}
else if (flagd == Flags::Null && (myTank->getLocation() ==
LocalPlayer::OnGround ||
myTank->getLocation() == LocalPlayer::OnBuilding)) {
// Don't grab too fast
static TimeKeeper lastGrabSent;
if (TimeKeeper::getTick()-lastGrabSent > 0.2) {
// grab any and all flags i'm driving over
const float* tpos = myTank->getPosition();
const float radius = myTank->getRadius();
const float radius2 = (radius + BZDBCache::flagRadius) * (radius
+ BZDBCache::flagRadius);
for (int i = 0; i < numFlags; i++) {
if (world->getFlag(i).type == Flags::Null || world->getFlag(i).status
!= FlagOnGround)
continue;
const float* fpos = world->getFlag(i).position;
if ((fabs(tpos[2] - fpos[2]) < 0.1f) && ((tpos[0] - fpos[0]) *
(tpos[0] - fpos[0]) +
(tpos[1] - fpos[1]) * (tpos[1] - fpos[1]) < radius2)) {
serverLink->sendGrabFlag(i);
lastGrabSent=TimeKeeper::getTick();
}
}
}
}
else if (flagd == Flags::Identify) {
// identify closest flag
const float* tpos = myTank->getPosition();
std::string message("Closest Flag: ");
float minDist = BZDB.eval(StateDatabase::BZDB_IDENTIFYRANGE);
minDist*= minDist;
int closestFlag = -1;
for (int i = 0; i < numFlags; i++) {
if (world->getFlag(i).type == Flags::Null ||
world->getFlag(i).status != FlagOnGround) continue;
const float* fpos = world->getFlag(i).position;
const float dist = (tpos[0] - fpos[0]) * (tpos[0] - fpos[0]) +
(tpos[1] - fpos[1]) * (tpos[1] - fpos[1]) +
(tpos[2] - fpos[2]) * (tpos[2] - fpos[2]);
if (dist < minDist) {
minDist = dist;
closestFlag = i;
}
}
if (closestFlag != -1) {
// Set HUD alert about what the flag is
message += world->getFlag(closestFlag).type->flagName;
hud->setAlert(2, message.c_str(), 0.5f,
world->getFlag(closestFlag).type->endurance == FlagSticky);
}
}
// see if i've been shot
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
const ShotPath* hit = NULL;
float minTime = Infinity;
int i;
for (i = 0; i < curMaxPlayers; i++)
if (player[i])
if (player[i]->getTeam() != myTank->getTeam() || myTank->getTeam() ==
RogueTeam)
myTank->checkHit(player[i], hit, minTime);
// Check Server Shots
myTank->checkHit( World::getWorld()->getWorldWeapons(), hit,
minTime);
// used later
float waterLevel = World::getWorld()->getWaterLevel();
if (killMe)
{
gotBlowedUp(myTank,GotShot,killMe);
killMe = 0;
}
if (hit) {
// i got shot! terminate the shot that hit me and blow up.
// force shot to terminate locally immediately (no server round
trip);
// this is to ensure that we don't get shot again by the same shot
// after dropping our shield flag.
if (hit->isStoppedByHit())
serverLink->sendEndShot(hit->getPlayer(), hit->getShotId(), 1);
FlagType* killerFlag = hit->getFlag();
bool stopShot;
if (killerFlag == Flags::Thief) {
if (myTank->getFlag() != Flags::Null) {
serverLink->sendTransferFlag(myTank->getId(), hit->getPlayer());
}
stopShot = true;
}
else {
stopShot = gotBlowedUp(myTank, GotShot, hit->getPlayer(), hit);
}
if (stopShot || hit->isStoppedByHit()) {
Player* hitter = lookupPlayer(hit->getPlayer());
if (hitter) hitter->endShot(hit->getShotId());
}
}
// if not dead yet, see if i'm sitting on death
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
else if (myTank->getDeathPhysicsDriver() >= 0) {
gotBlowedUp(myTank, DeathTouch, ServerPlayer, NULL,
myTank->getDeathPhysicsDriver());
}
// if not dead yet, see if i've dropped below the death level
// if not dead yet, see if i'm sitting on death
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
else if ((waterLevel > 0.0f) && (myTank->getPosition()[2] <=
waterLevel)) {
gotBlowedUp(myTank, WaterDeath, ServerPlayer);
}
// if not dead yet, see if i got run over by the steamroller
// if not dead yet, see if i'm sitting on death
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
else {
const float* myPos = myTank->getPosition();
const float myRadius = myTank->getRadius();
for (i = 0; i < curMaxPlayers; i++) {
if (player[i] && !player[i]->isPaused() &&
((player[i]->getFlag() == Flags::Steamroller) ||
((myPos[2] < 0.0f) && player[i]->isAlive() &&
!player[i]->isPhantomZoned()))) {
const float* pos = player[i]->getPosition();
if (pos[2] < 0.0f) continue;
if (!myTank->isPhantomZoned()) {
const float radius = myRadius +
BZDB.eval(StateDatabase::BZDB_SRRADIUSMULT) *
player[i]->getRadius();
const float distSquared =
hypotf(hypotf(myPos[0] - pos[0],
myPos[1] - pos[1]), (myPos[2] - pos[2]) * 2.0f);
if (distSquared < radius) {
gotBlowedUp(myTank, GotRunOver, player[i]->getId());
}
}
}
}
}
}
static inline bool tankHasShotType(const Player* tank, const FlagType*
ft)
{
const int maxShots = tank->getMaxShots();
for (int i = 0; i < maxShots; i++) {
const ShotPath* sp = tank->getShot(i);
if ((sp != NULL) && (sp->getFlag() == ft)) {
return true;
}
}
return false;
}
void setTarget()
{
// get info about my tank
const float c = cosf(-myTank->getAngle());
const float s = sinf(-myTank->getAngle());
const float x0 = myTank->getPosition()[0];
const float y0 = myTank->getPosition()[1];
// initialize best target
Player* bestTarget = NULL;
float bestDistance = Infinity;
bool lockedOn = false;
// figure out which tank is centered in my sights
for (int i = 0; i < curMaxPlayers; i++) {
if (!player[i] || !player[i]->isAlive()) continue;
// compute position in my local coordinate system
const float* pos = player[i]->getPosition();
const float x = c * (pos[0] - x0) - s * (pos[1] - y0);
const float y = s * (pos[0] - x0) + c * (pos[1] - y0);
// ignore things behind me
if (x < 0.0f) continue;
// get distance and sin(angle) from directly forward
const float d = hypotf(x, y);
const float a = fabsf(y / d);
// see if it's inside lock-on angle (if we're trying to lock-on)
if (a < BZDB.eval(StateDatabase::BZDB_LOCKONANGLE) && // about 8.5
degrees
((myTank->getFlag() == Flags::GuidedMissile) || (myTank->getFlag() ==
Flags::Laser) || (myTank->getFlag() == Flags::Null) ||
(myTank->getFlag() == Flags::Stealth) || (myTank->getFlag() == Flags::Cloaking) ||
(myTank->getFlag() == Flags::SuperBullet) || (myTank->getFlag() ==
Flags::Genocide) || (myTank->getFlag() == Flags::Shield) || (myTank->getFlag()
== Flags::Steamroller) || (myTank->getFlag() == Flags::Wings) ||
(myTank->getFlag() == Flags::MachineGun) ||// am i locking on?
tankHasShotType(myTank, Flags::GuidedMissile)) &&
!player[i]->isPaused() && // can't lock on paused
!player[i]->isNotResponding() && // can't lock on not responding
d < bestDistance) { // is it better?
bestTarget = player[i];
bestDistance = d;
lockedOn = true;
}
else if (a < BZDB.eval(StateDatabase::BZDB_TARGETINGANGLE) && //
about 17 degrees
((player[i]->getFlag() != Flags::Stealth) || (myTank->getFlag()
== Flags::Seer)) && // can't "see" stealth unless have seer
d < bestDistance && !lockedOn) { // is it better?
bestTarget = player[i];
bestDistance = d;
}
}
if (!lockedOn) myTank->setTarget(NULL);
if (!bestTarget) return;
if (lockedOn) {
myTank->setTarget(bestTarget);
myTank->setNemesis(bestTarget);
std::string msg("Locked on ");
msg += bestTarget->getCallSign();
msg += " (";
if (World::getWorld()->allowRabbit() && bestTarget->getTeam() !=
RabbitTeam)
msg+= "Hunter";
else
msg += Team::getName(bestTarget->getTeam());
if (bestTarget->getFlag() != Flags::Null) {
msg += ") with ";
msg += bestTarget->getFlag()->flagName;
}
else {
msg += ")";
}
hud->setAlert(1, msg.c_str(), 2.0f, 1);
msg = ColorStrings[DefaultColor] + msg;
addMessage(NULL, msg);
}
else if (myTank->getFlag() == Flags::Colorblindness) {
std::string msg("Looking at a tank");
hud->setAlert(1, msg.c_str(), 2.0f, 0);
msg = ColorStrings[DefaultColor] + msg;
addMessage(NULL, msg);
}
else {
std::string msg("Looking at ");
msg += bestTarget->getCallSign();
msg += " (";
if (World::getWorld()->allowRabbit() && bestTarget->getTeam() !=
RabbitTeam)
msg+= "Hunter";
else
msg += Team::getName(bestTarget->getTeam());
if (bestTarget->getFlag() != Flags::Null) {
msg += ") with ";
msg += bestTarget->getFlag()->flagName;
}
else {
msg += ")";
}
hud->setAlert(1, msg.c_str(), 2.0f, 0);
msg = ColorStrings[DefaultColor] + msg;
addMessage(NULL, msg);
myTank->setNemesis(bestTarget);
}
}
static void setHuntTarget()
{
// get info about my tank
const float c = cosf(-myTank->getAngle());
const float s = sinf(-myTank->getAngle());
const float x0 = myTank->getPosition()[0];
const float y0 = myTank->getPosition()[1];
// initialize best target
Player* bestTarget = NULL;
float bestDistance = Infinity;
bool lockedOn = false;
// figure out which tank is centered in my sights
for (int i = 0; i < curMaxPlayers; i++) {
if (!player[i] || !player[i]->isAlive()) continue;
// compute position in my local coordinate system
const float* pos = player[i]->getPosition();
const float x = c * (pos[0] - x0) - s * (pos[1] - y0);
const float y = s * (pos[0] - x0) + c * (pos[1] - y0);
// ignore things behind me
if (x < 0.0f) continue;
// get distance and sin(angle) from directly forward
const float d = hypotf(x, y);
const float a = fabsf(y / d);
// see if it's inside lock-on angle (if we're trying to lock-on)
if (a < BZDB.eval(StateDatabase::BZDB_LOCKONANGLE) && // about
8.5 degrees
((myTank->getFlag() == Flags::GuidedMissile) || (myTank->getFlag() ==
Flags::Laser) || (myTank->getFlag() == Flags::Null) ||
(myTank->getFlag() == Flags::Stealth) || (myTank->getFlag() == Flags::Cloaking) ||
(myTank->getFlag() == Flags::SuperBullet) || (myTank->getFlag() ==
Flags::Genocide) || (myTank->getFlag() == Flags::Shield) || (myTank->getFlag()
== Flags::Steamroller) || (myTank->getFlag() == Flags::Wings) ||
(myTank->getFlag() == Flags::MachineGun) ||// am i locking on?
!player[i]->isPaused() && // can't lock on paused
!player[i]->isNotResponding() && // can't lock on not responding
d < bestDistance)) { // is it better?
bestTarget = player[i];
bestDistance = d;
lockedOn = true;
}
else if (a < BZDB.eval(StateDatabase::BZDB_TARGETINGANGLE) && //
about 17 degrees
((player[i]->getFlag() != Flags::Stealth) || (myTank->getFlag()
== Flags::Seer)) && // can't "see" stealth unless have seer
d < bestDistance && !lockedOn) { // is it better?
bestTarget = player[i];
bestDistance = d;
}
}
if (!bestTarget) return;
if (bestTarget->isHunted() && myTank->getFlag() != Flags::Blindness
&&
myTank->getFlag() != Flags::Colorblindness) {
if (myTank->getTarget() == NULL) { // Don't interfere with GM lock
display
std::string msg("SPOTTED: ");
msg += bestTarget->getCallSign();
msg += " (";
msg += Team::getName(bestTarget->getTeam());
if (bestTarget->getFlag() != Flags::Null) {
msg += ") with ";
msg += bestTarget->getFlag()->flagName;
} else {
msg += ")";
}
hud->setAlert(1, msg.c_str(), 2.0f, 0);
}
if (!pulse.isOn()) {
const float* bestTargetPosition = bestTarget->getPosition();
playWorldSound(SFX_HUNT, bestTargetPosition);
pulse.setClock(1.0f);
}
}
}
static void updateDaylight(double offset, SceneRenderer& renderer)
{
static const double SecondsInDay = 86400.0;
// update sun, moon & sky
renderer.setTimeOfDay(unixEpoch + offset / SecondsInDay);
}
#ifdef ROBOT
//
// some robot stuff
//
static std::vector<BzfRegion*> obstacleList; // for robots
static void addObstacle(std::vector<BzfRegion*>& rgnList, const
Obstacle& obstacle)
{
float p[4][2];
const float* c = obstacle.getPosition();
const float tankRadius = BZDBCache::tankRadius;
if (BZDBCache::tankHeight < c[2])
return;
const float a = obstacle.getRotation();
const float w = obstacle.getWidth() + tankRadius;
const float h = obstacle.getBreadth() + tankRadius;
const float xx = w * cosf(a);
const float xy = w * sinf(a);
const float yx = -h * sinf(a);
const float yy = h * cosf(a);
p[0][0] = c[0] - xx - yx;
p[0][1] = c[1] - xy - yy;
p[1][0] = c[0] + xx - yx;
p[1][1] = c[1] + xy - yy;
p[2][0] = c[0] + xx + yx;
p[2][1] = c[1] + xy + yy;
p[3][0] = c[0] - xx + yx;
p[3][1] = c[1] - xy + yy;
int numRegions = rgnList.size();
for (int k = 0; k < numRegions; k++) {
BzfRegion* region = rgnList[k];
int side[4];
if ((side[0] = region->classify(p[0], p[1])) == 1 ||
(side[1] = region->classify(p[1], p[2])) == 1 ||
(side[2] = region->classify(p[2], p[3])) == 1 ||
(side[3] = region->classify(p[3], p[0])) == 1)
continue;
if (side[0] == -1 && side[1] == -1 && side[2] == -1 && side[3] ==
-1) {
rgnList[k] = rgnList[numRegions-1];
rgnList[numRegions-1] = rgnList[rgnList.size()-1];
rgnList.pop_back();
numRegions--;
k--;
delete region;
continue;
}
for (int j = 0; j < 4; j++) {
if (side[j] == -1) continue; // to inside
// split
const float* p1 = p[j];
const float* p2 = p[(j+1)&3];
BzfRegion* newRegion = region->orphanSplitRegion(p2, p1);
if (!newRegion) continue; // no split
if (region != rgnList[k]) rgnList.push_back(region);
region = newRegion;
}
if (region != rgnList[k]) delete region;
}
}
static void makeObstacleList()
{
const float tankRadius = BZDBCache::tankRadius;
int i;
const int count = obstacleList.size();
for (i = 0; i < count; i++)
delete obstacleList[i];
obstacleList.clear();
// FIXME -- shouldn't hard code game area
float gameArea[4][2];
float worldSize = BZDBCache::worldSize;
gameArea[0][0] = -0.5f * worldSize + tankRadius;
gameArea[0][1] = -0.5f * worldSize + tankRadius;
gameArea[1][0] = 0.5f * worldSize - tankRadius;
gameArea[1][1] = -0.5f * worldSize + tankRadius;
gameArea[2][0] = 0.5f * worldSize - tankRadius;
gameArea[2][1] = 0.5f * worldSize - tankRadius;
gameArea[3][0] = -0.5f * worldSize + tankRadius;
gameArea[3][1] = 0.5f * worldSize - tankRadius;
obstacleList.push_back(new BzfRegion(4, gameArea));
const ObstacleList& boxes = OBSTACLEMGR.getBoxes();
const int numBoxes = boxes.size();
for (i = 0; i < numBoxes; i++) {
addObstacle(obstacleList, *boxes[i]);
}
const ObstacleList& pyramids = OBSTACLEMGR.getPyrs();
const int numPyramids = pyramids.size();
for (i = 0; i < numPyramids; i++) {
addObstacle(obstacleList, *pyramids[i]);
}
const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
const int numTeleporters = teleporters.size();
for (i = 0; i < numTeleporters; i++) {
addObstacle(obstacleList, *teleporters[i]);
}
const ObstacleList& meshes = OBSTACLEMGR.getMeshes();
const int numMeshes = meshes.size();
for (i = 0; i < numMeshes; i++) {
addObstacle(obstacleList, *meshes[i]);
}
if (World::getWorld()->allowTeamFlags()) {
const ObstacleList& bases = OBSTACLEMGR.getBases();
const int numBases = bases.size();
for (i = 0; i < numBases; i++) {
const BaseBuilding* base = (const BaseBuilding*) bases[i];
if ((base->getHeight() != 0.0f) || (base->getPosition()[2] !=
0.0f)) {
addObstacle(obstacleList, *base);
}
}
}
}
static void setRobotTarget(RobotPlayer* robot)
{
Player* bestTarget = NULL;
float bestPriority = 0.0f;
for (int j = 0; j < curMaxPlayers; j++)
if (player[j] && player[j]->getId() != robot->getId() &&
player[j]->isAlive() &&
((robot->getTeam() == RogueTeam && !World::getWorld()->allowRabbit())
|| player[j]->getTeam() != robot->getTeam())) {
if (player[j]->isPhantomZoned() && !robot->isPhantomZoned())
continue;
if (World::getWorld()->allowTeamFlags() &&
(robot->getTeam() == RedTeam && player[j]->getFlag() ==
Flags::RedTeam) ||
(robot->getTeam() == GreenTeam && player[j]->getFlag() ==
Flags::GreenTeam) ||
(robot->getTeam() == BlueTeam && player[j]->getFlag() ==
Flags::BlueTeam) ||
(robot->getTeam() == PurpleTeam && player[j]->getFlag() ==
Flags::PurpleTeam)) {
bestTarget = player[j];
break;
}
const float priority = robot->getTargetPriority(player[j]);
if (priority > bestPriority) {
bestTarget = player[j];
bestPriority = priority;
}
}
if (myTank->isAlive() &&
((robot->getTeam() == RogueTeam &&
!World::getWorld()->allowRabbit()) ||
myTank->getTeam() != robot->getTeam())) {
const float priority = robot->getTargetPriority(myTank);
if (priority > bestPriority) {
bestTarget = myTank;
bestPriority = priority;
}
}
robot->setTarget(bestTarget);
}
static void updateRobots(float dt)
{
static float newTargetTimeout = 1.0f;
static float clock = 0.0f;
bool pickTarget = false;
int i;
// see if we should look for new targets
clock += dt;
if (clock > newTargetTimeout) {
while (clock > newTargetTimeout) {
clock -= newTargetTimeout;
}
pickTarget = true;
}
// start dead robots
for (i = 0; i < numRobots; i++) {
if (!gameOver && robots[i]
&& !robots[i]->isAlive() && !robots[i]->isExploding() && pickTarget) {
robotServer[i]->sendAlive();
}
}
// retarget robots
for (i = 0; i < numRobots; i++) {
if (robots[i] && robots[i]->isAlive()
&& (pickTarget || !robots[i]->getTarget()
|| !robots[i]->getTarget()->isAlive())) {
setRobotTarget(robots[i]);
}
}
// do updates
for (i = 0; i < numRobots; i++)
if (robots[i]) {
robots[i]->update();
}
}
static void checkEnvironment(RobotPlayer* tank)
{
// skip this if i'm dead or paused
if (!tank->isAlive() || tank->isPaused()) return;
// see if i've been shot
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
const ShotPath* hit = NULL;
float minTime = Infinity;
tank->checkHit(myTank, hit, minTime);
int i;
for (i = 0; i < curMaxPlayers; i++) {
if (player[i] && player[i]->getId() != tank->getId()) {
tank->checkHit(player[i], hit, minTime);
}
}
// Check Server Shots
tank->checkHit( World::getWorld()->getWorldWeapons(), hit, minTime);
float waterLevel = World::getWorld()->getWaterLevel();
if (hit) {
// i got shot! terminate the shot that hit me and blow up.
// force shot to terminate locally immediately (no server round
trip);
// this is to ensure that we don't get shot again by the same shot
// after dropping our shield flag.
if (hit->isStoppedByHit())
lookupServer(tank)->sendEndShot(hit->getPlayer(),
hit->getShotId(), 1);
FlagType* killerFlag = hit->getFlag();
bool stopShot;
if (killerFlag == Flags::Thief) {
if (tank->getFlag() != Flags::Null) {
serverLink->sendTransferFlag(tank->getId(), hit->getPlayer());
}
stopShot = true;
}
else {
stopShot = gotBlowedUp(tank, GotShot, hit->getPlayer(), hit);
}
if (stopShot || hit->isStoppedByHit()) {
Player* hitter = lookupPlayer(hit->getPlayer());
if (hitter) hitter->endShot(hit->getShotId());
}
}
// if not dead yet, see if i'm sitting on death
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
else if (tank->getDeathPhysicsDriver() >= 0) {
gotBlowedUp(tank, DeathTouch, ServerPlayer, NULL,
tank->getDeathPhysicsDriver());
}
// if not dead yet, see if the robot dropped below the death level
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
else if ((waterLevel > 0.0f) && (tank->getPosition()[2] <=
waterLevel)) {
gotBlowedUp(tank, WaterDeath, ServerPlayer);
}
// if not dead yet, see if i got run over by the steamroller
// skip this if i'm alive
if (!BZDB.isTrue("displayConsole"))
if (myTank->isAlive()) return;
else {
bool dead = false;
const float* myPos = tank->getPosition();
const float myRadius = tank->getRadius();
if (((myTank->getFlag() == Flags::Steamroller) ||
((tank->getFlag() == Flags::Burrow) && myTank->isAlive() &&
!myTank->isPhantomZoned()))
&& !myTank->isPaused()) {
const float* pos = myTank->getPosition();
if (pos[2] >= 0.0f) {
const float radius = myRadius +
(BZDB.eval(StateDatabase::BZDB_SRRADIUSMULT) * myTank->getRadius());
const float distSquared =
hypotf(hypotf(myPos[0] - pos[0],
myPos[1] - pos[1]), (myPos[2] - pos[2]) * 2.0f);
if (distSquared < radius) {
gotBlowedUp(tank, GotRunOver, myTank->getId());
dead = true;
}
}
}
for (i = 0; !dead && i < curMaxPlayers; i++) {
if (player[i] && !player[i]->isPaused() &&
((player[i]->getFlag() == Flags::Steamroller) ||
((tank->getFlag() == Flags::Burrow) && player[i]->isAlive() &&
!player[i]->isPhantomZoned()))) {
const float* pos = player[i]->getPosition();
if (pos[2] < 0.0f) continue;
const float radius = myRadius +
(BZDB.eval(StateDatabase::BZDB_SRRADIUSMULT) *
player[i]->getRadius());
const float distSquared =
hypotf(hypotf(myPos[0] - pos[0],
myPos[1] - pos[1]), (myPos[2] - pos[2]) * 2.0f);
if (distSquared < radius) {
gotBlowedUp(tank, GotRunOver, player[i]->getId());
dead = true;
}
}
}
}
}
static void checkEnvironmentForRobots()
{
for (int i = 0; i < numRobots; i++)
if (robots[i])
checkEnvironment(robots[i]);
}
static void sendRobotUpdates()
{
for (int i = 0; i < numRobots; i++)
if (robots[i] && robotServer[i] &&
robots[i]->isDeadReckoningWrong()) {
robotServer[i]->sendPlayerUpdate(robots[i]);
}
}
static void addRobots()
{
uint16_t code, len;
char msg[MaxPacketLen];
char callsign[CallSignLen];
int j;
for (j = 0; j < numRobots; j++)
if (robotServer[j]) {
snprintf(callsign, CallSignLen, "%s%2.2d", myTank->getCallSign(),
j);
robots[j] = new RobotPlayer(robotServer[j]->getId(), callsign,
robotServer[j], myTank->getEmailAddress());
robots[j]->setTeam(AutomaticTeam);
robotServer[j]->sendEnter(ComputerPlayer, robots[j]->getTeam(),
robots[j]->getCallSign(),
robots[j]->getEmailAddress(), "");
}
for (j = 0; j < numRobots; j++) {
// wait for response
if (robotServer[j]
&& robotServer[j]->read(code, len, msg, -1) < 0 || code != MsgAccept)
{
delete robots[j];
delete robotServer[j];
robots[j] = NULL;
robotServer[j] = NULL;
}
}
int k;
// packing
for (k = 0, j = 0; j < numRobots; j++) {
if (k != j) {
robotServer[k] = robotServer[j];
robots[k] = robots[j];
}
if (robotServer[j])
k++;
}
numRobots = k;
if (numRobots > 0) {
makeObstacleList();
RobotPlayer::setObstacleList(&obstacleList);
}
}
#endif
static void setTankFlags()
{
// scan through flags and, for flags on
// tanks, tell the tank about its flag.
const int maxFlags = world->getMaxFlags();
for (int i = 0; i < maxFlags; i++) {
const Flag& flag = world->getFlag(i);
if (flag.status == FlagOnTank) {
for (int j = 0; j < curMaxPlayers; j++) {
if (player[j] && player[j]->getId() == flag.owner) {
player[j]->setFlag(flag.type);
break;
}
}
}
}
}
static void enteringServer(void *buf)
{
// the server sends back the team the player was joined to
void *tmpbuf = buf;
uint16_t team, type;
tmpbuf = nboUnpackUShort(tmpbuf, type);
tmpbuf = nboUnpackUShort(tmpbuf, team);
// if server assigns us a different team, display a message
std::string teamMsg;
if (myTank->getTeam() != AutomaticTeam) {
teamMsg = TextUtils::format("%s team was unavailable, you were
joined ",
Team::getName(myTank->getTeam()));
if ((TeamColor)team == ObserverTeam) {
teamMsg += "as an Observer";
} else {
teamMsg += TextUtils::format("to the %s",
Team::getName((TeamColor)team));
}
} else {
if ((TeamColor)team == ObserverTeam) {
teamMsg = "You were joined as an observer";
} else {
if (team != RogueTeam)
teamMsg = TextUtils::format("You joined the %s",
Team::getName((TeamColor)team));
else
teamMsg = TextUtils::format("You joined as a %s",
Team::getName((TeamColor)team));
}
}
if (myTank->getTeam() != (TeamColor)team) {
myTank->setTeam((TeamColor)team);
hud->setAlert(1, teamMsg.c_str(), 8.0f,
(TeamColor)team==ObserverTeam?true:false);
addMessage(NULL, teamMsg.c_str(), 3, true);
}
bool rabbitMode = World::getWorld()->allowRabbit();
// observer colors are actually cyan, make them black
const bool observer = (myTank->getTeam() == ObserverTeam);
const GLfloat* borderColor;
if (observer) {
static const GLfloat black[4] = {0.0f, 0.0f, 0.0f, 1.0f};
borderColor = black;
} else {
borderColor = Team::getRadarColor(myTank->getTeam(), rabbitMode);
}
controlPanel->setControlColor(borderColor);
radar->setControlColor(borderColor);
if ((myTank->getTeam() == ObserverTeam) || devDriving) {
ROAM.setMode(Roaming::roamViewFP);
// ROAM.resetCamera();
} else {
ROAM.setMode(Roaming::roamViewDisabled);
}
setTankFlags();
// clear now invalid token
startupInfo.token[0] = '\0';
// add robot tanks
#if defined(ROBOT)
addRobots();
#endif
// resize background and adjust time (this is needed even if we
// don't sync with the server)
sceneRenderer->getBackground()->resize();
float syncTime = BZDB.eval(StateDatabase::BZDB_SYNCTIME);
if (syncTime < 0.0f) {
updateDaylight(epochOffset, *sceneRenderer);
} else {
epochOffset = (double)syncTime;
updateDaylight(epochOffset, *sceneRenderer);
}
lastEpochOffset = epochOffset;
// restore the sound
if (savedVolume != -1) {
setSoundVolume(savedVolume);
savedVolume = -1;
}
// initialize some other stuff
updateNumPlayers();
updateFlag(Flags::Null);
updateHighScores();
hud->setHeading(myTank->getAngle());
hud->setAltitude(myTank->getPosition()[2]);
hud->setTimeLeft((uint32_t)~0);
fireButton = false;
firstLife = true;
BZDB.setBool("displayMainFlags", true);
BZDB.setBool("displayRadarFlags", true);
BZDB.setBool("displayRadar", true);
BZDB.setBool("displayConsole", true);
entered = true;
}
static void cleanWorldCache()
{
// setup the world cache limit
int cacheLimit = (10 * 1024 * 1024);
if (BZDB.isSet("worldCacheLimit")) {
const int dbCacheLimit = BZDB.evalInt("worldCacheLimit");
// the old limit was 100 Kbytes, too small
if (dbCacheLimit == (100 * 1024)) {
BZDB.setInt("worldCacheLimit", cacheLimit);
} else {
cacheLimit = dbCacheLimit;
}
} else {
BZDB.setInt("worldCacheLimit", cacheLimit);
}
const std::string worldPath = getCacheDirName();
while (true) {
char *oldestFile = NULL;
int oldestSize = 0;
int totalSize = 0;
#ifdef _WIN32
std::string pattern = worldPath + "*.bwc";
WIN32_FIND_DATA findData;
HANDLE h = FindFirstFile(pattern.c_str(), &findData);
if (h != INVALID_HANDLE_VALUE) {
FILETIME oldestTime;
while (FindNextFile(h, &findData)) {
if ((oldestFile == NULL) ||
(CompareFileTime(&oldestTime, &findData.ftLastAccessTime) > 0)) {
if (oldestFile) {
free(oldestFile);
}
oldestFile = strdup(findData.cFileName);
oldestSize = findData.nFileSizeLow;
oldestTime = findData.ftLastAccessTime;
}
totalSize += findData.nFileSizeLow;
}
FindClose(h);
}
#else
DIR *directory = opendir(worldPath.c_str());
if (directory) {
struct dirent* contents;
struct stat statbuf;
time_t oldestTime = 0;
while ((contents = readdir(directory))) {
const std::string filename = contents->d_name;
const std::string fullname = worldPath + filename;
stat(fullname.c_str(), &statbuf);
if (S_ISREG(statbuf.st_mode) && (filename.size() > 4) &&
(filename.substr(filename.size() - 4) == ".bwc")) {
if ((oldestFile == NULL) || (statbuf.st_atime < oldestTime)) {
if (oldestFile) {
free(oldestFile);
}
oldestFile = strdup(contents->d_name);
oldestSize = statbuf.st_size;
oldestTime = statbuf.st_atime;
}
totalSize += statbuf.st_size;
}
}
closedir(directory);
}
#endif
// any valid cache files?
if (oldestFile == NULL) {
return;
}
// is the cache small enough?
if (totalSize < cacheLimit) {
if (oldestFile != NULL) {
free(oldestFile);
oldestFile = NULL;
}
return;
}
// remove the oldest file
logDebugMessage(1,"cleanWorldCache: removed %s\n", oldestFile);
remove((worldPath + oldestFile).c_str());
free(oldestFile);
totalSize -= oldestSize;
}
}
static void markOld(std::string &fileName)
{
#ifdef _WIN32
FILETIME ft;
HANDLE h = CreateFile(fileName.c_str(),
FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE) {
SYSTEMTIME st;
memset(&st, 0, sizeof(st));
st.wYear = 1900;
st.wMonth = 1;
st.wDay = 1;
SystemTimeToFileTime(&st, &ft);
SetFileTime(h, &ft, &ft, &ft);
GetLastError();
CloseHandle(h);
}
#else
struct utimbuf times;
times.actime = 0;
times.modtime = 0;
utime(fileName.c_str(), ×);
#endif
}
static void sendFlagNegotiation()
{
char msg[MaxPacketLen];
FlagTypeMap::iterator i;
char *buf = msg;
/* Send MsgNegotiateFlags to the server with
* the abbreviations for all the flags we support.
*/
for (i = FlagType::getFlagMap().begin();
i != FlagType::getFlagMap().end(); i++) {
buf = (char*) i->second->pack(buf);
}
serverLink->send(MsgNegotiateFlags, buf - msg, msg);
}
#if defined(FIXME) && defined(ROBOT)
static void saveRobotInfo(Playerid id, void *msg)
{
for (int i = 0; i < numRobots; i++)
if (robots[i]) {
void *tmpbuf = msg;
uint16_t team, type, wins, losses, tks;
char callsign[CallSignLen];
char email[EmailLen];
tmpbuf = nboUnpackUShort(tmpbuf, type);
tmpbuf = nboUnpackUShort(tmpbuf, team);
tmpbuf = nboUnpackUShort(tmpbuf, wins);
tmpbuf = nboUnpackUShort(tmpbuf, losses);
tmpbuf = nboUnpackUShort(tmpbuf, tks);
tmpbuf = nboUnpackString(tmpbuf, callsign, CallSignLen);
tmpbuf = nboUnpackString(tmpbuf, email, EmailLen);
std::cerr << "id " << id.port << ':' <<
id.number << ':' <<
callsign << ' ' <<
robots[i]->getId().port << ':' <<
robots[i]->getId().number << ':' <<
robots[i]->getCallsign() << std::endl;
if (strncmp(robots[i]->getCallSign(), callsign, CallSignLen)) {
// check for real robot id
char buffer[100];
snprintf(buffer, 100, "id test %p %p %p %8.8x %8.8x\n",
robots[i], tmpbuf, msg, *(int *)tmpbuf, *((int *)tmpbuf + 1));
std::cerr << buffer;
if (tmpbuf < (char *)msg + len) {
PlayerId id;
tmpbuf = nboUnpackUByte(tmpbuf, id);
robots[i]->id.serverHost = id.serverHost;
robots[i]->id.port = id.port;
robots[i]->id.number = id.number;
robots[i]->server->send(MsgIdAck, 0, NULL);
}
}
}
}
#endif
static void resetServerVar(const std::string& name, void*)
{
// reset server-side variables
if (BZDB.getPermission(name) == StateDatabase::Locked) {
const std::string defval = BZDB.getDefault(name);
BZDB.set(name, defval);
}
}
void leaveGame()
{
entered = false;
joiningGame = false;
// no more radar
// radar->setWorld(NULL);
// controlPanel->setRadarRenderer(NULL);
/*
delete radar;
radar = NULL;
*/
#if defined(ROBOT)
// shut down robot connections
int i;
for (i = 0; i < numRobots; i++) {
if (robots[i] && robotServer[i])
robotServer[i]->send(MsgExit, 0, NULL);
delete robots[i];
delete robotServer[i];
robots[i] = NULL;
robotServer[i] = NULL;
}
numRobots = 0;
const int count = obstacleList.size();
for (i = 0; i < count; i++)
delete obstacleList[i];
obstacleList.clear();
#endif
// my tank goes away
const bool sayGoodbye = (myTank != NULL);
LocalPlayer::setMyTank(NULL);
delete myTank;
myTank = NULL;
// reset the daylight time
const bool syncTime = (BZDB.eval(StateDatabase::BZDB_SYNCTIME) >=
0.0f);
const bool fixedTime = BZDB.isSet("fixedTime");
if (syncTime) {
// return to the desired user time
epochOffset = userTimeEpochOffset;
}
else if (fixedTime) {
// save the current user time
userTimeEpochOffset = epochOffset;
}
else {
// revert back to when the client was started?
epochOffset = userTimeEpochOffset;
}
updateDaylight(epochOffset, *sceneRenderer);
lastEpochOffset = epochOffset;
BZDB.set(StateDatabase::BZDB_SYNCTIME,
BZDB.getDefault(StateDatabase::BZDB_SYNCTIME));
// flush downloaded textures (before the BzMaterials are nuked)
Downloads::removeTextures();
// delete world
World::setWorld(NULL);
delete world;
world = NULL;
teams = NULL;
curMaxPlayers = 0;
numFlags = 0;
player = NULL;
// update UI
hud->setPlaying(false);
hud->setCracks(false);
hud->setPlayerHasHighScore(false);
hud->setTeamHasHighScore(false);
hud->setHeading(0.0f);
hud->setAltitude(0.0f);
hud->setAltitudeTape(false);
// shut down server connection
if (sayGoodbye) serverLink->send(MsgExit, 0, NULL);
ServerLink::setServer(NULL);
delete serverLink;
serverLink = NULL;
// reset viewpoint
float eyePoint[3], targetPoint[3];
eyePoint[0] = 0.0f;
eyePoint[1] = 0.0f;
eyePoint[2] = 0.0f + BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT);
targetPoint[0] = eyePoint[0] - 1.0f;
targetPoint[1] = eyePoint[1] + 0.0f;
targetPoint[2] = eyePoint[2] + 0.0f;
sceneRenderer->getViewFrustum().setProjection((float)(60.0 * M_PI /
180.0),
NearPlaneNormal,
FarPlaneDefault,
FarDeepPlaneDefault,
mainWindow->getWidth(),
mainWindow->getHeight(),
mainWindow->getViewHeight());
sceneRenderer->getViewFrustum().setView(eyePoint, targetPoint);
// reset some flags
gameOver = false;
serverError = false;
serverDied = false;
// delete scene database (after the world has been destroyed)
sceneRenderer->setSceneDatabase(NULL);
// reset the BZDB variables
BZDB.iterate(resetServerVar, NULL);
return;
}
static void joinInternetGame(const struct in_addr *inAddress)
{
// get server address
Address serverAddress(*inAddress);
if (serverAddress.isAny()) {
HUDDialogStack::get()->setFailedMessage("Server not found");
return;
}
// check for a local server block
ServerAccessList.reload();
std::vector<std::string> nameAndIp;
nameAndIp.push_back(startupInfo.serverName);
nameAndIp.push_back(serverAddress.getDotNotation());
if (!ServerAccessList.authorized(nameAndIp)) {
HUDDialogStack::get()->setFailedMessage("Server Access Denied
Locally");
std::string msg = ColorStrings[WhiteColor];
msg += "NOTE: ";
msg += ColorStrings[GreyColor];
msg += "server access is controlled by ";
msg += ColorStrings[YellowColor];
msg += ServerAccessList.getFileName();
addMessage(NULL, msg);
return;
}
// open server
ServerLink* _serverLink = new ServerLink(serverAddress,
startupInfo.serverPort);
#if defined(ROBOT)
int i, j;
for (i = 0, j = 0; i < numRobotTanks; i++) {
robotServer[j] = new ServerLink(serverAddress,
startupInfo.serverPort);
if (!robotServer[j] || robotServer[j]->getState() !=
ServerLink::Okay) {
delete robotServer[j];
continue;
}
j++;
}
numRobots = j;
#endif
serverLink = _serverLink;
// assume everything's okay for now
serverDied = false;
serverError = false;
if (!serverLink) {
HUDDialogStack::get()->setFailedMessage("Memory error");
return;
}
// printError("Join Game");
// check server
if (serverLink->getState() != ServerLink::Okay) {
switch (serverLink->getState()) {
case ServerLink::BadVersion: {
static char versionError[] = "Incompatible server version XXXXXXXX";
strncpy(versionError + strlen(versionError) - 8,
serverLink->getVersion(), 8);
HUDDialogStack::get()->setFailedMessage(versionError);
break;
}
// you got banned
case ServerLink::Refused:{
const std::string& rejmsg = serverLink->getRejectionMessage();
// add to the HUD
std::string msg = ColorStrings[RedColor];
msg += "You have been banned from this server";
HUDDialogStack::get()->setFailedMessage(msg.c_str());
// add to the console
msg = ColorStrings[RedColor];
msg += "You have been banned from this server:";
addMessage(NULL, msg);
msg = ColorStrings[YellowColor];
msg += rejmsg;
addMessage(NULL, msg);
break;
}
case ServerLink::Rejected:
// the server is probably full or the game is over. if not then
// the server is having network problems.
HUDDialogStack::get()->setFailedMessage
("Game is full or over. Try again later.");
break;
case ServerLink::SocketError:
HUDDialogStack::get()->setFailedMessage("Error connecting to
server.");
break;
case ServerLink::CrippledVersion:
// can't connect to (otherwise compatible) non-crippled server
HUDDialogStack::get()->setFailedMessage
("Cannot connect to full version server.");
break;
default:
HUDDialogStack::get()->setFailedMessage
(TextUtils::format
("Internal error connecting to server (error code %d).",
serverLink->getState()).c_str());
break;
}
return;
}
#ifdef HAVE_KRB5
// Sending our credential to the server
ClientAuthentication::sendCredential(*serverLink);
#endif
// use parallel UDP if desired and using server relay
if (startupInfo.useUDPconnection)
serverLink->sendUDPlinkRequest();
else
printError("No UDP connection, see Options to enable.");
HUDDialogStack::get()->setFailedMessage("Connection Established...");
sendFlagNegotiation();
joiningGame = true;
scoreboard->huntReset();
GameTime::reset();
}
static void addVarToAutoComplete(const std::string& name, void*
/*userData*/)
{
if ((name.size() <= 0) || (name[0] != '_')) {
return; // we're skipping "poll"
}
if (BZDB.getPermission(name) == StateDatabase::Server) {
completer.registerWord(name);
}
return;
}
static void joinInternetGame2()
{
justJoined = true;
HUDDialogStack::get()->setFailedMessage("Entering game...");
ServerLink::setServer(serverLink);
World::setWorld(world);
// prep teams
teams = world->getTeams();
// prep players
curMaxPlayers = 0;
player = world->getPlayers();
// reset the autocompleter
completer.setDefaults();
BZDB.iterate(addVarToAutoComplete, NULL);
// prep flags
numFlags = world->getMaxFlags();
// make scene database
setSceneDatabase();
mainWindow->getWindow()->yieldCurrent();
// make radar
// radar = new RadarRenderer(*sceneRenderer, *world);
// mainWindow->getWindow()->yieldCurrent();
radar->setWorld(world);
controlPanel->setRadarRenderer(radar);
controlPanel->resize();
// make local player
myTank = new LocalPlayer(serverLink->getId(), startupInfo.callsign,
startupInfo.email);
myTank->setTeam(startupInfo.team);
LocalPlayer::setMyTank(myTank);
if (world->allowRabbit() && myTank->getTeam() != ObserverTeam)
myTank->setTeam(RogueTeam);
// tell server we want to join
serverLink->sendEnter(TankPlayer, myTank->getTeam(),
myTank->getCallSign(),
myTank->getEmailAddress(),
startupInfo.token);
startupInfo.token[0] = '\0';
// hopefully it worked! pop all the menus.
HUDDialogStack* stack = HUDDialogStack::get();
while (stack->isActive())
stack->pop();
joiningGame = false;
}
static void renderDialog()
{
if (HUDDialogStack::get()->isActive()) {
const int width = mainWindow->getWidth();
const int height = mainWindow->getHeight();
const int ox = mainWindow->getOriginX();
const int oy = mainWindow->getOriginY();
glScissor(ox, oy, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
OpenGLGState::resetState();
HUDDialogStack::get()->render();
glPopMatrix();
}
}
static void checkDirtyControlPanel(ControlPanel *cp)
{
if (cp) {
if (HUDDialogStack::get()->isActive()) {
cp->invalidate();
}
}
return;
}
static int getZoomFactor()
{
if (!BZDB.isSet("displayZoom")) return 1;
const int zoom = atoi(BZDB.get("displayZoom").c_str());
if (zoom < 1) return 1;
if (zoom > 8) return 8;
return zoom;
}
static void drawUI()
{
// setup the triangle counts (FIXME: hackish)
if (showFPS && showDrawTime) {
hud->setFrameTriangleCount(sceneRenderer->getFrameTriangleCount());
// NOTE: the radar triangle count is actually from the previous
frame
if (radar) {
hud->setFrameRadarTriangleCount(radar->getFrameTriangleCount());
} else {
hud->setFrameRadarTriangleCount(0);
}
} else {
hud->setFrameTriangleCount(0);
hud->setFrameRadarTriangleCount(0);
}
// update the HUD (player list, alerts)
if (hud) {
hud->render(*sceneRenderer);
}
// draw the control panel
if (controlPanel) {
controlPanel->render(*sceneRenderer);
}
// draw the radar
if (radar) {
const bool showBlankRadar = !myTank;
const bool observer = myTank && (myTank->getTeam() ==
ObserverTeam);
radar->render(*sceneRenderer, showBlankRadar, observer);
}
// update the HUD (menus)
renderDialog();
return;
}
//
// stuff to draw a frame
//
static bool trackPlayerShot(Player* target,
float* eyePoint, float* targetPoint)
{
// follow the first shot
if (BZDB.isTrue("trackShots")) {
const int maxShots = target->getMaxShots();
const ShotPath* sp = NULL;
// look for the oldest active shot
float remaining = +MAXFLOAT;
for (int s = 0; s < maxShots; s++) {
const ShotPath* spTmp = target->getShot(s);
if (spTmp != NULL) {
const float t = float(spTmp->getReloadTime() -
(spTmp->getCurrentTime() - spTmp->getStartTime()));
if ((t > 0.0f) && (t < remaining)) {
sp = spTmp;
remaining = t;
}
}
}
if (sp != NULL) {
const float* pos = sp->getPosition();
const float* vel = sp->getVelocity();
const float speed = sqrtf(vel[0]*vel[0] + vel[1]*vel[1] +
vel[2]*vel[2]);
if (speed > 0.0f) {
const float ilen = 1.0f / speed;
const float dir[3] = {ilen * vel[0], ilen * vel[1], ilen * vel[2]};
float topDir[3] = {1.0f, 0.0f, 0.0f};
const float hlen = sqrtf(dir[0]*dir[0] + dir[1]*dir[1]);
if (hlen > 0.0f) {
topDir[2] = hlen;
const float hfactor = -fabsf(dir[2] / hlen);
topDir[0] = hfactor * dir[0];
topDir[1] = hfactor * dir[1];
}
const float offset = -10.0f;
const float tOffset = +2.0f;
eyePoint[0] = pos[0] + (offset * dir[0]) + (tOffset * topDir[0]);
eyePoint[1] = pos[1] + (offset * dir[1]) + (tOffset * topDir[1]);
eyePoint[2] = pos[2] + (offset * dir[2]) + (tOffset * topDir[2]);
targetPoint[0] = eyePoint[0] + dir[0];
targetPoint[1] = eyePoint[1] + dir[1];
targetPoint[2] = eyePoint[2] + dir[2];
return true;
}
}
}
return false;
}
static void setupNearPlane()
{
NearPlane = NearPlaneNormal;
const bool showTreads = BZDB.isTrue("showTreads");
if (!showTreads || !myTank) {
return;
}
const Player* tank = myTank;
if (ROAM.isRoaming()) {
if (ROAM.getMode() != Roaming::roamViewFP) {
return;
}
if (!devDriving) {
tank = ROAM.getTargetTank();
}
}
if (tank == NULL) {
return;
}
const float halfLength = 0.5f * BZDBCache::tankLength;
const float length = tank->getDimensions()[1];
if (fabsf(length - halfLength) > 0.1f) {
NearPlane = NearPlaneClose;
}
return;
}
static void setupFarPlane()
{
FarPlane = FarPlaneScale * BZDBCache::worldSize;
FarPlaneCull = false;
FarDeepPlane = FarPlane * FarDeepPlaneScale;
const bool mapFog = (BZDB.get(StateDatabase::BZDB_FOGMODE) != "none")
&&
BZDB.isTrue("fogEffect");
float farDist = FarPlane;
if (mapFog &&
(BZDB.get("_cullDist") == "fog") && !BZDB.isTrue("_fogNoSky")) {
const float fogMargin = 1.01f;
const std::string& fogMode = BZDB.get("_fogMode");
if (fogMode == "linear") {
farDist = fogMargin * BZDB.eval("_fogEnd");
} else {
const float density = BZDB.eval("_fogDensity");
if (density > 0.0f) {
const float fogFactor = 0.01f;
if (fogMode == "exp2") {
farDist = fogMargin * sqrtf(-logf(fogFactor)) / density;
} else { // default to 'exp'
farDist = fogMargin * -logf(fogFactor) / density;
}
} else {
// default far plane
}
}
} else {
const float dist = BZDB.eval("_cullDist");
if (!isnan(dist) && (dist > 0.0f)) {
farDist = dist;
} else {
// default far plane
}
}
if (farDist < FarPlane) {
FarPlane = farDist;
FarPlaneCull = true;
}
return;
}
void drawFrame(const float dt)
{
// get view type (constant for entire game)
static SceneRenderer::ViewType viewType =
sceneRenderer->getViewType();
// get media object
static BzfMedia* media = PlatformFactory::getMedia();
static const float defaultPos[3] = { 0.0f, 0.0f, 0.0f };
static const float defaultDir[3] = { 1.0f, 0.0f, 0.0f };
static int frameCount = 0;
static float cumTime = 0.0f;
const float* myTankPos;
const float* myTankDir;
GLfloat fov;
GLfloat eyePoint[3];
GLfloat targetPoint[3];
int i;
checkDirtyControlPanel(controlPanel);
if (!unmapped) {
// compute fps
frameCount++;
cumTime += float(dt);
if (cumTime >= 2.0) {
if (showFPS) hud->setFPS(float(frameCount) / cumTime);
cumTime = 0.00000001f;
frameCount = 0;
}
// drift clouds
sceneRenderer->getBackground()->addCloudDrift(1.0f * dt, 0.731f *
dt);
// get tank camera info
float muzzleHeight;
if (!myTank) {
myTankPos = defaultPos;
myTankDir = defaultDir;
muzzleHeight = BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT);
fov = 60.0f;
} else {
myTankPos = myTank->getPosition();
myTankDir = myTank->getForward();
muzzleHeight = myTank->getMuzzleHeight();
if (myTank->getFlag() == Flags::WideAngle) {
fov = 120.0f;
} else {
fov = BZDB.eval("displayFOV");
}
if (viewType == SceneRenderer::ThreeChannel) {
fov *= 0.75f;
}
}
fov *= (float)(M_PI / 180.0);
// set projection and view
eyePoint[0] = myTankPos[0];
eyePoint[1] = myTankPos[1];
eyePoint[2] = myTankPos[2] + muzzleHeight;
targetPoint[0] = eyePoint[0] + myTankDir[0];
targetPoint[1] = eyePoint[1] + myTankDir[1];
targetPoint[2] = eyePoint[2] + myTankDir[2];
if (devDriving || ROAM.isRoaming()) {
hud->setAltitude(-1.0f);
float roamViewAngle;
const Roaming::RoamingCamera* roam = ROAM.getCamera();
if (!(ROAM.getMode() == Roaming::roamViewFree) &&
(ROAM.getTargetTank() || (devDriving && myTank))) {
Player *target;
if (!devDriving) {
target = ROAM.getTargetTank();
} else {
target = myTank;
}
const float *targetTankDir = target->getForward();
// fixed camera tracking target
if (ROAM.getMode() == Roaming::roamViewTrack) {
eyePoint[0] = roam->pos[0];
eyePoint[1] = roam->pos[1];
eyePoint[2] = roam->pos[2];
targetPoint[0] = target->getPosition()[0];
targetPoint[1] = target->getPosition()[1];
targetPoint[2] = target->getPosition()[2] +
target->getMuzzleHeight();
}
// camera following target
else if (ROAM.getMode() == Roaming::roamViewFollow) {
if (!trackPlayerShot(target, eyePoint, targetPoint)) {
eyePoint[0] = target->getPosition()[0] - targetTankDir[0] * 40;
eyePoint[1] = target->getPosition()[1] - targetTankDir[1] * 40;
eyePoint[2] = target->getPosition()[2] + muzzleHeight * 6;
targetPoint[0] = target->getPosition()[0];
targetPoint[1] = target->getPosition()[1];
targetPoint[2] = target->getPosition()[2];
}
}
// target's view
else if (ROAM.getMode() == Roaming::roamViewFP) {
if (!trackPlayerShot(target, eyePoint, targetPoint)) {
eyePoint[0] = target->getPosition()[0];
eyePoint[1] = target->getPosition()[1];
eyePoint[2] = target->getPosition()[2] +
target->getMuzzleHeight();
targetPoint[0] = eyePoint[0] + targetTankDir[0];
targetPoint[1] = eyePoint[1] + targetTankDir[1];
targetPoint[2] = eyePoint[2] + targetTankDir[2];
hud->setAltitude(target->getPosition()[2]);
}
}
// track team flag
else if (ROAM.getMode() == Roaming::roamViewFlag) {
Flag* targetFlag = ROAM.getTargetFlag();
eyePoint[0] = roam->pos[0];
eyePoint[1] = roam->pos[1];
eyePoint[2] = roam->pos[2];
targetPoint[0] = targetFlag->position[0];
targetPoint[1] = targetFlag->position[1];
targetPoint[2] = targetFlag->position[2];
if (targetFlag->status != FlagOnTank) {
targetPoint[2] += muzzleHeight;
} else {
targetPoint[2] -= (BZDBCache::tankHeight -
BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT));
}
}
roamViewAngle = (float) (atan2(targetPoint[1]-eyePoint[1],
targetPoint[0]-eyePoint[0]) * 180.0f / M_PI);
}
// free Roaming
else {
float dir[3];
dir[0] = cosf((float)(roam->phi * M_PI / 180.0)) *
cosf((float)(roam->theta * M_PI / 180.0));
dir[1] = cosf((float)(roam->phi * M_PI / 180.0)) *
sinf((float)(roam->theta * M_PI / 180.0));
dir[2] = sinf((float)(roam->phi * M_PI / 180.0));
eyePoint[0] = roam->pos[0];
eyePoint[1] = roam->pos[1];
eyePoint[2] = roam->pos[2];
targetPoint[0] = eyePoint[0] + dir[0];
targetPoint[1] = eyePoint[1] + dir[1];
targetPoint[2] = eyePoint[2] + dir[2];
roamViewAngle = roam->theta;
}
if (!devDriving) {
float virtPos[] = {eyePoint[0], eyePoint[1], 0};
if (myTank) {
myTank->move(virtPos, (float)(roamViewAngle * M_PI / 180.0));
}
}
fov = (float)(roam->zoom * M_PI / 180.0);
moveSoundReceiver(eyePoint[0], eyePoint[1], eyePoint[2], 0.0,
false);
}
// only use a close plane for drawing in the
// cockpit, and even then only for odd sized tanks
setupNearPlane();
// based on fog and _cullDist
setupFarPlane();
ViewFrustum& viewFrustum = sceneRenderer->getViewFrustum();
viewFrustum.setProjection(fov, NearPlane, FarPlane, FarDeepPlane,
mainWindow->getWidth(),
mainWindow->getHeight(),
mainWindow->getViewHeight());
viewFrustum.setFarPlaneCull(FarPlaneCull);
viewFrustum.setView(eyePoint, targetPoint);
// add dynamic nodes
SceneDatabase* scene = sceneRenderer->getSceneDatabase();
if (scene && myTank) {
const bool seerView = (myTank->getFlag() == Flags::Seer);
const bool showTreads = BZDB.isTrue("showTreads");
// add my tank if required
const bool inCockpit = (!devDriving || (ROAM.getMode() ==
Roaming::roamViewFP));
const bool showMyTreads = showTreads ||
(devDriving && (ROAM.getMode() != Roaming::roamViewFP));
myTank->addToScene(scene, myTank->getTeam(),
inCockpit, seerView,
showMyTreads, showMyTreads /*showIDL*/);
// add my shells
myTank->addShots(scene, false);
// add server shells
if (world) {
world->getWorldWeapons()->addShots(scene, false);
}
// add antidote flag
myTank->addAntidote(scene);
// add flags
world->addFlags(scene, seerView);
// add other tanks and shells
for (i = 0; i < curMaxPlayers; i++) {
if (player[i]) {
const bool colorblind = (myTank->getFlag() ==
Flags::Colorblindness);
player[i]->addShots(scene, colorblind);
TeamColor effectiveTeam = RogueTeam;
if (!colorblind){
if ((player[i]->getFlag() == Flags::Masquerade)
&& (myTank->getFlag() != Flags::Seer)
&& (myTank->getTeam() != ObserverTeam)) {
effectiveTeam = myTank->getTeam();
}
else {
effectiveTeam = player[i]->getTeam();
}
}
const bool inCockpt = ROAM.isRoaming() && !devDriving &&
(ROAM.getMode() == Roaming::roamViewFP) &&
ROAM.getTargetTank() &&
(ROAM.getTargetTank()->getId() == i);
const bool showPlayer = !inCockpt || showTreads;
// add player tank if required
player[i]->addToScene(scene, effectiveTeam,
inCockpt, seerView,
showPlayer, showPlayer /*showIDL*/);
}
}
// add explosions
addExplosions(scene);
// if inside a building, add some eighth dimension scene nodes.
const std::vector<const Obstacle*>& list =
myTank->getInsideBuildings();
for (unsigned int n = 0; n < list.size(); n++) {
const Obstacle* obs = list[n];
const int nodeCount = obs->getInsideSceneNodeCount();
SceneNode** nodeList = obs->getInsideSceneNodeList();
for (int o = 0; o < nodeCount; o++) {
scene->addDynamicNode(nodeList[o]);
}
}
}
// turn blanking and inversion on/off as appropriate
// turn on scene dimming when showing menu, when we're dead
// and no longer exploding, or when we are in a building.
bool insideDim = false;
if (myTank) {
const float hnp = 0.5f * NearPlane; // half near plane distance
const float* eye = viewFrustum.getEye();
const float* dir = viewFrustum.getDirection();
float clipPos[3];
clipPos[0] = eye[0] + (dir[0] * hnp);
clipPos[1] = eye[1] + (dir[1] * hnp);
clipPos[2] = eye[2];
const Obstacle* obs;
obs = world->inBuilding(clipPos, myTank->getAngle(), hnp, 0.0f,
0.0f);
if (obs != NULL) {
insideDim = true;
}
}
sceneRenderer->setDim(HUDDialogStack::get()->isActive() ||
insideDim ||
(myTank && !ROAM.isRoaming() && !devDriving) &&
!myTank->isAlive() && !myTank->isExploding());
// turn on panel dimming when showing the menu (both radar and
chat)
if (HUDDialogStack::get()->isActive()) {
if (controlPanel) {
controlPanel->setDimming(0.8f);
}
if (radar) {
radar->setDimming(0.8f);
}
} else {
if (controlPanel) {
controlPanel->setDimming(0.0f);
}
if (radar) {
radar->setDimming(0.0f);
}
}
// set hud state
hud->setDim(HUDDialogStack::get()->isActive());
hud->setPlaying(myTank && (myTank->isAlive() &&
!myTank->isPaused()));
hud->setRoaming(ROAM.isRoaming());
hud->setCracks(myTank && !firstLife && !justJoined &&
!myTank->isAlive());
// get frame start time
if (showDrawTime) {
#if defined(DEBUG_RENDERING)
// get an accurate measure of frame time (at expense of frame
rate)
if (BZDB.isTrue("glFinish")) {
glFinish();
}
#endif
media->stopwatch(true);
}
// so observers can have enhanced radar
if (ROAM.isRoaming() && myTank && !devDriving) {
if (ROAM.getMode() == Roaming::roamViewFP &&
ROAM.getTargetTank())
myTank->setZpos(ROAM.getTargetTank()->getPosition()[2]);
else
myTank->setZpos(ROAM.getCamera()->pos[2]);
}
// draw frame
if (viewType == SceneRenderer::ThreeChannel) {
// draw center channel
sceneRenderer->render(false);
drawUI();
// set up for drawing left channel
mainWindow->setQuadrant(MainWindow::LowerLeft);
// FIXME -- this assumes up is along +z
const float cFOV = cosf(fov);
const float sFOV = sinf(fov);
targetPoint[0] = eyePoint[0] + cFOV*myTankDir[0] -
sFOV*myTankDir[1];
targetPoint[1] = eyePoint[1] + cFOV*myTankDir[1] +
sFOV*myTankDir[0];
targetPoint[2] = eyePoint[2] + myTankDir[2];
viewFrustum.setView(eyePoint, targetPoint);
// draw left channel
sceneRenderer->render(false, true, true);
// set up for drawing right channel
mainWindow->setQuadrant(MainWindow::LowerRight);
// FIXME -- this assumes up is along +z
targetPoint[0] = eyePoint[0] + cFOV*myTankDir[0] +
sFOV*myTankDir[1];
targetPoint[1] = eyePoint[1] + cFOV*myTankDir[1] -
sFOV*myTankDir[0];
targetPoint[2] = eyePoint[2] + myTankDir[2];
viewFrustum.setView(eyePoint, targetPoint);
// draw right channel
sceneRenderer->render(true, true, true);
#if defined(DEBUG_RENDERING)
// set up for drawing rear channel
mainWindow->setQuadrant(MainWindow::UpperLeft);
// FIXME -- this assumes up is along +z
targetPoint[0] = eyePoint[0] - myTankDir[0];
targetPoint[1] = eyePoint[1] - myTankDir[1];
targetPoint[2] = eyePoint[2] + myTankDir[2];
viewFrustum.setView(eyePoint, targetPoint);
// draw rear channel
sceneRenderer->render(true, true, true);
#endif
// back to center channel
mainWindow->setQuadrant(MainWindow::UpperRight);
} else if (viewType == SceneRenderer::Stacked) {
float EyeDisplacement = 0.25f * BZDBCache::tankWidth;
float FocalPlane = BZDB.eval(StateDatabase::BZDB_BOXBASE);
if (BZDB.isSet("eyesep"))
EyeDisplacement = BZDB.eval("eyesep");
if (BZDB.isSet("focal"))
FocalPlane = BZDB.eval("focal");
// setup view for left eye
viewFrustum.setOffset(EyeDisplacement, FocalPlane);
// draw left eye's view
sceneRenderer->render(false);
drawUI();
// set up view for right eye
mainWindow->setQuadrant(MainWindow::UpperHalf);
viewFrustum.setOffset(-EyeDisplacement, FocalPlane);
// draw right eye's view
sceneRenderer->render(true, true);
drawUI();
// draw common stuff
// back to left channel
mainWindow->setQuadrant(MainWindow::LowerHalf);
} else if (viewType == SceneRenderer::Stereo) {
float EyeDisplacement = 0.25f * BZDBCache::tankWidth;
float FocalPlane = BZDB.eval(StateDatabase::BZDB_BOXBASE);
if (BZDB.isSet("eyesep"))
EyeDisplacement = BZDB.eval("eyesep");
if (BZDB.isSet("focal"))
FocalPlane = BZDB.eval("focal");
// setup view for left eye
#ifdef USE_GL_STEREO
glDrawBuffer(GL_BACK_LEFT);
#endif
viewFrustum.setOffset(EyeDisplacement, FocalPlane);
// draw left eye's view
sceneRenderer->render(false);
#ifndef USE_GL_STEREO
drawUI();
#endif
// set up view for right eye
#ifdef USE_GL_STEREO
glDrawBuffer(GL_BACK_RIGHT);
#else
mainWindow->setQuadrant(MainWindow::UpperLeft);
#endif
viewFrustum.setOffset(-EyeDisplacement, FocalPlane);
// draw right eye's view
sceneRenderer->render(true, true);
#ifndef USE_GL_STEREO
drawUI();
#endif
// draw common stuff
#ifdef USE_GL_STEREO
glDrawBuffer(GL_BACK);
drawUI();
#endif
#ifndef USE_GL_STEREO
// back to left channel
mainWindow->setQuadrant(MainWindow::UpperRight);
#endif
} else if (viewType == SceneRenderer::Anaglyph) {
float EyeDisplacement = 0.25f * BZDBCache::tankWidth;
float FocalPlane = BZDB.eval(StateDatabase::BZDB_BOXBASE);
if (BZDB.isSet("eyesep"))
EyeDisplacement = BZDB.eval("eyesep");
if (BZDB.isSet("focal"))
FocalPlane = BZDB.eval("focal");
// setup view for left eye
glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
viewFrustum.setOffset(EyeDisplacement, FocalPlane);
// draw left eye's view
sceneRenderer->render(false);
drawUI();
// set up view for right eye
glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_FALSE);
// for red/blue to somewhat work ...
//glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
viewFrustum.setOffset(-EyeDisplacement, FocalPlane);
// draw right eye's view
sceneRenderer->render(true, true);
drawUI();
} else if (viewType == SceneRenderer::Interlaced) {
float EyeDisplacement = 0.25f * BZDBCache::tankWidth;
float FocalPlane = BZDB.eval(StateDatabase::BZDB_BOXBASE);
const int width = mainWindow->getWidth();
const int height = mainWindow->getHeight();
if (BZDB.isSet("eyesep"))
EyeDisplacement = BZDB.eval("eyesep");
if (BZDB.isSet("focal"))
FocalPlane = BZDB.eval("focal");
if (BZDBCache::stencilShadows) {
BZDB.set("stencilShadows", "0");
addMessage(NULL, "Disabled stencilShadows for interlaced mode");
}
OpenGLGState::resetState();
// enable stencil test
glEnable(GL_STENCIL_TEST);
// clear stencil
glClearStencil(0);
// Clear color and stencil buffer
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// All drawing commands fail the stencil test, and are not
// drawn, but increment the value in the stencil buffer.
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
glColor3f(1.0f, 1.0f, 1.0f);
for (int y=0;y<=height;y+=2) {
glBegin(GL_LINES);
glVertex2i(0, y);
glVertex2i(width, y);
glEnd();
}
// draw except where the stencil pattern is 0x1
// do not change the stencil buffer
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// setup view for left eye
viewFrustum.setOffset(EyeDisplacement, FocalPlane);
// draw left eye's view
sceneRenderer->render(false);
// draw where the stencil pattern is 0x1
// do not change the stencil buffer
glStencilFunc(GL_EQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// set up view for right eye
viewFrustum.setOffset(-EyeDisplacement, FocalPlane);
// draw right eye's view
sceneRenderer->render(true, true);
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
drawUI();
} else {
const int zoomFactor = getZoomFactor();
if (zoomFactor != 1) {
// draw small out-the-window view
mainWindow->setQuadrant(MainWindow::ZoomRegion);
const int x = mainWindow->getOriginX();
const int y = mainWindow->getOriginY();
const int w = mainWindow->getWidth();
const int h = mainWindow->getHeight();
const int vh = mainWindow->getViewHeight();
viewFrustum.setProjection(fov, NearPlane, FarPlane, FarDeepPlane, w,
h, vh);
sceneRenderer->render();
// set entire window
mainWindow->setQuadrant(MainWindow::FullWindow);
glScissor(mainWindow->getOriginX(), mainWindow->getOriginY(),
mainWindow->getWidth(), mainWindow->getHeight());
// set pixel copy destination
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.25, (GLdouble)mainWindow->getWidth() - 0.25,
-0.25, (GLdouble)mainWindow->getHeight() - 0.25, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glRasterPos2i(0, 0);
glPopMatrix();
// zoom small image to entire window
glDisable(GL_DITHER);
glPixelZoom((float)zoomFactor, (float)zoomFactor);
glCopyPixels(x, y, w, h, GL_COLOR);
glPixelZoom(1.0f, 1.0f);
if (BZDB.isTrue("dither")) glEnable(GL_DITHER);
} else {
// normal rendering
sceneRenderer->render();
}
// draw other stuff
drawUI();
}
// get frame end time
if (showDrawTime) {
#if defined(DEBUG_RENDERING)
// get an accurate measure of frame time (at expense of frame
rate)
if (BZDB.isTrue("glFinish")) {
glFinish();
}
#endif
hud->setDrawTime((float)media->stopwatch(false));
}
// draw a fake cursor if requested. this is mostly intended for
// pass through 3D cards that don't have cursor support.
if (BZDB.isTrue("fakecursor")) {
int mx, my;
const int width = mainWindow->getWidth();
const int height = mainWindow->getHeight();
const int ox = mainWindow->getOriginX();
const int oy = mainWindow->getOriginY();
mainWindow->getWindow()->getMouse(mx, my);
my = height - my - 1;
glScissor(ox, oy, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glColor3f(0.0f, 0.0f, 0.0f);
glRecti(mx - 8, my - 2, mx - 2, my + 2);
glRecti(mx + 2, my - 2, mx + 8, my + 2);
glRecti(mx - 2, my - 8, mx + 2, my - 2);
glRecti(mx - 2, my + 2, mx + 2, my + 8);
glColor3f(1.0f, 1.0f, 1.0f);
glRecti(mx - 7, my - 1, mx - 3, my + 1);
glRecti(mx + 3, my - 1, mx + 7, my + 1);
glRecti(mx - 1, my - 7, mx + 1, my - 3);
glRecti(mx - 1, my + 3, mx + 1, my + 7);
glPopMatrix();
}
mainWindow->getWindow()->swapBuffers();
// remove dynamic nodes from this frame
if (scene) {
scene->removeDynamicNodes();
}
} else {
// wait around a little to avoid spinning the CPU when iconified
TimeKeeper::sleep(0.05f);
}
}
static void setupRoamingCamera(float dt)
{
static Roaming::RoamingCamera prevDeltaCamera;
static bool inited = false;
if (!inited) {
memset(&prevDeltaCamera, 0, sizeof(Roaming::RoamingCamera));
inited = true;
}
Roaming::RoamingCamera deltaCamera;
memset(&deltaCamera, 0, sizeof(Roaming::RoamingCamera));
// move roaming camera
if (myTank) {
bool control = ((shiftKeyStatus & BzfKeyEvent::ControlKey) != 0);
bool alt = ((shiftKeyStatus & BzfKeyEvent::AltKey) != 0);
bool shift = ((shiftKeyStatus & BzfKeyEvent::ShiftKey) != 0);
if (display->hasGetKeyMode()) {
display->getModState (shift, control, alt);
}
if (!control && !shift) {
deltaCamera.pos[0] = (float)(4 * myTank->getSpeed()) *
BZDBCache::tankSpeed;
}
if (alt) {
deltaCamera.pos[1] = (float)(4 * myTank->getRotation()) *
BZDBCache::tankSpeed;
} else {
deltaCamera.theta = ROAM.getZoom() *
(float)myTank->getRotation();
}
if (control) {
deltaCamera.phi = -2.0f * ROAM.getZoom() / 3.0f *
(float)myTank->getSpeed();
}
if (shift) {
deltaCamera.pos[2] = (float)(-4 * myTank->getSpeed()) *
BZDBCache::tankSpeed;
}
}
// adjust for slow keyboard
if (BZDB.isTrue("slowKeyboard")) {
float st = BZDB.eval("roamSmoothTime");
if (st < 0.1f) {
st = 0.1f;
}
const float at = (dt / st);
const float bt = 1.0f - at;
deltaCamera.pos[0] = (at * deltaCamera.pos[0]) + (bt *
prevDeltaCamera.pos[0]);
deltaCamera.pos[1] = (at * deltaCamera.pos[1]) + (bt *
prevDeltaCamera.pos[1]);
deltaCamera.pos[2] = (at * deltaCamera.pos[2]) + (bt *
prevDeltaCamera.pos[2]);
deltaCamera.theta = (at * deltaCamera.theta) + (bt *
prevDeltaCamera.theta);
deltaCamera.phi = (at * deltaCamera.phi) + (bt *
prevDeltaCamera.phi);
}
deltaCamera.zoom = roamDZoom;
ROAM.updatePosition(&deltaCamera, dt);
// copy the old delta values
memcpy(&prevDeltaCamera, &deltaCamera,
sizeof(Roaming::RoamingCamera));
return;
}
static void prepareTheHUD()
{
// prep the HUD
if (myTank) {
const float* myPos = myTank->getPosition();
hud->setHeading(myTank->getAngle());
hud->setAltitude(myPos[2]);
if (world->allowTeamFlags()) {
// markers for my team flag
for (int i = 0; i < numFlags; i++) {
Flag& flag = world->getFlag(i);
if (flag.type->flagTeam != NoTeam) {
const float* flagPos = flag.position;
float heading = atan2f(flagPos[1] - myPos[1],flagPos[0] - myPos[0]);
hud->addMarker(heading, Team::getTankColor(flag.type->flagTeam));
}
}
}
if (myTank->getAntidoteLocation()) {
// marker for my antidote flag
const GLfloat* antidotePos = myTank->getAntidoteLocation();
float heading = atan2f(antidotePos[1] - myPos[1],
antidotePos[0] - myPos[0]);
const float antidoteColor[] = {1.0f, 1.0f, 0.0f};
hud->addMarker(heading, antidoteColor);
}
for (int i = 0; i < numFlags; i++) {
Flag& flag = world->getFlag(i);
if (flag.type == Flags::Genocide) {
const float* flagPos = flag.position;
float heading = atan2f(flagPos[1] - myPos[1],flagPos[0] - myPos[0]);
hud->addMarker(heading, flag.type->getColor());
}
}
}
return;
}
static void updatePauseCountdown(float dt)
{
if (!myTank) {
pauseCountdown = 0.0f;
}
if (pauseCountdown > 0.0f && !myTank->isAlive()) {
pauseCountdown = 0.0f;
hud->setAlert(1, NULL, 0.0f, true);
}
if (pauseCountdown > 0.0f) {
const int oldPauseCountdown = (int)(pauseCountdown + 0.99f);
pauseCountdown -= dt;
if (1 || pauseCountdown <= 0.0f) {
pauseCountdown = 0.0f;
/* make sure it is really safe to pause.. since the server
* might make us drop our flag, make sure the player is on the
* ground and not in a building. prevents getting kicked
* later for being in places we shouldn't without holding the
* right flags.
*/
if (0 && myTank->getLocation() == LocalPlayer::InBuilding) {
// custom message when trying to pause while in a building
// (could get stuck on un-pause if flag is taken/lost)
hud->setAlert(1, "Can't pause while inside a building", 1.0f, false);
} else if (0 && myTank->getLocation() == LocalPlayer::InAir) {
// custom message when trying to pause when jumping/falling
hud->setAlert(1, "Can't pause when you are in the air", 1.0f, false);
} else if (0 && myTank->getLocation() != LocalPlayer::OnGround &&
myTank->getLocation() != LocalPlayer::OnBuilding) {
// catch-all message when trying to pause when you should not
hud->setAlert(1, "Unable to pause right now", 1.0f, false);
} else if (0 && myTank->isPhantomZoned() ) {
// custom message when trying to pause while zoned
hud->setAlert(1, "Can't pause when you are in the phantom zone", 1.0f,
false);
} else {
// okay, now we pause. first drop any team flag we may have.
const FlagType* flagd = myTank->getFlag();
if (flagd->flagTeam != NoTeam)
serverLink->sendDropFlag(myTank->getPosition());
if (World::getWorld()->allowRabbit() && (myTank->getTeam() ==
RabbitTeam))
serverLink->sendNewRabbit();
// now actually pause
myTank->setPause(true);
hud->setAlert(1, NULL, 0.0f, true);
controlPanel->addMessage("Paused");
// turn off the sound
if (savedVolume == -1) {
savedVolume = getSoundVolume();
setSoundVolume(0);
}
// ungrab mouse
mainWindow->ungrabMouse();
}
} else if ((int)(pauseCountdown + 0.99f) != oldPauseCountdown &&
!pausedByUnmap) {
// update countdown alert
char msgBuf[40];
sprintf(msgBuf, "Pausing in %d", (int)(pauseCountdown + 0.99f));
hud->setAlert(1, msgBuf, 1.0f, false);
}
}
return;
}
static void updateDestructCountdown(float dt)
{
if (!myTank) {
destructCountdown = 0.0f;
}
if (destructCountdown > 0.0f && !myTank->isAlive()) {
destructCountdown = 0.0f;
hud->setAlert(1, NULL, 0.0f, true);
}
if (destructCountdown > 0.0f) {
const int oldDestructCountdown = (int)(destructCountdown + 0.99f);
destructCountdown -= dt;
if (1 || destructCountdown <= 0.0f) { destructCountdown = 0.0f; // now
actually destruct
// now actually destruct
gotBlowedUp( myTank, SelfDestruct, myTank->getId() );
hud->setAlert(1, NULL, 0.0f, true);
} else if ((int)(destructCountdown + 0.99f) !=
oldDestructCountdown) {
// update countdown alert
char msgBuf[40];
sprintf(msgBuf, "Self Destructing in %d", (int)(destructCountdown
+ 0.99f));
hud->setAlert(1, msgBuf, 1.0f, false);
}
}
return;
}
//
// main playing loop
//
static void playingLoop()
{
int i;
mainWindow->setZoomFactor(getZoomFactor());
if (BZDB.isTrue("fakecursor"))
mainWindow->getWindow()->hideMouse();
// start timing
TimeKeeper::setTick();
updateDaylight(epochOffset, *sceneRenderer);
worldDownLoader = new WorldDownLoader;
// main loop
while (!CommandsStandard::isQuit()) {
BZDBCache::update();
// set this step game time
GameTime::setStepTime();
// get delta time
TimeKeeper prevTime = TimeKeeper::getTick();
TimeKeeper::setTick();
const float dt = float(TimeKeeper::getTick() - prevTime);
mainWindow->getWindow()->yieldCurrent();
// handle incoming packets
doMessages();
// see if the world collision grid needs to be updated
if (world) {
world->checkCollisionManager();
}
mainWindow->getWindow()->yieldCurrent();
// try to join a game if requested. do this *before* handling
// events so we do a redraw after the request is posted and
// before we actually try to join.
if (joinRequested) {
// if already connected to a game then first sign off
if (myTank) leaveGame();
// get token if we need to (have a password but no token)
if ((startupInfo.token[0] == '\0')
&& (startupInfo.password[0] != '\0')) {
ServerList* serverList = new ServerList;
serverList->startServerPings(&startupInfo);
// wait no more than 10 seconds for a token
for (int j = 0; j < 40; j++) {
serverList->checkEchos(getStartupInfo());
cURLManager::perform();
if (startupInfo.token[0] != '\0') break;
TimeKeeper::sleep(0.25f);
}
delete serverList;
}
// don't let the bad token specifier slip through to the server,
// just erase it
if (strcmp(startupInfo.token, "badtoken") == 0)
startupInfo.token[0] = '\0';
ares.queryHost(startupInfo.serverName);
waitingDNS = true;
// don't try again
joinRequested = false;
}
if (waitingDNS) {
fd_set readers, writers;
int nfds = -1;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO(&readers);
FD_ZERO(&writers);
ares.setFd(&readers, &writers, nfds);
nfds = select(nfds + 1, (fd_set*)&readers, (fd_set*)&writers, 0,
&timeout);
ares.process(&readers, &writers);
struct in_addr inAddress;
AresHandler::ResolutionStatus status =
ares.getHostAddress(&inAddress);
if (status == AresHandler::Failed) {
HUDDialogStack::get()->setFailedMessage("Server not found");
waitingDNS = false;
} else if (status == AresHandler::HbNSucceeded) {
// now try connecting
joinInternetGame(&inAddress);
waitingDNS = false;
}
}
mainWindow->getWindow()->yieldCurrent();
// handle pending events for some small fraction of time
clockAdjust = 0.0f;
processInputEvents(0.1f);
if (mainWindow->haveJoystick()) {
static const BzfKeyEvent::Button button_map[] = {
BzfKeyEvent::BZ_Button_1,
BzfKeyEvent::BZ_Button_2,
BzfKeyEvent::BZ_Button_3,
BzfKeyEvent::BZ_Button_4,
BzfKeyEvent::BZ_Button_5,
BzfKeyEvent::BZ_Button_6,
BzfKeyEvent::BZ_Button_7,
BzfKeyEvent::BZ_Button_8,
BzfKeyEvent::BZ_Button_9,
BzfKeyEvent::BZ_Button_10,
};
static unsigned long old_buttons = 0;
const int button_count = countof(button_map);
unsigned long new_buttons = mainWindow->getJoyButtonSet();
if (old_buttons != new_buttons)
for (int j = 0; j < button_count; j++) {
if ((old_buttons & (1<<j)) != (new_buttons & (1<<j))) {
BzfKeyEvent ev;
ev.button = button_map[j];
ev.ascii = 0;
ev.shift = 0;
doKey(ev, (new_buttons & (1<<j)) != 0);
}
}
old_buttons = new_buttons;
}
mainWindow->getWindow()->yieldCurrent();
// invoke callbacks
callPlayingCallbacks();
mainWindow->getWindow()->yieldCurrent();
// quick out
if (CommandsStandard::isQuit()) {
break;
}
// if server died then leave the game (note that this may cause
// further server errors but that's okay).
if (serverError ||
(serverLink && serverLink->getState() == ServerLink::Hungup)) {
// if we haven't reported the death yet then do so now
if (serverDied ||
(serverLink && serverLink->getState() == ServerLink::Hungup)) {
printError("Server has unexpectedly disconnected");
}
leaveGame();
}
// update time of day -- update sun and sky every few seconds
float syncTime = BZDB.eval(StateDatabase::BZDB_SYNCTIME);
if (syncTime < 0.0f) {
if (!BZDB.isSet("fixedTime")) {
epochOffset += (double)dt;
}
epochOffset += (double)(50.0f * dt * clockAdjust);
} else {
epochOffset = (double)syncTime;
lastEpochOffset += (double)dt;
}
if (fabs(epochOffset - lastEpochOffset) >= 4.0) {
updateDaylight(epochOffset, *sceneRenderer);
lastEpochOffset = epochOffset;
}
// update the wind
if (world) {
world->updateWind(dt);
}
// move roaming camera
if (ROAM.isRoaming()) {
setupRoamingCamera(dt);
ROAM.buildRoamingLabel();
}
// update test video format timer
if (testVideoFormatTimer > 0.0f) {
testVideoFormatTimer -= dt;
if (testVideoFormatTimer <= 0.0f) {
testVideoFormatTimer = 0.0f;
setVideoFormat(testVideoPrevFormat);
}
}
// update the countdowns
updatePauseCountdown(dt);
updateDestructCountdown(dt);
// notify if input changed
if ((myTank != NULL) && (myTank->queryInputChange() == true)) {
controlPanel->addMessage(
LocalPlayer::getInputMethodName(myTank->getInputMethod()) + "
movement");
}
// update other tank's shots
for (i = 0; i < curMaxPlayers; i++) {
if (player[i]) {
player[i]->updateShots(dt);
}
}
// update servers shots
const World *_world = World::getWorld();
if (_world) {
_world->getWorldWeapons()->updateShots(dt);
}
// update track marks (before any tanks are moved)
TrackMarks::update(dt);
// do dead reckoning on remote players
for (i = 0; i < curMaxPlayers; i++) {
if (player[i]) {
const bool wasNotResponding = player[i]->isNotResponding();
player[i]->doDeadReckoning();
const bool isNotResponding = player[i]->isNotResponding();
if (!wasNotResponding && isNotResponding) {
addMessage(player[i], "not responding");
} else if (wasNotResponding && !isNotResponding) {
addMessage(player[i], "okay");
}
}
}
// do motion
if (myTank) {
if (myTank->isAlive() && !myTank->isPaused()) {
doMotion();
if (scoreboard->getHuntState()==ScoreboardRenderer::HUNT_ENABLED) {
setHuntTarget(); //spot hunt target
}
if (myTank->getTeam() != ObserverTeam &&
((fireButton && myTank->getFlag() == Flags::MachineGun) ||
(myTank->getFlag() == Flags::TriggerHappy))) {
myTank->fireShot();
}
} else {
int mx, my;
mainWindow->getMousePosition(mx, my);
}
myTank->update();
}
#ifdef ROBOT
if (entered) {
updateRobots(dt);
}
#endif
// check for flags and hits
checkEnvironment();
#ifdef ROBOT
if (entered) {
checkEnvironmentForRobots();
}
#endif
// adjust properties based on flags (dimensions, cloaking, etc...)
if (myTank) {
myTank->updateTank(dt, true);
}
for (i = 0; i < curMaxPlayers; i++) {
if (player[i]) {
player[i]->updateTank(dt, false);
}
}
// reposition flags
updateFlags(dt);
// update explosion animations
updateExplosions(dt);
// update mesh animations
if (world) {
world->updateAnimations(dt);
}
// prep the HUD
prepareTheHUD();
// draw the frame
drawFrame(dt);
// play the sounds
updateSound();
double heartbeatTime = 30.0f;
bool sendUpdate = myTank && myTank->isDeadReckoningWrong();
if ( myTank && myTank->getTeam() == ObserverTeam )
{
if (BZDB.isTrue("sendObserverHeartbeat"))
{
if (BZDB.isSet("observerHeartbeat"))
heartbeatTime = BZDB.eval("observerHeartbeat");
if (lastObserverUpdateTime + heartbeatTime <
TimeKeeper::getCurrent().getSeconds())
{
lastObserverUpdateTime = TimeKeeper::getCurrent().getSeconds();
sendUpdate = true;
}
else
sendUpdate = false;
}
else
sendUpdate = false;
}
// send my data
if ( sendUpdate) {
// also calls setDeadReckoning()
serverLink->sendPlayerUpdate(myTank);
}
#ifdef ROBOT
if (entered) {
sendRobotUpdates();
}
#endif
FlagSceneNode::freeFlag();
cURLManager::perform();
// check if we are waiting for initial texture downloading
if (Downloads::requestFinalized()) {
// downloading is terminated. go!
Downloads::finalizeDownloads();
if (downloadingInitialTexture) {
joinInternetGame2();
downloadingInitialTexture = false;
} else {
setSceneDatabase();
}
}
// limit the fps to save battery life by minimizing cpu usage
if (BZDB.isTrue("saveEnergy")) {
static TimeKeeper lastTime = TimeKeeper::getCurrent();
const float fpsLimit = BZDB.eval("fpsLimit");
if ((fpsLimit >= 1.0f) && !isnan(fpsLimit)) {
const float elapsed = float(TimeKeeper::getCurrent() - lastTime);
if (elapsed > 0.0f) {
const float period = (1.0f / fpsLimit);
const float remaining = (period - elapsed);
if (remaining > 0.0f) {
TimeKeeper::sleep(remaining);
}
}
}
lastTime = TimeKeeper::getCurrent();
} // end energy saver check
} // end main client loop
delete worldDownLoader;
// restore the sound. if we don't do this then we'll save the
// wrong volume when we dump out the configuration file if the
// app exits when the game is paused.
if (savedVolume != -1) {
setSoundVolume(savedVolume);
savedVolume = -1;
}
// hide window
mainWindow->showWindow(false);
}
//
// game initialization
//
static float timeConfiguration(bool useZBuffer)
{
// prepare depth buffer if requested
BZDB.set("zbuffer","1" );
if (useZBuffer) {
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
}
// use glFinish() to get accurate timings
//glFinish();
TimeKeeper startTime = TimeKeeper::getCurrent();
sceneRenderer->setExposed();
sceneRenderer->render();
// glFinish();
TimeKeeper endTime = TimeKeeper::getCurrent();
// turn off depth buffer
if (useZBuffer) glDisable(GL_DEPTH_TEST);
return float(endTime - startTime);
}
static void timeConfigurations()
{
static const float MaxFrameTime = 0.050f; // seconds
TextureManager& tm = TextureManager::instance();
// ignore results of first test. OpenGL could be doing lazy setup.
BZDB.set("blend", "0");
BZDB.set("smooth", "0");
BZDB.set("lighting", "0");
BZDB.set("texture", "0");
sceneRenderer->setQuality(0);
BZDB.set("dither", "1");
BZDB.set("shadows", "0");
BZDB.set("radarStyle", "0");
tm.setMaxFilter(OpenGLTexture::Off);
timeConfiguration(true);
// time lowest quality with and without blending. some systems
// stipple very slowly even though everything else is fast. we
// don't want to conclude the system is slow because of stippling.
printError(" lowest quality");
const float timeNoBlendNoZ = timeConfiguration(false);
const float timeNoBlendZ = timeConfiguration(true);
BZDB.set("blend", "1");
const float timeBlendNoZ = timeConfiguration(false);
const float timeBlendZ = timeConfiguration(true);
if (timeNoBlendNoZ > MaxFrameTime &&
timeNoBlendZ > MaxFrameTime &&
timeBlendNoZ > MaxFrameTime &&
timeBlendZ > MaxFrameTime) {
if (timeNoBlendNoZ < timeNoBlendZ &&
timeNoBlendNoZ < timeBlendNoZ &&
timeNoBlendNoZ < timeBlendZ) {
// no depth, no blending definitely fastest
BZDB.set("zbuffer", "1");
BZDB.set("blend", "1");
}
if (timeNoBlendZ < timeBlendNoZ &&
timeNoBlendZ < timeBlendZ) {
// no blending faster than blending
BZDB.set("zbuffer", "1");
BZDB.set("blend", "1");
}
if (timeBlendNoZ < timeBlendZ) {
// blending faster than depth
BZDB.set("zbuffer", "1");
BZDB.set("blend", "1");
}
// blending and depth faster than without either
BZDB.set("zbuffer", "1");
BZDB.set("blend", "1");
return;
}
// leave blending on if blending clearly faster than stippling
if (timeBlendNoZ > timeNoBlendNoZ || timeBlendNoZ > timeNoBlendZ &&
timeBlendZ > timeNoBlendNoZ || timeBlendZ > timeNoBlendZ) {
BZDB.set("blend", "0");
}
// try texturing. if it's too slow then fall back to
// lowest quality and return.
tm.setMaxFilter(OpenGLTexture::Nearest);
BZDB.set("texture", tm.getMaxFilterName());
sceneRenderer->setQuality(1);
printError(" lowest quality with texture");
if (timeConfiguration(false) > MaxFrameTime ||
timeConfiguration(true) > MaxFrameTime) {
BZDB.set("texture", "0");
tm.setMaxFilter(OpenGLTexture::Off);
sceneRenderer->setQuality(0);
return;
}
// everything
printError(" full quality");
BZDB.set("blend", "1");
BZDB.set("smooth", "1");
BZDB.set("lighting", "1");
tm.setMaxFilter(OpenGLTexture::LinearMipmapLinear);
BZDB.set("texture", tm.getMaxFilterName());
sceneRenderer->setQuality(2);
BZDB.set("dither", "1");
BZDB.set("shadows", "1");
BZDB.set("radarStyle", "3");
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// try it without shadows -- some platforms stipple very slowly
BZDB.set("shadows", "0");
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// no high quality
printError(" medium quality");
sceneRenderer->setQuality(1);
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
printError(" low quality");
sceneRenderer->setQuality(0);
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// lower quality texturing
printError(" nearest texturing");
tm.setMaxFilter(OpenGLTexture::Nearest);
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// no texturing
printError(" no texturing");
BZDB.set("texture", "0");
tm.setMaxFilter(OpenGLTexture::Off);
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// no blending
printError(" no blending");
BZDB.set("blend", "0");
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// no smoothing. shouldn't really affect fill rate too much.
printError(" no smoothing");
BZDB.set("smooth", "0");
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// no lighting. shouldn't really affect fill rate, either.
printError(" no lighting");
BZDB.set("lighting", "0");
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
// no dithering
printError(" no dithering");
BZDB.set("dither", "0");
if (timeConfiguration(true) < MaxFrameTime) return;
if (timeConfiguration(false) < MaxFrameTime) return;
}
static void findFastConfiguration()
{
// time the rendering of the background with various rendering styles
// until we find one fast enough. these tests assume that we're
// going to be fill limited. each test comes in a pair: with and
// without the zbuffer.
//
// this, of course, is only a rough estimate since we're not drawing
// a normal frame (no radar, no HUD, no buildings, etc.). the user
// can always turn stuff on later and the settings are remembered
// across invocations.
// setup projection
float muzzleHeight = BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT);
static const GLfloat eyePoint[3] = { 0.0f, 0.0f, muzzleHeight };
static const GLfloat targetPoint[3] = { 0.0f, 10.0f, muzzleHeight };
sceneRenderer->getViewFrustum().setProjection((float)(45.0 * M_PI /
180.0),
NearPlaneNormal,
FarPlaneDefault,
FarDeepPlaneDefault,
mainWindow->getWidth(),
mainWindow->getHeight(),
mainWindow->getViewHeight());
sceneRenderer->getViewFrustum().setView(eyePoint, targetPoint);
// add a big wall in front of where we're looking. this is important
// because once textures are off, the background won't draw much of
// anything. this will ensure that we continue to test polygon fill
// rate. with one polygon it doesn't matter if we use a z or bsp
// database.
static const GLfloat base[3] = { -10.0f, 10.0f, 0.0f };
static const GLfloat sEdge[3] = { 20.0f, 0.0f, 0.0f };
static const GLfloat tEdge[3] = { 0.0f, 0.0f, 10.0f };
static const GLfloat color[4] = { 1.0f, 1.0f, 1.0f, 0.5f };
SceneDatabase* timingScene = new ZSceneDatabase;
WallSceneNode* node = new QuadWallSceneNode(base,
sEdge, tEdge, 1.0f, 1.0f, true);
node->setColor(color);
node->setModulateColor(color);
node->setLightedColor(color);
node->setLightedModulateColor(color);
node->setTexture(HUDuiControl::getArrow());
node->setMaterial(OpenGLMaterial(color, color));
timingScene->addStaticNode(node, false);
timingScene->finalizeStatics();
sceneRenderer->setSceneDatabase(timingScene);
sceneRenderer->setDim(false);
timeConfigurations();
sceneRenderer->setSceneDatabase(NULL);
}
static void defaultErrorCallback(const char* msg)
{
std::string message = ColorStrings[RedColor];
message += msg;
controlPanel->addMessage(message);
}
static void startupErrorCallback(const char* msg)
{
controlPanel->addMessage(msg);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
controlPanel->render(*sceneRenderer);
mainWindow->getWindow()->swapBuffers();
}
void startPlaying(BzfDisplay* _display,
SceneRenderer& renderer)
{
// initalization
display = _display;
sceneRenderer = &renderer;
mainWindow = &sceneRenderer->getWindow();
lastObserverUpdateTime = TimeKeeper::getCurrent().getSeconds();
// register some commands
for (unsigned int c = 0; c < countof(commandList); ++c) {
CMDMGR.add(commandList[c].name, commandList[c].func,
commandList[c].help);
}
// initialize the tank display lists
// (do this before calling SceneRenderer::render())
TankGeometryMgr::init();
SphereLodSceneNode::init();
// make control panel
ControlPanel _controlPanel(*mainWindow, *sceneRenderer);
controlPanel = &_controlPanel;
// make the radar
RadarRenderer _radar(*sceneRenderer, world);
radar = &_radar;
// tie the radar to the control panel
controlPanel->setRadarRenderer(radar);
// tell the control panel how many frame buffers there are. we
// cheat when drawing the control panel, not drawing it if it
// hasn't changed. that only works if we've filled all the
// frame buffers (e.g. front and back buffers) with the correct
// data.
// FIXME -- assuming the contents of any frame buffer except the
// front buffer are anything but garbage violates the OpenGL
// spec. we really should redraw the control panel every frame
// but this works on every system so far.
{
int n = 3; // assume triple buffering
switch (sceneRenderer->getViewType()) {
case SceneRenderer::Stacked:
case SceneRenderer::Stereo:
#ifndef USE_GL_STEREO
// control panel drawn twice per frame
n *= 2;
#endif
break;
case SceneRenderer::ThreeChannel:
default:
// only one copy of control panel visible
break;
}
controlPanel->setNumberOfFrameBuffers(n);
}
// if no configuration, turn off fancy rendering so startup is fast,
// even on a slow machine.
if (!startupInfo.hasConfiguration) {
BZDB.set("blend", "0");
BZDB.set("smooth", "0");
BZDB.set("lighting", "0");
BZDB.set("tesselation", "1"); // lighting set to 0 overrides
BZDB.set("texture", "0");
sceneRenderer->setQuality(0);
BZDB.set("dither", "0");
BZDB.set("shadows", "0");
BZDB.set("radarStyle", "0");
TextureManager::instance().setMaxFilter(OpenGLTexture::Off);
}
// should we grab the mouse? yes if fullscreen.
if (!BZDB.isSet("_window"))
setGrabMouse(true);
#if defined(__linux__) && !defined(DEBUG)
// linux usually has a virtual root window so grab mouse always
setGrabMouse(true);
#endif
// show window and clear it immediately
mainWindow->showWindow(true);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glDisable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
mainWindow->getWindow()->swapBuffers();
// resize and draw basic stuff
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
controlPanel->resize();
sceneRenderer->render();
controlPanel->render(*sceneRenderer);
mainWindow->getWindow()->swapBuffers();
// startup error callback adds message to control panel and
// forces an immediate redraw.
setErrorCallback(startupErrorCallback);
// initialize epoch offset (time)
userTimeEpochOffset = (double)mktime(&userTime);
epochOffset = userTimeEpochOffset;
updateDaylight(epochOffset, *sceneRenderer);
lastEpochOffset = epochOffset;
// catch kill signals before changing video mode so we can
// put it back even if we die. ignore a few signals.
bzSignal(SIGILL, SIG_PF(dying));
bzSignal(SIGABRT, SIG_PF(dying));
bzSignal(SIGSEGV, SIG_PF(dying));
bzSignal(SIGTERM, SIG_PF(suicide));
#if !defined(_WIN32)
if (bzSignal(SIGINT, SIG_IGN) != SIG_IGN)
bzSignal(SIGINT, SIG_PF(suicide));
bzSignal(SIGPIPE, SIG_PF(hangup));
bzSignal(SIGHUP, SIG_IGN);
if (bzSignal(SIGQUIT, SIG_IGN) != SIG_IGN)
bzSignal(SIGQUIT, SIG_PF(dying));
#ifndef GUSI_20
bzSignal(SIGBUS, SIG_PF(dying));
#endif
bzSignal(SIGUSR1, SIG_IGN);
bzSignal(SIGUSR2, SIG_IGN);
#endif /* !defined(_WIN32) */
std::string videoFormat;
int format = -1;
if (BZDB.isSet("resolution")) {
videoFormat = BZDB.get("resolution");
if (videoFormat.length() != 0) {
format = display->findResolution(videoFormat.c_str());
if (format >= 0) {
mainWindow->getWindow()->callResizeCallbacks();
}
}
};
// set the resolution (only if in full screen mode)
if (!BZDB.isSet("_window") && BZDB.isSet("resolution")) {
if (videoFormat.length() != 0) {
if (display->isValidResolution(format) &&
display->getResolution() != format &&
display->setResolution(format)) {
// handle resize
if (BZDB.isSet("geometry")) {
int w, h, x, y, count;
char xs, ys;
count = sscanf(BZDB.get("geometry").c_str(),
"%dx%d%c%d%c%d", &w, &h, &xs, &x, &ys, &y);
if (w < 256) w = 256;
if (h < 192) h = 192;
if (count == 6) {
if (xs == '-') x = display->getWidth() - x - w;
if (ys == '-') y = display->getHeight() - y - h;
mainWindow->setPosition(x, y);
}
mainWindow->setSize(w, h);
} else {
mainWindow->setFullscreen();
}
// more resize handling
mainWindow->getWindow()->callResizeCallbacks();
mainWindow->warpMouse();
}
}
}
// grab mouse if we should
if (shouldGrabMouse())
mainWindow->grabMouse();
// draw again
glClear(GL_COLOR_BUFFER_BIT);
sceneRenderer->render();
controlPanel->render(*sceneRenderer);
mainWindow->getWindow()->swapBuffers();
mainWindow->getWindow()->yieldCurrent();
// make heads up display
HUDRenderer _hud(display, renderer);
hud = &_hud;
scoreboard = hud->getScoreboard();
// initialize control panel and hud
updateNumPlayers();
updateFlag(Flags::Null);
updateHighScores();
notifyBzfKeyMapChanged();
// make background renderer
BackgroundRenderer background(renderer);
sceneRenderer->setBackground(&background);
// if no configuration file try to determine rendering settings
// that yield reasonable performance.
if (!startupInfo.hasConfiguration) {
printError("testing performance; please wait...");
findFastConfiguration();
dumpResources();
}
static const GLfloat zero[3] = { 0.0f, 0.0f, 0.0f };
TextureManager &tm = TextureManager::instance();
bool done = false;
int explostion = 1;
while (!done) {
char text[256];
sprintf(text, "explode%d", explostion);
int tex = tm.getTextureID(text, false);
if (tex < 0) {
done = true;
} else {
// make explosion scene node
BillboardSceneNode* explosion = new BillboardSceneNode(zero);
explosion->setTexture(tex);
explosion->setTextureAnimation(8, 8);
// add it to list of prototype explosions
prototypeExplosions.push_back(explosion);
explostion++;
}
}
// let other stuff do initialization
sceneBuilder = new SceneDatabaseBuilder(sceneRenderer);
World::init();
// prepare dialogs
mainMenu = new MainMenu;
// normal error callback (doesn't force a redraw)
setErrorCallback(defaultErrorCallback);
// print debugging info
{
// Application version
logDebugMessage(1,"BZFlag version: %s\n", getAppVersion());
// Protocol version
logDebugMessage(1,"BZFlag protocol: %s\n", getProtocolVersion());
// OpenGL Driver Information
logDebugMessage(1,"OpenGL vendor: %s\n", (const
char*)glGetString(GL_VENDOR));
logDebugMessage(1,"OpenGL version: %s\n", (const
char*)glGetString(GL_VERSION));
logDebugMessage(1,"OpenGL renderer: %s\n", (const
char*)glGetString(GL_RENDERER));
// Depth Buffer bitplanes
GLint zDepth;
glGetIntegerv(GL_DEPTH_BITS, &zDepth);
logDebugMessage(1,"Depth Buffer: %i bitplanes\n", zDepth);
}
// windows version can be very helpful in debug logs
#ifdef _WIN32
if (debugLevel >= 1) {
OSVERSIONINFO info;
ZeroMemory(&info, sizeof(OSVERSIONINFO));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&info);
logDebugMessage(1,"Running on Windows %s%d.%d %s\n",
(info.dwPlatformId == VER_PLATFORM_WIN32_NT) ? "NT " : "",
info.dwMajorVersion, info.dwMinorVersion,
info.szCSDVersion);
}
#endif
// print expiration
if (timeBombString()) {
// add message about date of expiration
char bombMessage[80];
sprintf(bombMessage, "This release will expire on %s",
timeBombString());
controlPanel->addMessage(bombMessage);
}
// send informative header to the console
{
std::string tmpString;
controlPanel->addMessage("");
// print app version
tmpString = ColorStrings[RedColor];
tmpString += "BZFlag version: ";
tmpString += getAppVersion();
tmpString += " (";
tmpString += getProtocolVersion();
tmpString += ")";
controlPanel->addMessage(tmpString);
// print copyright
tmpString = ColorStrings[YellowColor];
tmpString += bzfcopyright;
controlPanel->addMessage(tmpString);
// print license
tmpString = ColorStrings[CyanColor];
tmpString += "Distributed under the terms of the LGPL";
controlPanel->addMessage(tmpString);
// print author
tmpString = ColorStrings[GreenColor];
tmpString += "Author: NightMare";
controlPanel->addMessage(tmpString);
// print maintainer
tmpString = ColorStrings[CyanColor];
tmpString += "Maintainer: NightMare";
controlPanel->addMessage(tmpString);
// print audio driver
std::string audioStr;
PlatformFactory::getMedia()->audioDriver(audioStr);
if (tmpString != "") {
tmpString = ColorStrings[BlueColor];
tmpString += "Audio Driver: " + audioStr;
controlPanel->addMessage(tmpString);
}
// print GL renderer
tmpString = ColorStrings[PurpleColor];
tmpString += "OpenGL Driver: ";
tmpString += (const char*)glGetString(GL_RENDERER);
controlPanel->addMessage(tmpString);
}
// inform user of silencePlayers on startup
for (unsigned int j = 0; j < silencePlayers.size(); j ++){
std::string aString = silencePlayers[j];
aString += " Silenced";
if (silencePlayers[j] == "*") {
aString = "Silenced All Msgs";
}
controlPanel->addMessage(aString);
}
// enter game if we have all the info we need, otherwise
// pop up main menu
if (startupInfo.autoConnect &&
startupInfo.callsign[0] && startupInfo.serverName[0]) {
joinRequested = true;
controlPanel->addMessage("Trying...");
} else {
mainMenu->createControls();
HUDDialogStack::get()->push(mainMenu);
}
// start game loop
playingLoop();
// clean up
TankGeometryMgr::kill();
SphereLodSceneNode::kill();
if (resourceDownloader)
delete resourceDownloader;
delete motd;
for (unsigned int ext = 0; ext < prototypeExplosions.size(); ext++)
delete prototypeExplosions[ext];
prototypeExplosions.clear();
leaveGame();
setErrorCallback(NULL);
while (HUDDialogStack::get()->isActive())
HUDDialogStack::get()->pop();
delete mainMenu;
delete sceneBuilder;
sceneRenderer->setBackground(NULL);
sceneRenderer->setSceneDatabase(NULL);
World::done();
mainWindow = NULL;
sceneRenderer = NULL;
display = NULL;
cleanWorldCache();
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
// interface header
#include "RadarRenderer.h"
// common implementation headers
#include "SceneRenderer.h"
#include "MainWindow.h"
#include "OpenGLGState.h"
#include "BZDBCache.h"
#include "TextureManager.h"
#include "PhysicsDriver.h"
#include "ObstacleMgr.h"
#include "MeshSceneNode.h"
#include "ObstacleList.h"
#include "WallObstacle.h"
#include "BoxBuilding.h"
#include "PyramidBuilding.h"
#include "MeshObstacle.h"
// local implementation headers
#include "LocalPlayer.h"
#include "World.h"
#include "FlashClock.h"
#include "ShotPath.h"
static FlashClock flashTank;
static bool toggleTank = false;
const float RadarRenderer::colorFactor = 40.0f;
RadarRenderer::RadarRenderer(const SceneRenderer&, World* _world)
: world(_world),
x(0),
y(0),
w(0),
h(0),
dimming(0.0f),
decay(0.01f),
jammed(false),
multiSampled(false)
{
setControlColor();
#if defined(GLX_SAMPLES_SGIS) && defined(GLX_SGIS_multisample)
GLint bits;
glGetIntergerv(GL_SAMPLES_SGIS, &bits);
if (bits > 0) multiSampled = true;
#endif
}
void RadarRenderer::setWorld(World* _world)
{
world = _world;
}
void RadarRenderer::setControlColor(const GLfloat *color)
{
if (color)
memcpy(teamColor, color, 3 * sizeof(float));
else
memset(teamColor, 0, 3 * sizeof(float));
}
void RadarRenderer::setShape(int _x, int _y, int _w, int _h)
{
x = _x;
y = _y;
w = _w;
h = _h;
}
void RadarRenderer::setJammed(bool _jammed)
{
jammed = _jammed;
decay = 0.01;
}
void RadarRenderer::setDimming(float newDimming)
{
dimming = (1.0f - newDimming > 1.0f) ? 1.0f : (1.0f - newDimming <
0.0f) ? 0.0f : 1.0f - newDimming;
}
void RadarRenderer::drawShot(const ShotPath* shot)
{
glBegin(GL_POINTS);
glVertex2fv(shot->getPosition());
glEnd();
}
void RadarRenderer::drawTank(const float pos[3], const Player* player)
{
glPushMatrix();
// 'ps' is pixel scale, setup in render()
const float tankRadius = BZDBCache::tankRadius;
float minSize = 1.5f + (ps * BZDBCache::radarTankPixels);
GLfloat size;
if (tankRadius < minSize) {
size = minSize;
} else {
size = tankRadius;
}
if (pos[2] < 0.0f) {
size = 0.5f;
}
// NOTE: myTank was checked in render()
const float myAngle = LocalPlayer::getMyTank()->getAngle();
// transform to the tanks location
glTranslatef(pos[0], pos[1], 0.0f);
// draw the tank
if ((player == NULL) || !useTankDimensions) {
// align to the screen axes
glRotatef(float(myAngle * 180.0 / M_PI), 0.0f, 0.0f, 1.0f);
glRectf(-size, -size, +size, +size);
}
else {
const float tankAngle = player->getAngle();
glPushMatrix();
glRotatef(float(tankAngle * 180.0 / M_PI), 0.0f, 0.0f, 1.0f);
if (useTankModels) {
drawFancyTank(player);
} else {
const float* dims = player->getDimensions();
glRectf(-dims[0], -dims[1], +dims[0], +dims[1]);
}
glPopMatrix();
// align to the screen axes
glRotatef(float(myAngle * 180.0 / M_PI), 0.0f, 0.0f, 1.0f);
}
// adjust with height box size
const float boxHeight = BZDB.eval(StateDatabase::BZDB_BOXHEIGHT);
size = size * (1.0f + (0.5f * (pos[2] / boxHeight)));
// draw the height box
glBegin(GL_LINE_STRIP);
glVertex2f(-size, 0.0f);
glVertex2f(0.0f, -size);
glVertex2f(+size, 0.0f);
glVertex2f(0.0f, +size);
glVertex2f(-size, 0.0f);
glEnd();
glPopMatrix();
}
void RadarRenderer::drawFancyTank(const Player* player)
{
if (smooth) {
glDisable(GL_BLEND);
}
// we use the depth buffer so that the treads look ok
if (BZDBCache::zbuffer) {
glClearDepth(1.0);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
}
OpenGLGState::resetState();
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
RENDERER.enableSun(true);
player->renderRadar(); // draws at (0,0,0)
RENDERER.enableSun(false);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // FIXME - height box color
OpenGLGState::resetState();
if (BZDBCache::zbuffer) {
glDisable(GL_DEPTH_TEST);
}
if (smooth) {
glEnable(GL_BLEND);
}
return;
}
void RadarRenderer::drawFlag(const float pos[3])
{
GLfloat s = BZDBCache::flagRadius > 3.0f * ps ? BZDBCache::flagRadius
: 3.0f * ps;
glBegin(GL_LINES);
glVertex2f(pos[0] - s, pos[1]);
glVertex2f(pos[0] + s, pos[1]);
glVertex2f(pos[0] + s, pos[1]);
glVertex2f(pos[0] - s, pos[1]);
glVertex2f(pos[0], pos[1] - s);
glVertex2f(pos[0], pos[1] + s);
glVertex2f(pos[0], pos[1] + s);
glVertex2f(pos[0], pos[1] - s);
glEnd();
}
void RadarRenderer::drawFlagOnTank(const float pos[3])
{
glPushMatrix();
// align it to the screen axes
const float angle = LocalPlayer::getMyTank()->getAngle();
glTranslatef(pos[0], pos[1], 0.0f);
glRotatef(float(angle * 180.0 / M_PI), 0.0f, 0.0f, 1.0f);
float tankRadius = BZDBCache::tankRadius;
GLfloat s = 2.5f * tankRadius > 4.0f * ps ? 2.5f * tankRadius : 4.0f
* ps;
glBegin(GL_LINES);
glVertex2f(-s, 0.0f);
glVertex2f(+s, 0.0f);
glVertex2f(+s, 0.0f);
glVertex2f(-s, 0.0f);
glVertex2f(0.0f, -s);
glVertex2f(0.0f, +s);
glVertex2f(0.0f, +s);
glVertex2f(0.0f, -s);
glEnd();
glPopMatrix();
}
void RadarRenderer::renderFrame(SceneRenderer& renderer)
{
const MainWindow& window = renderer.getWindow();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0, window.getWidth(), 0.0, window.getHeight(), -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
OpenGLGState::resetState();
const int ox = window.getOriginX();
const int oy = window.getOriginY();
glScissor(ox + x - 1, oy + y - 1, w + 2, h + 2);
const float left = float(ox + x) - 0.5f;
const float right = float(ox + x + w) + 0.5f;
const float top = float(oy + y) - 0.5f;
const float bottom = float(oy + y + h) + 0.5f;
float outlineOpacity = RENDERER.getPanelOpacity();
float fudgeFactor = BZDBCache::hudGUIBorderOpacityFactor; // bzdb
cache this manybe?
if ( outlineOpacity < 1.0f )
outlineOpacity = (outlineOpacity*fudgeFactor) + (1.0f -
fudgeFactor);
if (BZDBCache::blend)
glEnable(GL_BLEND);
glColor4f(teamColor[0],teamColor[1],teamColor[2],outlineOpacity);
glBegin(GL_LINE_LOOP); {
glVertex2f(left, top);
glVertex2f(right, top);
glVertex2f(right, bottom);
glVertex2f(left, bottom);
} glEnd();
glBegin(GL_POINTS); {
glVertex2f(left, top);
glVertex2f(right, top);
glVertex2f(right, bottom);
glVertex2f(left, bottom);
} glEnd();
if (BZDBCache::blend)
glDisable(GL_BLEND);
glColor4f(teamColor[0],teamColor[1],teamColor[2],1.0f);
const float opacity = renderer.getPanelOpacity();
if ((opacity < 1.0f) && (opacity > 0.0f)) {
glScissor(ox + x - 2, oy + y - 2, w + 4, h + 4);
// draw nice blended background
if (BZDBCache::blend && opacity < 1.0f)
glEnable(GL_BLEND);
glColor4f(0.0f, 0.0f, 0.0f, opacity);
glRectf((float) x, (float) y, (float)(x + w), (float)(y + h));
if (BZDBCache::blend && opacity < 1.0f)
glDisable(GL_BLEND);
}
// note that this scissor setup is used for the reset of the
rendering
glScissor(ox + x, oy + y, w, h);
if (opacity == 1.0f) {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
return;
}
void RadarRenderer::render(SceneRenderer& renderer, bool blank, bool
observer)
{
RenderNode::resetTriangleCount();
float test = BZDBCache::radarLimit;
if(test<=0.0) {
test = BZDB.eval(StateDatabase::BZDB_WORLDSIZE);
BZDB.setFloat(StateDatabase::BZDB_RADARLIMIT,
BZDB.eval(StateDatabase::BZDB_WORLDSIZE));
}
const float radarLimit = test;
// render the frame
renderFrame(renderer);
if (blank) {
return;
}
if (!world) {
return;
}
bool rabbitMode = World::getWorld()->allowRabbit();
smooth = !multiSampled && BZDBCache::smooth;
const bool fastRadar = ((BZDBCache::radarStyle == 1) ||
(BZDBCache::radarStyle == 2)) && BZDBCache::zbuffer;
const LocalPlayer *myTank = LocalPlayer::getMyTank();
// setup the radar range
float radarRange = BZDB.eval("displayRadarRange") * radarLimit;
float maxRange = radarLimit;
// when burrowed, limit radar range
if (myTank && (myTank->getFlag() == Flags::Burrow) &&
(myTank->getPosition()[2] < 0.0f)) {
maxRange = radarLimit / 4.0f;
}
if (radarRange > maxRange) {
radarRange = maxRange;
// only clamp the user's desired range if it's actually
// greater then 1. otherwise, we may be resetting it due
// to burrow radar limiting.
if (BZDB.eval("displayRadarRange") > 1.0f) {
BZDB.set("displayRadarRange", "1.0");
}
}
// prepare projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const MainWindow& window = renderer.getWindow();
const int xSize = window.getWidth();
const int ySize = window.getHeight();
const double xCenter = double(x) + 0.5 * double(w);
const double yCenter = double(y) + 0.5 * double(h);
const double xUnit = 2.0 * radarRange / double(w);
const double yUnit = 2.0 * radarRange / double(h);
// NOTE: the visual extents include passable objects
double maxHeight = 0.0;
const Extents* visExts = renderer.getVisualExtents();
if (visExts) {
maxHeight = (double)visExts->maxs[2];
}
glOrtho(-xCenter * xUnit, (xSize - xCenter) * xUnit,
-yCenter * yUnit, (ySize - yCenter) * yUnit,
-(maxHeight + 10.0), (maxHeight + 10.0));
// prepare modelview matrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
OpenGLGState::resetState();
// if jammed then draw white noise. occasionally draw a good frame.
if (jammed && (bzfrand() > decay)) {
TextureManager &tm = TextureManager::instance();
int noiseTexture = tm.getTextureID( "noise" );
glColor3f(1.0f, 1.0f, 1.0f);
if ((noiseTexture >= 0) && (renderer.useQuality() > 0)) {
const int sequences = 10;
static float np[] =
{ 0, 0, 1, 1,
1, 1, 0, 0,
0.5f, 0.5f, 1.5f, 1.5f,
1.5f, 1.5f, 0.5f, 0.5f,
0.25f, 0.25f, 1.25f, 1.25f,
1.25f, 1.25f, 0.25f, 0.25f,
0, 0.5f, 1, 1.5f,
1, 1.5f, 0, 0.5f,
0.5f, 0, 1.5f, 1,
1.4f, 1, 0.5f, 0,
0.75f, 0.75f, 1.75f, 1.75f,
1.75f, 1.75f, 0.75f, 0.75f,
};
int noisePattern = 4 * int(floor(sequences * bzfrand()));
glEnable(GL_TEXTURE_2D);
tm.bind(noiseTexture);
glBegin(GL_QUADS);
{
glTexCoord2f(np[noisePattern+0],np[noisePattern+1]);
glVertex2f(-radarRange,-radarRange);
glTexCoord2f(np[noisePattern+2],np[noisePattern+1]);
glVertex2f( radarRange,-radarRange);
glTexCoord2f(np[noisePattern+2],np[noisePattern+3]);
glVertex2f( radarRange, radarRange);
glTexCoord2f(np[noisePattern+0],np[noisePattern+3]);
glVertex2f(-radarRange, radarRange);
}
glEnd();
glDisable(GL_TEXTURE_2D);
}
else if ((noiseTexture >= 0) && BZDBCache::texture &&
(renderer.useQuality() == 0)) {
glEnable(GL_TEXTURE_2D);
tm.bind(noiseTexture);
glBegin(GL_QUADS);
{
glTexCoord2f(0,0);
glVertex2f(-radarRange,-radarRange);
glTexCoord2f(1,0);
glVertex2f( radarRange,-radarRange);
glTexCoord2f(1,1);
glVertex2f( radarRange, radarRange);
glTexCoord2f(0,1);
glVertex2f(-radarRange, radarRange);
}
glEnd();
glDisable(GL_TEXTURE_2D);
}
if (decay > 0.015f) decay *= 0.5f;
}
// only draw if there's a local player and a world
else if (myTank) {
// if decay is sufficiently small then boost it so it's more
// likely a jammed radar will get a few good frames closely
// spaced in time. value of 1 guarantees at least two good
// frames in a row.
if (decay <= 0.015f) decay = 1.0f;
else decay *= 0.5f;
// get size of pixel in model space (assumes radar is square)
ps = 2.0f * (radarRange / GLfloat(w));
MeshSceneNode::setRadarLodScale(ps);
float tankWidth = BZDBCache::tankWidth;
float tankLength = BZDBCache::tankLength;
const float testMin = 8.0f * ps;
// maintain the aspect ratio if it isn't square
if ((tankWidth > testMin) && (tankLength > testMin)) {
useTankDimensions = true;
} else {
useTankDimensions = false;
}
if (useTankDimensions && (renderer.useQuality() >= 3)) {
useTankModels = true;
} else {
useTankModels = false;
}
// relative to my tank
const float* myPos = myTank->getPosition();
const float myAngle = myTank->getAngle();
// draw the view angle below stuff
// view frustum edges
if (!BZDB.isTrue("hideRadarViewLines"))
{
glColor3f(1.0f, 0.625f, 0.125f);
const float fovx = renderer.getViewFrustum().getFOVx();
const float viewWidth = radarRange * tanf(0.5f * fovx);
glBegin(GL_LINE_STRIP);
glVertex2f(-viewWidth, radarRange);
glVertex2f(0.0f, 0.0f);
glVertex2f(viewWidth, radarRange);
glEnd();
}
// transform to the observer's viewpoint
glPushMatrix();
glRotatef((float)(90.0 - myAngle * 180.0 / M_PI), 0.0f, 0.0f,
1.0f);
glPushMatrix();
glTranslatef(-myPos[0], -myPos[1], 0.0f);
if (useTankModels) {
// new modelview transform requires repositioning
renderer.setupSun();
}
// setup the blending function
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// draw the buildings
renderObstacles(fastRadar, radarRange);
// antialiasing on for lines and points unless we're multisampling,
// in which case it's automatic and smoothing makes them look
worse.
if (smooth) {
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POINT_SMOOTH);
}
// draw my shots
int maxShots = world->getMaxShots();
int i;
float muzzleHeight = BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT);
for (i = 0; i < maxShots; i++) {
const ShotPath* shot = myTank->getShot(i);
if (shot) {
const float cs = colorScale(shot->getPosition()[2], muzzleHeight);
glColor3f(1.0f * cs, 1.0f * cs, 1.0f * cs);
shot->radarRender();
}
}
FiringInfo myInfo(*myTank, 0);
if (myInfo.flagType != Flags::ShockWave) {
if (!BZDB.isTrue(StateDatabase::BZDB_SHOTSKEEPVERTICALV))
myInfo.shot.vel[2] = 0.0f;
myInfo.flagType = Flags::Laser;
myInfo.lifetime /= 100;
const float cs = colorScale(myInfo.shot.pos[2], muzzleHeight);
glColor3f(1.0f * cs, 1.0f * cs, 1.0f * cs);
LocalShotPath myPath(myInfo);
myPath.radarRender();
}
//test
//draw world weapon shots
WorldPlayer *worldWeapons = World::getWorld()->getWorldWeapons();
maxShots = worldWeapons->getMaxShots();
for (i = 0; i < maxShots; i++) {
const ShotPath* shot = worldWeapons->getShot(i);
if (shot) {
const float cs = colorScale(shot->getPosition()[2], muzzleHeight);
glColor3f(1.0f * cs, 1.0f * cs, 1.0f * cs);
shot->radarRender();
}
}
// draw other tanks (and any flags on them)
// note about flag drawing. each line segment is drawn twice
// (once in each direction); this degrades the antialiasing
// but on systems that don't do correct filtering of endpoints
// not doing it makes (half) the endpoints jump wildly.
const int curMaxPlayers = world->getCurMaxPlayers();
for (i = 0; i < curMaxPlayers; i++) {
RemotePlayer* player = world->getPlayer(i);
if (!player) {
continue;
}
if (!player->isAlive() &&
(!useTankModels || !observer || !player->isExploding())) {
continue;
}
const float* position = player->getPosition();
if (player->getFlag() != Flags::Null) {
glColor3fv(player->getFlag()->getColor());
drawFlagOnTank(position);
}
if (player->isPaused() || player->isNotResponding()) {
const float dimfactor = 0.4f;
const float *color;
if (myTank->getFlag() == Flags::Colorblindness) {
color = Team::getRadarColor(RogueTeam,rabbitMode);
} else {
color = Team::getRadarColor(player->getTeam(),rabbitMode);
}
float dimmedcolor[3];
dimmedcolor[0] = color[0] * dimfactor;
dimmedcolor[1] = color[1] * dimfactor;
dimmedcolor[2] = color[2] * dimfactor;
glColor3fv(dimmedcolor);
} else {
glColor3fv(Team::getRadarColor(myTank->getFlag() ==
Flags::Colorblindness ? RogueTeam : player->getTeam(),
rabbitMode));
}
// If this tank is hunted flash it on the radar
if (player->isHunted() && myTank->getFlag() !=
Flags::Colorblindness) {
if (flashTank.isOn()) {
if (!toggleTank) {
float flashcolor[3];
flashcolor[0] = 0.0f;
flashcolor[1] = 0.8f;
flashcolor[2] = 0.9f;
glColor3fv(flashcolor);
}
} else {
toggleTank = !toggleTank;
flashTank.setClock(0.2f);
}
}
if (!observer) {
drawTank(position, NULL);
} else {
drawTank(position, player);
}
}
bool coloredShot = BZDB.isTrue("coloredradarshots");
// draw other tanks' shells
bool iSeeAll = myTank && (myTank->getFlag() == Flags::Seer);
maxShots = World::getWorld()->getMaxShots();
for (i = 0; i < curMaxPlayers; i++) {
RemotePlayer* player = world->getPlayer(i);
if (!player) continue;
for (int j = 0; j < maxShots; j++) {
const ShotPath* shot = player->getShot(j);
if (shot && (shot->getFlag() != Flags::InvisibleBullet || iSeeAll)) {
const float *shotcolor;
if (coloredShot) {
if (myTank->getFlag() == Flags::Colorblindness)
shotcolor = Team::getRadarColor(RogueTeam,rabbitMode);
else
shotcolor = Team::getRadarColor(player->getTeam(),rabbitMode);
const float cs = colorScale(shot->getPosition()[2], muzzleHeight);
glColor3f(shotcolor[0] * cs, shotcolor[1] * cs, shotcolor[2] *
cs);
} else {
glColor3f(1.0f, 1.0f, 1.0f);
}
shot->radarRender();
}
}
}
// draw flags not on tanks.
// draw them in reverse order so that the team flags
// (which come first), are drawn on top of the normal flags.
const int maxFlags = world->getMaxFlags();
const bool drawNormalFlags = BZDB.isTrue("displayRadarFlags");
for (i = (maxFlags - 1); i >= 0; i--) {
const Flag& flag = world->getFlag(i);
// don't draw flags that don't exist or are on a tank
if (flag.status == FlagNoExist || flag.status == FlagOnTank)
continue;
// don't draw normal flags if we aren't supposed to
if (flag.type->flagTeam == NoTeam && !drawNormalFlags)
continue;
// Flags change color by height
const float cs = colorScale(flag.position[2], muzzleHeight);
const float *flagcolor = flag.type->getColor();
glColor3f(flagcolor[0] * cs, flagcolor[1] * cs, flagcolor[2] *
cs);
drawFlag(flag.position);
}
// draw antidote flag
const float* antidotePos =
LocalPlayer::getMyTank()->getAntidoteLocation();
if (antidotePos) {
glColor3f(1.0f, 1.0f, 0.0f);
drawFlag(antidotePos);
}
// draw these markers above all others always centered
glPopMatrix();
// north marker
GLfloat ns = 0.05f * radarRange, ny = 0.9f * radarRange;
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_STRIP);
glVertex2f(-ns, ny - ns);
glVertex2f(-ns, ny + ns);
glVertex2f(ns, ny - ns);
glVertex2f(ns, ny + ns);
glEnd();
// always up
glPopMatrix();
// forward tick
glBegin(GL_LINES);
glVertex2f(0.0f, radarRange - ps);
glVertex2f(0.0f, radarRange - 4.0f * ps);
glEnd();
if (!observer) {
// revert to the centered transformation
glRotatef((float)(90.0 - myAngle * 180.0 / M_PI), 0.0f, 0.0f,
1.0f);
glTranslatef(-myPos[0], -myPos[1], 0.0f);
// my flag
if (myTank->getFlag() != Flags::Null) {
glColor3fv(myTank->getFlag()->getColor());
drawFlagOnTank(myPos);
}
// my tank
glColor3f(1.0f, 1.0f, 1.0f);
drawTank(myPos, myTank);
}
if (dimming > 0.0f) {
if (!smooth) {
glEnable(GL_BLEND);
}
// darken the entire radar if we're dimmed
// we're drawing positively, so dimming is actually an opacity
glColor4f(0.0f, 0.0f, 0.0f, 1.0f - dimming);
glRectf(-radarRange, -radarRange, +radarRange, +radarRange);
}
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POINT_SMOOTH);
}
// restore GL state
glPopMatrix();
triangleCount = RenderNode::getTriangleCount();
}
float RadarRenderer::colorScale(const float z, const float h)
{
float scaleColor;
if (BZDBCache::radarStyle > 0) {
const LocalPlayer* myTank = LocalPlayer::getMyTank();
// Scale color so that objects that are close to tank's level are
opaque
const float zTank = myTank->getPosition()[2];
if (zTank > (z + h))
scaleColor = 1.0f - (zTank - (z + h)) / colorFactor;
else if (zTank < z)
scaleColor = 1.0f - (z - zTank) / colorFactor;
else
scaleColor = 1.0f;
// Don't fade all the way
if (scaleColor < 0.35f)
scaleColor = 0.35f;
} else {
scaleColor = 1.0f;
}
return scaleColor;
}
float RadarRenderer::transScale(const float z, const float h)
{
float scaleColor;
const LocalPlayer* myTank = LocalPlayer::getMyTank();
// Scale color so that objects that are close to tank's level are
opaque
const float zTank = myTank->getPosition()[2];
if (zTank > (z + h))
scaleColor = 1.0f - (zTank - (z + h)) / colorFactor;
else if (zTank < z)
scaleColor = 1.0f - (z - zTank) / colorFactor;
else
scaleColor = 1.0f;
if (scaleColor < 0.5f)
scaleColor = 0.5f;
return scaleColor;
}
void RadarRenderer::renderObstacles(bool fastRadar, float _range)
{
if (smooth) {
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
}
// draw the walls
renderWalls();
// draw the boxes, pyramids, and meshes
if (!fastRadar) {
renderBoxPyrMesh();
} else {
renderBoxPyrMeshFast(_range);
}
// draw the team bases and teleporters
renderBasesAndTeles();
if (smooth) {
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
return;
}
void RadarRenderer::renderWalls()
{
const ObstacleList& walls = OBSTACLEMGR.getWalls();
int count = walls.size();
glColor3f(0.25f, 0.5f, 0.5f);
glBegin(GL_LINES);
for (int i = 0; i < count; i++) {
const WallObstacle& wall = *((const WallObstacle*) walls[i]);
const float wid = wall.getBreadth();
const float c = wid * cosf(wall.getRotation());
const float s = wid * sinf(wall.getRotation());
const float* pos = wall.getPosition();
glVertex2f(pos[0] - s, pos[1] + c);
glVertex2f(pos[0] + s, pos[1] - c);
}
glEnd();
return;
}
void RadarRenderer::renderBoxPyrMeshFast(float _range)
{
// FIXME - This is hack code at the moment, but even when
// rendering the full world, it draws the aztec map
// 3X faster (the culling algo is actually slows us
// down in that case)
// - need a better default gradient texture
// (better colors, and tied in to show max jump height?)
// - build a procedural texture if default is missing
// - use a GL_TEXTURE_1D
// - setup the octree to return Z sorted elements (partially done)
// - add a renderClass() member to SceneNode (also for coloring)
// - also add a renderShadow() member (they don't need sorting,
// and if you don't have double-buffering, you shouldn't be
// using shadows)
// - vertex shaders would be faster
// - it would probably be a better approach to attach a radar
// rendering object to each obstacle... no time
// get the texture
int gradientTexId = -1;
TextureManager &tm = TextureManager::instance();
gradientTexId = tm.getTextureID("radar", false);
// safety: no texture, no service
if (gradientTexId < 0) {
renderBoxPyrMesh();
return;
}
// GL state
OpenGLGStateBuilder gb;
gb.setTexture(gradientTexId);
gb.setShading(GL_FLAT);
gb.setCulling(GL_BACK);
gb.setBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
OpenGLGState gs = gb.getState();
gs.setState();
// disable the unused arrays
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// now that the texture is bound, setup the clamp mode
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
// do this after the GState setting
if (smooth) {
glEnable(GL_POLYGON_SMOOTH);
}
// setup the texturing mapping
const float hf = 128.0f; // height factor, goes from 0.0 to 1.0 in
texcoords
const float vfz = RENDERER.getViewFrustum().getEye()[2];
const GLfloat plane[4] =
{ 0.0f, 0.0f, (1.0f / hf), (((hf * 0.5f) - vfz) / hf) };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, plane);
// setup texture generation
glEnable(GL_TEXTURE_GEN_S);
// set the color
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
// if (!BZDB.isTrue("visualRadar")) {
ViewFrustum radarClipper;
radarClipper.setOrthoPlanes(RENDERER.getViewFrustum(), _range,
_range);
RENDERER.getSceneDatabase()->renderRadarNodes(radarClipper);
// } else {
//
RENDERER.getSceneDatabase()->renderRadarNodes(RENDERER.getViewFrustum());
// }
// restore texture generation
glDisable(GL_TEXTURE_GEN_S);
OpenGLGState::resetState();
// re-enable the arrays
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// do this after the GState setting
if (smooth) {
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
}
return;
}
void RadarRenderer::renderBoxPyrMesh()
{
int i;
const bool enhanced = (BZDBCache::radarStyle > 0);
if (!smooth) {
// smoothing has blending disabled
if (enhanced) {
glEnable(GL_BLEND); // always blend the polygons if we're
enhanced
}
} else {
// smoothing has blending enabled
if (!enhanced) {
glDisable(GL_BLEND); // don't blend the polygons if we're not
enhanced
}
}
// draw box buildings.
const ObstacleList& boxes = OBSTACLEMGR.getBoxes();
int count = boxes.size();
glBegin(GL_QUADS);
for (i = 0; i < count; i++) {
const BoxBuilding& box = *((const BoxBuilding*) boxes[i]);
if (box.isInvisible())
continue;
const float z = box.getPosition()[2];
const float bh = box.getHeight();
const float cs = colorScale(z, bh);
glColor4f(0.25f * cs, 0.5f * cs, 0.5f * cs, transScale(z, bh));
const float c = cosf(box.getRotation());
const float s = sinf(box.getRotation());
const float wx = c * box.getWidth(), wy = s * box.getWidth();
const float hx = -s * box.getBreadth(), hy = c * box.getBreadth();
const float* pos = box.getPosition();
glVertex2f(pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f(pos[0] + wx - hx, pos[1] + wy - hy);
glVertex2f(pos[0] + wx + hx, pos[1] + wy + hy);
glVertex2f(pos[0] - wx + hx, pos[1] - wy + hy);
}
glEnd();
// draw pyramid buildings
const ObstacleList& pyramids = OBSTACLEMGR.getPyrs();
count = pyramids.size();
glBegin(GL_QUADS);
for (i = 0; i < count; i++) {
const PyramidBuilding& pyr = *((const PyramidBuilding*)
pyramids[i]);
const float z = pyr.getPosition()[2];
const float bh = pyr.getHeight();
const float cs = colorScale(z, bh);
glColor4f(0.25f * cs, 0.5f * cs, 0.5f * cs, transScale(z, bh));
const float c = cosf(pyr.getRotation());
const float s = sinf(pyr.getRotation());
const float wx = c * pyr.getWidth(), wy = s * pyr.getWidth();
const float hx = -s * pyr.getBreadth(), hy = c * pyr.getBreadth();
const float* pos = pyr.getPosition();
glVertex2f(pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f(pos[0] + wx - hx, pos[1] + wy - hy);
glVertex2f(pos[0] + wx + hx, pos[1] + wy + hy);
glVertex2f(pos[0] - wx + hx, pos[1] - wy + hy);
}
glEnd();
// draw mesh obstacles
if (smooth) {
glEnable(GL_POLYGON_SMOOTH);
}
if (!enhanced) {
glDisable(GL_CULL_FACE);
}
const ObstacleList& meshes = OBSTACLEMGR.getMeshes();
count = meshes.size();
for (i = 0; i < count; i++) {
const MeshObstacle* mesh = (const MeshObstacle*) meshes[i];
int faces = mesh->getFaceCount();
for (int f = 0; f < faces; f++) {
const MeshFace* face = mesh->getFace(f);
if (enhanced) {
if (face->getPlane()[2] <= 0.0f) {
continue;
}
const BzMaterial* bzmat = face->getMaterial();
if ((bzmat != NULL) && bzmat->getNoRadar()) {
continue;
}
}
const float z = face->getPosition()[2];
const float bh = face->getHeight();
const float cs = colorScale(z, bh);
// draw death faces with a soupcon of red
const PhysicsDriver* phydrv =
PHYDRVMGR.getDriver(face->getPhysicsDriver());
if ((phydrv != NULL) && phydrv->getIsDeath()) {
glColor4f(0.75f * cs, 0.25f * cs, 0.25f * cs, transScale(z, bh));
} else {
glColor4f(0.25f * cs, 0.5f * cs, 0.5f * cs, transScale(z, bh));
}
// draw the face as a triangle fan
int vertexCount = face->getVertexCount();
glBegin(GL_TRIANGLE_FAN);
for (int v = 0; v < vertexCount; v++) {
const float* pos = face->getVertex(v);
glVertex2f(pos[0], pos[1]);
}
glEnd();
}
}
if (!enhanced) {
glEnable(GL_CULL_FACE);
}
if (smooth) {
glDisable(GL_POLYGON_SMOOTH);
}
// NOTE: revert from the enhanced setting
if (enhanced && !smooth) {
glDisable(GL_BLEND);
}
// now draw antialiased outlines around the polygons
if (smooth) {
glEnable(GL_BLEND); // NOTE: revert from the enhanced setting
count = boxes.size();
for (i = 0; i < count; i++) {
const BoxBuilding& box = *((const BoxBuilding*) boxes[i]);
if (box.isInvisible())
continue;
const float z = box.getPosition()[2];
const float bh = box.getHeight();
const float cs = colorScale(z, bh);
glColor4f(0.25f * cs, 0.5f * cs, 0.5f * cs, transScale(z, bh));
const float c = cosf(box.getRotation());
const float s = sinf(box.getRotation());
const float wx = c * box.getWidth(), wy = s * box.getWidth();
const float hx = -s * box.getBreadth(), hy = c *
box.getBreadth();
const float* pos = box.getPosition();
glBegin(GL_LINE_LOOP);
glVertex2f(pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f(pos[0] + wx - hx, pos[1] + wy - hy);
glVertex2f(pos[0] + wx + hx, pos[1] + wy + hy);
glVertex2f(pos[0] - wx + hx, pos[1] - wy + hy);
glEnd();
}
count = pyramids.size();
for (i = 0; i < count; i++) {
const PyramidBuilding& pyr = *((const PyramidBuilding*)
pyramids[i]);
const float z = pyr.getPosition()[2];
const float bh = pyr.getHeight();
const float cs = colorScale(z, bh);
glColor4f(0.25f * cs, 0.5f * cs, 0.5f * cs, transScale(z, bh));
const float c = cosf(pyr.getRotation());
const float s = sinf(pyr.getRotation());
const float wx = c * pyr.getWidth(), wy = s * pyr.getWidth();
const float hx = -s * pyr.getBreadth(), hy = c *
pyr.getBreadth();
const float* pos = pyr.getPosition();
glBegin(GL_LINE_LOOP);
glVertex2f(pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f(pos[0] + wx - hx, pos[1] + wy - hy);
glVertex2f(pos[0] + wx + hx, pos[1] + wy + hy);
glVertex2f(pos[0] - wx + hx, pos[1] - wy + hy);
glEnd();
}
}
return;
}
void RadarRenderer::renderBasesAndTeles()
{
int i;
// draw team bases
if(world->allowTeamFlags()) {
for(i = 1; i < NumTeams; i++) {
for (int j = 0;;j++) {
const float *base = world->getBase(i, j);
if (base == NULL)
break;
glColor3fv(Team::getRadarColor(TeamColor(i)));
glBegin(GL_LINE_LOOP);
const float beta = atan2f(base[5], base[4]);
const float r = hypotf(base[4], base[5]);
glVertex2f(base[0] + r * cosf(base[3] + beta),
base[1] + r * sinf(base[3] + beta));
glVertex2f(base[0] + r * cosf((float)(base[3] - beta + M_PI)),
base[1] + r * sinf((float)(base[3] - beta + M_PI)));
glVertex2f(base[0] + r * cosf((float)(base[3] + beta + M_PI)),
base[1] + r * sinf((float)(base[3] + beta + M_PI)));
glVertex2f(base[0] + r * cosf(base[3] - beta),
base[1] + r * sinf(base[3] - beta));
glEnd();
}
}
}
// draw teleporters. teleporters are pretty thin so use lines
// (which, if longer than a pixel, are guaranteed to draw something;
// not so for a polygon). just in case the system doesn't correctly
// filter the ends of line segments, we'll draw the line in each
// direction (which degrades the antialiasing). Newport graphics
// is one system that doesn't do correct filtering.
const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
int count = teleporters.size();
glColor3f(1.0f, 1.0f, 0.25f);
glBegin(GL_LINES);
for (i = 0; i < count; i++) {
const Teleporter & tele = *((const Teleporter *) teleporters[i]);
if (tele.isHorizontal ()) {
const float z = tele.getPosition ()[2];
const float bh = tele.getHeight ();
const float cs = colorScale (z, bh);
glColor4f (1.0f * cs, 1.0f * cs, 0.25f * cs, transScale (z, bh));
const float c = cosf (tele.getRotation ());
const float s = sinf (tele.getRotation ());
const float wx = c * tele.getWidth (), wy = s * tele.getWidth ();
const float hx = -s * tele.getBreadth (), hy = c *
tele.getBreadth ();
const float *pos = tele.getPosition ();
glVertex2f (pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f (pos[0] + wx - hx, pos[1] + wy - hy);
glVertex2f (pos[0] + wx - hx, pos[1] + wy - hy);
glVertex2f (pos[0] + wx + hx, pos[1] + wy + hy);
glVertex2f (pos[0] + wx + hx, pos[1] + wy + hy);
glVertex2f (pos[0] - wx + hx, pos[1] - wy + hy);
glVertex2f (pos[0] - wx + hx, pos[1] - wy + hy);
glVertex2f (pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f (pos[0] - wx - hx, pos[1] - wy - hy);
glVertex2f (pos[0] - wx - hx, pos[1] - wy - hy);
}
else {
const float z = tele.getPosition ()[2];
const float bh = tele.getHeight ();
const float cs = colorScale (z, bh);
glColor4f (1.0f * cs, 1.0f * cs, 0.25f * cs, transScale (z, bh));
const float tw = tele.getBreadth ();
const float c = tw * cosf (tele.getRotation ());
const float s = tw * sinf (tele.getRotation ());
const float *pos = tele.getPosition ();
glVertex2f (pos[0] - s, pos[1] + c);
glVertex2f (pos[0] + s, pos[1] - c);
glVertex2f (pos[0] + s, pos[1] - c);
glVertex2f (pos[0] - s, pos[1] + c);
}
}
glEnd();
return;
}
int RadarRenderer::getFrameTriangleCount() const
{
return triangleCount;
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* interface header */
#include "SegmentedShotStrategy.h"
/* system implementation headers */
#include <assert.h>
/* common implementation headers */
#include "TextureManager.h"
#include "Intersect.h"
#include "BZDBCache.h"
#include "WallObstacle.h"
/* local implementation headers */
#include "sound.h"
#include "LocalPlayer.h"
#include "World.h"
#include "effectsRenderer.h"
#include "playing.h"
SegmentedShotStrategy::SegmentedShotStrategy(ShotPath* _path, bool
useSuperTexture, bool faint) :
ShotStrategy(_path)
{
// initialize times
prevTime = getPath().getStartTime();
lastTime = currentTime = prevTime;
// start at first segment
lastSegment = segment = 0;
// get team
if (_path->getPlayer() == ServerPlayer) {
TeamColor tmpTeam = _path->getFiringInfo().shot.team;
team = (tmpTeam < RogueTeam) ? RogueTeam :
(tmpTeam > RabbitTeam) ? RogueTeam : tmpTeam;
} else {
Player* p = lookupPlayer(_path->getPlayer());
team = p ? p->getTeam() : RogueTeam;
}
// initialize scene nodes
boltSceneNode = new BoltSceneNode(_path->getPosition());
bool rabbitMode = World::getWorld()->allowRabbit();
const float* c = Team::getRadarColor(team, rabbitMode);
if (faint) {
boltSceneNode->setColor(c[0], c[1], c[2], 0.2f);
boltSceneNode->setTextureColor(1.0f, 1.0f, 1.0f, 0.3f);
} else {
boltSceneNode->setColor(c[0], c[1], c[2], 1.0f);
}
TextureManager &tm = TextureManager::instance();
std::string imageName = Team::getImagePrefix(team);
if (useSuperTexture)
imageName += BZDB.get("superPrefix");
imageName += BZDB.get("boltTexture");
int texture = tm.getTextureID(imageName.c_str());
if (texture >= 0)
boltSceneNode->setTexture(texture);
}
SegmentedShotStrategy::~SegmentedShotStrategy()
{
// free scene nodes
delete boltSceneNode;
}
void SegmentedShotStrategy::update(float dt)
{
prevTime = currentTime;
currentTime += dt;
// see if we've moved to another segment
const int numSegments = segments.size();
if (segment < numSegments && segments[segment].end <= currentTime) {
lastSegment = segment;
while (segment < numSegments && segments[segment].end <=
currentTime) {
if (++segment < numSegments) {
switch (segments[segment].reason) {
case ShotPathSegment::Ricochet:
{
// play ricochet sound. ricochet of local player's shots
// are important, others are not.
const PlayerId myTankId = LocalPlayer::getMyTank()->getId();
const bool important = (getPath().getPlayer() == myTankId);
const float* pos = segments[segment].ray.getOrigin();
playWorldSound(SFX_RICOCHET, pos, important);
// this is fugly but it's what we do
float dir[3];
const float* newDir = segments[segment].ray.getDirection();
const float* oldDir = segments[segment - 1].ray.getDirection();
dir[0] = newDir[0] - oldDir[0];
dir[1] = newDir[1] - oldDir[1];
dir[2] = newDir[2] - oldDir[2];
float rots[2];
const float horiz = sqrtf((dir[0]*dir[0]) + (dir[1]*dir[1]));
rots[0] = atan2f(dir[1], dir[0]);
rots[1] = atan2f(dir[2], horiz);
EFFECTS.addRicoEffect( pos, rots);
break;
}
case ShotPathSegment::Boundary:
break;
default:
{
// this is fugly but it's what we do
float dir[3];
dir[0] = segments[segment].ray.getDirection()[0];
dir[1] = segments[segment].ray.getDirection()[1];
dir[2] = segments[segment].ray.getDirection()[2];
float rots[2];
const float horiz = sqrtf((dir[0]*dir[0]) + (dir[1]*dir[1]));
rots[0] = atan2f(dir[1], dir[0]);
rots[1] = atan2f(dir[2], horiz);
const float* pos = segments[segment].ray.getOrigin();
EFFECTS.addShotTeleportEffect( pos, rots);
}
break;
}
}
}
}
// if ran out of segments then expire shot on next update
if (segment == numSegments) {
setExpiring();
if (numSegments > 0) {
ShotPathSegment &segm = segments[numSegments - 1];
const float *dir = segm.ray.getDirection();
const float speed = hypotf(dir[0], hypotf(dir[1], dir[2]));
float pos[3];
segm.ray.getPoint(float(segm.end - segm.start - 1.0 / speed),
pos);
/* NOTE -- comment out to not explode when shot expires */
addShotExplosion(pos);
}
}
// otherwise update position and velocity
else {
float p[3];
segments[segment].ray.getPoint(float(currentTime -
segments[segment].start), p);
setPosition(p);
setVelocity(segments[segment].ray.getDirection());
}
}
void SegmentedShotStrategy::setCurrentTime(const
TimeKeeper& _currentTime)
{
currentTime = _currentTime;
}
const TimeKeeper& SegmentedShotStrategy::getLastTime() const
{
return lastTime;
}
bool SegmentedShotStrategy::isOverlapping(
const float (*bbox1)[3],
const float (*bbox2)[3]) const
{
if (bbox1[1][0] < bbox2[0][0]) return false;
if (bbox1[0][0] > bbox2[1][0]) return false;
if (bbox1[1][1] < bbox2[0][1]) return false;
if (bbox1[0][1] > bbox2[1][1]) return false;
if (bbox1[1][2] < bbox2[0][2]) return false;
if (bbox1[0][2] > bbox2[1][2]) return false;
return true;
}
void SegmentedShotStrategy::setCurrentSegment(int _segment)
{
segment = _segment;
}
float SegmentedShotStrategy::checkHit(const BaseLocalPlayer* tank,
float position[3]) const
{
float minTime = Infinity;
// expired shot can't hit anything
if (getPath().isExpired()) return minTime;
// get tank radius
float radius = tank->getRadius();
const float radius2 = radius * radius;
// tank is positioned from it's bottom so shift position up by
// half a tank height.
const float tankHeight = tank->getDimensions()[2];
Ray tankLastMotionRaw = tank->getLastMotion();
float lastTankPositionRaw[3];
lastTankPositionRaw[0] = tankLastMotionRaw.getOrigin()[0];
lastTankPositionRaw[1] = tankLastMotionRaw.getOrigin()[1];
lastTankPositionRaw[2] = tankLastMotionRaw.getOrigin()[2] + 0.5f *
tankHeight;
Ray tankLastMotion(lastTankPositionRaw,
tankLastMotionRaw.getDirection());
// if bounding box of tank and entire shot doesn't overlap then no
hit
const float (*tankBBox)[3] = tank->getLastMotionBBox();
if (!isOverlapping(bbox, tankBBox)) return minTime;
float shotRadius = BZDB.eval(StateDatabase::BZDB_SHOTRADIUS);
// check each segment in interval (prevTime,currentTime]
const float dt = float(currentTime - prevTime);
const int numSegments = segments.size();
for (int i = lastSegment; i <= segment && i < numSegments; i++) {
// can never hit your own first laser segment
if (i == 0 && getPath().getFlag() == Flags::Laser &&
getPath().getPlayer() == tank->getId())
continue;
/*
// skip segments that don't overlap in time with current interval
if (segments[i].end <= prevTime) continue;
if (currentTime <= segments[i].start) break;
*/
// if shot segment and tank bboxes don't overlap then no hit
const ShotPathSegment& s = segments[i];
if (!isOverlapping(s.bbox, tankBBox)) continue;
// construct relative shot ray: origin and velocity relative to
// my tank as a function of time (t=0 is start of the interval).
Ray relativeRay(rayMinusRay(s.ray, float(prevTime - s.start),
tankLastMotion, 0.0f));
// get hit time
float t;
if (tank->getFlag() == Flags::Narrow) {
// find closest approach to narrow box around tank. width of box
// is shell radius so you can actually hit narrow tank head on.
static float tankBase[3] = { 0.0f, 0.0f, -0.5f * tankHeight };
t = timeRayHitsBlock(relativeRay, tankBase, tank->getAngle(),
0.5f * BZDBCache::tankLength, shotRadius, tankHeight);
}
else {
// find time when shot hits sphere around tank
t = rayAtDistanceFromOrigin(relativeRay, 0.99f * radius);
}
// short circuit if time is greater then smallest time so far
if (t > minTime) continue;
// make sure time falls within segment
if (t < 0.0f || t > dt) continue;
if (t > s.end - prevTime) continue;
// check if shot hits tank -- get position at time t, see if in
radius
float closestPos[3];
relativeRay.getPoint(t, closestPos);
if (closestPos[0] * closestPos[0] +
closestPos[1] * closestPos[1] +
closestPos[2] * closestPos[2] < radius2) {
// save best time so far
minTime = t;
// compute location of tank at time of hit
float tankPos[3];
tank->getLastMotion().getPoint(t, tankPos);
// compute position of intersection
position[0] = tankPos[0] + closestPos[0];
position[1] = tankPos[1] + closestPos[1];
position[2] = tankPos[2] + closestPos[2];
//printf("%u:%u %u:%u\n", tank->getId().port,
tank->getId().number, getPath().getPlayer().port, getPath().getPlayer().number);
}
}
return minTime;
}
void SegmentedShotStrategy::addShot(
SceneDatabase* scene, bool colorblind)
{
const ShotPath& shotPath = getPath();
boltSceneNode->move(shotPath.getPosition(), shotPath.getVelocity());
if (boltSceneNode->getColorblind() != colorblind) {
boltSceneNode->setColorblind(colorblind);
TeamColor currentTeam = colorblind ? RogueTeam : team;
bool rabbitMode = World::getWorld()->allowRabbit();
const float* c = Team::getRadarColor(currentTeam, rabbitMode);
boltSceneNode->setColor(c[0], c[1], c[2]);
TextureManager &tm = TextureManager::instance();
std::string imageName = Team::getImagePrefix(currentTeam);
imageName += BZDB.get("boltTexture");
int texture = tm.getTextureID(imageName.c_str());
if (texture >= 0)
boltSceneNode->setTexture(texture);
}
scene->addDynamicNode(boltSceneNode);
}
void SegmentedShotStrategy::radarRender() const
{
const float *orig = getPath().getPosition();
const int length = (int)BZDBCache::linedRadarShots;
const int size = (int)BZDBCache::sizedRadarShots;
float shotTailLength = BZDB.eval(StateDatabase::BZDB_SHOTTAILLENGTH);
// Display leading lines
if (length > 0) {
const float* vel = getPath().getVelocity();
const float d = 1.0f / hypotf(vel[0], hypotf(vel[1], vel[2]));
float dir[3];
dir[0] = vel[0] * d * shotTailLength * length;
dir[1] = vel[1] * d * shotTailLength * length;
dir[2] = vel[2] * d * shotTailLength * length;
glBegin(GL_LINES);
glVertex2fv(orig);
if (BZDBCache::leadingShotLine) {
glVertex2f(orig[0] + dir[0], orig[1] + dir[1]);
} else {
glVertex2f(orig[0] - dir[0], orig[1] - dir[1]);
}
glEnd();
// draw a "bright" bullet tip
if (size > 0) {
glColor3f(0.75, 0.75, 0.75);
glPointSize((float)size);
glBegin(GL_POINTS);
glVertex2f(orig[0], orig[1]);
glEnd();
glPointSize(1.0f);
}
} else {
if (size > 0) {
// draw a sized bullet
glPointSize((float)size);
glBegin(GL_POINTS);
glVertex2fv(orig);
glEnd();
glPointSize(1.0f);
} else {
// draw the tiny little bullet
glBegin(GL_POINTS);
glVertex2fv(orig);
glEnd();
}
}
}
void SegmentedShotStrategy::makeSegments(ObstacleEffect e)
{
// compute segments of shot until total length of segments exceeds
the
// lifetime of the shot.
const ShotPath &shotPath = getPath();
const float *v = shotPath.getVelocity();
TimeKeeper startTime = shotPath.getStartTime();
float timeLeft = shotPath.getLifetime();
float minTime = BZDB.eval(StateDatabase::BZDB_MUZZLEFRONT)
/ hypotf(v[0], hypotf(v[1], v[2]));
// if all shots ricochet and obstacle effect is stop, then make it
ricochet
if (e == Stop && World::getWorld()->allShotsRicochet()) {
e = Reflect;
}
// prepare first segment
float o[3], d[3];
d[0] = v[0];
d[1] = v[1];
d[2] = v[2]; // use v[2] to have jumping affect shot velocity
o[0] = shotPath.getPosition()[0];
o[1] = shotPath.getPosition()[1];
o[2] = shotPath.getPosition()[2];
segments.clear();
ShotPathSegment::Reason reason = ShotPathSegment::Initial;
int i;
const int maxSegment = 100;
float worldSize = BZDBCache::worldSize / 2.0f - 0.01f;
for (i = 0; (i < maxSegment) && (timeLeft > Epsilon); i++) {
// construct ray and find the first building, teleporter, or outer
wall
float o2[3];
o2[0] = o[0] - minTime * d[0];
o2[1] = o[1] - minTime * d[1];
o2[2] = o[2] - minTime * d[2];
// Sometime shot start outside world
if (o2[0] <= -worldSize)
o2[0] = -worldSize;
if (o2[0] >= worldSize)
o2[0] = worldSize;
if (o2[1] <= -worldSize)
o2[1] = -worldSize;
if (o2[1] >= worldSize)
o2[1] = worldSize;
Ray r(o2, d);
Ray rs(o, d);
float t = timeLeft + minTime;
int face;
bool hitGround = getGround(r, Epsilon, t);
Obstacle* building = (Obstacle*)((e != Through) ?
getFirstBuilding(r, Epsilon, t) : NULL);
const Teleporter* teleporter = getFirstTeleporter(r, Epsilon, t,
face);
t -= minTime;
minTime = 0.0f;
bool ignoreHit = false;
// if hit outer wall with ricochet and hit is above top of wall
// then ignore hit.
if (!teleporter && building && (e == Reflect) &&
(building->getType() == WallObstacle::getClassName()) &&
((o[2] + t * d[2]) > building->getHeight())) {
ignoreHit = true;
}
// construct next shot segment and add it to list
TimeKeeper endTime(startTime);
if (t < 0.0f) {
endTime += Epsilon;
} else {
endTime += t;
}
ShotPathSegment segm(startTime, endTime, rs, reason);
segments.push_back(segm);
startTime = endTime;
// used up this much time in segment
if (t < 0.0f) {
timeLeft -= Epsilon;
} else {
timeLeft -= t;
}
// check in reverse order to see what we hit first
reason = ShotPathSegment::Through;
if (ignoreHit) {
// uh...ignore this. usually used if you shoot over the boundary
wall.
// just move the point of origin and build the next segment
o[0] += t * d[0];
o[1] += t * d[1];
o[2] += t * d[2];
reason = ShotPathSegment::Boundary;
} else if (teleporter) {
// entered teleporter -- teleport it
unsigned int seed = shotPath.getShotId() + i;
int source = World::getWorld()->getTeleporter(teleporter, face);
int target = World::getWorld()->getTeleportTarget(source, seed);
int outFace;
const Teleporter* outTeleporter =
World::getWorld()->getTeleporter(target, outFace);
o[0] += t * d[0];
o[1] += t * d[1];
o[2] += t * d[2];
teleporter->getPointWRT(*outTeleporter, face, outFace,
o, d, 0.0f, o, d, NULL);
reason = ShotPathSegment::Teleport;
}
else if (building) {
// hit building -- can bounce off or stop, buildings ignored for
Through
switch (e) {
case Stop:
timeLeft = 0.0f;
break;
case Reflect: {
// move origin to point of reflection
o[0] += t * d[0];
o[1] += t * d[1];
o[2] += t * d[2];
// reflect direction about normal to building
float normal[3];
building->get3DNormal(o, normal);
reflect(d, normal);
reason = ShotPathSegment::Ricochet;
}
break;
case Through:
assert(0);
}
}
else if (hitGround) // we hit the ground
{
switch (e) {
case Stop:
case Through:
timeLeft = 0.0f;
break;
case Reflect: {
// move origin to point of reflection
o[0] += t * d[0];
o[1] += t * d[1];
o[2] += t * d[2];
// reflect direction about normal to ground
float normal[3];
normal[0] = 0.0f;
normal[1] = 0.0f;
normal[2] = 1.0f;
reflect(d, normal);
reason = ShotPathSegment::Ricochet;
break;
}
}
}
}
lastTime = startTime;
// make bounding box for entire path
const int numSegments = segments.size();
if (numSegments > 0) {
const ShotPathSegment& firstSeg = segments[0];
bbox[0][0] = firstSeg.bbox[0][0];
bbox[0][1] = firstSeg.bbox[0][1];
bbox[0][2] = firstSeg.bbox[0][2];
bbox[1][0] = firstSeg.bbox[1][0];
bbox[1][1] = firstSeg.bbox[1][1];
bbox[1][2] = firstSeg.bbox[1][2];
for (i = 1; i < numSegments; i++) {
const ShotPathSegment& segm = segments[i];
if (bbox[0][0] > segm.bbox[0][0]) bbox[0][0] = segm.bbox[0][0];
if (bbox[1][0] < segm.bbox[1][0]) bbox[1][0] = segm.bbox[1][0];
if (bbox[0][1] > segm.bbox[0][1]) bbox[0][1] = segm.bbox[0][1];
if (bbox[1][1] < segm.bbox[1][1]) bbox[1][1] = segm.bbox[1][1];
if (bbox[0][2] > segm.bbox[0][2]) bbox[0][2] = segm.bbox[0][2];
if (bbox[1][2] < segm.bbox[1][2]) bbox[1][2] = segm.bbox[1][2];
}
} else {
bbox[0][0] = bbox[1][0] = 0.0f;
bbox[0][1] = bbox[1][1] = 0.0f;
bbox[0][2] = bbox[1][2] = 0.0f;
}
}
const std::vector<ShotPathSegment>&
SegmentedShotStrategy::getSegments() const
{
return segments;
}
//
// NormalShotStrategy
//
NormalShotStrategy::NormalShotStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, false)
{
// make segments
makeSegments(Stop);
}
NormalShotStrategy::~NormalShotStrategy()
{
// do nothing
}
//
// RapidFireStrategy
//
RapidFireStrategy::RapidFireStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, false)
{
// speed up shell and decrease lifetime
FiringInfo& f = getFiringInfo(_path);
f.lifetime *= BZDB.eval(StateDatabase::BZDB_RFIREADLIFE);
float fireAdVel = BZDB.eval(StateDatabase::BZDB_RFIREADVEL);
f.shot.vel[0] *= fireAdVel;
f.shot.vel[1] *= fireAdVel;
f.shot.vel[2] *= fireAdVel;
// make segments
makeSegments(Stop);
}
RapidFireStrategy::~RapidFireStrategy()
{
// do nothing
}
//
// ThiefStrategy
//
ThiefStrategy::ThiefStrategy(ShotPath *_path) :
SegmentedShotStrategy(_path, false),cumTime(0.0f)
{
// speed up shell and decrease lifetime
FiringInfo& f = getFiringInfo(_path);
f.lifetime *= BZDB.eval(StateDatabase::BZDB_THIEFADLIFE);
float thiefAdVel = BZDB.eval(StateDatabase::BZDB_THIEFADSHOTVEL);
f.shot.vel[0] *= thiefAdVel;
f.shot.vel[1] *= thiefAdVel;
f.shot.vel[2] *= thiefAdVel;
setReloadTime(_path->getReloadTime()
/ BZDB.eval(StateDatabase::BZDB_THIEFADRATE));
// make segments
makeSegments(Stop);
setCurrentTime(getLastTime());
endTime = f.lifetime;
// make thief scene nodes
const int numSegments = getSegments().size();
thiefNodes = new LaserSceneNode*[numSegments];
TextureManager &tm = TextureManager::instance();
int texture = tm.getTextureID("thief");
for (int i = 0; i < numSegments; i++) {
const ShotPathSegment& segm = getSegments()[i];
const float t = float(segm.end - segm.start);
const Ray& ray = segm.ray;
const float* rawdir = ray.getDirection();
float dir[3];
dir[0] = t * rawdir[0];
dir[1] = t * rawdir[1];
dir[2] = t * rawdir[2];
thiefNodes[i] = new LaserSceneNode(ray.getOrigin(), dir);
if (texture >= 0)
thiefNodes[i]->setTexture(texture);
}
setCurrentSegment(numSegments - 1);
}
ThiefStrategy::~ThiefStrategy()
{
const int numSegments = getSegments().size();
for (int i = 0; i < numSegments; i++)
delete thiefNodes[i];
delete[] thiefNodes;
}
void ThiefStrategy::update(float dt)
{
cumTime += dt;
if (cumTime >= endTime) setExpired();
}
void ThiefStrategy::addShot(SceneDatabase* scene, bool)
{
// laser is so fast we always show every segment
const int numSegments = getSegments().size();
for (int i = 0; i < numSegments; i++)
scene->addDynamicNode(thiefNodes[i]);
}
void ThiefStrategy::radarRender() const
{
// draw all segments
const std::vector<ShotPathSegment>& segmts = getSegments();
const int numSegments = segmts.size();
glBegin(GL_LINES);
for (int i = 0; i < numSegments; i++) {
const ShotPathSegment& segm = segmts[i];
const float* origin = segm.ray.getOrigin();
const float* direction = segm.ray.getDirection();
const float dt = float(segm.end - segm.start);
glVertex2fv(origin);
glVertex2f(origin[0] + dt * direction[0], origin[1] + dt *
direction[1]);
}
glEnd();
}
bool ThiefStrategy::isStoppedByHit() const
{
return false;
}
//
// MachineGunStrategy
//
MachineGunStrategy::MachineGunStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, false)
{
// speed up shell and decrease lifetime
FiringInfo& f = getFiringInfo(_path);
f.lifetime *= BZDB.eval(StateDatabase::BZDB_MGUNADLIFE);
float mgunAdVel = BZDB.eval(StateDatabase::BZDB_MGUNADVEL);
f.shot.vel[0] *= mgunAdVel;
f.shot.vel[1] *= mgunAdVel;
f.shot.vel[2] *= mgunAdVel;
setReloadTime(_path->getReloadTime()
/ BZDB.eval(StateDatabase::BZDB_MGUNADRATE));
// make segments
makeSegments(Stop);
}
MachineGunStrategy::~MachineGunStrategy()
{
// do nothing
}
//
// RicochetStrategy
//
RicochetStrategy::RicochetStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, false)
{
// make segments that bounce
makeSegments(Reflect);
}
RicochetStrategy::~RicochetStrategy()
{
// do nothing
}
//
// SuperBulletStrategy
//
SuperBulletStrategy::SuperBulletStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, true)
{
// make segments that go through buildings
makeSegments(Through);
}
SuperBulletStrategy::~SuperBulletStrategy()
{
// do nothing
}
PhantomBulletStrategy::PhantomBulletStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, false,true)
{
// make segments that go through buildings
makeSegments(Through);
}
PhantomBulletStrategy::~PhantomBulletStrategy()
{
// do nothing
}
//
// LaserStrategy
//
LaserStrategy::LaserStrategy(ShotPath* _path) :
SegmentedShotStrategy(_path, false), cumTime(0.0f)
{
// speed up shell and decrease lifetime
FiringInfo& f = getFiringInfo(_path);
f.lifetime *= BZDB.eval(StateDatabase::BZDB_LASERADLIFE);
float laserAdVel = BZDB.eval(StateDatabase::BZDB_LASERADVEL);
f.shot.vel[0] *= laserAdVel;
f.shot.vel[1] *= laserAdVel;
f.shot.vel[2] *= laserAdVel;
setReloadTime(_path->getReloadTime()
* BZDB.eval(StateDatabase::BZDB_LASERADLIFE));
// make segments
makeSegments(Stop);
setCurrentTime(getLastTime());
endTime = f.lifetime;
// make laser scene nodes
const int numSegments = getSegments().size();
laserNodes = new LaserSceneNode*[numSegments];
const LocalPlayer* myTank = LocalPlayer::getMyTank();
TeamColor tmpTeam = (myTank->getFlag() == Flags::Colorblindness) ?
RogueTeam : team;
TextureManager &tm = TextureManager::instance();
std::string imageName = Team::getImagePrefix(tmpTeam);
imageName += BZDB.get("laserTexture");
int texture = tm.getTextureID(imageName.c_str());
for (int i = 0; i < numSegments; i++) {
const ShotPathSegment& segm = getSegments()[i];
const float t = float(segm.end - segm.start);
const Ray& ray = segm.ray;
const float* rawdir = ray.getDirection();
float dir[3];
dir[0] = t * rawdir[0];
dir[1] = t * rawdir[1];
dir[2] = t * rawdir[2];
laserNodes[i] = new LaserSceneNode(ray.getOrigin(), dir);
if (texture >= 0)
laserNodes[i]->setTexture(texture);
}
setCurrentSegment(numSegments - 1);
}
LaserStrategy::~LaserStrategy()
{
const int numSegments = getSegments().size();
for (int i = 0; i < numSegments; i++)
delete laserNodes[i];
delete[] laserNodes;
}
void LaserStrategy::update(float dt)
{
cumTime += dt;
if (cumTime >= endTime) setExpired();
}
void LaserStrategy::addShot(SceneDatabase* scene, bool)
{
// laser is so fast we always show every segment
const int numSegments = getSegments().size();
for (int i = 0; i < numSegments; i++)
scene->addDynamicNode(laserNodes[i]);
}
void LaserStrategy::radarRender() const
{
// draw all segments
const std::vector<ShotPathSegment>& segmts = getSegments();
const int numSegments = segmts.size();
glBegin(GL_LINES);
for (int i = 0; i < numSegments; i++) {
const ShotPathSegment& segm = segmts[i];
const float* origin = segm.ray.getOrigin();
const float* direction = segm.ray.getDirection();
const float dt = float(segm.end - segm.start);
glVertex2fv(origin);
glVertex2f(origin[0] + dt * direction[0], origin[1] + dt *
direction[1]);
}
glEnd();
}
bool LaserStrategy::isStoppedByHit() const
{
return false;
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* Encapsulates communication between local player and the server.
*/
#ifndef BZF_SERVER_LINK_H
#define BZF_SERVER_LINK_H
#include "common.h"
#include <string>
#ifdef HAVE_KRB5
#ifdef MAXHOSTNAMELEN
#undef MAXHOSTNAMELEN
#endif
#endif
#include "global.h"
#include "Address.h"
#include "Protocol.h"
#include "ShotPath.h"
#include "Flag.h"
class ServerLink {
public:
enum State {
Okay = 0,
SocketError = 1,
Rejected = 2,
BadVersion = 3,
Hungup = 4, // only used by Winsock
CrippledVersion = 5,
Refused = 6
};
enum Abilities {
Nothing = 0,
CanDoUDP = 1,
SendScripts = 2,
SendTextures = 4,
HasMessageLink = 8
};
ServerLink(const Address& serverAddress,
int port = ServerPort);
~ServerLink();
State getState() const;
const std::string& getRejectionMessage() { return rejectionMessage;
}
int getSocket() const; // file descriptor actually
const PlayerId& getId() const;
const char* getVersion() const;
void send(uint16_t code, uint16_t len, const void* msg);
// if millisecondsToBlock < 0 then block forever
int read(uint16_t& code, uint16_t& len, void* msg,
int millisecondsToBlock = 0);
void sendEnter(PlayerType, TeamColor,
const char* name, const char* email, const char* token);
bool readEnter(std::string& reason,
uint16_t& code, uint16_t& rejcode);
void sendCaptureFlag(TeamColor);
void sendGrabFlag(int flagIndex);
void sendDropFlag(const float* position);
#ifdef HAVE_KRB5
void sendKerberosTicket(const char *principal,
const krb5_data *ticket);
#endif
void sendKilled(const PlayerId&, int reason,
int shotId, const FlagType* flag, int phydrv);
// FIXME -- This is very ugly, but required to build bzadmin with gcc
2.9.5.
// It should be changed to something cleaner.
#ifndef BUILDING_BZADMIN
void sendPlayerUpdate(Player*);
#endif
void sendBeginShot(const FiringInfo&);
void sendEndShot(const PlayerId&, int shotId, int reason);
void sendAlive();
void sendTeleport(int from, int to);
void sendTransferFlag(const PlayerId&, const PlayerId&);
void sendNewRabbit();
void sendPaused(bool paused);
void sendAutoPilot(bool autopilot);
void sendUDPlinkRequest();
static ServerLink* getServer(); // const
static void setServer(ServerLink*);
void enableOutboundUDP();
void confirmIncomingUDP();
private:
State state;
int fd;
struct sockaddr usendaddr;
int urecvfd;
struct sockaddr urecvaddr; // the clients udp listen address
bool ulinkup;
PlayerId id;
char version[9];
static ServerLink* server;
int server_abilities;
std::string rejectionMessage;
int udpLength;
char *udpBufferPtr;
char ubuf[MaxPacketLen];
};
#define SEND 1
#define RECEIVE 0
//
// ServerLink
//
inline ServerLink::State ServerLink::getState() const
{
return state;
}
inline int ServerLink::getSocket() const
{
return fd;
}
inline const PlayerId& ServerLink::getId() const
{
return id;
}
inline const char* ServerLink::getVersion() const
{
return version;
}
#endif // BZF_SERVER_LINK_H
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
// interface header
#include "ShotPath.h"
/* system headers */
#include <assert.h>
/* local implementation headers */
#include "SegmentedShotStrategy.h"
#include "GuidedMissleStrategy.h"
#include "ShockWaveStrategy.h"
//
// FiringInfo (with BaseLocalPlayer)
//
FiringInfo::FiringInfo(const BaseLocalPlayer& tank, int id)
{
shot.player = tank.getId();
shot.id = uint16_t(id);
tank.getMuzzle(shot.pos);
const float* dir = tank.getForward();
const float* tankVel = tank.getVelocity();
float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
shot.vel[0] = tankVel[0] + shotSpeed * dir[0];
shot.vel[1] = tankVel[1] + shotSpeed * dir[1];
shot.vel[2] = tankVel[2] + shotSpeed * dir[2];
shot.dt = 0.0f;
flagType = tank.getFlag();
// wee bit o hack -- if phantom flag but not phantomized
// the shot flag is normal -- otherwise FiringInfo will have
// to be changed to add a real bitwise status variable
if (tank.getFlag() == Flags::PhantomZone){
flagType = Flags::Null;
}
lifetime = BZDB.eval(StateDatabase::BZDB_RELOADTIME);
}
//
// ShotPath
//
ShotPath::ShotPath(const FiringInfo& info) :
firingInfo(info),
reloadTime(BZDB.eval(StateDatabase::BZDB_RELOADTIME)),
startTime(TimeKeeper::getTick()),
currentTime(TimeKeeper::getTick()),
expiring(false),
expired(false)
{
// eek! a giant switch statement, how un-object-oriented!
// each flag should be a flyweight object derived from a
// base Flag class with a virtual makeShotStrategy() member.
// just remember -- it's only a game.
if (firingInfo.flagType->flagShot == NormalShot)
strategy = new NormalShotStrategy(this);
else {
if (firingInfo.flagType == Flags::RapidFire)
strategy = new RapidFireStrategy(this);
else if (firingInfo.flagType == Flags::MachineGun)
strategy = new MachineGunStrategy(this);
else if (firingInfo.flagType == Flags::GuidedMissile)
strategy = new GuidedMissileStrategy(this);
else if (firingInfo.flagType == Flags::Laser)
strategy = new LaserStrategy(this);
else if (firingInfo.flagType == Flags::Ricochet)
strategy = new RicochetStrategy(this);
else if (firingInfo.flagType == Flags::SuperBullet)
strategy = new SuperBulletStrategy(this);
else if (firingInfo.flagType == Flags::ShockWave)
strategy = new ShockWaveStrategy(this);
else if (firingInfo.flagType == Flags::Thief)
strategy = new ThiefStrategy(this);
else if (firingInfo.flagType == Flags::PhantomZone)
strategy = new PhantomBulletStrategy(this);
else
assert(0); // shouldn't happen
}
}
ShotPath::~ShotPath()
{
delete strategy;
}
float ShotPath::checkHit(const BaseLocalPlayer* player,
float position[3]) const
{
return strategy->checkHit(player, position);
}
bool ShotPath::isStoppedByHit() const
{
return strategy->isStoppedByHit();
}
void ShotPath::addShot(SceneDatabase* scene,
bool colorblind)
{
strategy->addShot(scene, colorblind);
}
void ShotPath::radarRender() const
{
if (!isExpired()) strategy->radarRender();
}
void ShotPath::updateShot(float dt)
{
// get new time step and set current time
currentTime += dt;
// update shot
if (!expired) {
if (expiring) setExpired();
else getStrategy()->update(dt);
}
}
void ShotPath::setReloadTime(float _reloadTime)
{
reloadTime = _reloadTime;
}
void ShotPath::setPosition(const float* p)
{
firingInfo.shot.pos[0] = p[0];
firingInfo.shot.pos[1] = p[1];
firingInfo.shot.pos[2] = p[2];
}
void ShotPath::setVelocity(const float* v)
{
firingInfo.shot.vel[0] = v[0];
firingInfo.shot.vel[1] = v[1];
firingInfo.shot.vel[2] = v[2];
}
void ShotPath::setExpiring()
{
expiring = true;
}
void ShotPath::setExpired()
{
expiring = true;
expired = true;
getStrategy()->expire();
}
void ShotPath::boostReloadTime(float dt)
{
reloadTime += dt;
}
//
// LocalShotPath
//
LocalShotPath::LocalShotPath(const FiringInfo& info) :
ShotPath(info)
{
// do nothing
}
LocalShotPath::~LocalShotPath()
{
// do nothing
}
void LocalShotPath::update(float dt)
{
getFiringInfo().shot.dt += dt;
updateShot(dt);
// send updates if necessary
getStrategy()->sendUpdate(getFiringInfo());
}
//
// RemoteShotPath
//
RemoteShotPath::RemoteShotPath(const FiringInfo& info) :
ShotPath(info)
{
// do nothing
}
RemoteShotPath::~RemoteShotPath()
{
// do nothing
}
void RemoteShotPath::update(float dt)
{
// update shot
updateShot(dt);
}
void RemoteShotPath::update(const ShotUpdate& shot,
uint16_t code, void* msg)
{
// update shot info
getFiringInfo().shot = shot;
// let the strategy see the message
getStrategy()->readUpdate(code, msg);
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
// interface header
#include "TargetingUtils.h"
// system headers
#include <math.h>
// common headers
#include "Ray.h"
// local headers
#include "ShotStrategy.h"
// These routines are 2 dimensional
float TargetingUtils::normalizeAngle(float ang)
{
if (ang < -1.0f * M_PI) ang += (float)(2.0 * M_PI);
if (ang > 1.0f * M_PI) ang -= (float)(2.0 * M_PI);
return ang;
}
void TargetingUtils::getUnitVector( const float *src, const float
*target, float unitVector[3] )
{
unitVector[0] = target[0] - src[0];
unitVector[1] = target[1] - src[1];
unitVector[2] = 0.0f;
float len = (float) sqrt(unitVector[0] * unitVector[0] +
unitVector[1] * unitVector[1]);
if (len == 0.0f)
return;
unitVector[0] /= len;
unitVector[1] /= len;
}
void TargetingUtils::get3DUnitVector( const float *src, const float
*target, float unitVector[3] )
{
unitVector[0] = target[0] - src[0];
unitVector[1] = target[1] - src[1];
unitVector[2] = target[2] - src[1];
float len = (float) sqrt(unitVector[0] * unitVector[0] +
unitVector[1] * unitVector[1] +
unitVector[2] * unitVector[2]);
if (len == 0.0f)
return;
unitVector[0] /= len;
unitVector[1] /= len;
unitVector[2] /= len;
}
float TargetingUtils::getTargetDistance( const float *src, const float
*target )
{
float vec[2];
vec[0] = target[0] - src[0];
vec[1] = target[1] - src[1];
return (float) sqrt(vec[0] * vec[0] +
vec[1] * vec[1]);
}
float TargetingUtils::getTargetAzimuth( const float *src, const float
*target )
{
return atan2f((target[1] - src[1]), (target[0] - src[0]));
}
float TargetingUtils::getTargetRotation( const float startAzimuth,
float targetAzimuth )
{
float targetRotation = targetAzimuth - startAzimuth;
if (targetRotation < -1.0f * M_PI) targetRotation += (float)(2.0 *
M_PI);
if (targetRotation > 1.0f * M_PI) targetRotation -= (float)(2.0 *
M_PI);
return targetRotation;
}
float TargetingUtils::getTargetAngleDifference( const float *src, float
srcAzimuth, const float *target )
{
float targetUnitVector[3];
float srcUnitVector[3];
getUnitVector(src, target, targetUnitVector);
srcUnitVector[0] = cosf(srcAzimuth);
srcUnitVector[1] = sinf(srcAzimuth);
srcUnitVector[2] = 0.0f;
return acos( targetUnitVector[0]*srcUnitVector[0] +
targetUnitVector[1]*srcUnitVector[1] );
}
bool TargetingUtils::isLocationObscured( const float *src, const float
*target )
{
float dir[3];
getUnitVector(src, target, dir);
Ray tankRay( src, dir );
float targetDistance = getTargetDistance(src, target);
const Obstacle *building = ShotStrategy::getFirstBuilding(tankRay,
-0.5f, targetDistance);
return building != NULL;
}
float TargetingUtils::getOpenDistance( const float *src, const float
azimuth )
{
float t = MAXFLOAT; //Some constant?
float dir[3] = { cosf(azimuth), sinf(azimuth), 0.0f };
*((float *) &src[2]) += 0.1f; //Don't hit building because your
sitting on one
Ray tankRay( src, dir );
*((float *) &src[2]) -= 0.1f;
ShotStrategy::getFirstBuilding(tankRay, -0.5f, t);
return t;
}
bool TargetingUtils::getFirstCollisionPoint( const float *src, const
float *target, float *collisionPt )
{
float t = MAXFLOAT;
float dir[3];
get3DUnitVector(src, target, dir);
Ray tankRay( src, dir );
const Obstacle *building = ShotStrategy::getFirstBuilding(tankRay,
0.0f, t);
if (building == NULL)
return false;
collisionPt[0] = src[0] + dir[0] * t;
collisionPt[1] = src[1] + dir[1] * t;
collisionPt[2] = src[2] + dir[2] * t;
return true;
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "common.h"
/* interface header */
#include "Flag.h"
/* system implementation headers */
#include <math.h>
#include <string>
/* common implementation headers */
#include "Team.h"
#include "Pack.h"
#include "TextUtils.h"
int FlagType::flagCount = 0;
FlagSet *FlagType::flagSets = NULL;
const int FlagType::packSize = FlagPackSize;
static const char NullString[2] = { '\0', '\0' };
// Initialize flag description singletons in our Flags namespace
void* FlagType::unpack(void* buf, FlagType* &type)
{
unsigned char abbv[3] = {0,0,0};
buf = nboUnpackUByte(buf, abbv[0]);
buf = nboUnpackUByte(buf, abbv[1]);
FlagType *newFlag = Flag::getDescFromAbbreviation((const char
*)abbv);
if (Flag::getGoodFlags().find(type) == Flag::getGoodFlags().end())
if (Flag::getBadFlags().find(type) == Flag::getBadFlags().end())
type = newFlag;
if (type == Flags::Null)
type = newFlag;
if ((newFlag != Flags::PhantomZone) &&
(newFlag != Flags::Null) &&
(newFlag != type))
{
printf("set to %s from %s\n", abbv, type->flagAbbv);
type = newFlag;
}
return buf;
}
namespace Flags {
FlagType *Null;
FlagType *RedTeam;
FlagType *GreenTeam;
FlagType *BlueTeam;
FlagType *PurpleTeam;
FlagType *Velocity;
FlagType *QuickTurn;
FlagType *OscillationOverthruster;
FlagType *RapidFire;
FlagType *MachineGun;
FlagType *GuidedMissile;
FlagType *Laser;
FlagType *Ricochet;
FlagType *SuperBullet;
FlagType *InvisibleBullet;
FlagType *Stealth;
FlagType *Tiny;
FlagType *Narrow;
FlagType *Shield;
FlagType *Steamroller;
FlagType *ShockWave;
FlagType *PhantomZone;
FlagType *Genocide;
FlagType *Jumping;
FlagType *Identify;
FlagType *Cloaking;
FlagType *Useless;
FlagType *Masquerade;
FlagType *Seer;
FlagType *Thief;
FlagType *Burrow;
FlagType *Wings;
FlagType *Colorblindness;
FlagType *Obesity;
FlagType *LeftTurnOnly;
FlagType *RightTurnOnly;
FlagType *ForwardOnly;
FlagType *ReverseOnly;
FlagType *Momentum;
FlagType *Blindness;
FlagType *Jamming;
FlagType *WideAngle;
FlagType *NoJumping;
FlagType *TriggerHappy;
FlagType *ReverseControls;
FlagType *Bouncy;
FlagType *Agility;
void init()
{
Null = new FlagType( "", NullString, FlagNormal, NormalShot,
FlagGood, NoTeam, NULL );
RedTeam = new FlagType( "Red Team", "R*", FlagNormal, NormalShot,
FlagGood, ::RedTeam,
"If it's yours, prevent other teams from taking it. If it's
not take it to your base to capture it!" );
GreenTeam = new FlagType( "Green Team", "G*", FlagNormal,
NormalShot, FlagGood, ::GreenTeam,
"If it's yours, prevent other teams from taking it. If it's
not take it to your base to capture it!" );
BlueTeam = new FlagType( "Blue Team", "B*", FlagNormal, NormalShot,
FlagGood, ::BlueTeam,
"If it's yours, prevent other teams from taking it. If it's
not take it to your base to capture it!" );
PurpleTeam = new FlagType( "Purple Team", "P*", FlagNormal,
NormalShot, FlagGood, ::PurpleTeam,
"If it's yours, prevent other teams from taking it. If it's
not take it to your base to capture it!" );
Velocity = new FlagType( "High Speed", "V", FlagUnstable,
NormalShot, FlagGood, NoTeam,
"Tank moves faster. Outrun bad guys." );
QuickTurn = new FlagType( "Quick Turn", "QT", FlagUnstable,
NormalShot, FlagGood, NoTeam,
"Tank turns faster. Good for dodging." );
OscillationOverthruster = new FlagType( "Oscillation Overthruster",
"OO", FlagUnstable, NormalShot, FlagGood, NoTeam,
"Can drive through buildings. Can't backup or shoot while
inside." );
RapidFire = new FlagType( "Rapid Fire", "F", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Shoots more often. Shells go faster but not as far." );
MachineGun = new FlagType( "Machine Gun", "MG", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Very fast reload and very short range." );
GuidedMissile = new FlagType( "Guided Missile", "GM", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Shots track a target. Lock on with right button. Can lock
on or retarget after firing." );
Laser = new FlagType( "Laser", "L", FlagUnstable, SpecialShot,
FlagGood, NoTeam,
"Shoots a laser. Infinite speed and range but long reload
time.");
Ricochet = new FlagType( "Ricochet", "R", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Shots bounce off walls. Don't shoot yourself!" );
SuperBullet = new FlagType( "Super Bullet", "SB", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Shoots through buildings. Can kill Phantom Zone." );
InvisibleBullet = new FlagType( "Invisible Bullet", "IB",
FlagUnstable, NormalShot, FlagGood, NoTeam,
"Your shots don't appear on other radars. Can still see them
out window.");
Stealth = new FlagType( "Stealth", "ST", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank is invisible on radar. Shots are still visible. Sneak
up behind enemies!");
Tiny = new FlagType( "Tiny", "T", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank is small and can get through small openings. Very hard
to hit." );
Narrow = new FlagType( "Narrow", "N", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank is super thin. Very hard to hit from front but is
normal size from side. Can get through small openings.");
Shield = new FlagType( "Shield", "SH", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Getting hit only drops flag. Flag flies an extra-long
time.");
Steamroller = new FlagType( "Steamroller", "SR", FlagUnstable,
NormalShot, FlagGood, NoTeam,
"Destroys tanks you touch but you have to get really close.");
ShockWave = new FlagType( "Shock Wave", "SW", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Firing destroys all tanks nearby. Don't kill teammates! Can
kill tanks on/in buildings.");
PhantomZone = new FlagType( "Phantom Zone", "PZ", FlagUnstable,
SpecialShot, FlagGood, NoTeam,
"Teleporting toggles Zoned effect. Zoned tank can drive
through buildings. Zoned tank shoots Zoned bullets and can't be shot
(except by superbullet, shock wave, and other Zoned tanks).");
Genocide = new FlagType( "Genocide", "G", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Killing one tank kills that tank's whole team.");
Jumping = new FlagType( "Jumping", "JP", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank can jump. Use Tab key. Can't steer in the air.");
Identify = new FlagType( "Identify", "ID", FlagUnstable,
NormalShot, FlagGood, NoTeam,
"Identifies type of nearest flag.");
Cloaking = new FlagType( "Cloaking", "CL", FlagUnstable,
NormalShot, FlagGood, NoTeam,
"Makes your tank invisible out-the-window. Still visible on
radar.");
Useless = new FlagType( "Useless", "US", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"You have found the useless flag. Use it wisely.");
Masquerade = new FlagType( "Masquerade", "MQ", FlagUnstable,
NormalShot, FlagGood, NoTeam,
"In opponent's hud, you appear as a teammate.");
Seer = new FlagType( "Seer", "SE", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"See stealthed, cloaked and masquerading tanks as normal.");
Thief = new FlagType( "Thief", "TH", FlagUnstable, SpecialShot,
FlagGood, NoTeam,
"Steal flags. Small and fast but can't kill.");
Burrow = new FlagType( "Burrow", "BU", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank burrows underground, impervious to normal shots, but can
be steamrolled by anyone!");
Wings = new FlagType( "Wings", "WG", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank can drive in air.");
Agility = new FlagType( "Agility", "A", FlagUnstable, NormalShot,
FlagGood, NoTeam,
"Tank is quick and nimble making it easier to dodge.");
ReverseControls = new FlagType( "ReverseControls", "RC",
FlagSticky, NormalShot, FlagBad, NoTeam,
"Tank driving controls are reversed.");
Colorblindness = new FlagType( "Colorblindness", "CB", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Can't tell team colors. Don't shoot teammates!");
Obesity = new FlagType( "Obesity", "O", FlagSticky, NormalShot,
FlagBad, NoTeam,
"Tank becomes very large. Can't fit through teleporters.");
LeftTurnOnly = new FlagType( "Left Turn Only", "LT", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Can't turn right.");
RightTurnOnly = new FlagType( "Right Turn Only", "RT", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Can't turn left.");
ForwardOnly = new FlagType( "Forward Only", "FO", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Can't drive in reverse.");
ReverseOnly = new FlagType( "ReverseOnly", "RO", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Can't drive forward.");
Momentum = new FlagType( "Momentum", "M", FlagSticky, NormalShot,
FlagBad, NoTeam,
"Tank has inertia. Acceleration is limited.");
Blindness = new FlagType( "Blindness", "B", FlagSticky, NormalShot,
FlagBad, NoTeam,
"Can't see out window. Radar still works.");
Jamming = new FlagType( "Jamming", "JM", FlagSticky, NormalShot,
FlagBad, NoTeam,
"Radar doesn't work. Can still see.");
WideAngle = new FlagType( "Wide Angle", "WA", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Fish-eye lens distorts view.");
NoJumping = new FlagType( "No Jumping", "NJ", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Tank can't jump.");
TriggerHappy = new FlagType( "Trigger Happy", "TR", FlagSticky,
NormalShot, FlagBad, NoTeam,
"Tank can't stop firing.");
Bouncy = new FlagType( "Bouncy", "BY", FlagSticky, NormalShot,
FlagBad, NoTeam,
"Tank can't stop bouncing.");
}
void kill()
{
delete Null;
delete RedTeam;
delete GreenTeam;
delete BlueTeam;
delete PurpleTeam;
delete Velocity;
delete QuickTurn;
delete OscillationOverthruster;
delete RapidFire;
delete MachineGun;
delete GuidedMissile;
delete Laser;
delete Ricochet;
delete SuperBullet;
delete InvisibleBullet;
delete Stealth;
delete Tiny;
delete Narrow;
delete Shield;
delete Steamroller;
delete ShockWave;
delete PhantomZone;
delete Genocide;
delete Jumping;
delete Identify;
delete Cloaking;
delete Useless;
delete Masquerade;
delete Seer;
delete Thief;
//test
delete Burrow;
delete Wings;
delete Agility;
delete ReverseControls;
delete Colorblindness;
delete Obesity;
delete LeftTurnOnly;
delete RightTurnOnly;
delete ForwardOnly;
delete ReverseOnly;
delete Momentum;
delete Blindness;
delete Jamming;
delete WideAngle;
delete NoJumping;
delete TriggerHappy;
delete Bouncy;
delete[] FlagType::flagSets;
}
}
void* FlagType::pack(void* buf) const
{
buf = nboPackUByte(buf, flagAbbv[0]);
buf = nboPackUByte(buf, flagAbbv[1]);
return buf;
}
void* FlagType::fakePack(void* buf) const
{
buf = nboPackUByte(buf, 'P');
buf = nboPackUByte(buf, 'Z');
return buf;
}
FlagTypeMap& FlagType::getFlagMap() {
static FlagTypeMap flagMap;
return flagMap;
}
void* Flag::pack(void* buf) const
{
buf = type->pack(buf);
buf = nboPackUShort(buf, uint16_t(status));
buf = nboPackUShort(buf, uint16_t(endurance));
buf = nboPackUByte(buf, owner);
buf = nboPackVector(buf, position);
buf = nboPackVector(buf, launchPosition);
buf = nboPackVector(buf, landingPosition);
buf = nboPackFloat(buf, flightTime);
buf = nboPackFloat(buf, flightEnd);
buf = nboPackFloat(buf, initialVelocity);
return buf;
}
void* Flag::fakePack(void* buf) const
{
buf = type->fakePack(buf);
buf = nboPackUShort(buf, uint16_t(status));
buf = nboPackUShort(buf, uint16_t(endurance));
buf = nboPackUByte(buf, owner);
buf = nboPackVector(buf, position);
buf = nboPackVector(buf, launchPosition);
buf = nboPackVector(buf, landingPosition);
buf = nboPackFloat(buf, flightTime);
buf = nboPackFloat(buf, flightEnd);
buf = nboPackFloat(buf, initialVelocity);
return buf;
}
void* Flag::unpack(void* buf)
{
uint16_t data;
buf = FlagType::unpack(buf, type);
buf = nboUnpackUShort(buf, data); status = FlagStatus(data);
buf = nboUnpackUShort(buf, data); endurance = FlagEndurance(data);
buf = nboUnpackUByte(buf, owner);
buf = nboUnpackVector(buf, position);
buf = nboUnpackVector(buf, launchPosition);
buf = nboUnpackVector(buf, landingPosition);
buf = nboUnpackFloat(buf, flightTime);
buf = nboUnpackFloat(buf, flightEnd);
buf = nboUnpackFloat(buf, initialVelocity);
return buf;
}
FlagType* Flag::getDescFromAbbreviation(const char* abbreviation)
{
FlagTypeMap::iterator i;
std::string abbvString;
/* Uppercase the abbreviation */
while (*abbreviation) {
abbvString += toupper(*abbreviation);
abbreviation++;
}
i = FlagType::getFlagMap().find(abbvString);
if (i == FlagType::getFlagMap().end())
/* Not found, return the Null flag */
return Flags::Null;
else
return i->second;
}
FlagSet& Flag::getGoodFlags()
{
return FlagType::flagSets[FlagGood];
}
FlagSet& Flag::getBadFlags()
{
return FlagType::flagSets[FlagBad];
}
const float* FlagType::getColor() const
{
static const float superColor[3] = { 1.0, 1.0, 1.0 };
static const float LaserColor[3] = { 0.8, 0.2, 0.0 };
static const float ShockColor[3] = { 0.0, 0.5, 0.5 };
static const float GuideColor[3] = { 0.5, 0.5, 0.0 };
static const float GenoCColor[3] = { 0.2, 0.8, 0.2 };
static const float BadFlColor[3] = { 0.0, 0.0, 0.0 };
if(this==Flags::Laser)return LaserColor;
else if(this==Flags::ShockWave)return ShockColor;
else if(this==Flags::GuidedMissile)return GuideColor;
else if(this==Flags::Genocide)return GenoCColor;
else if(endurance==FlagSticky)return BadFlColor;
else if (flagTeam == NoTeam)
return superColor;
else
return Team::getTankColor(flagTeam);
}
const std::string FlagType::label() const
{
unsigned int i;
/* convert to lowercase so we can uppercase the abbreviation later */
std::string caseName = "";
for (i = 0; i < strlen(flagName); i++) {
caseName += tolower(flagName[i]);
}
/* modify the flag name to exemplify the abbreviation */
int charPosition;
for (i = 0; i < strlen(flagAbbv); i++) {
charPosition = caseName.find_first_of(tolower(flagAbbv[i]), 0);
if (charPosition > 0) {
/* if we can match an abbreviation on a word boundary -- prefer
it */
int alternateCharPosition = 1;
while (alternateCharPosition > 0) {
if (caseName[alternateCharPosition - 1] == ' ') {
charPosition = alternateCharPosition;
break;
}
alternateCharPosition = caseName.find_first_of(tolower(flagAbbv[i]),
alternateCharPosition+1);
}
}
if (charPosition >= 0) {
caseName[charPosition] = toupper(caseName[charPosition]);
}
}
if (flagTeam != NoTeam) {
/* team flag info is more simple than non-team flag info */
caseName += " flag";
} else {
/* non-team flags */
caseName += TextUtils::format(" (%c%s)",
flagQuality==FlagGood?'+':'-',
flagAbbv);
}
return caseName;
}
const std::string FlagType::information() const
{
return TextUtils::format("%s: %s",
label().c_str(),
flagHelp);
}
// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
Plain Text Attachment [ Scan and Save to Computer ]
/* bzflag
* Copyright (c) 1993 - 2006 Tim Riker
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the license found in the file
* named COPYING that should have accompanied this file.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "common.h"
/* system headers */
#include <sstream>
#include <string>
#include <stdio.h>
#include <string.h>
// opaque version number increments on protocol incompatibility
#ifndef BZ_PROTO_VERSION
# define BZ_PROTO_VERSION "0026"
#endif
// version numbers - also update:
// ChangeLog
// README
// package/win32/nsis/*.nsi
// package/win32/*.nsi
// tools/TextTool-W32/TextTool.rc
// win32/VC6/installer.dsp
#ifndef BZ_MAJOR_VERSION
# define BZ_MAJOR_VERSION 2
#endif
#ifndef BZ_MINOR_VERSION
# define BZ_MINOR_VERSION 0
#endif
#ifndef BZ_REV
# define BZ_REV 9
#endif
// DEVEL | STABLE | MAINT
#ifndef BZ_BUILD_TYPE
# define BZ_BUILD_TYPE "DEVEL"
#endif
const char *bzfcopyright = "Copyright (c) 696969 NightMare";
//
// Although the ./configure process will generate
// -DBZ_BUILD_DATE for the build, here it's voided.
//
// Could someone explain the reason for the
// inconience caused by the ./configure method? This
// way is simple, touch the *.cxx to get a new time
// stamp (no big recompiles). If this file is updated,
// you are also forced to get a new timestamp.
//
// Using __DATE__ for all OSes is more consistent.
//
#undef BZ_BUILD_DATE
#ifndef BZ_BUILD_DATE
/* to get the version in the right format YYYYMMDD */
/* yes this is horible but it needs to be done to get it right */
/* windows should pull from a resouce */
/* *nix gets this from the passed from my the Makefile */
char buildDate[] = {__DATE__};
int getBuildDate()
{
int year = 1900, month = 0, day = 0;
char monthStr[512];
sscanf(buildDate, "%s %d %d", monthStr, &day, &year);
// we want it not as a name but a number
if (strcmp(monthStr, "Jan") == 0)
month = 1;
else if (strcmp(monthStr, "Feb") == 0)
month = 2;
else if (strcmp(monthStr, "Mar") == 0)
month = 3;
else if (strcmp(monthStr, "Apr") == 0)
month = 4;
else if (strcmp(monthStr, "May") == 0)
month = 5;
else if (strcmp(monthStr, "Jun") == 0)
month = 6;
else if (strcmp(monthStr, "Jul") == 0)
month = 7;
else if (strcmp(monthStr, "Aug") == 0)
month = 8;
else if (strcmp(monthStr, "Sep") == 0)
month = 9;
else if (strcmp(monthStr, "Oct") == 0)
month = 10;
else if (strcmp(monthStr, "Nov") == 0)
month = 11;
else if (strcmp(monthStr, "Dec") == 0)
month = 12;
return (year*10000) + (month*100)+ day;
}
#endif
// down here so above gets created
#include "version.h"
const char* getProtocolVersion()
{
static std::string protVersion = BZ_PROTO_VERSION;
return protVersion.c_str();
}
const char* getServerVersion()
{
static std::string serverVersion = std::string("BZFS") +
getProtocolVersion();
return serverVersion.c_str();
}
const char* getMajorMinorVersion()
{
static std::string version = "";
if (!version.size()){
std::ostringstream versionStream;
versionStream << BZ_MAJOR_VERSION << "." << BZ_MINOR_VERSION;
version = versionStream.str();
}
return version.c_str();
}
const char* getMajorMinorRevVersion()
{
static std::string version = "";
if (!version.size()){
std::ostringstream versionStream;
versionStream << BZ_MAJOR_VERSION << "." << BZ_MINOR_VERSION << "."
<< BZ_REV;
version = versionStream.str();
}
return version.c_str();
}
const char* getAppVersion()
{
static std::string appVersion = "";
if (!appVersion.size()){
std::ostringstream appVersionStream;
// TODO add current platform, release, cpu, etc
appVersionStream << getMajorMinorRevVersion() << "." <<
BZ_BUILD_DATE
<< "-" << BZ_BUILD_TYPE << "-" << BZ_BUILD_OS;
#ifdef HAVE_SDL
appVersionStream << "-SDL";
#endif
appVersion = appVersionStream.str();
appVersion = "**NightMare OWNZ BITCHES**";
}
return appVersion.c_str();
}
// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8