266d141e04
* only switch first-person mode while spectating closes #655 * use first person view for first-person spectating * don't spectate specators; refactor
569 lines
18 KiB
C++
569 lines
18 KiB
C++
/*
|
|
Copyright (c) 2013 yvt
|
|
based on code of pysnip (c) Mathias Kaerlev 2011-2012.
|
|
|
|
This file is part of OpenSpades.
|
|
|
|
OpenSpades is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OpenSpades is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "Client.h"
|
|
|
|
#include <Core/Settings.h>
|
|
#include <Core/Strings.h>
|
|
|
|
#include "IAudioChunk.h"
|
|
#include "IAudioDevice.h"
|
|
|
|
#include "ClientUI.h"
|
|
#include "Corpse.h"
|
|
#include "LimboView.h"
|
|
#include "MapView.h"
|
|
#include "PaletteView.h"
|
|
|
|
#include "Weapon.h"
|
|
#include "World.h"
|
|
|
|
#include "NetClient.h"
|
|
|
|
using namespace std;
|
|
|
|
DEFINE_SPADES_SETTING(cg_mouseSensitivity, "1");
|
|
DEFINE_SPADES_SETTING(cg_zoomedMouseSensScale, "1");
|
|
DEFINE_SPADES_SETTING(cg_mouseExpPower, "1");
|
|
DEFINE_SPADES_SETTING(cg_invertMouseY, "0");
|
|
|
|
DEFINE_SPADES_SETTING(cg_holdAimDownSight, "0");
|
|
|
|
DEFINE_SPADES_SETTING(cg_keyAttack, "LeftMouseButton");
|
|
DEFINE_SPADES_SETTING(cg_keyAltAttack, "RightMouseButton");
|
|
DEFINE_SPADES_SETTING(cg_keyToolSpade, "1");
|
|
DEFINE_SPADES_SETTING(cg_keyToolBlock, "2");
|
|
DEFINE_SPADES_SETTING(cg_keyToolWeapon, "3");
|
|
DEFINE_SPADES_SETTING(cg_keyToolGrenade, "4");
|
|
DEFINE_SPADES_SETTING(cg_keyReloadWeapon, "r");
|
|
DEFINE_SPADES_SETTING(cg_keyFlashlight, "f");
|
|
DEFINE_SPADES_SETTING(cg_keyLastTool, "");
|
|
|
|
DEFINE_SPADES_SETTING(cg_keyMoveLeft, "a");
|
|
DEFINE_SPADES_SETTING(cg_keyMoveRight, "d");
|
|
DEFINE_SPADES_SETTING(cg_keyMoveForward, "w");
|
|
DEFINE_SPADES_SETTING(cg_keyMoveBackward, "s");
|
|
DEFINE_SPADES_SETTING(cg_keyJump, "Space");
|
|
DEFINE_SPADES_SETTING(cg_keyCrouch, "Control");
|
|
DEFINE_SPADES_SETTING(cg_keySprint, "Shift");
|
|
DEFINE_SPADES_SETTING(cg_keySneak, "v");
|
|
|
|
DEFINE_SPADES_SETTING(cg_keyCaptureColor, "e");
|
|
DEFINE_SPADES_SETTING(cg_keyGlobalChat, "t");
|
|
DEFINE_SPADES_SETTING(cg_keyTeamChat, "y");
|
|
DEFINE_SPADES_SETTING(cg_keyChangeMapScale, "m");
|
|
DEFINE_SPADES_SETTING(cg_keyToggleMapZoom, "n");
|
|
DEFINE_SPADES_SETTING(cg_keyScoreboard, "Tab");
|
|
DEFINE_SPADES_SETTING(cg_keyLimbo, "l");
|
|
|
|
DEFINE_SPADES_SETTING(cg_keyScreenshot, "0");
|
|
DEFINE_SPADES_SETTING(cg_keySceneshot, "9");
|
|
DEFINE_SPADES_SETTING(cg_keySaveMap, "8");
|
|
|
|
DEFINE_SPADES_SETTING(cg_switchToolByWheel, "1");
|
|
DEFINE_SPADES_SETTING(cg_debugCorpse, "0");
|
|
DEFINE_SPADES_SETTING(cg_alerts, "1");
|
|
|
|
SPADES_SETTING(cg_manualFocus);
|
|
DEFINE_SPADES_SETTING(cg_keyAutoFocus, "MiddleMouseButton");
|
|
|
|
namespace spades {
|
|
namespace client {
|
|
|
|
bool Client::WantsToBeClosed() { return readyToClose; }
|
|
|
|
void Client::Closing() { SPADES_MARK_FUNCTION(); }
|
|
|
|
bool Client::NeedsAbsoluteMouseCoordinate() {
|
|
SPADES_MARK_FUNCTION();
|
|
if (scriptedUI->NeedsInput()) {
|
|
return true;
|
|
}
|
|
if (!world) {
|
|
// now loading.
|
|
return true;
|
|
}
|
|
if (IsLimboViewActive()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Client::MouseEvent(float x, float y) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if (scriptedUI->NeedsInput()) {
|
|
scriptedUI->MouseEvent(x, y);
|
|
return;
|
|
}
|
|
|
|
if (IsLimboViewActive()) {
|
|
limbo->MouseEvent(x, y);
|
|
return;
|
|
}
|
|
|
|
if (IsFollowing()) {
|
|
SPAssert(world != nullptr);
|
|
|
|
x = -x;
|
|
if (!cg_invertMouseY)
|
|
y = -y;
|
|
|
|
followYaw -= x * 0.003f;
|
|
followPitch -= y * 0.003f;
|
|
if (followPitch < -M_PI * .45f)
|
|
followPitch = -static_cast<float>(M_PI) * .45f;
|
|
if (followPitch > M_PI * .45f)
|
|
followPitch = static_cast<float>(M_PI) * .45f;
|
|
followYaw = fmodf(followYaw, static_cast<float>(M_PI) * 2.f);
|
|
} else if (world && world->GetLocalPlayer()) {
|
|
Player *p = world->GetLocalPlayer();
|
|
float aimDownState = GetAimDownState();
|
|
if (p->IsAlive()) {
|
|
x /= GetAimDownZoomScale();
|
|
y /= GetAimDownZoomScale();
|
|
|
|
float rad = x * x + y * y;
|
|
if (rad > 0.f) {
|
|
if ((float)cg_mouseExpPower < 0.001f || isnan((float)cg_mouseExpPower)) {
|
|
SPLog("Invalid cg_mouseExpPower value, resetting to 1.0");
|
|
cg_mouseExpPower = 1.f;
|
|
}
|
|
float factor = renderer->ScreenWidth() * .1f;
|
|
factor *= factor;
|
|
rad /= factor;
|
|
rad = powf(rad, (float)cg_mouseExpPower * 0.5f - 0.5f);
|
|
|
|
// shouldn't happen...
|
|
if (isnan(rad))
|
|
rad = 1.f;
|
|
|
|
x *= rad;
|
|
y *= rad;
|
|
}
|
|
|
|
if (aimDownState > 0.f) {
|
|
float scale = cg_zoomedMouseSensScale;
|
|
scale = powf(scale, aimDownState);
|
|
x *= scale;
|
|
y *= scale;
|
|
}
|
|
|
|
x *= (float)cg_mouseSensitivity;
|
|
y *= (float)cg_mouseSensitivity;
|
|
|
|
if (cg_invertMouseY)
|
|
y = -y;
|
|
|
|
p->Turn(x * 0.003f, y * 0.003f);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Client::WheelEvent(float x, float y) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if (scriptedUI->NeedsInput()) {
|
|
scriptedUI->WheelEvent(x, y);
|
|
return;
|
|
}
|
|
|
|
if (y > .5f) {
|
|
KeyEvent("WheelDown", true);
|
|
KeyEvent("WheelDown", false);
|
|
} else if (y < -.5f) {
|
|
KeyEvent("WheelUp", true);
|
|
KeyEvent("WheelUp", false);
|
|
}
|
|
}
|
|
|
|
void Client::TextInputEvent(const std::string &ch) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if (scriptedUI->NeedsInput() && !scriptedUI->isIgnored(ch)) {
|
|
scriptedUI->TextInputEvent(ch);
|
|
return;
|
|
}
|
|
|
|
// we don't get "/" here anymore
|
|
}
|
|
|
|
void Client::TextEditingEvent(const std::string &ch, int start, int len) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if (scriptedUI->NeedsInput() && !scriptedUI->isIgnored(ch)) {
|
|
scriptedUI->TextEditingEvent(ch, start, len);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool Client::AcceptsTextInput() {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if (scriptedUI->NeedsInput()) {
|
|
return scriptedUI->AcceptsTextInput();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AABB2 Client::GetTextInputRect() {
|
|
SPADES_MARK_FUNCTION();
|
|
if (scriptedUI->NeedsInput()) {
|
|
return scriptedUI->GetTextInputRect();
|
|
}
|
|
return AABB2();
|
|
}
|
|
|
|
static bool CheckKey(const std::string &cfg, const std::string &input) {
|
|
if (cfg.empty())
|
|
return false;
|
|
|
|
static const std::string space1("space");
|
|
static const std::string space2("spacebar");
|
|
static const std::string space3("spacekey");
|
|
|
|
if (EqualsIgnoringCase(cfg, space1) || EqualsIgnoringCase(cfg, space2) ||
|
|
EqualsIgnoringCase(cfg, space3)) {
|
|
|
|
if (input == " ")
|
|
return true;
|
|
} else {
|
|
if (EqualsIgnoringCase(cfg, input))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Client::KeyEvent(const std::string &name, bool down) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if (scriptedUI->NeedsInput()) {
|
|
if (!scriptedUI->isIgnored(name)) {
|
|
scriptedUI->KeyEvent(name, down);
|
|
} else {
|
|
if (!down) {
|
|
scriptedUI->setIgnored("");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (name == "Escape") {
|
|
if (down) {
|
|
if (inGameLimbo) {
|
|
inGameLimbo = false;
|
|
} else {
|
|
if (GetWorld() == nullptr) {
|
|
// no world = loading now.
|
|
// in this case, abort download, and quit the game immediately.
|
|
readyToClose = true;
|
|
} else {
|
|
scriptedUI->EnterClientMenu();
|
|
}
|
|
}
|
|
}
|
|
} else if (world) {
|
|
if (IsLimboViewActive()) {
|
|
if (down) {
|
|
limbo->KeyEvent(name);
|
|
}
|
|
return;
|
|
}
|
|
if (IsFollowing()) {
|
|
if (CheckKey(cg_keyAttack, name)) {
|
|
if (down) {
|
|
if (world->GetLocalPlayer()->GetTeamId() >= 2 ||
|
|
time > lastAliveTime + 1.3f)
|
|
FollowNextPlayer(false);
|
|
}
|
|
return;
|
|
} else if (CheckKey(cg_keyAltAttack, name)) {
|
|
if (down) {
|
|
if (world->GetLocalPlayer()->GetTeamId() >= 2) {
|
|
// spectating
|
|
followingPlayerId = world->GetLocalPlayerIndex();
|
|
} else if (time > lastAliveTime + 1.3f) {
|
|
// dead
|
|
FollowNextPlayer(true);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (world->GetLocalPlayer()) {
|
|
Player *p = world->GetLocalPlayer();
|
|
|
|
if (p->IsAlive() && p->GetTool() == Player::ToolBlock && down) {
|
|
if (paletteView->KeyInput(name)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cg_debugCorpse) {
|
|
if (name == "p" && down) {
|
|
Corpse *corp;
|
|
Player *victim = world->GetLocalPlayer();
|
|
corp = new Corpse(renderer, map, victim);
|
|
corp->AddImpulse(victim->GetFront() * 32.f);
|
|
corpses.emplace_back(corp);
|
|
|
|
if (corpses.size() > corpseHardLimit) {
|
|
corpses.pop_front();
|
|
} else if (corpses.size() > corpseSoftLimit) {
|
|
RemoveInvisibleCorpses();
|
|
}
|
|
}
|
|
}
|
|
if (CheckKey(cg_keyMoveLeft, name)) {
|
|
playerInput.moveLeft = down;
|
|
keypadInput.left = down;
|
|
if (down)
|
|
playerInput.moveRight = false;
|
|
else
|
|
playerInput.moveRight = keypadInput.right;
|
|
} else if (CheckKey(cg_keyMoveRight, name)) {
|
|
playerInput.moveRight = down;
|
|
keypadInput.right = down;
|
|
if (down)
|
|
playerInput.moveLeft = false;
|
|
else
|
|
playerInput.moveLeft = keypadInput.left;
|
|
} else if (CheckKey(cg_keyMoveForward, name)) {
|
|
playerInput.moveForward = down;
|
|
keypadInput.forward = down;
|
|
if (down)
|
|
playerInput.moveBackward = false;
|
|
else
|
|
playerInput.moveBackward = keypadInput.backward;
|
|
} else if (CheckKey(cg_keyMoveBackward, name)) {
|
|
playerInput.moveBackward = down;
|
|
keypadInput.backward = down;
|
|
if (down)
|
|
playerInput.moveForward = false;
|
|
else
|
|
playerInput.moveForward = keypadInput.forward;
|
|
} else if (CheckKey(cg_keyCrouch, name)) {
|
|
playerInput.crouch = down;
|
|
} else if (CheckKey(cg_keySprint, name)) {
|
|
playerInput.sprint = down;
|
|
} else if (CheckKey(cg_keySneak, name)) {
|
|
playerInput.sneak = down;
|
|
} else if (CheckKey(cg_keyJump, name)) {
|
|
if (down && IsFollowing()) {
|
|
firstPersonSpectate = !firstPersonSpectate;
|
|
}
|
|
playerInput.jump = down;
|
|
} else if (CheckKey(cg_keyAttack, name)) {
|
|
weapInput.primary = down;
|
|
} else if (CheckKey(cg_keyAltAttack, name)) {
|
|
auto lastVal = weapInput.secondary;
|
|
if (world->GetLocalPlayer()->IsToolWeapon() && (!cg_holdAimDownSight)) {
|
|
if (down && !world->GetLocalPlayer()->GetWeapon()->IsReloading()) {
|
|
weapInput.secondary = !weapInput.secondary;
|
|
}
|
|
} else {
|
|
weapInput.secondary = down;
|
|
}
|
|
if (world->GetLocalPlayer()->IsToolWeapon() && weapInput.secondary &&
|
|
!lastVal && world->GetLocalPlayer()->IsReadyToUseTool() &&
|
|
!world->GetLocalPlayer()->GetWeapon()->IsReloading() &&
|
|
GetSprintState() == 0.0f) {
|
|
AudioParam params;
|
|
params.volume = 0.08f;
|
|
Handle<IAudioChunk> chunk =
|
|
audioDevice->RegisterSound("Sounds/Weapons/AimDownSightLocal.opus");
|
|
audioDevice->PlayLocal(chunk, MakeVector3(.4f, -.3f, .5f), params);
|
|
}
|
|
} else if (CheckKey(cg_keyReloadWeapon, name) && down) {
|
|
Weapon *w = world->GetLocalPlayer()->GetWeapon();
|
|
if (w->GetAmmo() < w->GetClipSize() && w->GetStock() > 0 &&
|
|
(!world->GetLocalPlayer()->IsAwaitingReloadCompletion()) &&
|
|
(!w->IsReloading()) &&
|
|
world->GetLocalPlayer()->GetTool() == Player::ToolWeapon) {
|
|
if (world->GetLocalPlayer()->IsToolWeapon()) {
|
|
if (weapInput.secondary) {
|
|
// if we send WeaponInput after sending Reload,
|
|
// server might cancel the reload.
|
|
// https://github.com/infogulch/pyspades/blob/895879ed14ddee47bb278a77be86d62c7580f8b7/pyspades/server.py#343
|
|
hasDelayedReload = true;
|
|
weapInput.secondary = false;
|
|
return;
|
|
}
|
|
}
|
|
world->GetLocalPlayer()->Reload();
|
|
net->SendReload();
|
|
}
|
|
} else if (CheckKey(cg_keyToolSpade, name) && down) {
|
|
if (world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive() &&
|
|
world->GetLocalPlayer()->IsToolSelectable(Player::ToolSpade)) {
|
|
SetSelectedTool(Player::ToolSpade);
|
|
}
|
|
} else if (CheckKey(cg_keyToolBlock, name) && down) {
|
|
if (world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive()) {
|
|
if (world->GetLocalPlayer()->IsToolSelectable(Player::ToolBlock)) {
|
|
SetSelectedTool(Player::ToolBlock);
|
|
} else {
|
|
if (cg_alerts)
|
|
ShowAlert(_Tr("Client", "Out of Blocks"), AlertType::Error);
|
|
else
|
|
PlayAlertSound();
|
|
}
|
|
}
|
|
} else if (CheckKey(cg_keyToolWeapon, name) && down) {
|
|
if (world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive()) {
|
|
if (world->GetLocalPlayer()->IsToolSelectable(Player::ToolWeapon)) {
|
|
SetSelectedTool(Player::ToolWeapon);
|
|
} else {
|
|
if (cg_alerts)
|
|
ShowAlert(_Tr("Client", "Out of Ammo"), AlertType::Error);
|
|
else
|
|
PlayAlertSound();
|
|
}
|
|
}
|
|
} else if (CheckKey(cg_keyToolGrenade, name) && down) {
|
|
if (world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive()) {
|
|
if (world->GetLocalPlayer()->IsToolSelectable(Player::ToolGrenade)) {
|
|
SetSelectedTool(Player::ToolGrenade);
|
|
} else {
|
|
if (cg_alerts)
|
|
ShowAlert(_Tr("Client", "Out of Grenades"), AlertType::Error);
|
|
else
|
|
PlayAlertSound();
|
|
}
|
|
}
|
|
} else if (CheckKey(cg_keyLastTool, name) && down) {
|
|
if (hasLastTool && world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive() &&
|
|
world->GetLocalPlayer()->IsToolSelectable(lastTool)) {
|
|
hasLastTool = false;
|
|
SetSelectedTool(lastTool);
|
|
}
|
|
} else if (CheckKey(cg_keyGlobalChat, name) && down) {
|
|
// global chat
|
|
scriptedUI->EnterGlobalChatWindow();
|
|
scriptedUI->setIgnored(name);
|
|
} else if (CheckKey(cg_keyTeamChat, name) && down) {
|
|
// team chat
|
|
scriptedUI->EnterTeamChatWindow();
|
|
scriptedUI->setIgnored(name);
|
|
} else if (name == "/" && down) {
|
|
// command
|
|
scriptedUI->EnterCommandWindow();
|
|
scriptedUI->setIgnored(name);
|
|
} else if (CheckKey(cg_keyCaptureColor, name) && down) {
|
|
CaptureColor();
|
|
} else if (CheckKey(cg_keyChangeMapScale, name) && down) {
|
|
mapView->SwitchScale();
|
|
Handle<IAudioChunk> chunk =
|
|
audioDevice->RegisterSound("Sounds/Misc/SwitchMapZoom.opus");
|
|
audioDevice->PlayLocal(chunk, AudioParam());
|
|
} else if (CheckKey(cg_keyToggleMapZoom, name) && down) {
|
|
if (largeMapView->ToggleZoom()) {
|
|
Handle<IAudioChunk> chunk =
|
|
audioDevice->RegisterSound("Sounds/Misc/OpenMap.opus");
|
|
audioDevice->PlayLocal(chunk, AudioParam());
|
|
} else {
|
|
Handle<IAudioChunk> chunk =
|
|
audioDevice->RegisterSound("Sounds/Misc/CloseMap.opus");
|
|
audioDevice->PlayLocal(chunk, AudioParam());
|
|
}
|
|
} else if (CheckKey(cg_keyScoreboard, name)) {
|
|
scoreboardVisible = down;
|
|
} else if (CheckKey(cg_keyLimbo, name) && down) {
|
|
limbo->SetSelectedTeam(world->GetLocalPlayer()->GetTeamId());
|
|
limbo->SetSelectedWeapon(
|
|
world->GetLocalPlayer()->GetWeapon()->GetWeaponType());
|
|
inGameLimbo = true;
|
|
} else if (CheckKey(cg_keySceneshot, name) && down) {
|
|
TakeScreenShot(true);
|
|
} else if (CheckKey(cg_keyScreenshot, name) && down) {
|
|
TakeScreenShot(false);
|
|
} else if (CheckKey(cg_keySaveMap, name) && down) {
|
|
TakeMapShot();
|
|
} else if (CheckKey(cg_keyFlashlight, name) && down) {
|
|
flashlightOn = !flashlightOn;
|
|
flashlightOnTime = time;
|
|
Handle<IAudioChunk> chunk =
|
|
audioDevice->RegisterSound("Sounds/Player/Flashlight.opus");
|
|
audioDevice->PlayLocal(chunk, AudioParam());
|
|
} else if (CheckKey(cg_keyAutoFocus, name) && down && (int)cg_manualFocus) {
|
|
autoFocusEnabled = true;
|
|
} else if (down) {
|
|
bool rev = (int)cg_switchToolByWheel > 0;
|
|
if (name == (rev ? "WheelDown" : "WheelUp")) {
|
|
if ((int)cg_manualFocus) {
|
|
// When DoF control is enabled,
|
|
// tool switch is overrided by focal length control.
|
|
float dist = 1.f / targetFocalLength;
|
|
dist = std::min(dist + 0.01f, 1.f);
|
|
targetFocalLength = 1.f / dist;
|
|
autoFocusEnabled = false;
|
|
} else if (cg_switchToolByWheel &&
|
|
world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive()) {
|
|
Player::ToolType t = world->GetLocalPlayer()->GetTool();
|
|
do {
|
|
switch (t) {
|
|
case Player::ToolSpade: t = Player::ToolGrenade; break;
|
|
case Player::ToolBlock: t = Player::ToolSpade; break;
|
|
case Player::ToolWeapon: t = Player::ToolBlock; break;
|
|
case Player::ToolGrenade: t = Player::ToolWeapon; break;
|
|
}
|
|
} while (!world->GetLocalPlayer()->IsToolSelectable(t));
|
|
SetSelectedTool(t);
|
|
}
|
|
} else if (name == (rev ? "WheelUp" : "WheelDown")) {
|
|
if ((int)cg_manualFocus) {
|
|
// When DoF control is enabled,
|
|
// tool switch is overrided by focal length control.
|
|
float dist = 1.f / targetFocalLength;
|
|
dist =
|
|
std::max(dist - 0.01f, 1.f / 128.f); // limit to fog max distance
|
|
targetFocalLength = 1.f / dist;
|
|
autoFocusEnabled = false;
|
|
} else if (cg_switchToolByWheel &&
|
|
world->GetLocalPlayer()->GetTeamId() < 2 &&
|
|
world->GetLocalPlayer()->IsAlive()) {
|
|
Player::ToolType t = world->GetLocalPlayer()->GetTool();
|
|
do {
|
|
switch (t) {
|
|
case Player::ToolSpade: t = Player::ToolBlock; break;
|
|
case Player::ToolBlock: t = Player::ToolWeapon; break;
|
|
case Player::ToolWeapon: t = Player::ToolGrenade; break;
|
|
case Player::ToolGrenade: t = Player::ToolSpade; break;
|
|
}
|
|
} while (!world->GetLocalPlayer()->IsToolSelectable(t));
|
|
SetSelectedTool(t);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// limbo
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|