2013-08-29 11:45:22 +09:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 yvt
|
2013-09-05 00:52:03 +09:00
|
|
|
based on code of pysnip (c) Mathias Kaerlev 2011-2012.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
This file is part of OpenSpades.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
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.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
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.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
*/
|
2013-08-18 16:18:06 +09:00
|
|
|
|
2014-03-09 19:24:28 +01:00
|
|
|
#include <cstdarg>
|
2016-12-03 18:23:47 +09:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <ctime>
|
|
|
|
|
|
|
|
#include "Client.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
#include "Fonts.h"
|
|
|
|
#include <Core/FileManager.h>
|
|
|
|
#include <Core/IStream.h>
|
2016-12-03 18:23:47 +09:00
|
|
|
#include <Core/Settings.h>
|
|
|
|
#include <Core/Strings.h>
|
2014-03-09 21:43:38 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
#include "IAudioChunk.h"
|
|
|
|
#include "IAudioDevice.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
#include "CenterMessageView.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
#include "ChatWindow.h"
|
2016-12-03 18:23:47 +09:00
|
|
|
#include "ClientPlayer.h"
|
|
|
|
#include "ClientUI.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
#include "HurtRingView.h"
|
2013-08-18 16:18:06 +09:00
|
|
|
#include "LimboView.h"
|
2016-12-03 18:23:47 +09:00
|
|
|
#include "MapView.h"
|
2013-08-18 16:18:06 +09:00
|
|
|
#include "PaletteView.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
#include "ScoreboardView.h"
|
2016-12-03 18:23:47 +09:00
|
|
|
#include "TCProgressView.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
#include "Corpse.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
#include "ILocalEntity.h"
|
|
|
|
#include "SmokeSpriteEntity.h"
|
|
|
|
|
|
|
|
#include "GameMap.h"
|
|
|
|
#include "GameMapWrapper.h"
|
2016-12-03 18:23:47 +09:00
|
|
|
#include "Weapon.h"
|
|
|
|
#include "World.h"
|
2014-03-09 21:43:38 +09:00
|
|
|
|
|
|
|
#include "NetClient.h"
|
2013-08-18 16:18:06 +09:00
|
|
|
|
2016-11-19 21:03:51 +09:00
|
|
|
DEFINE_SPADES_SETTING(cg_chatBeep, "1");
|
2013-08-18 16:18:06 +09:00
|
|
|
|
2016-11-19 21:03:51 +09:00
|
|
|
DEFINE_SPADES_SETTING(cg_serverAlert, "1");
|
2014-03-11 03:42:04 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
namespace spades {
|
|
|
|
namespace client {
|
2016-11-22 23:08:35 +02:00
|
|
|
|
|
|
|
std::random_device r_device_client;
|
2016-12-03 18:23:47 +09:00
|
|
|
std::mt19937_64 mt_engine_client(
|
|
|
|
r_device_client()); // Seed Mersenne twister with non-deterministic 32-bit seed
|
|
|
|
|
|
|
|
Client::Client(IRenderer *r, IAudioDevice *audioDev, const ServerAddress &host,
|
2016-12-16 05:05:23 +09:00
|
|
|
std::string playerName, FontManager *fontManager)
|
2016-12-03 18:23:47 +09:00
|
|
|
: renderer(r),
|
|
|
|
audioDevice(audioDev),
|
|
|
|
playerName(playerName),
|
|
|
|
hasDelayedReload(false),
|
|
|
|
hostname(host),
|
|
|
|
logStream(nullptr),
|
2016-12-16 05:05:23 +09:00
|
|
|
fontManager(fontManager),
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
readyToClose(false),
|
|
|
|
scoreboardVisible(false),
|
|
|
|
flashlightOn(false),
|
|
|
|
inGameLimbo(false),
|
|
|
|
|
|
|
|
frameToRendererInit(5),
|
|
|
|
time(0.f),
|
|
|
|
timeSinceInit(0.f),
|
|
|
|
|
|
|
|
lastAliveTime(0.f),
|
|
|
|
lastKills(0),
|
|
|
|
|
|
|
|
focalLength(20.f),
|
|
|
|
targetFocalLength(20.f),
|
|
|
|
autoFocusEnabled(true),
|
|
|
|
|
|
|
|
hitFeedbackIconState(0.f),
|
|
|
|
hitFeedbackFriendly(false),
|
|
|
|
localFireVibrationTime(-1.f),
|
|
|
|
lastPosSentTime(0.f),
|
|
|
|
worldSubFrame(0.f),
|
|
|
|
grenadeVibration(0.f),
|
2016-12-16 05:05:23 +09:00
|
|
|
grenadeVibrationSlow(0.f),
|
2016-12-03 18:23:47 +09:00
|
|
|
lastMyCorpse(nullptr),
|
|
|
|
hasLastTool(false),
|
|
|
|
|
|
|
|
nextScreenShotIndex(0),
|
|
|
|
nextMapShotIndex(0),
|
|
|
|
|
|
|
|
alertDisappearTime(-10000.f),
|
|
|
|
|
|
|
|
// FIXME: preferences?
|
|
|
|
corpseSoftTimeLimit(30.f), // FIXME: this is not used
|
|
|
|
corpseSoftLimit(6),
|
|
|
|
corpseHardLimit(16),
|
|
|
|
|
|
|
|
followYaw(0.f),
|
|
|
|
followPitch(0.f) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2013-08-26 01:27:44 +09:00
|
|
|
SPLog("Initializing...");
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
renderer->SetFogDistance(128.f);
|
|
|
|
renderer->SetFogColor(MakeVector3(.8f, 1.f, 1.f));
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2016-12-16 05:05:23 +09:00
|
|
|
chatWindow.reset(new ChatWindow(this, GetRenderer(), fontManager->GetGuiFont(), false));
|
|
|
|
killfeedWindow.reset(
|
|
|
|
new ChatWindow(this, GetRenderer(), fontManager->GetGuiFont(), true));
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
hurtRingView.reset(new HurtRingView(this));
|
2016-12-16 05:05:23 +09:00
|
|
|
centerMessageView.reset(new CenterMessageView(this, fontManager->GetLargeFont()));
|
2014-03-09 21:43:38 +09:00
|
|
|
mapView.reset(new MapView(this, false));
|
|
|
|
largeMapView.reset(new MapView(this, true));
|
|
|
|
scoreboard.reset(new ScoreboardView(this));
|
|
|
|
limbo.reset(new LimboView(this));
|
|
|
|
paletteView.reset(new PaletteView(this));
|
|
|
|
tcView.reset(new TCProgressView(this));
|
2016-12-16 05:05:23 +09:00
|
|
|
scriptedUI.Set(new ClientUI(renderer, audioDev, fontManager, this), false);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
renderer->SetGameMap(nullptr);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
void Client::SetWorld(spades::client::World *w) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (world.get() == w) {
|
2013-08-18 16:18:06 +09:00
|
|
|
return;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-12-02 19:42:38 +09:00
|
|
|
scriptedUI->CloseUI();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
RemoveAllCorpses();
|
2014-03-09 21:43:38 +09:00
|
|
|
RemoveAllLocalEntities();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
lastHealth = 0;
|
|
|
|
lastHurtTime = -100.f;
|
|
|
|
hurtRingView->ClearAll();
|
|
|
|
scoreboardVisible = false;
|
|
|
|
flashlightOn = false;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
for (size_t i = 0; i < clientPlayers.size(); i++) {
|
|
|
|
if (clientPlayers[i]) {
|
2013-09-21 04:17:34 +09:00
|
|
|
clientPlayers[i]->Invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clientPlayers.clear();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (world) {
|
2014-03-09 21:43:38 +09:00
|
|
|
world->SetListener(nullptr);
|
|
|
|
renderer->SetGameMap(nullptr);
|
|
|
|
audioDevice->SetGameMap(nullptr);
|
|
|
|
world = nullptr;
|
|
|
|
map = nullptr;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2014-03-09 21:43:38 +09:00
|
|
|
world.reset(w);
|
2016-12-03 18:23:47 +09:00
|
|
|
if (world) {
|
2013-08-26 01:27:44 +09:00
|
|
|
SPLog("World set");
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
// initialize player view objects
|
2013-09-21 04:17:34 +09:00
|
|
|
clientPlayers.resize(world->GetNumPlayerSlots());
|
2016-12-03 18:23:47 +09:00
|
|
|
for (size_t i = 0; i < world->GetNumPlayerSlots(); i++) {
|
|
|
|
Player *p = world->GetPlayer(static_cast<unsigned int>(i));
|
|
|
|
if (p) {
|
2013-09-21 04:17:34 +09:00
|
|
|
clientPlayers[i] = new ClientPlayer(p, this);
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2014-03-09 21:43:38 +09:00
|
|
|
clientPlayers[i] = nullptr;
|
2013-09-21 04:17:34 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
world->SetListener(this);
|
|
|
|
map = world->GetMap();
|
|
|
|
renderer->SetGameMap(map);
|
|
|
|
audioDevice->SetGameMap(map);
|
2013-08-26 19:24:15 +09:00
|
|
|
NetLog("------ World Loaded ------");
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
|
|
|
|
2013-08-26 01:27:44 +09:00
|
|
|
SPLog("World removed");
|
2013-08-26 19:24:15 +09:00
|
|
|
NetLog("------ World Unloaded ------");
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
limbo->SetSelectedTeam(2);
|
|
|
|
limbo->SetSelectedWeapon(RIFLE_WEAPON);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
worldSubFrame = 0.f;
|
|
|
|
worldSetTime = time;
|
2014-03-09 21:43:38 +09:00
|
|
|
inGameLimbo = false;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
Client::~Client() {
|
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-26 19:24:15 +09:00
|
|
|
NetLog("Disconnecting");
|
2016-01-09 21:16:42 -05:00
|
|
|
|
|
|
|
DrawDisconnectScreen();
|
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
if (logStream) {
|
2013-08-26 19:24:15 +09:00
|
|
|
SPLog("Closing netlog");
|
2014-03-09 21:43:38 +09:00
|
|
|
logStream.reset();
|
2013-08-26 19:24:15 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (net) {
|
2013-09-02 23:09:13 +09:00
|
|
|
SPLog("Disconnecting");
|
|
|
|
net->Disconnect();
|
2014-03-09 21:43:38 +09:00
|
|
|
net.reset();
|
2013-09-02 23:09:13 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-26 01:27:44 +09:00
|
|
|
SPLog("Disconnected");
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
RemoveAllLocalEntities();
|
|
|
|
RemoveAllCorpses();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
renderer->SetGameMap(nullptr);
|
2016-12-03 18:23:47 +09:00
|
|
|
audioDevice->SetGameMap(nullptr);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < clientPlayers.size(); i++) {
|
|
|
|
if (clientPlayers[i]) {
|
2013-09-21 04:17:34 +09:00
|
|
|
clientPlayers[i]->Invalidate();
|
|
|
|
}
|
|
|
|
}
|
2014-03-09 21:43:38 +09:00
|
|
|
clientPlayers.clear();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-11-27 16:30:54 +09:00
|
|
|
scriptedUI->ClientDestroyed();
|
2014-03-09 21:43:38 +09:00
|
|
|
tcView.reset();
|
|
|
|
limbo.reset();
|
|
|
|
scoreboard.reset();
|
|
|
|
mapView.reset();
|
|
|
|
largeMapView.reset();
|
|
|
|
chatWindow.reset();
|
|
|
|
killfeedWindow.reset();
|
|
|
|
paletteView.reset();
|
|
|
|
centerMessageView.reset();
|
|
|
|
hurtRingView.reset();
|
|
|
|
world.reset();
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
/** Initiate an initialization which likely to take some time */
|
2013-09-02 23:09:13 +09:00
|
|
|
void Client::DoInit() {
|
|
|
|
renderer->Init();
|
2014-05-08 02:41:10 +09:00
|
|
|
SmokeSpriteEntity::Preload(renderer);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-09-06 11:44:36 +09:00
|
|
|
renderer->RegisterImage("Textures/Fluid.png");
|
|
|
|
renderer->RegisterImage("Textures/WaterExpl.png");
|
|
|
|
renderer->RegisterImage("Gfx/White.tga");
|
2016-12-18 00:45:17 +09:00
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Block/Build.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal1.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal2.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal3.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal4.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Misc/SwitchMapZoom.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Misc/OpenMap.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Misc/CloseMap.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Flashlight.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep1.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep2.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep3.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep4.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep5.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep6.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep7.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Footstep8.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade1.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade2.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade3.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade4.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade5.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade6.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade7.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Wade8.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run1.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run2.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run3.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run4.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run5.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run6.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run7.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run8.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run9.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run10.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run11.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Run12.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Jump.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Land.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/WaterJump.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/WaterLand.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/SwitchLocal.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Switch.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Restock.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/RestockLocal.opus");
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/AimDownSightLocal.opus");
|
2013-09-06 11:44:36 +09:00
|
|
|
renderer->RegisterImage("Gfx/Ball.png");
|
|
|
|
renderer->RegisterModel("Models/Player/Dead.kv6");
|
2016-07-16 00:14:59 +09:00
|
|
|
renderer->RegisterImage("Gfx/Spotlight.png");
|
|
|
|
renderer->RegisterImage("Gfx/Glare.png");
|
2013-09-06 11:44:36 +09:00
|
|
|
renderer->RegisterModel("Models/Weapons/Spade/Spade.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Block/Block2.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Grenade/Grenade.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/SMG/Weapon.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/SMG/WeaponNoMagazine.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/SMG/Magazine.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Rifle/Weapon.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Rifle/WeaponNoMagazine.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Rifle/Magazine.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Shotgun/Weapon.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Shotgun/WeaponNoPump.kv6");
|
|
|
|
renderer->RegisterModel("Models/Weapons/Shotgun/Pump.kv6");
|
2013-11-03 17:13:49 +01:00
|
|
|
renderer->RegisterModel("Models/Player/Arm.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/UpperArm.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/LegCrouch.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/TorsoCrouch.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/Leg.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/Torso.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/Arms.kv6");
|
|
|
|
renderer->RegisterModel("Models/Player/Head.kv6");
|
|
|
|
renderer->RegisterModel("Models/MapObjects/Intel.kv6");
|
2013-09-06 11:44:36 +09:00
|
|
|
renderer->RegisterModel("Models/MapObjects/CheckPoint.kv6");
|
2016-07-16 00:14:59 +09:00
|
|
|
renderer->RegisterImage("Gfx/Bullet/7.62mm.png");
|
|
|
|
renderer->RegisterImage("Gfx/Bullet/9mm.png");
|
|
|
|
renderer->RegisterImage("Gfx/Bullet/12gauge.png");
|
2013-09-06 11:44:36 +09:00
|
|
|
renderer->RegisterImage("Gfx/CircleGradient.png");
|
2013-09-12 23:26:08 +09:00
|
|
|
renderer->RegisterImage("Gfx/HurtSprite.png");
|
2013-12-16 22:54:04 +09:00
|
|
|
renderer->RegisterImage("Gfx/HurtRing2.png");
|
2016-12-18 00:45:17 +09:00
|
|
|
audioDevice->RegisterSound("Sounds/Feedback/Chat.opus");
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2016-08-03 17:08:45 +02:00
|
|
|
if (mumbleLink.init())
|
|
|
|
SPLog("Mumble linked");
|
|
|
|
else
|
|
|
|
SPLog("Mumble link failed");
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2016-08-03 17:08:45 +02:00
|
|
|
mumbleLink.setContext(hostname.asString(false));
|
|
|
|
mumbleLink.setIdentity(playerName);
|
2016-11-22 23:08:35 +02:00
|
|
|
|
2013-09-14 20:00:30 -07:00
|
|
|
SPLog("Started connecting to '%s'", hostname.asString(true).c_str());
|
2014-03-09 21:43:38 +09:00
|
|
|
net.reset(new NetClient(this));
|
2013-09-02 23:09:13 +09:00
|
|
|
net->Connect(hostname);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-09-02 23:09:13 +09:00
|
|
|
// decide log file name
|
2013-09-15 04:19:34 +02:00
|
|
|
std::string fn = hostname.asString(false);
|
2013-09-02 23:09:13 +09:00
|
|
|
std::string fn2;
|
|
|
|
{
|
|
|
|
time_t t;
|
|
|
|
struct tm tm;
|
|
|
|
::time(&t);
|
|
|
|
tm = *localtime(&t);
|
|
|
|
char buf[256];
|
2016-12-03 18:23:47 +09:00
|
|
|
sprintf(buf, "%04d%02d%02d%02d%02d%02d_", tm.tm_year + 1900, tm.tm_mon + 1,
|
|
|
|
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
2013-09-02 23:09:13 +09:00
|
|
|
fn2 = buf;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
for (size_t i = 0; i < fn.size(); i++) {
|
2013-09-02 23:09:13 +09:00
|
|
|
char c = fn[i];
|
2016-12-03 18:23:47 +09:00
|
|
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
2013-09-02 23:09:13 +09:00
|
|
|
fn2 += c;
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2013-09-02 23:09:13 +09:00
|
|
|
fn2 += '_';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn2 = "NetLogs/" + fn2 + ".log";
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
try {
|
2014-03-09 21:43:38 +09:00
|
|
|
logStream.reset(FileManager::OpenForWriting(fn2.c_str()));
|
2013-09-02 23:09:13 +09:00
|
|
|
SPLog("Netlog Started at '%s'", fn2.c_str());
|
2016-12-03 18:23:47 +09:00
|
|
|
} catch (const std::exception &ex) {
|
2013-09-20 23:03:32 +02:00
|
|
|
SPLog("Failed to open netlog file '%s' (%s)", fn2.c_str(), ex.what());
|
2013-09-02 23:09:13 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
void Client::RunFrame(float dt) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-31 23:09:47 +09:00
|
|
|
fpsCounter.MarkFrame();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (frameToRendererInit > 0) {
|
2013-09-02 23:09:13 +09:00
|
|
|
// waiting for renderer initialization
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-09-02 23:09:13 +09:00
|
|
|
DrawStartupScreen();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-09-02 23:09:13 +09:00
|
|
|
frameToRendererInit--;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (frameToRendererInit == 0) {
|
2013-09-02 23:09:13 +09:00
|
|
|
DoInit();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
} else {
|
2013-09-02 23:09:13 +09:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-09-04 04:03:03 +09:00
|
|
|
timeSinceInit += std::min(dt, .03f);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
// update network
|
2016-12-03 18:23:47 +09:00
|
|
|
try {
|
|
|
|
if (net->GetStatus() == NetClientStatusConnected)
|
2013-08-18 16:18:06 +09:00
|
|
|
net->DoEvents(0);
|
|
|
|
else
|
2013-11-28 00:46:45 +09:00
|
|
|
net->DoEvents(10);
|
2016-12-03 18:23:47 +09:00
|
|
|
} catch (const std::exception &ex) {
|
|
|
|
if (net->GetStatus() == NetClientStatusNotConnected) {
|
2013-08-26 19:24:15 +09:00
|
|
|
SPLog("Disconnected because of error:\n%s", ex.what());
|
|
|
|
NetLog("Disconnected because of error:\n%s", ex.what());
|
2013-08-18 16:18:06 +09:00
|
|
|
throw;
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2013-08-26 19:24:15 +09:00
|
|
|
SPLog("Exception while processing network packets (ignored):\n%s", ex.what());
|
|
|
|
}
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
hurtRingView->Update(dt);
|
|
|
|
centerMessageView->Update(dt);
|
|
|
|
mapView->Update(dt);
|
2013-08-27 22:27:49 +09:00
|
|
|
largeMapView->Update(dt);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2015-06-20 17:10:26 +09:00
|
|
|
UpdateAutoFocus(dt);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (world) {
|
2014-03-09 21:43:38 +09:00
|
|
|
UpdateWorld(dt);
|
2016-08-03 17:08:45 +02:00
|
|
|
mumbleLink.update(world->GetLocalPlayer());
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2013-08-18 16:18:06 +09:00
|
|
|
renderer->SetFogColor(MakeVector3(0.f, 0.f, 0.f));
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
chatWindow->Update(dt);
|
2013-08-26 17:53:14 +09:00
|
|
|
killfeedWindow->Update(dt);
|
2013-08-18 16:18:06 +09:00
|
|
|
limbo->Update(dt);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
// CreateSceneDefinition also can be used for sounds
|
|
|
|
SceneDefinition sceneDef = CreateSceneDefinition();
|
2013-08-18 16:18:06 +09:00
|
|
|
lastSceneDef = sceneDef;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
// Update sounds
|
2016-12-03 18:23:47 +09:00
|
|
|
try {
|
|
|
|
audioDevice->Respatialize(sceneDef.viewOrigin, sceneDef.viewAxis[2],
|
|
|
|
sceneDef.viewAxis[1]);
|
|
|
|
} catch (const std::exception &ex) {
|
|
|
|
SPLog("Audio subsystem returned error (ignored):\n%s", ex.what());
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
// render scene
|
|
|
|
DrawScene();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
// draw 2d
|
|
|
|
Draw2D();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-11-27 16:30:54 +09:00
|
|
|
// draw scripted GUI
|
|
|
|
scriptedUI->RunFrame(dt);
|
2016-12-03 18:23:47 +09:00
|
|
|
if (scriptedUI->WantsClientToBeClosed())
|
2013-11-27 16:30:54 +09:00
|
|
|
readyToClose = true;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
// Well done!
|
|
|
|
renderer->FrameDone();
|
|
|
|
renderer->Flip();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
// reset all "delayed actions" (in case we forget to reset these)
|
|
|
|
hasDelayedReload = false;
|
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
time += dt;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
bool Client::IsLimboViewActive() {
|
|
|
|
if (world) {
|
|
|
|
if (!world->GetLocalPlayer()) {
|
2014-03-09 21:43:38 +09:00
|
|
|
return true;
|
2016-12-03 18:23:47 +09:00
|
|
|
} else if (inGameLimbo) {
|
2014-03-09 21:43:38 +09:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-01 21:55:30 +09:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
void Client::SpawnPressed() {
|
|
|
|
WeaponType weap = limbo->GetSelectedWeapon();
|
|
|
|
int team = limbo->GetSelectedTeam();
|
|
|
|
inGameLimbo = false;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (team == 2)
|
2014-03-09 21:43:38 +09:00
|
|
|
team = 255;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (!world->GetLocalPlayer()) {
|
2014-03-09 21:43:38 +09:00
|
|
|
// join
|
2016-12-03 18:23:47 +09:00
|
|
|
net->SendJoin(team, weap, playerName, lastKills);
|
|
|
|
} else {
|
2013-08-18 16:18:06 +09:00
|
|
|
Player *p = world->GetLocalPlayer();
|
2016-12-03 18:23:47 +09:00
|
|
|
if (p->GetTeamId() != team) {
|
2014-03-09 21:43:38 +09:00
|
|
|
net->SendTeamChange(team);
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
if (team != 2 && p->GetWeapon()->GetWeaponType() != weap) {
|
2014-03-09 21:43:38 +09:00
|
|
|
net->SendWeaponChange(weap);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
void Client::ShowAlert(const std::string &contents, AlertType type) {
|
2014-03-11 03:42:04 +09:00
|
|
|
float timeout;
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
|
|
|
case AlertType::Notice: timeout = 2.5f; break;
|
|
|
|
case AlertType::Warning: timeout = 3.f; break;
|
|
|
|
case AlertType::Error: timeout = 3.f; break;
|
2014-03-11 03:42:04 +09:00
|
|
|
}
|
|
|
|
ShowAlert(contents, type, timeout);
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
void Client::ShowAlert(const std::string &contents, AlertType type, float timeout,
|
|
|
|
bool quiet) {
|
2014-03-11 03:42:04 +09:00
|
|
|
alertType = type;
|
|
|
|
alertContents = contents;
|
|
|
|
alertDisappearTime = time + timeout;
|
|
|
|
alertAppearTime = time;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (type != AlertType::Notice && !quiet) {
|
2014-03-11 03:42:04 +09:00
|
|
|
PlayAlertSound();
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-11 03:42:04 +09:00
|
|
|
void Client::PlayAlertSound() {
|
2016-12-18 00:45:17 +09:00
|
|
|
Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Alert.opus");
|
2014-03-11 03:42:04 +09:00
|
|
|
audioDevice->PlayLocal(chunk, AudioParam());
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
/** Records chat message/game events to the log file. */
|
|
|
|
void Client::NetLog(const char *format, ...) {
|
|
|
|
char buf[4096];
|
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
|
|
vsprintf(buf, format, va);
|
|
|
|
va_end(va);
|
|
|
|
std::string str = buf;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
time_t t;
|
|
|
|
struct tm tm;
|
|
|
|
::time(&t);
|
|
|
|
tm = *localtime(&t);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
std::string timeStr = asctime(&tm);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
// remove '\n' in the end of the result of asctime().
|
2016-12-03 18:23:47 +09:00
|
|
|
timeStr.resize(timeStr.size() - 1);
|
|
|
|
|
|
|
|
sprintf(buf, "%s %s\n", timeStr.c_str(), str.c_str());
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
std::string outStr = EscapeControlCharacters(buf);
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
printf("%s", outStr.c_str());
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (logStream) {
|
2014-03-09 21:43:38 +09:00
|
|
|
logStream->Write(outStr);
|
|
|
|
logStream->Flush();
|
2013-12-09 17:36:05 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
#pragma mark - Snapshots
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
void Client::TakeMapShot() {
|
|
|
|
|
|
|
|
try {
|
2013-11-22 19:34:20 +09:00
|
|
|
std::string name = MapShotPath();
|
2014-03-09 21:43:38 +09:00
|
|
|
{
|
|
|
|
std::unique_ptr<IStream> stream(FileManager::OpenForWriting(name.c_str()));
|
2016-12-03 18:23:47 +09:00
|
|
|
try {
|
2014-03-09 21:43:38 +09:00
|
|
|
GameMap *map = GetWorld()->GetMap();
|
2016-12-03 18:23:47 +09:00
|
|
|
if (map == nullptr) {
|
2014-03-09 21:43:38 +09:00
|
|
|
SPRaise("No map loaded");
|
|
|
|
}
|
|
|
|
map->Save(stream.get());
|
2016-12-03 18:23:47 +09:00
|
|
|
} catch (...) {
|
2014-03-09 21:43:38 +09:00
|
|
|
throw;
|
2013-11-22 19:34:20 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-11-22 19:34:20 +09:00
|
|
|
std::string msg;
|
2014-02-15 03:51:29 +09:00
|
|
|
msg = _Tr("Client", "Map saved: {0}", name);
|
2014-03-11 03:42:04 +09:00
|
|
|
ShowAlert(msg, AlertType::Notice);
|
2016-12-03 18:23:47 +09:00
|
|
|
} catch (const Exception &ex) {
|
2014-03-11 03:42:04 +09:00
|
|
|
std::string msg;
|
|
|
|
msg = _Tr("Client", "Saving map failed: ");
|
|
|
|
msg += ex.GetShortMessage();
|
|
|
|
ShowAlert(msg, AlertType::Error);
|
|
|
|
SPLog("Saving map failed: %s", ex.what());
|
2016-12-03 18:23:47 +09:00
|
|
|
} catch (const std::exception &ex) {
|
2013-11-22 19:34:20 +09:00
|
|
|
std::string msg;
|
2014-02-15 03:51:29 +09:00
|
|
|
msg = _Tr("Client", "Saving map failed: ");
|
2014-03-11 03:42:04 +09:00
|
|
|
msg += ex.what();
|
|
|
|
ShowAlert(msg, AlertType::Error);
|
|
|
|
SPLog("Saving map failed: %s", ex.what());
|
2013-11-22 19:34:20 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-11-22 19:34:20 +09:00
|
|
|
std::string Client::MapShotPath() {
|
|
|
|
char buf[256];
|
2016-12-03 18:23:47 +09:00
|
|
|
for (int i = 0; i < 10000; i++) {
|
2013-11-22 19:34:20 +09:00
|
|
|
sprintf(buf, "Mapshots/shot%04d.vxl", nextScreenShotIndex);
|
2016-12-03 18:23:47 +09:00
|
|
|
if (FileManager::FileExists(buf)) {
|
2013-11-22 19:34:20 +09:00
|
|
|
nextScreenShotIndex++;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (nextScreenShotIndex >= 10000)
|
2013-11-22 19:34:20 +09:00
|
|
|
nextScreenShotIndex = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-11-22 19:34:20 +09:00
|
|
|
return buf;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-11-22 19:34:20 +09:00
|
|
|
SPRaise("No free file name");
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
#pragma mark - Chat Messages
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
void Client::PlayerSentChatMessage(spades::client::Player *p, bool global,
|
|
|
|
const std::string &msg) {
|
2014-03-09 21:43:38 +09:00
|
|
|
{
|
|
|
|
std::string s;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (global)
|
2014-03-09 21:43:38 +09:00
|
|
|
/// prefix added to global chat messages.
|
|
|
|
s = _Tr("Client", "[Global] ");
|
|
|
|
s += ChatWindow::TeamColorMessage(p->GetName(), p->GetTeamId());
|
|
|
|
s += ": ";
|
|
|
|
s += msg;
|
|
|
|
chatWindow->AddMessage(s);
|
2013-09-12 23:30:03 +09:00
|
|
|
}
|
2014-03-09 21:43:38 +09:00
|
|
|
{
|
|
|
|
std::string s;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (global)
|
2014-03-09 21:43:38 +09:00
|
|
|
s = "[Global] ";
|
|
|
|
s += p->GetName();
|
|
|
|
s += ": ";
|
|
|
|
s += msg;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
auto col = p->GetTeamId() < 2 ? world->GetTeam(p->GetTeamId()).color
|
|
|
|
: IntVector3::Make(255, 255, 255);
|
|
|
|
|
|
|
|
scriptedUI->RecordChatLog(
|
|
|
|
s, MakeVector4(col.x / 255.f, col.y / 255.f, col.z / 255.f, 0.8f));
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
if (global)
|
|
|
|
NetLog("[Global] %s (%s): %s", p->GetName().c_str(),
|
|
|
|
world->GetTeam(p->GetTeamId()).name.c_str(), msg.c_str());
|
2014-03-09 21:43:38 +09:00
|
|
|
else
|
2016-12-03 18:23:47 +09:00
|
|
|
NetLog("[Team] %s (%s): %s", p->GetName().c_str(),
|
|
|
|
world->GetTeam(p->GetTeamId()).name.c_str(), msg.c_str());
|
|
|
|
|
|
|
|
if ((!IsMuted()) && (int)cg_chatBeep) {
|
2016-12-18 00:45:17 +09:00
|
|
|
Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Chat.opus");
|
2014-03-09 21:43:38 +09:00
|
|
|
audioDevice->PlayLocal(chunk, AudioParam());
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
void Client::ServerSentMessage(const std::string &msg) {
|
|
|
|
NetLog("%s", msg.c_str());
|
|
|
|
scriptedUI->RecordChatLog(msg, Vector4::Make(1.f, 1.f, 1.f, 0.8f));
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (cg_serverAlert) {
|
|
|
|
if (msg.substr(0, 3) == "N% ") {
|
2014-03-11 03:42:04 +09:00
|
|
|
ShowAlert(msg.substr(3), AlertType::Notice);
|
|
|
|
return;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
if (msg.substr(0, 3) == "!% ") {
|
2014-03-11 03:42:04 +09:00
|
|
|
ShowAlert(msg.substr(3), AlertType::Error);
|
|
|
|
return;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
if (msg.substr(0, 3) == "%% ") {
|
2014-03-11 03:42:04 +09:00
|
|
|
ShowAlert(msg.substr(3), AlertType::Warning);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-11 03:42:04 +09:00
|
|
|
chatWindow->AddMessage(msg);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
#pragma mark - Follow / Spectate
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
void Client::FollowNextPlayer(bool reverse) {
|
|
|
|
int myTeam = 2;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (world->GetLocalPlayer()) {
|
2014-03-09 21:43:38 +09:00
|
|
|
myTeam = world->GetLocalPlayer()->GetTeamId();
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
int nextId = followingPlayerId;
|
2016-12-03 18:23:47 +09:00
|
|
|
do {
|
2014-03-09 21:43:38 +09:00
|
|
|
reverse ? --nextId : ++nextId;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (nextId >= static_cast<int>(world->GetNumPlayerSlots()))
|
2014-03-09 21:43:38 +09:00
|
|
|
nextId = 0;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (nextId < 0)
|
|
|
|
nextId = static_cast<int>(world->GetNumPlayerSlots() - 1);
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
Player *p = world->GetPlayer(nextId);
|
2016-12-03 18:23:47 +09:00
|
|
|
if (p == nullptr)
|
2014-03-09 21:43:38 +09:00
|
|
|
continue;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (myTeam < 2 && p->GetTeamId() != myTeam)
|
2014-03-09 21:43:38 +09:00
|
|
|
continue;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (p->GetFront().GetPoweredLength() < .01f)
|
2014-03-09 21:43:38 +09:00
|
|
|
continue;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
break;
|
2016-12-03 18:23:47 +09:00
|
|
|
} while (nextId != followingPlayerId);
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
followingPlayerId = nextId;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
bool Client::IsFollowing() {
|
2016-12-03 18:23:47 +09:00
|
|
|
if (!world)
|
|
|
|
return false;
|
|
|
|
if (!world->GetLocalPlayer())
|
2014-03-09 21:43:38 +09:00
|
|
|
return false;
|
|
|
|
Player *p = world->GetLocalPlayer();
|
2016-12-03 18:23:47 +09:00
|
|
|
if (p->GetTeamId() >= 2)
|
2014-03-09 21:43:38 +09:00
|
|
|
return true;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (p->IsAlive())
|
2014-03-09 21:43:38 +09:00
|
|
|
return false;
|
2016-12-03 18:23:47 +09:00
|
|
|
else
|
|
|
|
return true;
|
2014-03-09 21:43:38 +09:00
|
|
|
}
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|