2014-03-09 21:43:38 +09:00
|
|
|
/*
|
|
|
|
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/ConcurrentDispatch.h>
|
|
|
|
#include <Core/Settings.h>
|
|
|
|
#include <Core/Strings.h>
|
|
|
|
|
|
|
|
#include "IAudioChunk.h"
|
|
|
|
#include "IAudioDevice.h"
|
|
|
|
|
|
|
|
#include "ClientUI.h"
|
|
|
|
#include "PaletteView.h"
|
|
|
|
#include "LimboView.h"
|
|
|
|
#include "MapView.h"
|
|
|
|
#include "Corpse.h"
|
|
|
|
#include "ClientPlayer.h"
|
|
|
|
#include "ILocalEntity.h"
|
|
|
|
#include "ChatWindow.h"
|
|
|
|
#include "CenterMessageView.h"
|
|
|
|
#include "Tracer.h"
|
|
|
|
#include "FallingBlock.h"
|
|
|
|
#include "HurtRingView.h"
|
|
|
|
|
|
|
|
#include "World.h"
|
|
|
|
#include "Weapon.h"
|
|
|
|
#include "GameMap.h"
|
|
|
|
#include "Grenade.h"
|
|
|
|
|
|
|
|
#include "NetClient.h"
|
|
|
|
|
2016-11-19 21:03:51 +09:00
|
|
|
DEFINE_SPADES_SETTING(cg_ragdoll, "1");
|
|
|
|
SPADES_SETTING(cg_blood);
|
|
|
|
DEFINE_SPADES_SETTING(cg_ejectBrass, "1");
|
2014-03-09 21:43:38 +09:00
|
|
|
|
2016-11-19 21:03:51 +09:00
|
|
|
SPADES_SETTING(cg_alerts);
|
|
|
|
SPADES_SETTING(cg_centerMessage);
|
2014-03-11 03:42:04 +09:00
|
|
|
|
2016-11-22 01:16:28 +09:00
|
|
|
SPADES_SETTING(cg_shake);
|
2014-03-09 21:43:38 +09:00
|
|
|
|
|
|
|
namespace spades {
|
|
|
|
namespace client {
|
|
|
|
|
|
|
|
#pragma mark - World States
|
|
|
|
|
|
|
|
float Client::GetSprintState() {
|
|
|
|
if(!world) return 0.f;
|
|
|
|
if(!world->GetLocalPlayer())
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
ClientPlayer *p = clientPlayers[(int)world->GetLocalPlayerIndex()];
|
|
|
|
if(!p)
|
|
|
|
return 0.f;
|
|
|
|
return p->GetSprintState();
|
|
|
|
}
|
|
|
|
|
|
|
|
float Client::GetAimDownState() {
|
|
|
|
if(!world) return 0.f;
|
|
|
|
if(!world->GetLocalPlayer())
|
|
|
|
return 0.f;
|
|
|
|
|
|
|
|
ClientPlayer *p = clientPlayers[(int)world->GetLocalPlayerIndex()];
|
|
|
|
if(!p)
|
|
|
|
return 0.f;
|
|
|
|
return p->GetAimDownState();
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - World Actions
|
|
|
|
/** Captures the color of the block player is looking at. */
|
|
|
|
void Client::CaptureColor() {
|
|
|
|
if(!world) return;
|
|
|
|
Player *p = world->GetLocalPlayer();
|
|
|
|
if(!p) return;
|
|
|
|
if(!p->IsAlive()) return;
|
|
|
|
|
|
|
|
IntVector3 outBlockCoord;
|
2014-03-11 03:46:14 +09:00
|
|
|
uint32_t col;
|
2014-03-09 21:43:38 +09:00
|
|
|
if(!world->GetMap()->CastRay(p->GetEye(),
|
|
|
|
p->GetFront(),
|
|
|
|
256.f, outBlockCoord)){
|
2014-03-11 03:46:14 +09:00
|
|
|
auto c = world->GetFogColor();
|
|
|
|
col = c.x | c.y<<8 | c.z<<16;
|
|
|
|
}else{
|
|
|
|
col = world->GetMap()->GetColorWrapped(outBlockCoord.x,
|
|
|
|
outBlockCoord.y,
|
|
|
|
outBlockCoord.z);
|
2014-03-09 21:43:38 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
IntVector3 colV;
|
|
|
|
colV.x = (uint8_t)(col);
|
|
|
|
colV.y = (uint8_t)(col >> 8);
|
|
|
|
colV.z = (uint8_t)(col >> 16);
|
|
|
|
|
|
|
|
p->SetHeldBlockColor(colV);
|
|
|
|
net->SendHeldBlockColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::SetSelectedTool(Player::ToolType type, bool quiet) {
|
|
|
|
if(type == world->GetLocalPlayer()->GetTool())
|
|
|
|
return;
|
2014-03-13 01:44:58 +09:00
|
|
|
lastTool = world->GetLocalPlayer()->GetTool();
|
|
|
|
hasLastTool = true;
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
world->GetLocalPlayer()->SetTool(type);
|
|
|
|
net->SendTool();
|
|
|
|
|
|
|
|
if(!quiet) {
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/SwitchLocal.wav");
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - World Update
|
|
|
|
|
|
|
|
void Client::UpdateWorld(float dt) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
Player* player = world->GetLocalPlayer();
|
|
|
|
|
|
|
|
if(player){
|
2014-03-25 20:41:09 +09:00
|
|
|
|
|
|
|
// disable input when UI is open
|
|
|
|
if(scriptedUI->NeedsInput()) {
|
|
|
|
weapInput.primary = false;
|
|
|
|
if(player->GetTeamId() >= 2 || player->GetTool() != Player::ToolWeapon) {
|
|
|
|
weapInput.secondary = false;
|
|
|
|
}
|
|
|
|
playerInput = PlayerInput();
|
|
|
|
}
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
if(player->GetTeamId() >= 2) {
|
|
|
|
UpdateLocalSpectator(dt);
|
|
|
|
}else{
|
|
|
|
UpdateLocalPlayer(dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// dynamic time step
|
|
|
|
// physics diverges from server
|
|
|
|
world->Advance(dt);
|
|
|
|
#else
|
|
|
|
// accurately resembles server's physics
|
|
|
|
// but not smooth
|
|
|
|
if(dt > 0.f)
|
|
|
|
worldSubFrame += dt;
|
|
|
|
|
|
|
|
float frameStep = 1.f / 60.f;
|
|
|
|
while(worldSubFrame >= frameStep){
|
|
|
|
world->Advance(frameStep);
|
|
|
|
worldSubFrame -= frameStep;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// update player view (doesn't affect physics/game logics)
|
|
|
|
for(size_t i = 0; i < clientPlayers.size(); i++){
|
|
|
|
if(clientPlayers[i]){
|
|
|
|
clientPlayers[i]->Update(dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// corpse never accesses audio nor renderer, so
|
|
|
|
// we can do it in the separate thread
|
|
|
|
class CorpseUpdateDispatch: public ConcurrentDispatch{
|
|
|
|
Client *client;
|
|
|
|
float dt;
|
|
|
|
public:
|
|
|
|
CorpseUpdateDispatch(Client *c, float dt):
|
|
|
|
client(c), dt(dt){}
|
|
|
|
virtual void Run(){
|
|
|
|
for(auto& c: client->corpses){
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
|
|
c->Update(dt / 4.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
CorpseUpdateDispatch corpseDispatch(this, dt);
|
|
|
|
corpseDispatch.Start();
|
|
|
|
|
|
|
|
// local entities should be done in the client thread
|
|
|
|
{
|
|
|
|
decltype(localEntities)::iterator it;
|
|
|
|
std::vector<decltype(it)> its;
|
|
|
|
for(it = localEntities.begin(); it != localEntities.end(); it++){
|
|
|
|
if(!(*it)->Update(dt))
|
|
|
|
its.push_back(it);
|
|
|
|
}
|
|
|
|
for(size_t i = 0; i < its.size(); i++){
|
|
|
|
localEntities.erase(its[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
corpseDispatch.Join();
|
2016-11-22 01:16:28 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
if(grenadeVibration > 0.f){
|
|
|
|
grenadeVibration -= dt;
|
|
|
|
if(grenadeVibration < 0.f)
|
|
|
|
grenadeVibration = 0.f;
|
|
|
|
}
|
2016-11-22 01:16:28 +09:00
|
|
|
|
|
|
|
if(grenadeVibrationSlow > 0.f){
|
|
|
|
grenadeVibrationSlow -= dt;
|
|
|
|
if(grenadeVibrationSlow < 0.f)
|
|
|
|
grenadeVibrationSlow = 0.f;
|
|
|
|
}
|
2014-03-09 21:43:38 +09:00
|
|
|
|
|
|
|
if(hitFeedbackIconState > 0.f) {
|
|
|
|
hitFeedbackIconState -= dt * 4.f;
|
|
|
|
if(hitFeedbackIconState < 0.f)
|
|
|
|
hitFeedbackIconState = 0.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(time > lastPosSentTime + 1.f &&
|
|
|
|
world->GetLocalPlayer()){
|
|
|
|
Player *p = world->GetLocalPlayer();
|
|
|
|
if(p->IsAlive() && p->GetTeamId() < 2){
|
|
|
|
net->SendPosition();
|
|
|
|
lastPosSentTime = time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Handles movement of spectating local player. */
|
|
|
|
void Client::UpdateLocalSpectator(float dt) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
Vector3 lastPos = followPos;
|
|
|
|
followVel *= powf(.3f, dt);
|
|
|
|
followPos += followVel * dt;
|
|
|
|
|
|
|
|
if(followPos.x < 0.f) {
|
|
|
|
followVel.x = fabsf(followVel.x) * 0.2f;
|
|
|
|
followPos = lastPos + followVel * dt;
|
|
|
|
}
|
|
|
|
if(followPos.y < 0.f) {
|
|
|
|
followVel.y = fabsf(followVel.y) * 0.2f;
|
|
|
|
followPos = lastPos + followVel * dt;
|
|
|
|
}
|
|
|
|
if(followPos.x > (float)GetWorld()->GetMap()->Width()) {
|
|
|
|
followVel.x = fabsf(followVel.x) * -0.2f;
|
|
|
|
followPos = lastPos + followVel * dt;
|
|
|
|
}
|
|
|
|
if(followPos.y > (float)GetWorld()->GetMap()->Height()) {
|
|
|
|
followVel.y = fabsf(followVel.y) * -0.2f;
|
|
|
|
followPos = lastPos + followVel * dt;
|
|
|
|
}
|
|
|
|
|
|
|
|
GameMap::RayCastResult minResult;
|
|
|
|
float minDist = 1.e+10f;
|
|
|
|
Vector3 minShift;
|
|
|
|
|
|
|
|
// check collision
|
|
|
|
if(followVel.GetLength() < .01){
|
|
|
|
followPos = lastPos;
|
|
|
|
followVel *= 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);
|
|
|
|
if(result.hit && !result.startSolid &&
|
|
|
|
Vector3::Dot(result.hitPos - followPos - shift,
|
|
|
|
followPos - lastPos) < 0.f){
|
|
|
|
|
|
|
|
float dist = Vector3::Dot(result.hitPos - followPos - shift,
|
|
|
|
(followPos - lastPos).Normalize());
|
|
|
|
if(dist < minDist){
|
|
|
|
minResult = result;
|
|
|
|
minDist = dist;
|
|
|
|
minShift = shift;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// acceleration
|
|
|
|
Vector3 front;
|
|
|
|
Vector3 up = {0, 0, -1};
|
|
|
|
|
|
|
|
front.x = -cosf(followYaw) * cosf(followPitch);
|
|
|
|
front.y = -sinf(followYaw) * cosf(followPitch);
|
|
|
|
front.z = sinf(followPitch);
|
|
|
|
|
|
|
|
Vector3 right = -Vector3::Cross(up, front).Normalize();
|
|
|
|
Vector3 up2 = Vector3::Cross(right, front).Normalize();
|
|
|
|
|
|
|
|
float scale = 10.f * dt;
|
|
|
|
if(playerInput.sprint){
|
|
|
|
scale *= 3.f;
|
|
|
|
}
|
|
|
|
front *= scale;
|
|
|
|
right *= scale;
|
|
|
|
up2 *= scale;
|
|
|
|
|
|
|
|
if(playerInput.moveForward){
|
|
|
|
followVel += front;
|
|
|
|
}else if(playerInput.moveBackward){
|
|
|
|
followVel -= front;
|
|
|
|
}
|
|
|
|
if(playerInput.moveLeft){
|
|
|
|
followVel -= right;
|
|
|
|
}else if(playerInput.moveRight){
|
|
|
|
followVel += right;
|
|
|
|
}
|
|
|
|
if(playerInput.jump){
|
|
|
|
followVel += up2;
|
|
|
|
}else if(playerInput.crouch){
|
|
|
|
followVel -= up2;
|
|
|
|
}
|
|
|
|
|
|
|
|
SPAssert(followVel.GetLength() < 100.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Handles movement of joined local player. */
|
|
|
|
void Client::UpdateLocalPlayer(float dt) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
auto *player = world->GetLocalPlayer();
|
|
|
|
|
|
|
|
PlayerInput inp = playerInput;
|
|
|
|
WeaponInput winp = weapInput;
|
|
|
|
|
|
|
|
// weapon/tools are disabled while/soon after sprinting
|
|
|
|
if(GetSprintState() > 0.001f) {
|
|
|
|
winp.primary = false;
|
|
|
|
winp.secondary = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't allow to stand up when ceilings are too low
|
|
|
|
if(inp.crouch == false){
|
|
|
|
if(player->GetInput().crouch){
|
|
|
|
if(!player->TryUncrouch(false)){
|
|
|
|
inp.crouch = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't allow jumping in the air
|
|
|
|
if(inp.jump){
|
|
|
|
if(!player->IsOnGroundOrWade())
|
|
|
|
inp.jump = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// weapon/tools are disabled while changing tools
|
|
|
|
if(clientPlayers[world->GetLocalPlayerIndex()]->IsChangingTool()) {
|
|
|
|
winp.primary = false;
|
|
|
|
winp.secondary = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// disable weapon while reloading (except shotgun)
|
|
|
|
if(player->GetTool() == Player::ToolWeapon &&
|
|
|
|
player->IsAwaitingReloadCompletion() &&
|
|
|
|
!player->GetWeapon()->IsReloadSlow()) {
|
|
|
|
winp.primary = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
player->SetInput(inp);
|
|
|
|
player->SetWeaponInput(winp);
|
|
|
|
|
|
|
|
//send player input
|
|
|
|
// FIXME: send only there are any changed?
|
|
|
|
net->SendPlayerInput(inp);
|
2015-06-14 04:05:51 +09:00
|
|
|
net->SendWeaponInput(winp);
|
2014-03-09 21:43:38 +09:00
|
|
|
|
|
|
|
if(hasDelayedReload) {
|
|
|
|
world->GetLocalPlayer()->Reload();
|
|
|
|
net->SendReload();
|
|
|
|
hasDelayedReload = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//PlayerInput actualInput = player->GetInput();
|
|
|
|
WeaponInput actualWeapInput = player->GetWeaponInput();
|
|
|
|
|
|
|
|
if(actualWeapInput.secondary &&
|
|
|
|
player->IsToolWeapon() &&
|
|
|
|
player->IsAlive()){
|
|
|
|
}else{
|
|
|
|
if(player->IsToolWeapon()){
|
|
|
|
// there is a possibility that player has respawned or something.
|
|
|
|
// stop aiming down
|
|
|
|
weapInput.secondary = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// is the selected tool no longer usable (ex. out of ammo)?
|
|
|
|
if(!player->IsToolSelectable(player->GetTool())) {
|
|
|
|
// release mouse button before auto-switching tools
|
|
|
|
winp.primary = false;
|
|
|
|
winp.secondary = false;
|
|
|
|
weapInput = winp;
|
|
|
|
net->SendWeaponInput(weapInput);
|
|
|
|
actualWeapInput = winp = player->GetWeaponInput();
|
|
|
|
|
|
|
|
// select another tool
|
|
|
|
Player::ToolType t = player->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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// send orientation
|
|
|
|
Vector3 curFront = player->GetFront();
|
|
|
|
if(curFront.x != lastFront.x ||
|
|
|
|
curFront.y != lastFront.y ||
|
|
|
|
curFront.z != lastFront.z) {
|
|
|
|
lastFront = curFront;
|
|
|
|
net->SendOrientation(curFront);
|
|
|
|
}
|
|
|
|
|
|
|
|
lastKills = world->GetPlayerPersistent(player->GetId()).kills;
|
|
|
|
|
2014-03-11 03:42:04 +09:00
|
|
|
// show block count when building block lines.
|
|
|
|
if(player->IsAlive() &&
|
|
|
|
player->GetTool() == Player::ToolBlock &&
|
|
|
|
player->GetWeaponInput().secondary &&
|
|
|
|
player->IsBlockCursorDragging()) {
|
|
|
|
if(player->IsBlockCursorActive()) {
|
|
|
|
auto blocks = std::move
|
|
|
|
(world->CubeLine(player->GetBlockCursorDragPos(),
|
|
|
|
player->GetBlockCursorPos(),
|
|
|
|
256));
|
|
|
|
auto msg = _TrN("Client",
|
|
|
|
"{0} block", "{0} blocks",
|
|
|
|
blocks.size());
|
|
|
|
AlertType type =
|
|
|
|
static_cast<int>(blocks.size()) > player->GetNumBlocks() ?
|
|
|
|
AlertType::Warning : AlertType::Notice;
|
|
|
|
ShowAlert(msg, type, 0.f, true);
|
|
|
|
}else{
|
|
|
|
// invalid
|
|
|
|
auto msg = _Tr("Client", "-- blocks");
|
|
|
|
AlertType type = AlertType::Warning;
|
|
|
|
ShowAlert(msg, type, 0.f, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
if(player->IsAlive())
|
|
|
|
lastAliveTime = time;
|
|
|
|
|
|
|
|
if(player->GetHealth() < lastHealth){
|
|
|
|
// ouch!
|
|
|
|
lastHealth = player->GetHealth();
|
|
|
|
lastHurtTime = world->GetTime();
|
|
|
|
|
|
|
|
Handle<IAudioChunk> c;
|
2016-11-22 23:08:35 +02:00
|
|
|
switch((mt_engine_client() >> 3) & 3){
|
2014-03-09 21:43:38 +09:00
|
|
|
case 0:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal1.wav");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal2.wav");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal3.wav");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal4.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
audioDevice->PlayLocal(c, AudioParam());
|
|
|
|
|
|
|
|
float hpper = player->GetHealth() / 100.f;
|
|
|
|
int cnt = 18 - (int)(player->GetHealth() / 100.f * 8.f);
|
|
|
|
hurtSprites.resize(std::max(cnt, 6));
|
|
|
|
for(size_t i = 0; i < hurtSprites.size(); i++) {
|
|
|
|
HurtSprite& spr = hurtSprites[i];
|
|
|
|
spr.angle = GetRandom() * (2.f * static_cast<float>(M_PI));
|
|
|
|
spr.scale = .2f + GetRandom() * GetRandom() * .7f;
|
|
|
|
spr.horzShift = GetRandom();
|
|
|
|
spr.strength = .3f + GetRandom() * .7f;
|
|
|
|
if(hpper > .5f) {
|
|
|
|
spr.strength *= 1.5f - hpper;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}else{
|
|
|
|
lastHealth = player->GetHealth();
|
|
|
|
}
|
|
|
|
|
|
|
|
inp.jump = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - IWorldListener Handlers
|
|
|
|
|
|
|
|
void Client::PlayerObjectSet(int id) {
|
|
|
|
if(clientPlayers[id]){
|
|
|
|
clientPlayers[id]->Invalidate();
|
|
|
|
clientPlayers[id] = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Player *p = world->GetPlayer(id);
|
|
|
|
if(p)
|
|
|
|
clientPlayers[id].Set(new ClientPlayer(p, this), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerJumped(spades::client::Player *p){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
|
|
|
|
Handle<IAudioChunk> c = p->GetWade() ?
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/WaterJump.wav"):
|
|
|
|
audioDevice->RegisterSound("Sounds/Player/Jump.wav");
|
|
|
|
audioDevice->Play(c, p->GetOrigin(),
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerLanded(spades::client::Player *p,
|
|
|
|
bool hurt) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c;
|
|
|
|
if(hurt)
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Player/FallHurt.wav");
|
|
|
|
else if(p->GetWade())
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Player/WaterLand.wav");
|
|
|
|
else
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Player/Land.wav");
|
|
|
|
audioDevice->Play(c, p->GetOrigin(),
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerMadeFootstep(spades::client::Player *p){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
const char *snds[] = {
|
|
|
|
"Sounds/Player/Footstep1.wav",
|
|
|
|
"Sounds/Player/Footstep2.wav",
|
|
|
|
"Sounds/Player/Footstep3.wav",
|
|
|
|
"Sounds/Player/Footstep4.wav",
|
|
|
|
"Sounds/Player/Footstep5.wav",
|
|
|
|
"Sounds/Player/Footstep6.wav",
|
|
|
|
"Sounds/Player/Footstep7.wav",
|
|
|
|
"Sounds/Player/Footstep8.wav"
|
|
|
|
};
|
2014-03-22 01:37:14 +09:00
|
|
|
const char *rsnds[] = {
|
|
|
|
"Sounds/Player/Run1.wav",
|
|
|
|
"Sounds/Player/Run2.wav",
|
|
|
|
"Sounds/Player/Run3.wav",
|
|
|
|
"Sounds/Player/Run4.wav",
|
|
|
|
"Sounds/Player/Run5.wav",
|
|
|
|
"Sounds/Player/Run6.wav",
|
|
|
|
"Sounds/Player/Run7.wav",
|
|
|
|
"Sounds/Player/Run8.wav",
|
|
|
|
"Sounds/Player/Run9.wav",
|
|
|
|
"Sounds/Player/Run10.wav",
|
|
|
|
"Sounds/Player/Run11.wav",
|
|
|
|
"Sounds/Player/Run12.wav",
|
|
|
|
};
|
2014-03-09 21:43:38 +09:00
|
|
|
const char *wsnds[] = {
|
|
|
|
"Sounds/Player/Wade1.wav",
|
|
|
|
"Sounds/Player/Wade2.wav",
|
|
|
|
"Sounds/Player/Wade3.wav",
|
2014-03-12 04:04:07 +09:00
|
|
|
"Sounds/Player/Wade4.wav",
|
|
|
|
"Sounds/Player/Wade5.wav",
|
|
|
|
"Sounds/Player/Wade6.wav",
|
|
|
|
"Sounds/Player/Wade7.wav",
|
|
|
|
"Sounds/Player/Wade8.wav"
|
2014-03-09 21:43:38 +09:00
|
|
|
};
|
2014-03-22 01:37:14 +09:00
|
|
|
bool sprinting = clientPlayers[p->GetId()] ? clientPlayers[p->GetId()]->GetSprintState() > 0.5f : false;
|
2014-03-09 21:43:38 +09:00
|
|
|
Handle<IAudioChunk> c = p->GetWade() ?
|
2016-11-22 23:08:35 +02:00
|
|
|
audioDevice->RegisterSound(wsnds[(mt_engine_client() >> 8) % 8]):
|
|
|
|
audioDevice->RegisterSound(snds[(mt_engine_client() >> 8) % 8]);
|
2014-03-09 21:43:38 +09:00
|
|
|
audioDevice->Play(c, p->GetOrigin(),
|
|
|
|
AudioParam());
|
2014-03-22 21:41:16 +09:00
|
|
|
if(sprinting && !p->GetWade()) {
|
2014-03-22 01:37:14 +09:00
|
|
|
AudioParam param;
|
|
|
|
param.volume *= clientPlayers[p->GetId()]->GetSprintState();
|
2016-11-22 23:08:35 +02:00
|
|
|
c = audioDevice->RegisterSound(rsnds[(mt_engine_client() >> 8) % 12]);
|
2014-03-22 01:37:14 +09:00
|
|
|
audioDevice->Play(c, p->GetOrigin(),
|
|
|
|
param);
|
|
|
|
}
|
2014-03-09 21:43:38 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerFiredWeapon(spades::client::Player *p) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(p == world->GetLocalPlayer()){
|
|
|
|
localFireVibrationTime = time;
|
|
|
|
}
|
|
|
|
|
|
|
|
clientPlayers[p->GetId()]->FiredWeapon();
|
|
|
|
}
|
|
|
|
void Client::PlayerDryFiredWeapon(spades::client::Player *p) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
bool isLocal = p == world->GetLocalPlayer();
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/DryFire.wav");
|
|
|
|
if(isLocal)
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
|
|
|
|
AudioParam());
|
|
|
|
else
|
|
|
|
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
|
|
|
|
- p->GetUp() * .3f
|
|
|
|
+ p->GetRight() * .4f,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerReloadingWeapon(spades::client::Player *p) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
clientPlayers[p->GetId()]->ReloadingWeapon();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerReloadedWeapon(spades::client::Player *p){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
|
|
|
|
clientPlayers[p->GetId()]->ReloadedWeapon();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerChangedTool(spades::client::Player *p){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
bool isLocal = p == world->GetLocalPlayer();
|
|
|
|
Handle<IAudioChunk> c;
|
|
|
|
if(isLocal){
|
2014-03-22 01:37:14 +09:00
|
|
|
// played by ClientPlayer::Update
|
|
|
|
return;
|
2014-03-09 21:43:38 +09:00
|
|
|
}else{
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Switch.wav");
|
|
|
|
}
|
|
|
|
if(isLocal)
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
|
|
|
|
AudioParam());
|
|
|
|
else
|
|
|
|
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
|
|
|
|
- p->GetUp() * .3f
|
|
|
|
+ p->GetRight() * .4f,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerRestocked(spades::client::Player *p){
|
|
|
|
if(!IsMuted()){
|
|
|
|
bool isLocal = p == world->GetLocalPlayer();
|
|
|
|
Handle<IAudioChunk> c = isLocal ?
|
2014-03-22 01:37:14 +09:00
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/RestockLocal.wav"):
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Restock.wav");
|
2014-03-09 21:43:38 +09:00
|
|
|
if(isLocal)
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
|
|
|
|
AudioParam());
|
|
|
|
else
|
|
|
|
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
|
|
|
|
- p->GetUp() * .3f
|
|
|
|
+ p->GetRight() * .4f,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerThrownGrenade(spades::client::Player *p, Grenade *g){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
bool isLocal = p == world->GetLocalPlayer();
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Throw.wav");
|
|
|
|
|
|
|
|
if(g && isLocal){
|
|
|
|
net->SendGrenade(g);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(isLocal)
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.4f, 0.1f, .3f),
|
|
|
|
AudioParam());
|
|
|
|
else
|
|
|
|
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
|
|
|
|
- p->GetUp() * .2f
|
|
|
|
+ p->GetRight() * .3f,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerMissedSpade(spades::client::Player *p){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
bool isLocal = p == world->GetLocalPlayer();
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Spade/Miss.wav");
|
|
|
|
if(isLocal)
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.2f, -.1f, 0.7f),
|
|
|
|
AudioParam());
|
|
|
|
else
|
|
|
|
audioDevice->Play(c, p->GetOrigin() + p->GetFront() * 0.8f
|
|
|
|
- p->GetUp() * .2f,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerHitBlockWithSpade(spades::client::Player *p,
|
|
|
|
Vector3 hitPos,
|
|
|
|
IntVector3 blockPos,
|
|
|
|
IntVector3 normal){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
uint32_t col = map->GetColor(blockPos.x, blockPos.y, blockPos.z);
|
|
|
|
IntVector3 colV = {(uint8_t)col,
|
|
|
|
(uint8_t)(col >> 8), (uint8_t)(col >> 16)};
|
|
|
|
Vector3 shiftedHitPos = hitPos;
|
|
|
|
shiftedHitPos.x += normal.x * .05f;
|
|
|
|
shiftedHitPos.y += normal.y * .05f;
|
|
|
|
shiftedHitPos.z += normal.z * .05f;
|
|
|
|
|
|
|
|
EmitBlockFragments(shiftedHitPos, colV);
|
|
|
|
|
2014-03-11 03:54:01 +09:00
|
|
|
if(p == world->GetLocalPlayer()){
|
|
|
|
localFireVibrationTime = time;
|
|
|
|
}
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
if(!IsMuted()){
|
|
|
|
bool isLocal = p == world->GetLocalPlayer();
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Spade/HitBlock.wav");
|
|
|
|
if(isLocal)
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.1f, -.1f, 1.2f),
|
|
|
|
AudioParam());
|
|
|
|
else
|
|
|
|
audioDevice->Play(c, p->GetOrigin() + p->GetFront() * 0.5f
|
|
|
|
- p->GetUp() * .2f,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::PlayerKilledPlayer(spades::client::Player *killer,
|
|
|
|
spades::client::Player *victim,
|
|
|
|
KillType kt) {
|
|
|
|
// play hit sound
|
|
|
|
if(kt == KillTypeWeapon ||
|
|
|
|
kt == KillTypeHeadshot) {
|
|
|
|
// don't play on local: see BullethitPlayer
|
|
|
|
if(victim != world->GetLocalPlayer()) {
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c;
|
2016-11-22 23:08:35 +02:00
|
|
|
switch(mt_engine_client()%3){
|
2014-03-09 21:43:38 +09:00
|
|
|
case 0:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh1.wav");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh2.wav");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh3.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
AudioParam param;
|
|
|
|
param.volume = 4.f;
|
|
|
|
audioDevice->Play(c, victim->GetEye(), param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// begin following
|
|
|
|
if(victim == world->GetLocalPlayer()){
|
|
|
|
followingPlayerId = victim->GetId();
|
|
|
|
|
|
|
|
Vector3 v = -victim->GetFront();
|
|
|
|
followYaw = atan2(v.y, v.x);
|
|
|
|
followPitch = 30.f * M_PI /180.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// emit blood (also for local player)
|
|
|
|
// FIXME: emiting blood for either
|
|
|
|
// client-side or server-side hit?
|
|
|
|
switch(kt){
|
|
|
|
case KillTypeGrenade:
|
|
|
|
case KillTypeHeadshot:
|
|
|
|
case KillTypeMelee:
|
|
|
|
case KillTypeWeapon:
|
|
|
|
Bleed(victim->GetEye());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create ragdoll corpse
|
|
|
|
if(cg_ragdoll && victim->GetTeamId() < 2){
|
|
|
|
Corpse *corp;
|
|
|
|
corp = new Corpse(renderer, map, victim);
|
|
|
|
if(victim == world->GetLocalPlayer())
|
|
|
|
lastMyCorpse = corp;
|
|
|
|
if(killer != victim && kt != KillTypeGrenade){
|
|
|
|
Vector3 dir = victim->GetPosition() - killer->GetPosition();
|
|
|
|
dir = dir.Normalize();
|
|
|
|
if(kt == KillTypeMelee){
|
|
|
|
dir *= 6.f;
|
|
|
|
}else{
|
|
|
|
if(killer->GetWeapon()->GetWeaponType() == SMG_WEAPON){
|
|
|
|
dir *= 2.8f;
|
|
|
|
}else if(killer->GetWeapon()->GetWeaponType() == SHOTGUN_WEAPON){
|
|
|
|
dir *= 4.5f;
|
|
|
|
}else{
|
|
|
|
dir *= 3.5f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
corp->AddImpulse(dir);
|
|
|
|
}else if(kt == KillTypeGrenade){
|
|
|
|
corp->AddImpulse(MakeVector3(0, 0, -4.f - GetRandom() * 4.f));
|
|
|
|
}
|
|
|
|
corp->AddImpulse(victim->GetVelocty() * 32.f);
|
|
|
|
corpses.emplace_back(corp);
|
|
|
|
|
|
|
|
if(corpses.size() > corpseHardLimit){
|
|
|
|
corpses.pop_front();
|
|
|
|
}else if(corpses.size() > corpseSoftLimit){
|
|
|
|
RemoveInvisibleCorpses();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add chat message
|
|
|
|
std::string s;
|
|
|
|
s = ChatWindow::TeamColorMessage(killer->GetName(), killer->GetTeamId());
|
|
|
|
|
|
|
|
std::string cause;
|
|
|
|
bool ff = killer->GetTeamId() == victim->GetTeamId();
|
|
|
|
if(killer == victim)
|
|
|
|
ff = false;
|
|
|
|
|
|
|
|
cause = " [";
|
|
|
|
Weapon* w = killer ? killer->GetWeapon() : nullptr; //only used in case of KillTypeWeapon
|
|
|
|
cause += ChatWindow::killImage( kt, w ? w->GetWeaponType() : RIFLE_WEAPON );
|
|
|
|
cause += "] ";
|
|
|
|
|
|
|
|
if(ff)
|
|
|
|
s += ChatWindow::ColoredMessage(cause, MsgColorRed);
|
2016-02-10 20:02:43 +02:00
|
|
|
else if (killer == world->GetLocalPlayer() || victim == world->GetLocalPlayer())
|
|
|
|
s += ChatWindow::ColoredMessage(cause, MsgColorBlack);
|
2014-03-09 21:43:38 +09:00
|
|
|
else
|
|
|
|
s += cause;
|
|
|
|
|
|
|
|
if(killer != victim){
|
|
|
|
s += ChatWindow::TeamColorMessage(victim->GetName(), victim->GetTeamId());
|
|
|
|
}
|
|
|
|
|
|
|
|
killfeedWindow->AddMessage(s);
|
|
|
|
|
|
|
|
// log to netlog
|
|
|
|
if(killer != victim) {
|
|
|
|
NetLog("%s (%s)%s%s (%s)",
|
|
|
|
killer->GetName().c_str(),
|
|
|
|
world->GetTeam(killer->GetTeamId()).name.c_str(),
|
|
|
|
cause.c_str(),
|
|
|
|
victim->GetName().c_str(),
|
|
|
|
world->GetTeam(victim->GetTeamId()).name.c_str());
|
|
|
|
}else{
|
|
|
|
NetLog("%s (%s)%s",
|
|
|
|
killer->GetName().c_str(),
|
|
|
|
world->GetTeam(killer->GetTeamId()).name.c_str(),
|
|
|
|
cause.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// show big message if player is involved
|
|
|
|
if(victim != killer){
|
|
|
|
Player* local = world->GetLocalPlayer();
|
|
|
|
if(killer == local || victim == local){
|
|
|
|
std::string msg;
|
|
|
|
if( killer == local ) {
|
2016-02-10 19:34:09 +02:00
|
|
|
if ((int)cg_centerMessage == 2) msg = _Tr("Client", "You have killed {0}", victim->GetName());
|
2014-03-09 21:43:38 +09:00
|
|
|
} else {
|
|
|
|
msg = _Tr("Client", "You were killed by {0}", killer->GetName());
|
|
|
|
}
|
|
|
|
centerMessageView->AddMessage(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::BulletHitPlayer(spades::client::Player *hurtPlayer,
|
|
|
|
HitType type,
|
|
|
|
spades::Vector3 hitPos,
|
|
|
|
spades::client::Player *by) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
SPAssert(type != HitTypeBlock);
|
|
|
|
|
|
|
|
// don't bleed local player
|
|
|
|
if(hurtPlayer != world->GetLocalPlayer() ||
|
|
|
|
ShouldRenderInThirdPersonView()){
|
|
|
|
Bleed(hitPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(hurtPlayer == world->GetLocalPlayer()){
|
|
|
|
// don't player hit sound now;
|
|
|
|
// local bullet impact sound is
|
|
|
|
// played by checking the decrease of HP
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
if(type == HitTypeMelee){
|
|
|
|
Handle<IAudioChunk> c =
|
|
|
|
audioDevice->RegisterSound("Sounds/Weapons/Spade/HitPlayer.wav");
|
|
|
|
audioDevice->Play(c, hitPos,
|
|
|
|
AudioParam());
|
|
|
|
}else{
|
|
|
|
Handle<IAudioChunk> c;
|
2016-11-22 23:08:35 +02:00
|
|
|
switch((mt_engine_client()>>6)%3){
|
2014-03-09 21:43:38 +09:00
|
|
|
case 0:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh1.wav");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh2.wav");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh3.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
AudioParam param;
|
|
|
|
param.volume = 4.f;
|
|
|
|
audioDevice->Play(c, hitPos,
|
|
|
|
param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(by == world->GetLocalPlayer() &&
|
|
|
|
hurtPlayer){
|
|
|
|
net->SendHit(hurtPlayer->GetId(), type);
|
|
|
|
|
|
|
|
hitFeedbackIconState = 1.f;
|
|
|
|
if(hurtPlayer->GetTeamId() == world->GetLocalPlayer()->GetTeamId()) {
|
|
|
|
hitFeedbackFriendly = true;
|
|
|
|
}else{
|
|
|
|
hitFeedbackFriendly = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::BulletHitBlock(Vector3 hitPos,
|
|
|
|
IntVector3 blockPos,
|
|
|
|
IntVector3 normal){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
uint32_t col = map->GetColor(blockPos.x, blockPos.y, blockPos.z);
|
|
|
|
IntVector3 colV = {(uint8_t)col,
|
|
|
|
(uint8_t)(col >> 8), (uint8_t)(col >> 16)};
|
|
|
|
Vector3 shiftedHitPos = hitPos;
|
|
|
|
shiftedHitPos.x += normal.x * .05f;
|
|
|
|
shiftedHitPos.y += normal.y * .05f;
|
|
|
|
shiftedHitPos.z += normal.z * .05f;
|
|
|
|
|
2014-03-12 03:38:45 +09:00
|
|
|
if(blockPos.z == 63) {
|
|
|
|
BulletHitWaterSurface(shiftedHitPos);
|
|
|
|
if(!IsMuted()){
|
|
|
|
AudioParam param;
|
|
|
|
param.volume = 2.f;
|
|
|
|
|
2014-03-22 01:37:14 +09:00
|
|
|
Handle<IAudioChunk> c;
|
2014-03-12 03:38:45 +09:00
|
|
|
|
|
|
|
param.pitch = .9f + GetRandom() * 0.2f;
|
2016-11-22 23:08:35 +02:00
|
|
|
switch((mt_engine_client() >> 6) & 3){
|
2014-03-12 03:38:45 +09:00
|
|
|
case 0:
|
2014-03-22 01:37:14 +09:00
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Water1.wav");
|
|
|
|
break;
|
2014-03-12 03:38:45 +09:00
|
|
|
case 1:
|
2014-03-22 01:37:14 +09:00
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Water2.wav");
|
|
|
|
break;
|
2014-03-12 03:38:45 +09:00
|
|
|
case 2:
|
2014-03-22 01:37:14 +09:00
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Water3.wav");
|
|
|
|
break;
|
2014-03-12 03:38:45 +09:00
|
|
|
case 3:
|
2014-03-22 01:37:14 +09:00
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Water4.wav");
|
2014-03-12 03:38:45 +09:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
audioDevice->Play(c, shiftedHitPos,
|
|
|
|
param);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
EmitBlockFragments(shiftedHitPos, colV);
|
2014-03-09 21:43:38 +09:00
|
|
|
|
2014-03-12 03:38:45 +09:00
|
|
|
if(!IsMuted()){
|
|
|
|
AudioParam param;
|
2014-03-22 01:37:14 +09:00
|
|
|
param.volume = 2.f;
|
|
|
|
|
|
|
|
Handle<IAudioChunk> c;
|
2014-03-12 03:38:45 +09:00
|
|
|
|
2016-11-22 23:08:35 +02:00
|
|
|
switch((mt_engine_client() >> 6) & 3) {
|
2014-03-22 01:37:14 +09:00
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Block.wav");
|
|
|
|
break;
|
|
|
|
}
|
2014-03-12 03:38:45 +09:00
|
|
|
audioDevice->Play(c, shiftedHitPos,
|
|
|
|
param);
|
|
|
|
|
|
|
|
param.pitch = .9f + GetRandom() * 0.2f;
|
2014-03-22 01:37:14 +09:00
|
|
|
param.volume = 2.f;
|
2016-11-22 23:08:35 +02:00
|
|
|
switch((mt_engine_client() >> 6) & 3){
|
2014-03-12 03:38:45 +09:00
|
|
|
case 0:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Ricochet1.wav");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Ricochet2.wav");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Ricochet3.wav");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Ricochet4.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
audioDevice->Play(c, shiftedHitPos,
|
|
|
|
param);
|
2014-03-09 21:43:38 +09:00
|
|
|
}
|
|
|
|
}
|
2014-03-12 03:38:45 +09:00
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
void Client::AddBulletTracer(spades::client::Player *player,
|
|
|
|
spades::Vector3 muzzlePos,
|
|
|
|
spades::Vector3 hitPos) {
|
2014-03-11 03:42:04 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
Tracer *t;
|
|
|
|
float vel;
|
|
|
|
switch(player->GetWeapon()->GetWeaponType()) {
|
|
|
|
case RIFLE_WEAPON:
|
|
|
|
vel = 700.f;
|
|
|
|
break;
|
|
|
|
case SMG_WEAPON:
|
|
|
|
vel = 360.f;
|
|
|
|
break;
|
|
|
|
case SHOTGUN_WEAPON:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
t = new Tracer(this, muzzlePos, hitPos, vel);
|
|
|
|
AddLocalEntity(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::BlocksFell(std::vector<IntVector3> blocks) {
|
2014-03-11 03:42:04 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
if(blocks.empty())
|
|
|
|
return;
|
|
|
|
FallingBlock *b = new FallingBlock(this, blocks);
|
|
|
|
AddLocalEntity(b);
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
|
|
|
|
IntVector3 v = blocks[0];
|
|
|
|
Vector3 o;
|
|
|
|
o.x = v.x; o.y = v.y; o.z = v.z;
|
|
|
|
o += .5f;
|
|
|
|
|
|
|
|
Handle<IAudioChunk> c =
|
|
|
|
audioDevice->RegisterSound("Sounds/Misc/BlockFall.wav");
|
|
|
|
audioDevice->Play(c, o,
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::GrenadeBounced(spades::client::Grenade *g){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(g->GetPosition().z < 63.f){
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Bounce.wav");
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::GrenadeDroppedIntoWater(spades::client::Grenade *g){
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/DropWater.wav");
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::GrenadeExploded(spades::client::Grenade *g) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
bool inWater = g->GetPosition().z > 63.f;
|
|
|
|
|
|
|
|
if(inWater){
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/WaterExplode.wav");
|
|
|
|
AudioParam param;
|
|
|
|
param.volume = 10.f;
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/WaterExplodeFar.wav");
|
|
|
|
param.volume = 6.f;
|
|
|
|
param.referenceDistance = 10.f;
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
|
|
|
|
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/WaterExplodeStereo.wav");
|
|
|
|
param.volume = 2.f;
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
}
|
|
|
|
|
|
|
|
GrenadeExplosionUnderwater(g->GetPosition());
|
|
|
|
}else{
|
|
|
|
|
|
|
|
GrenadeExplosion(g->GetPosition());
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c, cs;
|
|
|
|
|
2016-11-22 23:08:35 +02:00
|
|
|
switch((mt_engine_client() >> 8) & 1){
|
2014-03-09 21:43:38 +09:00
|
|
|
case 0:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Explode1.wav");
|
|
|
|
cs = audioDevice->RegisterSound("Sounds/Weapons/Grenade/ExplodeStereo1.wav");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Explode2.wav");
|
|
|
|
cs = audioDevice->RegisterSound("Sounds/Weapons/Grenade/ExplodeStereo2.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioParam param;
|
|
|
|
param.volume = 30.f;
|
|
|
|
param.referenceDistance = 5.f;
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
|
|
|
|
param.referenceDistance = 1.f;
|
|
|
|
audioDevice->Play(cs, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/ExplodeFar.wav");
|
|
|
|
param.volume = 6.f;
|
|
|
|
param.referenceDistance = 40.f;
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
|
|
|
|
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/ExplodeFarStereo.wav");
|
|
|
|
param.referenceDistance = 10.f;
|
|
|
|
audioDevice->Play(c, g->GetPosition(),
|
|
|
|
param);
|
|
|
|
|
|
|
|
// debri sound
|
|
|
|
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Debris.wav");
|
|
|
|
param.volume = 5.f;
|
|
|
|
param.referenceDistance = 3.f;
|
|
|
|
IntVector3 outPos;
|
|
|
|
Vector3 soundPos = g->GetPosition();
|
|
|
|
if(world->GetMap()->CastRay(soundPos,
|
|
|
|
MakeVector3(0,0,1),
|
|
|
|
8.f, outPos)){
|
|
|
|
soundPos.z = (float)outPos.z - .2f;
|
|
|
|
}
|
|
|
|
audioDevice->Play(c, soundPos,
|
|
|
|
param);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::LocalPlayerPulledGrenadePin() {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!IsMuted()){
|
|
|
|
Handle<IAudioChunk> c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Fire.wav");
|
|
|
|
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
|
|
|
|
AudioParam());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::LocalPlayerBlockAction(spades::IntVector3 v, BlockActionType type){
|
2014-03-11 03:42:04 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2014-03-09 21:43:38 +09:00
|
|
|
net->SendBlockAction(v, type);
|
|
|
|
}
|
|
|
|
void Client::LocalPlayerCreatedLineBlock(spades::IntVector3 v1, spades::IntVector3 v2) {
|
2014-03-11 03:42:04 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2014-03-09 21:43:38 +09:00
|
|
|
net->SendBlockLine(v1, v2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::LocalPlayerHurt(HurtType type,
|
|
|
|
bool sourceGiven,
|
|
|
|
spades::Vector3 source) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
2014-03-11 03:42:04 +09:00
|
|
|
|
2014-03-09 21:43:38 +09:00
|
|
|
if(sourceGiven){
|
|
|
|
Player * p = world->GetLocalPlayer();
|
|
|
|
if(!p)
|
|
|
|
return;
|
|
|
|
Vector3 rel = source - p->GetEye();
|
|
|
|
rel.z = 0.f; rel = rel.Normalize();
|
|
|
|
hurtRingView->Add(rel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-11 03:42:04 +09:00
|
|
|
void Client::LocalPlayerBuildError(BuildFailureReason reason) {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
|
|
|
|
if(!cg_alerts) {
|
|
|
|
PlayAlertSound();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(reason) {
|
|
|
|
case BuildFailureReason::InsufficientBlocks:
|
2014-03-12 00:12:11 +09:00
|
|
|
ShowAlert(_Tr("Client", "Insufficient blocks."),
|
2014-03-11 03:42:04 +09:00
|
|
|
AlertType::Error);
|
|
|
|
break;
|
|
|
|
case BuildFailureReason::InvalidPosition:
|
|
|
|
ShowAlert(_Tr("Client", "You cannot place a block there."),
|
|
|
|
AlertType::Error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-03-09 21:43:38 +09:00
|
|
|
}
|
|
|
|
}
|