parent
fdcb77789e
commit
660066fc2f
@ -393,6 +393,7 @@
|
||||
E82E67F318EA7EAB004DBA18 /* font-unifont.pak */ = {isa = PBXFileReference; lastKnownFileType = file; name = "font-unifont.pak"; path = "Resources/font-unifont.pak"; sourceTree = "<group>"; };
|
||||
E82E680318EA7F60004DBA18 /* libysrspades.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libysrspades.dylib; path = lib/libysrspades.dylib; sourceTree = "<group>"; };
|
||||
E82E680618EA9502004DBA18 /* XSpades.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = XSpades.entitlements; path = XSpades/XSpades.entitlements; sourceTree = "<group>"; };
|
||||
E831D8661FD65BD5003C0D97 /* ClientCameraMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClientCameraMode.h; sourceTree = "<group>"; };
|
||||
E834F54E17942C43004EBE88 /* Grenade.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Grenade.cpp; sourceTree = "<group>"; };
|
||||
E834F54F17942C43004EBE88 /* Grenade.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Grenade.h; sourceTree = "<group>"; };
|
||||
E834F55117944778004EBE88 /* NetClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetClient.cpp; sourceTree = "<group>"; };
|
||||
@ -1613,6 +1614,7 @@
|
||||
E8FE749518CC6F2900291338 /* Client_Scene.cpp */,
|
||||
E82E66B218E9A35C004DBA18 /* Client_FPSCounter.cpp */,
|
||||
E8CF03C2178EE6D8000683D4 /* Client.h */,
|
||||
E831D8661FD65BD5003C0D97 /* ClientCameraMode.h */,
|
||||
E834F56D1797D92F004EBE88 /* ChatWindow.cpp */,
|
||||
E834F56E1797D932004EBE88 /* ChatWindow.h */,
|
||||
E8E0AF92179942DB00C6B5A9 /* Corpse.cpp */,
|
||||
|
@ -103,11 +103,6 @@ namespace spades {
|
||||
targetFocalLength(20.f),
|
||||
autoFocusEnabled(true),
|
||||
|
||||
|
||||
// FIXME: preferences?
|
||||
|
||||
followYaw(0.f),
|
||||
followPitch(0.f),
|
||||
inGameLimbo(false),
|
||||
fontManager(fontManager),
|
||||
alertDisappearTime(-10000.f),
|
||||
@ -703,7 +698,7 @@ namespace spades {
|
||||
myTeam = world->GetLocalPlayer()->GetTeamId();
|
||||
}
|
||||
|
||||
int nextId = followingPlayerId;
|
||||
int nextId = followedPlayerId;
|
||||
do {
|
||||
reverse ? --nextId : ++nextId;
|
||||
if (nextId >= static_cast<int>(world->GetNumPlayerSlots()))
|
||||
@ -727,23 +722,12 @@ namespace spades {
|
||||
}
|
||||
|
||||
break;
|
||||
} while (nextId != followingPlayerId);
|
||||
} while (nextId != followedPlayerId);
|
||||
|
||||
followingPlayerId = nextId;
|
||||
}
|
||||
|
||||
bool Client::IsFollowing() {
|
||||
if (!world)
|
||||
return false;
|
||||
if (!world->GetLocalPlayer())
|
||||
return false;
|
||||
Player *p = world->GetLocalPlayer();
|
||||
if (p->GetTeamId() >= 2)
|
||||
return true;
|
||||
if (p->IsAlive())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
followedPlayerId = nextId;
|
||||
if (followedPlayerId == world->GetLocalPlayerIndex()) {
|
||||
followCameraState.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <Core/ServerAddress.h>
|
||||
#include <Core/Stopwatch.h>
|
||||
#include <Gui/View.h>
|
||||
#include "ClientCameraMode.h"
|
||||
|
||||
namespace spades {
|
||||
class IStream;
|
||||
@ -144,7 +145,6 @@ namespace spades {
|
||||
KeypadInput keypadInput;
|
||||
Player::ToolType lastTool;
|
||||
bool hasLastTool;
|
||||
bool firstPersonSpectate;
|
||||
Vector3 lastFront;
|
||||
float lastPosSentTime;
|
||||
int lastHealth;
|
||||
@ -160,6 +160,7 @@ namespace spades {
|
||||
float strength;
|
||||
};
|
||||
std::vector<HurtSprite> hurtSprites;
|
||||
|
||||
float GetAimDownState();
|
||||
float GetSprintState();
|
||||
|
||||
@ -205,17 +206,78 @@ namespace spades {
|
||||
float targetFocalLength;
|
||||
bool autoFocusEnabled;
|
||||
|
||||
// when dead...
|
||||
/** Following player ID, which may be local player itself */
|
||||
int followingPlayerId;
|
||||
float followYaw, followPitch;
|
||||
/** only for when spectating */
|
||||
Vector3 followPos;
|
||||
/** only for when spectating */
|
||||
Vector3 followVel;
|
||||
// Spectator camera control
|
||||
/** The state of the following camera used for spectating. */
|
||||
struct {
|
||||
bool firstPerson = true;
|
||||
|
||||
/** Controls whether the follow camera is enabled. */
|
||||
bool enabled = false;
|
||||
} followCameraState;
|
||||
|
||||
/** The state of the free floating camera used for spectating. */
|
||||
struct {
|
||||
/** The temporally smoothed position (I guess). */
|
||||
Vector3 position {0.0f, 0.0f, 0.0f};
|
||||
|
||||
/** The temporally smoothed velocity (I guess). */
|
||||
Vector3 velocity {0.0f, 0.0f, 0.0f};
|
||||
} freeCameraState;
|
||||
|
||||
/**
|
||||
* The state shared by both of the third-person and free-floating cameras.
|
||||
*
|
||||
* Note: These values are not used in the `cg_thirdperson` mode.
|
||||
*/
|
||||
struct {
|
||||
/** The yaw angle. */
|
||||
float yaw = 0.0f;
|
||||
|
||||
/** The pitch angle. */
|
||||
float pitch = 0.0f;
|
||||
} followAndFreeCameraState;
|
||||
|
||||
/**
|
||||
* The ID of the player being followed (in a spectator mode, or when the local player is
|
||||
* dead). Must be valid as long as the follow cam is enabled.
|
||||
*
|
||||
* Must *not* specify a local player.
|
||||
*/
|
||||
int followedPlayerId;
|
||||
|
||||
/**
|
||||
* Chooses the next player to follow and assigns it to `this->followingPlayerId`.
|
||||
* If the next player is the local player, disables the follow cam.
|
||||
*/
|
||||
void FollowNextPlayer(bool reverse);
|
||||
/** @return true following is activated (and followingPlayerId should be used) */
|
||||
bool IsFollowing();
|
||||
|
||||
/**
|
||||
* Retrieves the current camera mode.
|
||||
*/
|
||||
ClientCameraMode GetCameraMode();
|
||||
|
||||
/**
|
||||
* Retrieves the target player ID of the current camera mode (as returned by
|
||||
* `GetCameraMode`).
|
||||
*
|
||||
* Throws an exception if the current camera mode does not have a player in concern.
|
||||
*/
|
||||
int GetCameraTargetPlayerId();
|
||||
|
||||
/**
|
||||
* Retrieves the target player of the current camera mode (as returned by
|
||||
* `GetCameraMode`).
|
||||
*
|
||||
* Throws an exception if the current camera mode does not have a player in concern.
|
||||
*/
|
||||
Player &GetCameraTargetPlayer();
|
||||
|
||||
/**
|
||||
* Calculate the zoom value incorporating the effect of ADS for a first-person view.
|
||||
*
|
||||
* The camera mode must be first-person.
|
||||
*/
|
||||
float GetAimDownZoomScale();
|
||||
|
||||
bool inGameLimbo;
|
||||
|
||||
@ -301,9 +363,6 @@ namespace spades {
|
||||
void DrawCTFObjects();
|
||||
void DrawTCObjects();
|
||||
|
||||
float GetAimDownZoomScale();
|
||||
bool ShouldRenderInThirdPersonView();
|
||||
Player *GetViewedPlayer();
|
||||
SceneDefinition CreateSceneDefinition();
|
||||
|
||||
std::string ScreenShotPath();
|
||||
|
125
Sources/Client/ClientCameraMode.h
Normal file
125
Sources/Client/ClientCameraMode.h
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright (c) 2017 yvt
|
||||
|
||||
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/>.
|
||||
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Core/Exception.h>
|
||||
|
||||
namespace spades {
|
||||
namespace client {
|
||||
enum class ClientCameraMode {
|
||||
/**
|
||||
* The world is not loaded.
|
||||
*/
|
||||
None,
|
||||
|
||||
/**
|
||||
* That specific camera position (you know what I mean).
|
||||
*/
|
||||
NotJoined,
|
||||
|
||||
/**
|
||||
* First-person view of the local player.
|
||||
*/
|
||||
FirstPersonLocal,
|
||||
|
||||
/**
|
||||
* First-person view of a non-local player.
|
||||
*/
|
||||
FirstPersonFollow,
|
||||
|
||||
/**
|
||||
* Third-person view of the local player (only allowed in a limited situation where
|
||||
* it is enabled by `cg_thirdperson`, or the player is dead).
|
||||
*/
|
||||
ThirdPersonLocal,
|
||||
|
||||
/**
|
||||
* Third-person view of a non-lcoal player.
|
||||
*/
|
||||
ThirdPersonFollow,
|
||||
|
||||
/**
|
||||
* Free floating camera view, which is usable only when the user has joined a server
|
||||
* as a spectator.
|
||||
*/
|
||||
Free,
|
||||
};
|
||||
|
||||
inline bool IsThirdPerson(ClientCameraMode mode) {
|
||||
switch (mode) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
case ClientCameraMode::Free:
|
||||
return false;
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
case ClientCameraMode::ThirdPersonFollow:
|
||||
return true;
|
||||
}
|
||||
SPUnreachable();
|
||||
}
|
||||
|
||||
inline bool IsFirstPerson(ClientCameraMode mode) {
|
||||
switch (mode) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
case ClientCameraMode::ThirdPersonFollow:
|
||||
case ClientCameraMode::Free:
|
||||
return false;
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
return true;
|
||||
}
|
||||
SPUnreachable();
|
||||
}
|
||||
|
||||
inline bool HasTargetPlayer(ClientCameraMode mode) {
|
||||
switch (mode) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::Free:
|
||||
return false;
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
case ClientCameraMode::ThirdPersonFollow:
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
return true;
|
||||
}
|
||||
SPUnreachable();
|
||||
}
|
||||
|
||||
inline bool FollowsNonLocalPlayer(ClientCameraMode mode) {
|
||||
switch (mode) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::Free:
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
return false;
|
||||
case ClientCameraMode::ThirdPersonFollow:
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
return true;
|
||||
}
|
||||
SPUnreachable();
|
||||
}
|
||||
}
|
||||
}
|
@ -1042,11 +1042,8 @@ namespace spades {
|
||||
}
|
||||
|
||||
bool ClientPlayer::ShouldRenderInThirdPersonView() {
|
||||
// the player from whom's perspective the game is seen
|
||||
|
||||
if (player != client->GetViewedPlayer()) return true;
|
||||
|
||||
return client->ShouldRenderInThirdPersonView();
|
||||
// The player from whom's perspective the game is
|
||||
return !IsFirstPerson(client->GetCameraMode()) || player != &client->GetCameraTargetPlayer();
|
||||
}
|
||||
|
||||
struct ClientPlayer::AmbienceInfo {
|
||||
|
@ -756,19 +756,15 @@ namespace spades {
|
||||
DrawSpectateHUD();
|
||||
}
|
||||
|
||||
if (IsFollowing() && !cg_hideHud) {
|
||||
if (followingPlayerId == p->GetId()) {
|
||||
// just spectating
|
||||
} else {
|
||||
font = fontManager->GetGuiFont();
|
||||
std::string msg = _Tr("Client", "Following {0}",
|
||||
world->GetPlayerPersistent(followingPlayerId).name);
|
||||
Vector2 size = font->Measure(msg);
|
||||
Vector2 pos = MakeVector2(scrWidth - 8.f, 256.f + 32.f);
|
||||
pos.x -= size.x;
|
||||
font->DrawShadow(msg, pos, 1.f, MakeVector4(1, 1, 1, 1),
|
||||
MakeVector4(0, 0, 0, 0.5));
|
||||
}
|
||||
if (FollowsNonLocalPlayer(GetCameraMode()) && !cg_hideHud) {
|
||||
font = fontManager->GetGuiFont();
|
||||
std::string msg = _Tr("Client", "Following {0}",
|
||||
world->GetPlayerPersistent(GetCameraTargetPlayerId()).name);
|
||||
Vector2 size = font->Measure(msg);
|
||||
Vector2 pos = MakeVector2(scrWidth - 8.f, 256.f + 32.f);
|
||||
pos.x -= size.x;
|
||||
font->DrawShadow(msg, pos, 1.f, MakeVector4(1, 1, 1, 1),
|
||||
MakeVector4(0, 0, 0, 0.5));
|
||||
}
|
||||
|
||||
if (!cg_hideHud) {
|
||||
|
@ -120,60 +120,80 @@ namespace spades {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsFollowing()) {
|
||||
SPAssert(world != nullptr);
|
||||
auto cameraMode = GetCameraMode();
|
||||
|
||||
x = -x;
|
||||
if (!cg_invertMouseY)
|
||||
y = -y;
|
||||
switch (cameraMode) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
// No-op
|
||||
break;
|
||||
|
||||
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)
|
||||
case ClientCameraMode::Free:
|
||||
case ClientCameraMode::ThirdPersonFollow: {
|
||||
// Move the third-person or free-floating camera
|
||||
x = -x;
|
||||
if (!cg_invertMouseY)
|
||||
y = -y;
|
||||
|
||||
p->Turn(x * 0.003f, y * 0.003f);
|
||||
auto &state = followAndFreeCameraState;
|
||||
|
||||
state.yaw -= x * 0.003f;
|
||||
state.pitch -= y * 0.003f;
|
||||
if (state.pitch < -M_PI * .45f)
|
||||
state.pitch = -static_cast<float>(M_PI) * .45f;
|
||||
if (state.pitch > M_PI * .45f)
|
||||
state.pitch = static_cast<float>(M_PI) * .45f;
|
||||
state.yaw = fmodf(state.yaw, static_cast<float>(M_PI) * 2.f);
|
||||
break;
|
||||
}
|
||||
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
case ClientCameraMode::ThirdPersonLocal: {
|
||||
SPAssert(world);
|
||||
SPAssert(world->GetLocalPlayer());
|
||||
|
||||
Player *p = world->GetLocalPlayer();
|
||||
if (p->IsAlive()) {
|
||||
float aimDownState = GetAimDownState();
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,27 +307,60 @@ namespace spades {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (IsFollowing()) {
|
||||
if (CheckKey(cg_keyAttack, name)) {
|
||||
if (down) {
|
||||
if (world->GetLocalPlayer()->GetTeamId() >= 2 ||
|
||||
time > lastAliveTime + 1.3f)
|
||||
FollowNextPlayer(false);
|
||||
|
||||
auto cameraMode = GetCameraMode();
|
||||
|
||||
switch (cameraMode) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
break;
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
if (world->GetLocalPlayer()->IsAlive()) {
|
||||
break;
|
||||
}
|
||||
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);
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
case ClientCameraMode::ThirdPersonFollow:
|
||||
case ClientCameraMode::Free:
|
||||
if (CheckKey(cg_keyAttack, name)) {
|
||||
if (down) {
|
||||
if (cameraMode == ClientCameraMode::Free ||
|
||||
cameraMode == ClientCameraMode::ThirdPersonLocal) {
|
||||
// Start with the local player
|
||||
followedPlayerId = world->GetLocalPlayerIndex();
|
||||
}
|
||||
if (world->GetLocalPlayer()->IsSpectator() ||
|
||||
time > lastAliveTime + 1.3f) {
|
||||
FollowNextPlayer(false);
|
||||
followCameraState.enabled = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (CheckKey(cg_keyAltAttack, name)) {
|
||||
if (down) {
|
||||
if (cameraMode == ClientCameraMode::Free ||
|
||||
cameraMode == ClientCameraMode::ThirdPersonLocal) {
|
||||
// Start with the local player
|
||||
followedPlayerId = world->GetLocalPlayerIndex();
|
||||
}
|
||||
if (world->GetLocalPlayer()->IsSpectator()) {
|
||||
// Unfollow
|
||||
followCameraState.enabled = false;
|
||||
} else if (time > lastAliveTime + 1.3f) {
|
||||
FollowNextPlayer(true);
|
||||
followCameraState.enabled = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (CheckKey(cg_keyJump, name)) {
|
||||
if (down) {
|
||||
followCameraState.firstPerson = !followCameraState.firstPerson;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (world->GetLocalPlayer()) {
|
||||
Player *p = world->GetLocalPlayer();
|
||||
|
||||
@ -367,9 +420,6 @@ namespace spades {
|
||||
} 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;
|
||||
|
@ -112,22 +112,20 @@ namespace spades {
|
||||
}
|
||||
|
||||
Player *Client::HotTrackedPlayer(hitTag_t *hitFlag) {
|
||||
if (!world)
|
||||
if (!IsFirstPerson(GetCameraMode()))
|
||||
return nullptr;
|
||||
Player *p = world->GetLocalPlayer();
|
||||
if (!p || !p->IsAlive())
|
||||
return nullptr;
|
||||
if (ShouldRenderInThirdPersonView())
|
||||
return nullptr;
|
||||
Vector3 origin = p->GetEye();
|
||||
Vector3 dir = p->GetFront();
|
||||
World::WeaponRayCastResult result = world->WeaponRayCast(origin, dir, p);
|
||||
|
||||
auto &p = GetCameraTargetPlayer();
|
||||
|
||||
Vector3 origin = p.GetEye();
|
||||
Vector3 dir = p.GetFront();
|
||||
World::WeaponRayCastResult result = world->WeaponRayCast(origin, dir, &p);
|
||||
|
||||
if (result.hit == false || result.player == nullptr)
|
||||
return nullptr;
|
||||
|
||||
// don't hot track enemies (non-spectator only)
|
||||
if (result.player->GetTeamId() != p->GetTeamId() && p->GetTeamId() < 2)
|
||||
if (result.player->GetTeamId() != p.GetTeamId() && p.GetTeamId() < 2)
|
||||
return nullptr;
|
||||
if (hitFlag) {
|
||||
*hitFlag = result.hitFlag;
|
||||
|
@ -51,7 +51,7 @@ namespace spades {
|
||||
#pragma mark - Server Packet Handlers
|
||||
|
||||
void Client::LocalPlayerCreated() {
|
||||
followPos = world->GetLocalPlayer()->GetEye();
|
||||
freeCameraState.position = world->GetLocalPlayer()->GetEye();
|
||||
weapInput = WeaponInput();
|
||||
playerInput = PlayerInput();
|
||||
keypadInput = KeypadInput();
|
||||
@ -60,12 +60,12 @@ namespace spades {
|
||||
}
|
||||
|
||||
void Client::JoinedGame() {
|
||||
// note: local player doesn't exist yet now
|
||||
// Note: A local player doesn't exist yet
|
||||
|
||||
// tune for spectate mode
|
||||
followingPlayerId = world->GetLocalPlayerIndex();
|
||||
followPos = MakeVector3(256, 256, 30);
|
||||
followVel = MakeVector3(0, 0, 0);
|
||||
// Prepare the spectate mode
|
||||
followCameraState.enabled = false;
|
||||
freeCameraState.position = MakeVector3(256, 256, 30);
|
||||
freeCameraState.velocity = MakeVector3(0, 0, 0);
|
||||
}
|
||||
|
||||
void Client::PlayerCreatedBlock(spades::client::Player *p) {
|
||||
@ -233,6 +233,17 @@ namespace spades {
|
||||
}
|
||||
|
||||
void Client::PlayerLeaving(spades::client::Player *p) {
|
||||
// Choose the next player if a follow cam is active on this player
|
||||
if (FollowsNonLocalPlayer(GetCameraMode()) && &GetCameraTargetPlayer() == p) {
|
||||
FollowNextPlayer(false);
|
||||
|
||||
// Still unable to find a substitute?
|
||||
if (&GetCameraTargetPlayer() == p) {
|
||||
// Turn off the follow cam mode
|
||||
followCameraState.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string msg;
|
||||
msg = _Tr("Client", "Player {0} has left",
|
||||
|
@ -55,34 +55,62 @@ namespace spades {
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
bool Client::ShouldRenderInThirdPersonView() {
|
||||
if (IsFollowing()){
|
||||
if (!GetViewedPlayer()->IsAlive()){
|
||||
return true;
|
||||
ClientCameraMode Client::GetCameraMode() {
|
||||
if (!world) {
|
||||
return ClientCameraMode::None;
|
||||
}
|
||||
|
||||
Player *p = world->GetLocalPlayer();
|
||||
if (!p) {
|
||||
return ClientCameraMode::NotJoined;
|
||||
}
|
||||
|
||||
if (p->IsAlive() && !p->IsSpectator()) {
|
||||
// There exists an alive (non-spectator) local player
|
||||
if ((int)cg_thirdperson != 0 && world->GetNumPlayers() <= 1) {
|
||||
return ClientCameraMode::ThirdPersonLocal;
|
||||
}
|
||||
return ClientCameraMode::FirstPersonLocal;
|
||||
} else {
|
||||
// The local player is dead or a spectator
|
||||
if (followCameraState.enabled) {
|
||||
if (followCameraState.firstPerson) {
|
||||
return ClientCameraMode::FirstPersonFollow;
|
||||
} else {
|
||||
return ClientCameraMode::ThirdPersonFollow;
|
||||
}
|
||||
} else {
|
||||
if (p->IsSpectator()) {
|
||||
return ClientCameraMode::Free;
|
||||
} else {
|
||||
// Look at your own cadaver!
|
||||
return ClientCameraMode::ThirdPersonLocal;
|
||||
}
|
||||
}
|
||||
return !firstPersonSpectate;
|
||||
}
|
||||
|
||||
if (world && world->GetLocalPlayer()) {
|
||||
if (!world->GetLocalPlayer()->IsAlive())
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((int)cg_thirdperson != 0 && world->GetNumPlayers() <= 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Player *Client::GetViewedPlayer() {
|
||||
// what happens if we are in free mode?
|
||||
// doesn't matter for the current code, but keep this in mind
|
||||
if (IsFollowing()){
|
||||
return world->GetPlayer(followingPlayerId);
|
||||
}
|
||||
else {
|
||||
return world->GetLocalPlayer();
|
||||
int Client::GetCameraTargetPlayerId() {
|
||||
switch (GetCameraMode()) {
|
||||
case ClientCameraMode::None:
|
||||
case ClientCameraMode::NotJoined:
|
||||
case ClientCameraMode::Free:
|
||||
SPUnreachable();
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
SPAssert(world);
|
||||
return world->GetLocalPlayerIndex();
|
||||
case ClientCameraMode::FirstPersonFollow:
|
||||
case ClientCameraMode::ThirdPersonFollow:
|
||||
return followedPlayerId;
|
||||
}
|
||||
SPUnreachable();
|
||||
}
|
||||
|
||||
Player &Client::GetCameraTargetPlayer() {
|
||||
Player *p = world->GetPlayer(GetCameraTargetPlayerId());
|
||||
SPAssert(p);
|
||||
return *p;
|
||||
}
|
||||
|
||||
float Client::GetLocalFireVibration() {
|
||||
@ -95,30 +123,17 @@ namespace spades {
|
||||
}
|
||||
|
||||
float Client::GetAimDownZoomScale() {
|
||||
if (!world) {
|
||||
Player &player = GetCameraTargetPlayer();
|
||||
if (!player.IsToolWeapon() ||
|
||||
!player.IsAlive()) {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
if (ShouldRenderInThirdPersonView()) {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
Player* player = GetViewedPlayer();
|
||||
if (
|
||||
!player ||
|
||||
!player->IsToolWeapon() ||
|
||||
!player->IsAlive()
|
||||
) {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
ClientPlayer* clientPlayer = clientPlayers[player->GetId()];
|
||||
if (!clientPlayer) {
|
||||
return 1.f;
|
||||
}
|
||||
ClientPlayer* clientPlayer = clientPlayers[player.GetId()];
|
||||
SPAssert(clientPlayer);
|
||||
|
||||
float delta = .8f;
|
||||
switch (player->GetWeapon()->GetWeaponType()) {
|
||||
switch (player.GetWeapon()->GetWeaponType()) {
|
||||
case SMG_WEAPON: delta = .8f; break;
|
||||
case RIFLE_WEAPON: delta = 1.4f; break;
|
||||
case SHOTGUN_WEAPON: delta = .4f; break;
|
||||
@ -153,49 +168,143 @@ namespace spades {
|
||||
renderer->SetFogColor(
|
||||
MakeVector3(fogColor.x / 255.f, fogColor.y / 255.f, fogColor.z / 255.f));
|
||||
|
||||
Player *player = world->GetLocalPlayer();
|
||||
def.blurVignette = 0.0f;
|
||||
|
||||
def.blurVignette = .0f;
|
||||
float roll = 0.f;
|
||||
float scale = 1.f;
|
||||
float vibPitch = 0.f;
|
||||
float vibYaw = 0.f;
|
||||
|
||||
if (IsFollowing()) {
|
||||
int limit = 100;
|
||||
// if current following player has left,
|
||||
// or removed,
|
||||
// choose next player.
|
||||
while (!world->GetPlayer(followingPlayerId) ||
|
||||
world->GetPlayer(followingPlayerId)->GetFront().GetPoweredLength() <
|
||||
.01f) {
|
||||
FollowNextPlayer(false);
|
||||
if ((limit--) <= 0) {
|
||||
break;
|
||||
switch (GetCameraMode()) {
|
||||
case ClientCameraMode::None:
|
||||
SPUnreachable();
|
||||
|
||||
case ClientCameraMode::NotJoined:
|
||||
def.viewOrigin = MakeVector3(256, 256, 4);
|
||||
def.viewAxis[0] = MakeVector3(-1, 0, 0);
|
||||
def.viewAxis[1] = MakeVector3(0, 1, 0);
|
||||
def.viewAxis[2] = MakeVector3(0, 0, 1);
|
||||
|
||||
def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f;
|
||||
def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() /
|
||||
renderer->ScreenHeight()) * 2.f;
|
||||
|
||||
def.zNear = 0.05f;
|
||||
|
||||
def.skipWorld = false;
|
||||
break;
|
||||
|
||||
case ClientCameraMode::FirstPersonLocal:
|
||||
case ClientCameraMode::FirstPersonFollow: {
|
||||
Player &player = GetCameraTargetPlayer();
|
||||
|
||||
Matrix4 eyeMatrix = clientPlayers[player.GetId()]->GetEyeMatrix();
|
||||
|
||||
def.viewOrigin = eyeMatrix.GetOrigin();
|
||||
def.viewAxis[0] = -eyeMatrix.GetAxis(0);
|
||||
def.viewAxis[1] = -eyeMatrix.GetAxis(2);
|
||||
def.viewAxis[2] = eyeMatrix.GetAxis(1);
|
||||
|
||||
if (shakeLevel >= 1) {
|
||||
float localFireVibration = GetLocalFireVibration();
|
||||
localFireVibration *= localFireVibration;
|
||||
|
||||
if (player.GetTool() == Player::ToolSpade) {
|
||||
localFireVibration *= 0.4f;
|
||||
}
|
||||
|
||||
roll += (nextRandom() - nextRandom()) * 0.03f * localFireVibration;
|
||||
scale += nextRandom() * 0.04f * localFireVibration;
|
||||
|
||||
vibPitch += localFireVibration * (1.f - localFireVibration) * 0.01f;
|
||||
vibYaw += sinf(localFireVibration * (float)M_PI * 2.f) * 0.001f;
|
||||
|
||||
def.radialBlur += localFireVibration * 0.05f;
|
||||
|
||||
// sprint bob
|
||||
{
|
||||
float sp = SmoothStep(GetSprintState());
|
||||
vibYaw += sinf(player.GetWalkAnimationProgress() *
|
||||
static_cast<float>(M_PI) * 2.f) *
|
||||
0.01f * sp;
|
||||
roll -= sinf(player.GetWalkAnimationProgress() *
|
||||
static_cast<float>(M_PI) * 2.f) *
|
||||
0.005f * (sp);
|
||||
float p = cosf(player.GetWalkAnimationProgress() *
|
||||
static_cast<float>(M_PI) * 2.f);
|
||||
p = p * p;
|
||||
p *= p;
|
||||
p *= p;
|
||||
vibPitch += p * 0.01f * sp;
|
||||
|
||||
if (shakeLevel >= 2) {
|
||||
vibYaw += coherentNoiseSamplers[0].Sample(
|
||||
player.GetWalkAnimationProgress() * 2.5f) *
|
||||
0.005f * sp;
|
||||
vibPitch += coherentNoiseSamplers[1].Sample(
|
||||
player.GetWalkAnimationProgress() * 2.5f) *
|
||||
0.01f * sp;
|
||||
roll += coherentNoiseSamplers[2].Sample(
|
||||
player.GetWalkAnimationProgress() * 2.5f) *
|
||||
0.008f * sp;
|
||||
|
||||
scale += sp * 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f;
|
||||
def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() /
|
||||
renderer->ScreenHeight()) * 2.f;
|
||||
|
||||
// for 1st view, camera blur can be used
|
||||
def.denyCameraBlur = false;
|
||||
|
||||
// DoF when doing ADS
|
||||
float aimDownState = GetAimDownState();
|
||||
float per = aimDownState;
|
||||
per *= per * per;
|
||||
def.depthOfFieldFocalLength = per * 13.f + .054f;
|
||||
|
||||
// Hurt effect
|
||||
{
|
||||
float wTime = world->GetTime();
|
||||
if (wTime < lastHurtTime + .15f && wTime >= lastHurtTime) {
|
||||
float per = 1.f - (wTime - lastHurtTime) / .15f;
|
||||
per *= .5f - player.GetHealth() / 100.f * .3f;
|
||||
def.blurVignette += per * 6.f;
|
||||
}
|
||||
if (wTime < lastHurtTime + .2f && wTime >= lastHurtTime) {
|
||||
float per = 1.f - (wTime - lastHurtTime) / .2f;
|
||||
per *= .5f - player.GetHealth() / 100.f * .3f;
|
||||
def.saturation *= std::max(0.f, 1.f - per * 4.f);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply ADS zoom
|
||||
scale /= GetAimDownZoomScale();
|
||||
|
||||
// Update initial floating camera pos
|
||||
freeCameraState.position = def.viewOrigin;
|
||||
freeCameraState.velocity = MakeVector3(0, 0, 0);
|
||||
break;
|
||||
}
|
||||
player = world->GetPlayer(followingPlayerId);
|
||||
}
|
||||
if (player) {
|
||||
|
||||
float roll = 0.f;
|
||||
float scale = 1.f;
|
||||
float vibPitch = 0.f;
|
||||
float vibYaw = 0.f;
|
||||
scale /= GetAimDownZoomScale();
|
||||
case ClientCameraMode::ThirdPersonLocal:
|
||||
case ClientCameraMode::ThirdPersonFollow: {
|
||||
Player &player = GetCameraTargetPlayer();
|
||||
Vector3 center = player.GetEye();
|
||||
|
||||
if (ShouldRenderInThirdPersonView() ||
|
||||
(IsFollowing() && player != world->GetLocalPlayer())) {
|
||||
Vector3 center = player->GetEye();
|
||||
Vector3 playerFront = player->GetFront2D();
|
||||
Vector3 up = MakeVector3(0, 0, -1);
|
||||
|
||||
if ((!player->IsAlive()) && lastMyCorpse &&
|
||||
player == world->GetLocalPlayer()) {
|
||||
if (!player.IsAlive() && lastMyCorpse &&
|
||||
&player == world->GetLocalPlayer()) {
|
||||
center = lastMyCorpse->GetCenter();
|
||||
}
|
||||
if (map->IsSolidWrapped((int)floorf(center.x), (int)floorf(center.y),
|
||||
(int)floorf(center.z))) {
|
||||
(int)floorf(center.z))) {
|
||||
float z = center.z;
|
||||
while (z > center.z - 5.f) {
|
||||
if (!map->IsSolidWrapped((int)floorf(center.x),
|
||||
(int)floorf(center.y), (int)floorf(z))) {
|
||||
(int)floorf(center.y), (int)floorf(z))) {
|
||||
center.z = z;
|
||||
break;
|
||||
} else {
|
||||
@ -205,31 +314,21 @@ namespace spades {
|
||||
}
|
||||
|
||||
float distance = 5.f;
|
||||
if (player == world->GetLocalPlayer() &&
|
||||
world->GetLocalPlayer()->GetTeamId() < 2 &&
|
||||
!world->GetLocalPlayer()->IsAlive()) {
|
||||
if (&player == world->GetLocalPlayer() &&
|
||||
world->GetLocalPlayer()->GetTeamId() < 2 &&
|
||||
!world->GetLocalPlayer()->IsAlive()) {
|
||||
// deathcam.
|
||||
float elapsedTime = time - lastAliveTime;
|
||||
distance -= 3.f * expf(-elapsedTime * 1.f);
|
||||
}
|
||||
|
||||
auto &state = followAndFreeCameraState;
|
||||
Vector3 eye = center;
|
||||
// eye -= playerFront * 5.f;
|
||||
// eye += up * 2.0f;
|
||||
eye.x += cosf(followYaw) * cosf(followPitch) * distance;
|
||||
eye.y += sinf(followYaw) * cosf(followPitch) * distance;
|
||||
eye.z -= sinf(followPitch) * distance;
|
||||
eye.x += cosf(state.yaw) * cosf(state.pitch) * distance;
|
||||
eye.y += sinf(state.yaw) * cosf(state.pitch) * distance;
|
||||
eye.z -= sinf(state.pitch) * distance;
|
||||
|
||||
if (false) {
|
||||
// settings for making limbo stuff
|
||||
eye = center;
|
||||
eye += playerFront * 3.f;
|
||||
eye += up * -.1f;
|
||||
eye += player->GetRight() * 2.f;
|
||||
scale *= .6f;
|
||||
}
|
||||
|
||||
// try ray casting
|
||||
// Prevent the camera from being behind a wall
|
||||
GameMap::RayCastResult result;
|
||||
result = map->CastRay2(center, (eye - center).Normalize(), 256);
|
||||
if (result.hit) {
|
||||
@ -245,39 +344,32 @@ namespace spades {
|
||||
Vector3 front = center - eye;
|
||||
front = front.Normalize();
|
||||
|
||||
if (ShouldRenderInThirdPersonView()) {
|
||||
def.viewOrigin = eye;
|
||||
def.viewAxis[0] = -Vector3::Cross(up, front).Normalize();
|
||||
def.viewAxis[1] = -Vector3::Cross(front, def.viewAxis[0]).Normalize();
|
||||
def.viewAxis[2] = front;
|
||||
} else {
|
||||
def.viewOrigin = player->GetEye();
|
||||
def.viewAxis[0] = player->GetRight();
|
||||
def.viewAxis[1] = player->GetUp();
|
||||
def.viewAxis[2] = player->GetFront();
|
||||
}
|
||||
Vector3 up = MakeVector3(0, 0, -1);
|
||||
|
||||
def.viewOrigin = eye;
|
||||
def.viewAxis[0] = -Vector3::Cross(up, front).Normalize();
|
||||
def.viewAxis[1] = -Vector3::Cross(front, def.viewAxis[0]).Normalize();
|
||||
def.viewAxis[2] = front;
|
||||
|
||||
def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f;
|
||||
def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() /
|
||||
renderer->ScreenHeight()) *
|
||||
2.f;
|
||||
renderer->ScreenHeight()) * 2.f;
|
||||
|
||||
// update initial spectate pos
|
||||
// this is not used now, but if the local player is
|
||||
// is spectating, this is used when he/she's no
|
||||
// longer following
|
||||
followPos = def.viewOrigin;
|
||||
followVel = MakeVector3(0, 0, 0);
|
||||
|
||||
} else if (player->GetTeamId() >= 2) {
|
||||
// Update initial floating camera pos
|
||||
freeCameraState.position = def.viewOrigin;
|
||||
freeCameraState.velocity = MakeVector3(0, 0, 0);
|
||||
break;
|
||||
}
|
||||
case ClientCameraMode::Free: {
|
||||
// spectator view (noclip view)
|
||||
Vector3 center = followPos;
|
||||
Vector3 center = freeCameraState.position;
|
||||
Vector3 front;
|
||||
Vector3 up = {0, 0, -1};
|
||||
|
||||
front.x = -cosf(followYaw) * cosf(followPitch);
|
||||
front.y = -sinf(followYaw) * cosf(followPitch);
|
||||
front.z = sinf(followPitch);
|
||||
auto &state = followAndFreeCameraState;
|
||||
front.x = -cosf(state.yaw) * cosf(state.pitch);
|
||||
front.y = -sinf(state.yaw) * cosf(state.pitch);
|
||||
front.z = sinf(state.pitch);
|
||||
|
||||
def.viewOrigin = center;
|
||||
def.viewAxis[0] = -Vector3::Cross(up, front).Normalize();
|
||||
@ -286,180 +378,98 @@ namespace spades {
|
||||
|
||||
def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f;
|
||||
def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() /
|
||||
renderer->ScreenHeight()) *
|
||||
2.f;
|
||||
renderer->ScreenHeight()) * 2.f;
|
||||
|
||||
// for 1st view, camera blur can be used
|
||||
def.denyCameraBlur = false;
|
||||
|
||||
} else {
|
||||
Matrix4 eyeMatrix = clientPlayers[player->GetId()]->GetEyeMatrix();
|
||||
def.viewOrigin = eyeMatrix.GetOrigin();
|
||||
def.viewAxis[0] = -eyeMatrix.GetAxis(0);
|
||||
def.viewAxis[1] = -eyeMatrix.GetAxis(2);
|
||||
def.viewAxis[2] = eyeMatrix.GetAxis(1);
|
||||
|
||||
if (shakeLevel >= 1) {
|
||||
float localFireVibration = GetLocalFireVibration();
|
||||
localFireVibration *= localFireVibration;
|
||||
|
||||
if (player->GetTool() == Player::ToolSpade) {
|
||||
localFireVibration *= 0.4f;
|
||||
}
|
||||
|
||||
roll += (nextRandom() - nextRandom()) * 0.03f * localFireVibration;
|
||||
scale += nextRandom() * 0.04f * localFireVibration;
|
||||
|
||||
vibPitch += localFireVibration * (1.f - localFireVibration) * 0.01f;
|
||||
vibYaw += sinf(localFireVibration * (float)M_PI * 2.f) * 0.001f;
|
||||
|
||||
def.radialBlur += localFireVibration * 0.05f;
|
||||
|
||||
// sprint bob
|
||||
{
|
||||
float sp = SmoothStep(GetSprintState());
|
||||
vibYaw += sinf(player->GetWalkAnimationProgress() *
|
||||
static_cast<float>(M_PI) * 2.f) *
|
||||
0.01f * sp;
|
||||
roll -= sinf(player->GetWalkAnimationProgress() *
|
||||
static_cast<float>(M_PI) * 2.f) *
|
||||
0.005f * (sp);
|
||||
float p = cosf(player->GetWalkAnimationProgress() *
|
||||
static_cast<float>(M_PI) * 2.f);
|
||||
p = p * p;
|
||||
p *= p;
|
||||
p *= p;
|
||||
vibPitch += p * 0.01f * sp;
|
||||
|
||||
if (shakeLevel >= 2) {
|
||||
vibYaw += coherentNoiseSamplers[0].Sample(
|
||||
player->GetWalkAnimationProgress() * 2.5f) *
|
||||
0.005f * sp;
|
||||
vibPitch += coherentNoiseSamplers[1].Sample(
|
||||
player->GetWalkAnimationProgress() * 2.5f) *
|
||||
0.01f * sp;
|
||||
roll += coherentNoiseSamplers[2].Sample(
|
||||
player->GetWalkAnimationProgress() * 2.5f) *
|
||||
0.008f * sp;
|
||||
|
||||
scale += sp * 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f;
|
||||
def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() /
|
||||
renderer->ScreenHeight()) *
|
||||
2.f;
|
||||
|
||||
// for 1st view, camera blur can be used
|
||||
def.denyCameraBlur = false;
|
||||
|
||||
float aimDownState = GetAimDownState();
|
||||
float per = aimDownState;
|
||||
per *= per * per;
|
||||
def.depthOfFieldFocalLength = per * 13.f + .054f;
|
||||
|
||||
def.blurVignette = .0f;
|
||||
break;
|
||||
}
|
||||
|
||||
// add vibration for both 1st/3rd view
|
||||
{
|
||||
// add grenade vibration
|
||||
float grenVib = grenadeVibration;
|
||||
if (grenVib > 0.f) {
|
||||
if (shakeLevel >= 1) {
|
||||
grenVib *= 10.f;
|
||||
if (grenVib > 1.f)
|
||||
grenVib = 1.f;
|
||||
roll += (nextRandom() - nextRandom()) * 0.2f * grenVib;
|
||||
vibPitch += (nextRandom() - nextRandom()) * 0.1f * grenVib;
|
||||
vibYaw += (nextRandom() - nextRandom()) * 0.1f * grenVib;
|
||||
scale -= (nextRandom() - nextRandom()) * 0.1f * grenVib;
|
||||
|
||||
def.radialBlur += grenVib * 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// add grenade vibration
|
||||
float grenVib = grenadeVibrationSlow;
|
||||
if (grenVib > 0.f) {
|
||||
if (shakeLevel >= 2) {
|
||||
grenVib *= 4.f;
|
||||
if (grenVib > 1.f)
|
||||
grenVib = 1.f;
|
||||
grenVib *= grenVib;
|
||||
|
||||
roll +=
|
||||
coherentNoiseSamplers[0].Sample(time * 8.f) * 0.2f * grenVib;
|
||||
vibPitch +=
|
||||
coherentNoiseSamplers[1].Sample(time * 12.f) * 0.1f * grenVib;
|
||||
vibYaw +=
|
||||
coherentNoiseSamplers[2].Sample(time * 11.f) * 0.1f * grenVib;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add roll / scale
|
||||
{
|
||||
Vector3 right = def.viewAxis[0];
|
||||
Vector3 up = def.viewAxis[1];
|
||||
|
||||
def.viewAxis[0] = right * cosf(roll) - up * sinf(roll);
|
||||
def.viewAxis[1] = up * cosf(roll) + right * sinf(roll);
|
||||
|
||||
def.fovX = atanf(tanf(def.fovX * .5f) * scale) * 2.f;
|
||||
def.fovY = atanf(tanf(def.fovY * .5f) * scale) * 2.f;
|
||||
}
|
||||
{
|
||||
Vector3 u = def.viewAxis[1];
|
||||
Vector3 v = def.viewAxis[2];
|
||||
|
||||
def.viewAxis[1] = u * cosf(vibPitch) - v * sinf(vibPitch);
|
||||
def.viewAxis[2] = v * cosf(vibPitch) + u * sinf(vibPitch);
|
||||
}
|
||||
{
|
||||
Vector3 u = def.viewAxis[0];
|
||||
Vector3 v = def.viewAxis[2];
|
||||
|
||||
def.viewAxis[0] = u * cosf(vibYaw) - v * sinf(vibYaw);
|
||||
def.viewAxis[2] = v * cosf(vibYaw) + u * sinf(vibYaw);
|
||||
}
|
||||
{
|
||||
float wTime = world->GetTime();
|
||||
if (wTime < lastHurtTime + .15f && wTime >= lastHurtTime) {
|
||||
float per = 1.f - (wTime - lastHurtTime) / .15f;
|
||||
per *= .5f - player->GetHealth() / 100.f * .3f;
|
||||
def.blurVignette += per * 6.f;
|
||||
}
|
||||
if (wTime < lastHurtTime + .2f && wTime >= lastHurtTime) {
|
||||
float per = 1.f - (wTime - lastHurtTime) / .2f;
|
||||
per *= .5f - player->GetHealth() / 100.f * .3f;
|
||||
def.saturation *= std::max(0.f, 1.f - per * 4.f);
|
||||
}
|
||||
}
|
||||
|
||||
def.zNear = 0.05f;
|
||||
|
||||
def.skipWorld = false;
|
||||
} else {
|
||||
def.viewOrigin = MakeVector3(256, 256, 4);
|
||||
def.viewAxis[0] = MakeVector3(-1, 0, 0);
|
||||
def.viewAxis[1] = MakeVector3(0, 1, 0);
|
||||
def.viewAxis[2] = MakeVector3(0, 0, 1);
|
||||
|
||||
def.fovY = (float)cg_fov * static_cast<float>(M_PI) / 180.f;
|
||||
def.fovX = atanf(tanf(def.fovY * .5f) * renderer->ScreenWidth() /
|
||||
renderer->ScreenHeight()) *
|
||||
2.f;
|
||||
|
||||
def.zNear = 0.05f;
|
||||
|
||||
def.skipWorld = false;
|
||||
}
|
||||
|
||||
// Add vibration effects
|
||||
{
|
||||
float grenVib = grenadeVibration;
|
||||
if (grenVib > 0.f) {
|
||||
if (shakeLevel >= 1) {
|
||||
grenVib *= 10.f;
|
||||
if (grenVib > 1.f)
|
||||
grenVib = 1.f;
|
||||
roll += (nextRandom() - nextRandom()) * 0.2f * grenVib;
|
||||
vibPitch += (nextRandom() - nextRandom()) * 0.1f * grenVib;
|
||||
vibYaw += (nextRandom() - nextRandom()) * 0.1f * grenVib;
|
||||
scale -= (nextRandom() - nextRandom()) * 0.1f * grenVib;
|
||||
|
||||
def.radialBlur += grenVib * 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
float grenVib = grenadeVibrationSlow;
|
||||
if (grenVib > 0.f) {
|
||||
if (shakeLevel >= 2) {
|
||||
grenVib *= 4.f;
|
||||
if (grenVib > 1.f)
|
||||
grenVib = 1.f;
|
||||
grenVib *= grenVib;
|
||||
|
||||
roll +=
|
||||
coherentNoiseSamplers[0].Sample(time * 8.f) * 0.2f * grenVib;
|
||||
vibPitch +=
|
||||
coherentNoiseSamplers[1].Sample(time * 12.f) * 0.1f * grenVib;
|
||||
vibYaw +=
|
||||
coherentNoiseSamplers[2].Sample(time * 11.f) * 0.1f * grenVib;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add roll / scale
|
||||
{
|
||||
Vector3 right = def.viewAxis[0];
|
||||
Vector3 up = def.viewAxis[1];
|
||||
|
||||
def.viewAxis[0] = right * cosf(roll) - up * sinf(roll);
|
||||
def.viewAxis[1] = up * cosf(roll) + right * sinf(roll);
|
||||
|
||||
def.fovX = atanf(tanf(def.fovX * .5f) * scale) * 2.f;
|
||||
def.fovY = atanf(tanf(def.fovY * .5f) * scale) * 2.f;
|
||||
}
|
||||
{
|
||||
Vector3 u = def.viewAxis[1];
|
||||
Vector3 v = def.viewAxis[2];
|
||||
|
||||
def.viewAxis[1] = u * cosf(vibPitch) - v * sinf(vibPitch);
|
||||
def.viewAxis[2] = v * cosf(vibPitch) + u * sinf(vibPitch);
|
||||
}
|
||||
{
|
||||
Vector3 u = def.viewAxis[0];
|
||||
Vector3 v = def.viewAxis[2];
|
||||
|
||||
def.viewAxis[0] = u * cosf(vibYaw) - v * sinf(vibYaw);
|
||||
def.viewAxis[2] = v * cosf(vibYaw) + u * sinf(vibYaw);
|
||||
}
|
||||
|
||||
if (def.viewOrigin.z < 0.f) {
|
||||
// Need to move the far plane because there's no vertical fog
|
||||
def.zFar -= def.viewOrigin.z;
|
||||
}
|
||||
|
||||
if ((int)cg_manualFocus) {
|
||||
// Depth of field is manually controlled
|
||||
def.depthOfFieldNearBlurStrength = def.depthOfFieldFarBlurStrength =
|
||||
0.5f * (float)cg_depthOfFieldAmount;
|
||||
def.depthOfFieldFocalLength = focalLength;
|
||||
} else {
|
||||
def.depthOfFieldNearBlurStrength = cg_depthOfFieldAmount;
|
||||
def.depthOfFieldFarBlurStrength = 0.f;
|
||||
}
|
||||
|
||||
def.zNear = 0.05f;
|
||||
|
||||
def.skipWorld = false;
|
||||
} else {
|
||||
SPAssert(GetCameraMode() == ClientCameraMode::None);
|
||||
|
||||
// Let there be darkness
|
||||
def.viewOrigin = MakeVector3(0, 0, 0);
|
||||
def.viewAxis[0] = MakeVector3(1, 0, 0);
|
||||
def.viewAxis[1] = MakeVector3(0, 0, -1);
|
||||
@ -477,27 +487,12 @@ namespace spades {
|
||||
renderer->SetFogColor(MakeVector3(0, 0, 0));
|
||||
}
|
||||
|
||||
if (def.viewOrigin.z < 0.f) {
|
||||
// Need to move the far plane because there's no vertical fog
|
||||
def.zFar -= def.viewOrigin.z;
|
||||
}
|
||||
|
||||
SPAssert(!std::isnan(def.viewOrigin.x));
|
||||
SPAssert(!std::isnan(def.viewOrigin.y));
|
||||
SPAssert(!std::isnan(def.viewOrigin.z));
|
||||
|
||||
def.radialBlur = std::min(def.radialBlur, 1.f);
|
||||
|
||||
if ((int)cg_manualFocus) {
|
||||
// Depth of field is manually controlled
|
||||
def.depthOfFieldNearBlurStrength = def.depthOfFieldFarBlurStrength =
|
||||
0.5f * (float)cg_depthOfFieldAmount;
|
||||
def.depthOfFieldFocalLength = focalLength;
|
||||
} else {
|
||||
def.depthOfFieldNearBlurStrength = cg_depthOfFieldAmount;
|
||||
def.depthOfFieldFarBlurStrength = 0.f;
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
@ -276,25 +276,28 @@ namespace spades {
|
||||
void Client::UpdateLocalSpectator(float dt) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
Vector3 lastPos = followPos;
|
||||
followVel *= powf(.3f, dt);
|
||||
followPos += followVel * dt;
|
||||
auto &sharedState = followAndFreeCameraState;
|
||||
auto &freeState = freeCameraState;
|
||||
|
||||
if (followPos.x < 0.f) {
|
||||
followVel.x = fabsf(followVel.x) * 0.2f;
|
||||
followPos = lastPos + followVel * dt;
|
||||
Vector3 lastPos = freeState.position;
|
||||
freeState.velocity *= powf(.3f, dt);
|
||||
freeState.position += freeState.velocity * dt;
|
||||
|
||||
if (freeState.position.x < 0.f) {
|
||||
freeState.velocity.x = fabsf(freeState.velocity.x) * 0.2f;
|
||||
freeState.position = lastPos + freeState.velocity * dt;
|
||||
}
|
||||
if (followPos.y < 0.f) {
|
||||
followVel.y = fabsf(followVel.y) * 0.2f;
|
||||
followPos = lastPos + followVel * dt;
|
||||
if (freeState.position.y < 0.f) {
|
||||
freeState.velocity.y = fabsf(freeState.velocity.y) * 0.2f;
|
||||
freeState.position = lastPos + freeState.velocity * dt;
|
||||
}
|
||||
if (followPos.x > (float)GetWorld()->GetMap()->Width()) {
|
||||
followVel.x = fabsf(followVel.x) * -0.2f;
|
||||
followPos = lastPos + followVel * dt;
|
||||
if (freeState.position.x > (float)GetWorld()->GetMap()->Width()) {
|
||||
freeState.velocity.x = fabsf(freeState.velocity.x) * -0.2f;
|
||||
freeState.position = lastPos + freeState.velocity * dt;
|
||||
}
|
||||
if (followPos.y > (float)GetWorld()->GetMap()->Height()) {
|
||||
followVel.y = fabsf(followVel.y) * -0.2f;
|
||||
followPos = lastPos + followVel * dt;
|
||||
if (freeState.position.y > (float)GetWorld()->GetMap()->Height()) {
|
||||
freeState.velocity.y = fabsf(freeState.velocity.y) * -0.2f;
|
||||
freeState.position = lastPos + freeState.velocity * dt;
|
||||
}
|
||||
|
||||
GameMap::RayCastResult minResult;
|
||||
@ -302,22 +305,22 @@ namespace spades {
|
||||
Vector3 minShift;
|
||||
|
||||
// check collision
|
||||
if (followVel.GetLength() < .01) {
|
||||
followPos = lastPos;
|
||||
followVel *= 0.f;
|
||||
if (freeState.velocity.GetLength() < .01) {
|
||||
freeState.position = lastPos;
|
||||
freeState.velocity *= 0.f;
|
||||
} else {
|
||||
for (int sx = -1; sx <= 1; sx++)
|
||||
for (int sy = -1; sy <= 1; sy++)
|
||||
for (int sz = -1; sz <= 1; sz++) {
|
||||
GameMap::RayCastResult result;
|
||||
Vector3 shift = {sx * .1f, sy * .1f, sz * .1f};
|
||||
result = map->CastRay2(lastPos + shift, followPos - lastPos, 256);
|
||||
result = map->CastRay2(lastPos + shift, freeState.position - lastPos, 256);
|
||||
if (result.hit && !result.startSolid &&
|
||||
Vector3::Dot(result.hitPos - followPos - shift,
|
||||
followPos - lastPos) < 0.f) {
|
||||
Vector3::Dot(result.hitPos - freeState.position - shift,
|
||||
freeState.position - lastPos) < 0.f) {
|
||||
|
||||
float dist = Vector3::Dot(result.hitPos - followPos - shift,
|
||||
(followPos - lastPos).Normalize());
|
||||
float dist = Vector3::Dot(result.hitPos - freeState.position - shift,
|
||||
(freeState.position - lastPos).Normalize());
|
||||
if (dist < minDist) {
|
||||
minResult = result;
|
||||
minDist = dist;
|
||||
@ -329,25 +332,25 @@ namespace spades {
|
||||
if (minDist < 1.e+9f) {
|
||||
GameMap::RayCastResult result = minResult;
|
||||
Vector3 shift = minShift;
|
||||
followPos = result.hitPos - shift;
|
||||
followPos.x += result.normal.x * .02f;
|
||||
followPos.y += result.normal.y * .02f;
|
||||
followPos.z += result.normal.z * .02f;
|
||||
freeState.position = result.hitPos - shift;
|
||||
freeState.position.x += result.normal.x * .02f;
|
||||
freeState.position.y += result.normal.y * .02f;
|
||||
freeState.position.z += result.normal.z * .02f;
|
||||
|
||||
// bounce
|
||||
Vector3 norm = {(float)result.normal.x, (float)result.normal.y,
|
||||
(float)result.normal.z};
|
||||
float dot = Vector3::Dot(followVel, norm);
|
||||
followVel -= norm * (dot * 1.2f);
|
||||
float dot = Vector3::Dot(freeState.velocity, norm);
|
||||
freeState.velocity -= norm * (dot * 1.2f);
|
||||
}
|
||||
|
||||
// acceleration
|
||||
Vector3 front;
|
||||
Vector3 up = {0, 0, -1};
|
||||
|
||||
front.x = -cosf(followYaw) * cosf(followPitch);
|
||||
front.y = -sinf(followYaw) * cosf(followPitch);
|
||||
front.z = sinf(followPitch);
|
||||
front.x = -cosf(sharedState.yaw) * cosf(sharedState.pitch);
|
||||
front.y = -sinf(sharedState.yaw) * cosf(sharedState.pitch);
|
||||
front.z = sinf(sharedState.pitch);
|
||||
|
||||
Vector3 right = -Vector3::Cross(up, front).Normalize();
|
||||
Vector3 up2 = Vector3::Cross(right, front).Normalize();
|
||||
@ -361,22 +364,22 @@ namespace spades {
|
||||
up2 *= scale;
|
||||
|
||||
if (playerInput.moveForward) {
|
||||
followVel += front;
|
||||
freeState.velocity += front;
|
||||
} else if (playerInput.moveBackward) {
|
||||
followVel -= front;
|
||||
freeState.velocity -= front;
|
||||
}
|
||||
if (playerInput.moveLeft) {
|
||||
followVel -= right;
|
||||
freeState.velocity -= right;
|
||||
} else if (playerInput.moveRight) {
|
||||
followVel += right;
|
||||
freeState.velocity += right;
|
||||
}
|
||||
if (playerInput.jump) {
|
||||
followVel += up2;
|
||||
freeState.velocity += up2;
|
||||
} else if (playerInput.crouch) {
|
||||
followVel -= up2;
|
||||
freeState.velocity -= up2;
|
||||
}
|
||||
|
||||
SPAssert(followVel.GetLength() < 100.f);
|
||||
SPAssert(freeState.velocity.GetLength() < 100.f);
|
||||
}
|
||||
|
||||
/** Handles movement of joined local player. */
|
||||
@ -794,13 +797,13 @@ namespace spades {
|
||||
}
|
||||
}
|
||||
|
||||
// begin following
|
||||
// The local player is dead; initialize the look-you-are-dead cam
|
||||
if (victim == world->GetLocalPlayer()) {
|
||||
followingPlayerId = victim->GetId();
|
||||
followCameraState.enabled = false;
|
||||
|
||||
Vector3 v = -victim->GetFront();
|
||||
followYaw = atan2(v.y, v.x);
|
||||
followPitch = 30.f * M_PI / 180.f;
|
||||
followAndFreeCameraState.yaw = atan2(v.y, v.x);
|
||||
followAndFreeCameraState.pitch = 30.f * M_PI / 180.f;
|
||||
}
|
||||
|
||||
// emit blood (also for local player)
|
||||
@ -943,7 +946,7 @@ namespace spades {
|
||||
SPAssert(type != HitTypeBlock);
|
||||
|
||||
// don't bleed local player
|
||||
if (hurtPlayer != world->GetLocalPlayer() || ShouldRenderInThirdPersonView()) {
|
||||
if (!IsFirstPerson(GetCameraMode()) || &GetCameraTargetPlayer() != hurtPlayer) {
|
||||
Bleed(hitPos);
|
||||
}
|
||||
|
||||
@ -980,7 +983,7 @@ namespace spades {
|
||||
|
||||
if (by == world->GetLocalPlayer() && hurtPlayer) {
|
||||
net->SendHit(hurtPlayer->GetId(), type);
|
||||
|
||||
|
||||
if (type == HitTypeHead) {
|
||||
Handle<IAudioChunk> c =
|
||||
audioDevice->RegisterSound("Sounds/Feedback/HeadshotFeedback.opus");
|
||||
@ -988,7 +991,7 @@ namespace spades {
|
||||
param.volume = cg_hitFeedbackSoundGain;
|
||||
audioDevice->PlayLocal(c, param);
|
||||
}
|
||||
|
||||
|
||||
hitFeedbackIconState = 1.f;
|
||||
if (hurtPlayer->GetTeamId() == world->GetLocalPlayer()->GetTeamId()) {
|
||||
hitFeedbackFriendly = true;
|
||||
@ -1077,7 +1080,8 @@ namespace spades {
|
||||
spades::Vector3 hitPos) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
if (IsFollowing() && followingPlayerId == player->GetId()) {
|
||||
// Do not display tracers for bullets fired by the local player
|
||||
if (IsFirstPerson(GetCameraMode()) && GetCameraTargetPlayerId() == player->GetId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -241,12 +241,13 @@ namespace spades {
|
||||
if (!world)
|
||||
return;
|
||||
|
||||
Player *player = world->GetLocalPlayer();
|
||||
if (client->IsFollowing()) {
|
||||
player = world->GetPlayer(client->followingPlayerId);
|
||||
}
|
||||
if (!player)
|
||||
if (!HasTargetPlayer(client->GetCameraMode())) {
|
||||
// Do not display `MapView` until the player is joined and there is a player to
|
||||
// focus
|
||||
return;
|
||||
}
|
||||
|
||||
Player &player = client->GetCameraTargetPlayer();
|
||||
|
||||
if (largeMap)
|
||||
if (zoomState < .0001f)
|
||||
@ -255,11 +256,11 @@ namespace spades {
|
||||
GameMap *map = world->GetMap();
|
||||
Vector2 mapSize = MakeVector2(map->Width(), map->Height());
|
||||
|
||||
Vector3 pos = player->GetPosition();
|
||||
;
|
||||
if (player->GetTeamId() >= 2) {
|
||||
pos = client->followPos;
|
||||
Vector3 pos = player.GetPosition();
|
||||
if (player.IsSpectator()) {
|
||||
pos = client->freeCameraState.position;
|
||||
}
|
||||
|
||||
Vector2 center = {pos.x, pos.y};
|
||||
float cfgMapSize = cg_minimapSize;
|
||||
if (cfgMapSize < 32)
|
||||
@ -462,38 +463,37 @@ namespace spades {
|
||||
Vector4 teamColorF = ModifyColor(teamColor);
|
||||
teamColorF *= alpha;
|
||||
|
||||
// draw local player's view
|
||||
// Draw the local player's view
|
||||
{
|
||||
Player *p = player;
|
||||
Handle<IImage> viewIcon = renderer->RegisterImage("Gfx/Map/View.png");
|
||||
if (p->IsAlive()) {
|
||||
Vector3 front = p->GetFront2D();
|
||||
float ang = atan2(front.x, -front.y);
|
||||
if (player->GetTeamId() >= 2) {
|
||||
ang = client->followYaw - static_cast<float>(M_PI) * .5f;
|
||||
if (player.IsAlive()) {
|
||||
Vector3 front = player.GetFront2D();
|
||||
float ang;
|
||||
if (player.IsSpectator()) {
|
||||
ang = client->followAndFreeCameraState.yaw - static_cast<float>(M_PI) * .5f;
|
||||
} else {
|
||||
ang = atan2(front.x, -front.y);
|
||||
}
|
||||
|
||||
renderer->SetColorAlphaPremultiplied(teamColorF * 0.9f);
|
||||
|
||||
DrawIcon(player->GetTeamId() >= 2 ? client->followPos : p->GetPosition(),
|
||||
DrawIcon(player.IsSpectator() ? client->freeCameraState.position : player.GetPosition(),
|
||||
viewIcon, ang);
|
||||
}
|
||||
}
|
||||
|
||||
bool isSpectating = player->GetTeamId() >= 2;
|
||||
|
||||
// draw player's icon
|
||||
for (int i = 0; i < world->GetNumPlayerSlots(); i++) {
|
||||
Player *p = world->GetPlayer(i);
|
||||
if (p == nullptr ||
|
||||
(p->GetTeamId() != world->GetLocalPlayer()->GetTeamId() && !isSpectating) ||
|
||||
(p->GetTeamId() != world->GetLocalPlayer()->GetTeamId() && !player.IsSpectator()) ||
|
||||
!p->IsAlive())
|
||||
continue;
|
||||
|
||||
Vector3 front = p->GetFront2D();
|
||||
float ang = atan2(front.x, -front.y);
|
||||
if (player->GetTeamId() >= 2) {
|
||||
ang = client->followYaw - static_cast<float>(M_PI) * .5f;
|
||||
if (p->IsSpectator()) {
|
||||
ang = client->followAndFreeCameraState.yaw - static_cast<float>(M_PI) * .5f;
|
||||
}
|
||||
|
||||
// use a spec color for each player
|
||||
@ -511,24 +511,24 @@ namespace spades {
|
||||
if (iconMode) {
|
||||
WeaponType weapon = world->GetPlayer(i)->GetWeaponType();
|
||||
if (weapon == WeaponType::SMG_WEAPON) {
|
||||
DrawIcon(player->GetTeamId() >= 2 ? client->followPos
|
||||
DrawIcon(p->IsSpectator() ? client->freeCameraState.position
|
||||
: p->GetPosition(),
|
||||
playerSMG, ang);
|
||||
}
|
||||
|
||||
else if (weapon == WeaponType::RIFLE_WEAPON) {
|
||||
DrawIcon(player->GetTeamId() >= 2 ? client->followPos
|
||||
DrawIcon(p->IsSpectator() ? client->freeCameraState.position
|
||||
: p->GetPosition(),
|
||||
playerRifle, ang);
|
||||
}
|
||||
|
||||
else if (weapon == WeaponType::SHOTGUN_WEAPON) {
|
||||
DrawIcon(player->GetTeamId() >= 2 ? client->followPos
|
||||
DrawIcon(p->IsSpectator() ? client->freeCameraState.position
|
||||
: p->GetPosition(),
|
||||
playerShotgun, ang);
|
||||
}
|
||||
} else { // draw normal color
|
||||
DrawIcon(player->GetTeamId() >= 2 ? client->followPos : p->GetPosition(),
|
||||
DrawIcon(p->IsSpectator() ? client->freeCameraState.position : p->GetPosition(),
|
||||
playerIcon, ang);
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,7 @@ namespace spades {
|
||||
Weapon *GetWeapon() { return weapon; }
|
||||
WeaponType GetWeaponType() { return weaponType; }
|
||||
int GetTeamId() { return teamId; }
|
||||
bool IsSpectator() { return teamId >= 2; }
|
||||
std::string GetName();
|
||||
IntVector3 GetColor();
|
||||
IntVector3 GetBlockColor() { return blockColor; }
|
||||
|
@ -47,6 +47,8 @@ namespace spades {
|
||||
|
||||
#define SPNotImplemented() SPRaise("Not implemented")
|
||||
|
||||
#define SPUnreachable() SPRaise("Internal error; unreachable code")
|
||||
|
||||
#define SPInvalidArgument(name) SPRaise("Invalid argument: %s", name)
|
||||
|
||||
#define SPInvalidEnum(name, value) SPRaise("Invalid enum: %s: %d", name, (int)(value))
|
||||
|
Loading…
x
Reference in New Issue
Block a user