yvt 361c4df347 * Added grenade throw animation
* Added dig animation
2013-08-21 01:53:14 +09:00

4212 lines
119 KiB
C++

//
// Client.cpp
// OpenSpades
//
// Created by yvt on 7/11/13.
// Copyright (c) 2013 yvt.jp. All rights reserved.
//
#include "Client.h"
#include "IRenderer.h"
#include "GameMap.h"
#include "SceneDefinition.h"
#include "../Core/FileManager.h"
#include "../Core/IStream.h"
#include "GameMapWrapper.h"
#include "World.h"
#include "Player.h"
#include "IAudioChunk.h"
#include "IAudioDevice.h"
#include "Weapon.h"
#include "Grenade.h"
#include "../Core/Debug.h"
#include "NetClient.h"
#include "FontData.h"
#include "Quake3Font.h"
#include "../Core/Exception.h"
#include "ChatWindow.h"
#include "Corpse.h"
#include "CenterMessageView.h"
#include "HurtRingView.h"
#include "CTFGameMode.h"
#include "MapView.h"
#include "ScoreboardView.h"
#include "LimboView.h"
#include "ILocalEntity.h"
#include "ParticleSpriteEntity.h"
#include "SmokeSpriteEntity.h"
#include "GameMapWrapper.h"
#include "../Core/Settings.h"
#include "../Core/Bitmap.h"
#include <string.h>
#include "FallingBlock.h"
#include "GunCasing.h"
#include "../Core/ConcurrentDispatch.h"
#include "PaletteView.h"
#include "TCGameMode.h"
#include "TCProgressView.h"
static float nextRandom() {
return (float)rand() / (float)RAND_MAX;
}
SPADES_SETTING(cg_ragdoll, "1");
SPADES_SETTING(cg_blood, "1");
SPADES_SETTING(cg_ejectBrass, "1");
SPADES_SETTING(cg_mouseSensitivity, "1");
SPADES_SETTING(cg_keyAttack, "LeftMouseButton");
SPADES_SETTING(cg_keyAltAttack, "RightMouseButton");
SPADES_SETTING(cg_keyToolSpade, "1");
SPADES_SETTING(cg_keyToolBlock, "2");
SPADES_SETTING(cg_keyToolWeapon, "3");
SPADES_SETTING(cg_keyToolGrenade, "4");
SPADES_SETTING(cg_keyReloadWeapon, "r");
SPADES_SETTING(cg_keyFlashlight, "f");
SPADES_SETTING(cg_keyMoveLeft, "a");
SPADES_SETTING(cg_keyMoveRight, "s");
SPADES_SETTING(cg_keyMoveForward, "w");
SPADES_SETTING(cg_keyMoveBackward, "z");
SPADES_SETTING(cg_keyJump, " ");
SPADES_SETTING(cg_keyCrouch, "Control");
SPADES_SETTING(cg_keySprint, "Shift");
SPADES_SETTING(cg_keySneak, "v");
SPADES_SETTING(cg_keyCaptureColor, "e");
SPADES_SETTING(cg_keyGlobalChat, "t");
SPADES_SETTING(cg_keyTeamChat, "y");
SPADES_SETTING(cg_keyChangeMapScale, "m");
SPADES_SETTING(cg_keyScoreboard, "Tab");
SPADES_SETTING(cg_keyLimbo, "l");
SPADES_SETTING(cg_keyScreenshot, "0");
SPADES_SETTING(cg_keySceneshot, "9");
namespace spades {
namespace client {
Client::Client(IRenderer *r, IAudioDevice *audioDev,
std::string host, std::string playerName):
renderer(r), audioDevice(audioDev), playerName(playerName) {
SPADES_MARK_FUNCTION();
/*
designFont = new Quake3Font(renderer,
renderer->RegisterImage("Gfx/Fonts/Orbitron.tga"),
(const int*)OrbitronMap,
30,
18);
*/
designFont = new Quake3Font(renderer,
renderer->RegisterImage("Gfx/Fonts/UnsteadyOversteer.tga"),
(const int *)UnsteadyOversteerMap,
30,
18);
textFont = new Quake3Font(renderer,
renderer->RegisterImage("Gfx/Fonts/UbuntuCondensed.tga"),
(const int*)UbuntuCondensedMap,
24,
4);
bigTextFont = new Quake3Font(renderer,
renderer->RegisterImage("Gfx/Fonts/UbuntuCondensedBig.tga"),
(const int*)UbuntuCondensedBigMap,
48,
8);
world = NULL;
// preferences?
corpseSoftTimeLimit = 30.f; // TODO: this is not used
corpseSoftLimit = 6;
corpseHardLimit = 16;
lastMyCorpse = NULL;
renderer->SetFogDistance(128.f);
renderer->SetFogColor(MakeVector3(.8f, 1.f, 1.f));
chatWindow = new ChatWindow(this, textFont);
chatEditing = false;
hurtRingView = new HurtRingView(this);
centerMessageView = new CenterMessageView(this, bigTextFont);
mapView = new MapView(this);
scoreboard = new ScoreboardView(this);
limbo = new LimboView(this);
paletteView = new PaletteView(this);
tcView = new TCProgressView(this);
time = 0.f;
lastAliveTime = 0.f;
readyToClose = false;
scoreboardVisible = false;
flashlightOn = false;
lastKills = 0;
localFireVibrationTime = -1.f;
viewWeaponOffset = MakeVector3(0, 0, 0);
lastPosSentTime = 0.f;
worldSubFrame = 0.f;
grenadeVibration = 0.f;
inGameLimbo = false;
nextScreenShotIndex = 0;
// preload
SmokeSpriteEntity(this, Vector4(), 20.f);
net = new NetClient(this);
net->Connect(host);
//net->Connect("192.168.24.24");
//net->Connect("127.0.0.1");
}
void Client::SetWorld(spades::client::World *w) {
SPADES_MARK_FUNCTION();
if(world == w){
return;
}
RemoveAllCorpses();
lastHealth = 0;
lastHurtTime = -100.f;
hurtRingView->ClearAll();
scoreboardVisible = false;
flashlightOn = false;
if(world){
world->SetListener(NULL);
renderer->SetGameMap(NULL);
audioDevice->SetGameMap(NULL);
delete world;
world = NULL;
map = NULL;
}
world = w;
if(world){
world->SetListener(this);
map = world->GetMap();
renderer->SetGameMap(map);
audioDevice->SetGameMap(map);
}
limbo->SetSelectedTeam(2);
limbo->SetSelectedWeapon(RIFLE_WEAPON);
RemoveAllLocalEntities();
worldSubFrame = 0.f;
aimDownState = 0.f;
sprintState = 0.f;
inGameLimbo = false;
worldSetTime = time;
}
Client::~Client() {
SPADES_MARK_FUNCTION();
net->Disconnect();
delete net;
RemoveAllLocalEntities();
RemoveAllCorpses();
// do before deleting world
renderer->SetGameMap(NULL);
if(world)
delete world;
delete tcView;
delete limbo;
delete scoreboard;
delete mapView;
delete chatWindow;
delete paletteView;
delete centerMessageView;
delete hurtRingView;
delete designFont;
delete textFont;
delete bigTextFont;
}
bool Client::WantsToBeClosed() {
return readyToClose;
}
void Client::RunFrame(float dt) {
SPADES_MARK_FUNCTION();
try{
if(net->GetStatus() == NetClientStatusConnected)
net->DoEvents(0);
else
net->DoEvents(30);
}catch(const std::exception& ex){
puts("--- NETWORK EXCEPTION ---");
puts(ex.what());
if(net->GetStatus() == NetClientStatusNotConnected)
throw;
}
hurtRingView->Update(dt);
centerMessageView->Update(dt);
mapView->Update(dt);
//puts(net->GetStatusString().c_str());
if(world){
Player* player = world->GetLocalPlayer();
if(player && player->GetTeamId() >= 2){
// spectating
Vector3 lastPos = followPos;
followVel *= powf(.3f, dt);
followPos += followVel * dt;
GameMap::RayCastResult minResult;
float minDist = 1.e+10f;
Vector3 minShift;
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;
// reflect
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);
/*
int link = world->mapWrapper->GetLink(result.hitBlock.x,
result.hitBlock.y,
result.hitBlock.z);
printf("hit block: link = %d (", link);
switch(link){
case 0: puts("Invalid)"); break;
case 1: puts("Root)"); break;
case 2: puts("Neg X)"); break;
case 3: puts("Pos X)"); break;
case 4: puts("Neg Y)"); break;
case 5: puts("Pos Y)"); break;
case 6: puts("Neg Z)"); break;
case 7: puts("Pos Z)"); break;
}*/
}
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);
}else if(player){
// joined in a team
PlayerInput inp = playerInput;
if(inp.crouch == false){
if(player->GetInput().crouch){
if(!player->TryUncrouch(false)){
inp.crouch = true;
}
}
}
if(inp.jump){
if(!player->IsOnGroundOrWade())
inp.jump = false;
}
player->SetInput(inp);
player->SetWeaponInput(weapInput);
//send player input
net->SendPlayerInput(inp);
net->SendWeaponInput(weapInput);
PlayerInput actualInput = player->GetInput();
WeaponInput actualWeapInput = player->GetWeaponInput();
if(actualWeapInput.secondary && player->IsToolWeapon() &&
player->IsAlive()){
aimDownState += dt * 6.f;
if(aimDownState > 1.f)
aimDownState = 1.f;
}else{
aimDownState -= dt * 3.f;
if(aimDownState < 0.f)
aimDownState = 0.f;
if(player->IsToolWeapon()){
// there is a possibility that player has respawned or something.
// stop aiming down
weapInput.secondary = false;
}
}
Vector3 vel = player->GetVelocty();
vel.z = 0.f;
if(actualInput.sprint && player->IsAlive() &&
vel.GetLength() > .2f && !player->GetWade()){
sprintState += dt * 4.f;
if(sprintState > 1.f)
sprintState = 1.f;
}else{
sprintState -= dt * 3.f;
if(sprintState < 0.f)
sprintState = 0.f;
}
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;
if(player->IsAlive())
lastAliveTime = time;
if(player->GetHealth() < lastHealth){
// hurt!
lastHealth = player->GetHealth();
lastHurtTime = world->GetTime();
IAudioChunk *c;
switch((rand() >> 3) & 3){
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());
}else{
lastHealth = player->GetHealth();
}
inp.jump = false;
}
#if 0
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
// corpse never accesses audio nor renderer
class CorpseUpdateDispatch: public ConcurrentDispatch{
Client *client;
float dt;
public:
CorpseUpdateDispatch(Client *c, float dt):
client(c), dt(dt){}
virtual void Run(){
std::list<Corpse *>::iterator it;
for(it = client->corpses.begin(); it != client->corpses.end(); it++){
for(int i = 0; i < 4; i++)
(*it)->Update(dt / 4.f);
}
}
};
CorpseUpdateDispatch corpseDispatch(this, dt);
corpseDispatch.Start();
{
std::list<ILocalEntity *>::iterator it;
std::vector<std::list<ILocalEntity *>::iterator> 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++){
delete *(its[i]);
localEntities.erase(its[i]);
}
}
corpseDispatch.Join();
if(grenadeVibration > 0.f){
grenadeVibration -= dt;
if(grenadeVibration < 0.f)
grenadeVibration = 0.f;
}
if(time > lastPosSentTime + 1.f &&
world->GetLocalPlayer()){
Player *p = world->GetLocalPlayer();
if(p->IsAlive()){
net->SendPosition();
lastPosSentTime = time;
}
}
}else{
renderer->SetFogColor(MakeVector3(0.f, 0.f, 0.f));
}
chatWindow->Update(dt);
limbo->Update(dt);
// SceneDef also can be used for sounds
SceneDefinition sceneDef = SceneDef();
lastSceneDef = sceneDef;
// Update sounds
try{
audioDevice->Respatialize(sceneDef.viewOrigin,
sceneDef.viewAxis[2],
sceneDef.viewAxis[1]);
}catch(const std::exception& ex){
puts("---- OPENAL EXCEPTION -----");
puts(ex.what());
}
// render scene
DrawScene();
// draw 2d
Draw2D();
// Well done!
renderer->FrameDone();
renderer->Flip();
if(world){
Player* player = world->GetLocalPlayer();
// view effects
if(player){
float scale = dt;
Vector3 vel = player->GetVelocty();
Vector3 front = player->GetFront();
Vector3 right = player->GetRight();
Vector3 up = player->GetUp();
viewWeaponOffset.x += Vector3::Dot(vel, right) * scale;
viewWeaponOffset.y -= Vector3::Dot(vel, front) * scale;
viewWeaponOffset.z += Vector3::Dot(vel, up) * scale;
if(dt > 0.f)
viewWeaponOffset *= powf(.02f, dt);
}else{
viewWeaponOffset = MakeVector3(0,0,0);
}
}
time += dt;
}
void Client::Closing() {
SPADES_MARK_FUNCTION();
}
void Client::MouseEvent(float x, float y) {
SPADES_MARK_FUNCTION();
if(IsLimboViewActive()){
limbo->MouseEvent(x, y);
return;
}
if(IsFollowing()){
SPAssert(world != NULL);
if(world->GetLocalPlayer() &&
world->GetLocalPlayer()->GetTeamId() >= 2 &&
followingPlayerId == world->GetLocalPlayerIndex()){
// invert dir
x = -x; y = -y;
}
followYaw -= x * 0.003f;
followPitch -= y * 0.003f;
if(followPitch < -M_PI*.45f) followPitch = -M_PI*.45f;
if(followPitch > M_PI*.45f) followPitch = M_PI * .45f;
followYaw = fmodf(followYaw, M_PI*2.f);
}else if(world && world->GetLocalPlayer()){
Player *p = world->GetLocalPlayer();
if(p->IsAlive()){
x /= GetAimDownZoomScale();
y /= GetAimDownZoomScale();
x *= (float)cg_mouseSensitivity;
y *= (float)cg_mouseSensitivity;
p->Turn(x * 0.003f, y * 0.003f);
}
}
}
void Client::CharEvent(const std::string &ch){
SPADES_MARK_FUNCTION();
if(chatEditing){
ChatCharEvent(ch);
return;
}
if(ch == "/"){
ActivateChatTextEditor(false);
ChatCharEvent("/");
}
}
void Client::KeyEvent(const std::string& name, bool down){
SPADES_MARK_FUNCTION();
if(chatEditing){
if(down)
ChatKeyEvent(name);
return;
}
if(name == "Escape"){
if(down){
if(inGameLimbo){
inGameLimbo = false;
}else{
readyToClose = true;
}
}
}else if(world){
if(IsLimboViewActive()){
if(down){
limbo->KeyEvent(name);
}
return;
}
if(IsFollowing()){
if(name == cg_keyAttack){
if(down){
if(world->GetLocalPlayer()->GetTeamId() >= 2 ||
time > lastAliveTime + 1.3f)
FollowNextPlayer();
}
return;
}else if(name == cg_keyAltAttack){
if(down){
if(world->GetLocalPlayer()){
followingPlayerId = world->GetLocalPlayerIndex();
}
}
return;
}
}
if(world->GetLocalPlayer()){
Player *p = world->GetLocalPlayer();
if(p->IsAlive() && p->GetTool() == Player::ToolBlock && down) {
if(paletteView->KeyInput(name)){
return;
}
}
if(name == cg_keyMoveLeft){
playerInput.moveLeft = down;
keypadInput.left = down;
if(down) playerInput.moveRight = false;
else playerInput.moveRight = keypadInput.right;
}else if(name == cg_keyMoveRight){
playerInput.moveRight = down;
keypadInput.right = down;
if(down) playerInput.moveLeft = false;
else playerInput.moveLeft = keypadInput.left;
}else if(name == cg_keyMoveForward){
playerInput.moveForward = down;
keypadInput.forward = down;
if(down) playerInput.moveBackward = false;
else playerInput.moveBackward = keypadInput.backward;
}else if(name == cg_keyMoveBackward){
playerInput.moveBackward = down;
keypadInput.backward = down;
if(down) playerInput.moveForward = false;
else playerInput.moveForward = keypadInput.forward;
}else if(name == cg_keyCrouch){
playerInput.crouch = down;
}else if(name == cg_keySprint){
playerInput.sprint = down;
if(down){
if(world->GetLocalPlayer()->IsToolWeapon()){
weapInput.secondary = false;
}
}
}else if(name == cg_keySneak){
playerInput.sneak = down;
}else if(name == cg_keyJump){
playerInput.jump = down;
}else if(name == cg_keyAttack){
weapInput.primary = down;
}else if(name == cg_keyAltAttack){
if(world->GetLocalPlayer()->IsToolWeapon()){
if(down && !playerInput.sprint){
weapInput.secondary = !weapInput.secondary;
}
}else{
weapInput.secondary = down;
}
}else if(name == cg_keyReloadWeapon && down){
world->GetLocalPlayer()->Reload();
if(world->GetLocalPlayer()->IsToolWeapon()){
weapInput.secondary = false;
}
net->SendReload();
}else if(name == cg_keyToolSpade && down){
if(world->GetLocalPlayer()->GetTeamId() < 2){
world->GetLocalPlayer()->SetTool(Player::ToolSpade);
net->SendTool();
}
}else if(name == cg_keyToolBlock && down){
if(world->GetLocalPlayer()->GetTeamId() < 2){
world->GetLocalPlayer()->SetTool(Player::ToolBlock);
net->SendTool();
}
}else if(name == cg_keyToolWeapon && down){
if(world->GetLocalPlayer()->GetTeamId() < 2){
world->GetLocalPlayer()->SetTool(Player::ToolWeapon);
net->SendTool();
}
}else if(name == cg_keyToolGrenade && down){
if(world->GetLocalPlayer()->GetTeamId() < 2){
world->GetLocalPlayer()->SetTool(Player::ToolGrenade);
net->SendTool();
}
}else if(name == cg_keyGlobalChat && down){
// global chat
ActivateChatTextEditor(true);
}else if(name == cg_keyTeamChat && down){
// team chat
ActivateChatTextEditor(false);
}else if(name == cg_keyCaptureColor && down){
CaptureColor();
}else if(name == cg_keyChangeMapScale && down){
mapView->SwitchScale();
}else if(name == cg_keyScoreboard){
scoreboardVisible = down;
}else if(name == cg_keyLimbo && down){
limbo->SetSelectedTeam(world->GetLocalPlayer()->GetTeamId());
limbo->SetSelectedWeapon(world->GetLocalPlayer()->GetWeapon()->GetWeaponType());
inGameLimbo = true;
}else if(name == cg_keySceneshot && down){
TakeScreenShot(true);
}else if(name == cg_keyScreenshot && down){
TakeScreenShot(false);
}else if(name == cg_keyFlashlight && down){
flashlightOn = !flashlightOn;
flashlightOnTime = time;
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Player/Flashlight.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}
}else{
// limbo
}
}
}
void Client::CaptureColor() {
if(!world) return;
Player *p = world->GetLocalPlayer();
if(!p) return;
if(!p->IsAlive()) return;
IntVector3 outBlockCoord;
if(!world->GetMap()->CastRay(p->GetEye(),
p->GetFront(),
256.f, outBlockCoord)){
return;
}
uint32_t col = world->GetMap()->GetColorWrapped(outBlockCoord.x,
outBlockCoord.y,
outBlockCoord.z);
IntVector3 colV;
colV.x = (uint8_t)(col);
colV.y = (uint8_t)(col >> 8);
colV.z = (uint8_t)(col >> 16);
p->SetHeldBlockColor(colV);
net->SendHeldBlockColor();
}
bool Client::IsLimboViewActive(){
if(world){
if(!world->GetLocalPlayer()){
return true;
}else if(inGameLimbo){
return true;
}
}
// TODO: anytime limbo
return false;
}
void Client::SpawnPressed() {
WeaponType weap = limbo->GetSelectedWeapon();
int team = limbo->GetSelectedTeam();
inGameLimbo = false;
if(team == 2)
team = 255;
if(!world->GetLocalPlayer()){
// join
net->SendJoin(team, weap,
playerName, lastKills);
}else{
Player *p = world->GetLocalPlayer();
if(p->GetTeamId() != team){
net->SendTeamChange(team);
}
if(team != 2 && p->GetWeapon()->GetWeaponType() != weap){
net->SendWeaponChange(weap);
}
}
}
#pragma mark - Drawing
void Client::TakeScreenShot(bool sceneOnly){
SceneDefinition sceneDef = SceneDef();
lastSceneDef = sceneDef;
// render scene
flashDlights = flashDlightsOld;
DrawScene();
// draw 2d
if(!sceneOnly)
Draw2D();
// Well done!
renderer->FrameDone();
Bitmap *bmp = renderer->ReadBitmap();
try{
std::string name = ScreenShotPath();
bmp->Save(name);
std::string msg;
if(sceneOnly)
msg = "Sceneshot saved: ";
else
msg = "Screenshot saved: ";
msg += name;
msg = ChatWindow::ColoredMessage(msg, MsgColorSysInfo);
chatWindow->AddMessage(msg);
delete bmp;
}catch(const std::exception& ex){
std::string msg;
msg = "Screenshot failed: ";
std::vector<std::string> lines = SplitIntoLines(ex.what());
msg += lines[0];
msg = ChatWindow::ColoredMessage(msg, MsgColorRed);
chatWindow->AddMessage(msg);
delete bmp;
}
}
std::string Client::ScreenShotPath() {
char buf[256];
for(int i = 0; i < 10000;i++){
sprintf(buf, "Screenshots/shot%04d.tga", nextScreenShotIndex);
if(FileManager::FileExists(buf)){
nextScreenShotIndex++;
if(nextScreenShotIndex >= 10000)
nextScreenShotIndex = 0;
continue;
}
return buf;
}
SPRaise("No free file name");
}
bool Client::ShouldRenderInThirdPersonView() {
//^return true;
if(world && world->GetLocalPlayer()){
if(!world->GetLocalPlayer()->IsAlive())
return true;
}
return false;
}
float Client::GetLocalFireVibration() {
float localFireVibration = 0.f;
localFireVibration = time - localFireVibrationTime;
localFireVibration = 1.f - localFireVibration / 0.1f;
if(localFireVibration < 0.f)
localFireVibration = 0.f;
return localFireVibration;
}
float Client::GetAimDownZoomScale(){
if(world == NULL || world->GetLocalPlayer() == NULL ||
world->GetLocalPlayer()->IsToolWeapon() == false ||
world->GetLocalPlayer()->IsAlive() == false)
return 1.f;
float delta = .8f;
switch(world->GetLocalPlayer()->GetWeapon()->GetWeaponType()) {
case SMG_WEAPON:
delta = .8f;
break;
case RIFLE_WEAPON:
delta = 1.4f;
break;
case SHOTGUN_WEAPON:
delta = .4f;
break;
}
return 1.f + powf(aimDownState, 5.f) * delta;
}
SceneDefinition Client::SceneDef() {
SPADES_MARK_FUNCTION();
SceneDefinition def;
def.time = (unsigned int)(time * 1000.f);
def.denyCameraBlur = true;
if(world){
IntVector3 fogColor = world->GetFogColor();
renderer->SetFogColor(MakeVector3(fogColor.x / 255.f,
fogColor.y / 255.f,
fogColor.z / 255.f));
Player *player = world->GetLocalPlayer();
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();
if((limit--) <= 0){
break;
}
}
player = world->GetPlayer(followingPlayerId);
}
if(player){
float roll = 0.f;
float scale = 1.f;
float vibPitch = 0.f;
float vibYaw = 0.f;
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()){
center = lastMyCorpse->GetCenter();
}
if(map->IsSolidWrapped((int)floorf(center.x),
(int)floorf(center.y),
(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))){
center.z = z;
break;
}else{
z -= 1.f;
}
}
}
float distance = 5.f;
if(player == world->GetLocalPlayer() &&
world->GetLocalPlayer()->GetTeamId() < 2 &&
!world->GetLocalPlayer()->IsAlive()){
// deathcam.
float elapsedTime = time - lastAliveTime;
distance -= 3.f * expf(-elapsedTime * 1.f);
}
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;
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
GameMap::RayCastResult result;
result = map->CastRay2(center, (eye - center).Normalize(), 256);
if(result.hit) {
float dist = (result.hitPos - center).GetLength();
float curDist = (eye - center).GetLength();
dist -= 0.3f; // near clip plane
if(curDist > dist){
float diff = curDist - dist;
eye += (center - eye).Normalize() * diff;
}
}
Vector3 front = center - eye;
front = front.Normalize();
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 = 60.f * M_PI /180.f;
def.fovX = atanf(tanf(def.fovY * .5f) *
renderer->ScreenWidth() /
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){
// spectator view (noclip view)
Vector3 center = followPos;
Vector3 front;
Vector3 up = {0, 0, -1};
front.x = -cosf(followYaw) * cosf(followPitch);
front.y = -sinf(followYaw) * cosf(followPitch);
front.z = sinf(followPitch);
def.viewOrigin = center;
def.viewAxis[0] = -Vector3::Cross(up, front).Normalize();
def.viewAxis[1] = -Vector3::Cross(front, def.viewAxis[0]).Normalize();
def.viewAxis[2] = front;
def.fovY = 60.f * 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;
}else{
Vector3 front = player->GetFront();
Vector3 right = player->GetRight();
Vector3 up = player->GetUp();
float localFireVibration = GetLocalFireVibration();
localFireVibration *= localFireVibration;
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;
// sprint bob
if(sprintState > 0.f){
float sp = SmoothStep(sprintState);
vibYaw += sinf(player->GetWalkAnimationProgress() * M_PI * 2.f) * 0.01f * sp;
roll -= sinf(player->GetWalkAnimationProgress() * M_PI * 2.f) * 0.005f * sp;
float p = cosf(player->GetWalkAnimationProgress() * M_PI * 2.f);
p = p * p; p *= p; p *= p; p *= p;
vibPitch += p * 0.01f * sp;
}
scale /= GetAimDownZoomScale();
def.viewOrigin = player->GetEye();
def.viewAxis[0] = right;
def.viewAxis[1] = up;
def.viewAxis[2] = front;
def.fovY = 60.f * 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;
}
// add vibration for both 1st/3rd view
{
// add grenade vibration
float grenVib = grenadeVibration;
if(grenVib > 0.f){
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;
}
}
// 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 *= scale;
def.fovY *= scale;
}
{
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);
}
def.zNear = 0.05f;
def.zFar = 130.f;
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 = 60.f * M_PI /180.f;
def.fovX = atanf(tanf(def.fovY * .5f) *
renderer->ScreenWidth() /
renderer->ScreenHeight()) * 2.f;
def.zNear = 0.05f;
def.zFar = 130.f;
def.skipWorld = false;
}
}else{
def.viewOrigin = MakeVector3(0, 0, 0);
def.viewAxis[0] = MakeVector3(1, 0, 0);
def.viewAxis[1] = MakeVector3(0, 0, -1);
def.viewAxis[2] = MakeVector3(0, 0, 1);
def.fovY = 60.f * M_PI /180.f;
def.fovX = atanf(tanf(def.fovY * .5f) *
renderer->ScreenWidth() /
renderer->ScreenHeight()) * 2.f;
def.zNear = 0.05f;
def.zFar = 130.f;
def.skipWorld = true;
renderer->SetFogColor(MakeVector3(0,0,0));
}
SPAssert(!isnan(def.viewOrigin.x));
SPAssert(!isnan(def.viewOrigin.y));
SPAssert(!isnan(def.viewOrigin.z));
return def;
}
std::string Client::GetWeaponPrefix(std::string fold, WeaponType type){
SPADES_MARK_FUNCTION_DEBUG();
std::string weapPrefix;
switch(type){
case RIFLE_WEAPON:
weapPrefix = fold + "/Weapons/Rifle";
break;
case SMG_WEAPON:
weapPrefix = fold + "/Weapons/SMG";
break;
case SHOTGUN_WEAPON:
weapPrefix = fold + "/Weapons/Shotgun";
break;
}
return weapPrefix;
}
void Client::AddGrenadeToScene(spades::client::Grenade *g) {
SPADES_MARK_FUNCTION();
IModel *model;
model = renderer->RegisterModel("Models/Weapons/Grenade/Grenade.kv6");
ModelRenderParam param;
Matrix4 mat = Matrix4::Scale(0.03f);
mat = Matrix4::Translate(g->GetPosition()) * mat;
param.matrix = mat;
renderer->RenderModel(model, param);
}
void Client::AddPlayerToScene(spades::client::Player *p) {
SPADES_MARK_FUNCTION();
if(p->GetTeamId() >= 2){
// spectator, or dummy player
return;
}
// debug
if(false){
renderer->AddDebugLine(lastSceneDef.viewOrigin +
MakeVector3(0, 0, 1),
p->GetOrigin(),
MakeVector4(1, 1, 0, 0));
}
float distancePowered = (p->GetOrigin() - lastSceneDef.viewOrigin).GetPoweredLength();
if(distancePowered > 140.f * 140.f){
return;
}
if(!p->IsAlive()){
if(!cg_ragdoll){
ModelRenderParam param;
param.matrix = Matrix4::Translate(p->GetOrigin()+
MakeVector3(0,0,1));
param.matrix = param.matrix * Matrix4::Scale(.1f);
IntVector3 col = p->GetColor();
param.customColor = MakeVector3(col.x/255.f,
col.y/255.f,
col.z/255.f);
IModel *model = renderer->RegisterModel("Models/Player/Dead.kv6");
renderer->RenderModel(model, param);
}
return;
}
std::string weapPrefix = GetWeaponPrefix("Models", p->GetWeapon()->GetWeaponType());
Matrix4 eyeMatrix = Matrix4::FromAxis(-p->GetRight(),
p->GetFront(),
-p->GetUp(),
p->GetEye());
if(p == world->GetLocalPlayer() &&
!ShouldRenderInThirdPersonView()){
if(flashlightOn){
float brightness;
brightness = time - flashlightOnTime;
brightness = 1.f - expf(-brightness * 5.f);
// add flash light
DynamicLightParam light;
light.origin = (eyeMatrix * MakeVector3(0, -0.05f, -0.1f)).GetXYZ();
light.color = MakeVector3(1, .7f, .5f) * 1.5f * brightness;
light.radius = 40.f;
light.type = DynamicLightTypeSpotlight;
light.spotAngle = 30.f * M_PI / 180.f;
light.spotAxis[0] = p->GetRight();
light.spotAxis[1] = p->GetUp();
light.spotAxis[2] = p->GetFront();
light.image = renderer->RegisterImage("Gfx/Spotlight.tga");
renderer->AddLight(light);
light.color *= .3f;
light.radius = 10.f;
light.type = DynamicLightTypePoint;
light.image = NULL;
renderer->AddLight(light);
// add glare
renderer->SetColor(MakeVector4(1, .7f, .5f, 0) * brightness * .3f);
renderer->AddSprite(renderer->RegisterImage("Gfx/Glare.tga"),
(eyeMatrix * MakeVector3(0, 0.3f, -0.3f)).GetXYZ(),
.8f, 0.f);
}
Vector3 leftHand, rightHand;
// view weapon
float sprint = SmoothStep(sprintState);
if(p->GetTool() == Player::ToolSpade){
WeaponInput inp = p->GetWeaponInput();
Matrix4 mat = Matrix4::Scale(0.033f);
if(inp.primary) {
float per = p->GetSpadeAnimationProgress();
per = 1.f - per;
mat = Matrix4::Rotate(MakeVector3(1, 0, 0),
per * 1.7f) * mat;
mat = Matrix4::Translate(MakeVector3(0, per*0.3f, 0)) * mat;
}else if(inp.secondary) {
float per = p->GetDigAnimationProgress();
bool first = p->IsFirstDig();
float ang;
const float readyFront = -0.8f;
float front = readyFront;
float side = 1.f;
const float digAngle = .6f;
const float readyAngle = 0.6f;
if(per < .5f) {
if(first) {
// bringing to the position
per += .5f;
per *= per;
ang = per * readyAngle;
side = per;
front = per * readyFront;
}else{
// digged!
ang = readyAngle;
per = (.5f - per) / .5f;
per *= per;
per *= per;
ang += per * digAngle;
front += per * 2.f;
}
}else{
per = (per - .5f) / .5f;
per = 1.f - (1.f-per)*(1.f-per);
ang = readyAngle +
per * digAngle;
front += per * 2.f;
}
mat = Matrix4::Rotate(MakeVector3(1, 0, 0),
ang) * mat;
mat = Matrix4::Rotate(MakeVector3(0, 0, 1),
front * .15f) * mat;
side *= .3f;
front *= .1f;
mat = Matrix4::Translate(MakeVector3(side, front, front * .2f)) * mat;
}
if(sprint > 0.f){
mat = Matrix4::Rotate(MakeVector3(0, 1, 0),
sprint * 1.3f) * mat;
mat = Matrix4::Translate(MakeVector3(0.3f, -0.4f, -0.1f) * sprint) * mat;
}
mat = Matrix4::Translate(-0.3f, .7f, 0.3f) * mat;
mat = Matrix4::Translate(viewWeaponOffset) * mat;
leftHand = (mat * MakeVector3(0.0f, 0.0f, 7.f)).GetXYZ();
rightHand = (mat * MakeVector3(0.0f, 0.0f, -2.f)).GetXYZ();
mat = eyeMatrix * mat;
ModelRenderParam param;
param.matrix = mat;
param.depthHack = true;
IModel *model = renderer->RegisterModel("Models/Weapons/Spade/Spade.kv6");
renderer->RenderModel(model, param);
}else if(p->GetTool() == Player::ToolBlock){
if(p->IsReadyToUseTool()){
Matrix4 mat = Matrix4::Scale(0.033f);
if(sprint > 0.f){
mat = Matrix4::Rotate(MakeVector3(0, 0, 1),
sprint * -0.3f) * mat;
mat = Matrix4::Translate(MakeVector3(0.1f, -0.4f, -0.05f) * sprint) * mat;
}
mat = Matrix4::Translate(-0.3f, .7f, 0.3f) * mat;
mat = Matrix4::Translate(viewWeaponOffset) * mat;
leftHand = (mat * MakeVector3(5.0f, -1.0f, 4.f)).GetXYZ();
rightHand = (mat * MakeVector3(-5.5f, 3.0f, -5.f)).GetXYZ();
mat = eyeMatrix * mat;
ModelRenderParam param;
param.matrix = mat;
param.depthHack = true;
param.customColor = MakeVector3(p->GetBlockColor()) / 255.f;
IModel *model = renderer->RegisterModel("Models/Weapons/Block/Block2.kv6");
renderer->RenderModel(model, param);
}
}else if(p->GetTool() == Player::ToolGrenade){
float tim = p->GetTimeToNextGrenade();
if(p->IsReadyToUseTool()){
WeaponInput inp = p->GetWeaponInput();
float bring = 0.f;
float pin = 0.f;
float side = 0.f;
if(tim < 0.f) {
bring = std::min(1.f, -tim * 5.f);
bring = 1.f - bring;
bring = 1.f - bring * bring;
}
if(inp.primary) {
pin = p->GetGrenadeCookTime() * 8.f;
if(pin > 2.f)pin = 2.f;
if(pin > 1.f){
side += pin - 1.f;
bring -= (pin - 1.f) * 2.f;
}
}
Matrix4 mat = Matrix4::Scale(0.033f);
if(sprint > 0.f){
mat = Matrix4::Rotate(MakeVector3(0, 0, 1),
sprint * -0.3f) * mat;
mat = Matrix4::Translate(MakeVector3(0.1f, -0.4f, -0.05f) * sprint) * mat;
}
mat = Matrix4::Translate(-0.3f - side * .8f,
.8 - bring * .1f,
0.45f - bring * .15f) * mat;
mat = Matrix4::Translate(viewWeaponOffset) * mat;
leftHand = (mat * MakeVector3(10.0f, -1.0f, 10.f)).GetXYZ();
rightHand = (mat * MakeVector3(-3.f, 1.0f, 5.f)).GetXYZ();
Vector3 leftHand2 = (mat * MakeVector3(2.f, 1.0f, -2.f)).GetXYZ();
Vector3 leftHand3 = (mat * MakeVector3(8.0f, -1.0f, 10.f)).GetXYZ();
if(pin < 1.f){
leftHand = Mix(leftHand, leftHand2, pin);
}else{
leftHand = Mix(leftHand2, leftHand3, pin - 1.f);
}
mat = eyeMatrix * mat;
ModelRenderParam param;
param.matrix = mat;
param.depthHack = true;
IModel *model = renderer->RegisterModel("Models/Weapons/Grenade/Grenade.kv6");
renderer->RenderModel(model, param);
}else{
// throwing
float per = .5f - p->GetTimeToNextGrenade();
per *= 6.f;
if(per > 1.f) per = 1.f;
leftHand = MakeVector3(0.5f, 0.5f, 0.6f);
float p2 = per - .6f;
p2 = .9f - p2 * p2 * 2.5f;
rightHand = MakeVector3(-0.2f, p2,
-.9f + per * 1.8f);
}
}else if(p->GetTool() == Player::ToolWeapon){
Matrix4 mat = Matrix4::Scale(0.033f);
float vib = GetLocalFireVibration();
float aDown = SmoothStep(aimDownState);
float motion = 1.f - aDown * 0.4f;
float weapUp = aDown;
switch(p->GetWeapon()->GetWeaponType()){
case RIFLE_WEAPON: weapUp *= 0.05f; break;
case SMG_WEAPON: weapUp *= 0.028f; break;
case SHOTGUN_WEAPON: weapUp *= 0.05f; break;
default: SPInvalidEnum("p->GetWeapon()->GetWeaponType()", p->GetWeapon()->GetWeaponType());
}
if(sprint > 0.f){
mat = Matrix4::Rotate(MakeVector3(0, 0, 1),
sprint * -1.3f) * mat;
mat = Matrix4::Rotate(MakeVector3(0, 1, 0),
sprint *
0.2f) * mat;
mat = Matrix4::Translate(MakeVector3(0.2f, -0.2f, 0.05f) * sprint) * mat;
}
mat = Matrix4::Translate(-0.13f * (1.f - aDown),
.5f,
0.2f - weapUp) * mat;
mat = Matrix4::Translate(viewWeaponOffset * motion) * mat;
mat = Matrix4::Translate(sinf(vib*M_PI*2.f)*0.008f * motion,
vib*(vib-1.f)*0.14f * motion,
vib*(1.f-vib)*0.03f * motion) * mat;
bool reloading = p->GetWeapon()->IsReloading();
float reload = p->GetWeapon()->GetReloadProgress();
switch(p->GetWeapon()->GetWeaponType()){
case SMG_WEAPON:
{
leftHand = (mat * MakeVector3(1.0f, 6.0f, 1.f)).GetXYZ();
rightHand = (mat * MakeVector3(0.f, -8.0f, 2.f)).GetXYZ();
Vector3 leftHand2 = (mat * MakeVector3(5.0f, -10.0f, 4.f)).GetXYZ();
Vector3 leftHand3 = (mat * MakeVector3(1.0f, 6.0f, -4.f)).GetXYZ();
Vector3 leftHand4 = (mat * MakeVector3(1.0f, 9.0f, -6.f)).GetXYZ();
ModelRenderParam param;
param.depthHack = true;
param.matrix = eyeMatrix * mat;
{
std::string path = weapPrefix + "/WeaponNoMagazine.kv6";
IModel *model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
mat = mat * Matrix4::Translate(0.f, 3.f, 1.f);
reload *= 2.5f;
if(reloading) {
if(reload < 0.7f){
float per = reload / .7f;
mat = mat * Matrix4::Translate(0.f, 0.f, per * per * 50.f);
leftHand = Mix(leftHand, leftHand2, SmoothStep(per));
}else if(reload < 1.4f){
float per = (1.4f - reload) / .7f;
if(per < .3f) {
per *= 4.f;
per -= .4f;
per = std::max(std::min(per, .3f), 0.f);
}
mat = mat * Matrix4::Translate(0.f, 0.f, per * per * 10.f);
leftHand = (mat * MakeVector3(0.f, 0.f, 4.f)).GetXYZ();
}else if(reload < 1.9f){
float per = (reload - 1.4f) / .5f;
leftHand = (mat * MakeVector3(0.f, 0.f, 4.f)).GetXYZ();
leftHand = Mix(leftHand, leftHand3, SmoothStep(per));
}else if(reload < 2.2f){
float per = (reload - 1.9f) / .3f;
leftHand = Mix(leftHand3, leftHand4, SmoothStep(per));
}else {
float per = (reload - 2.2f) / .3f;
leftHand = Mix(leftHand4, leftHand, SmoothStep(per));
}
}
param.matrix = eyeMatrix * mat;
{
std::string path = weapPrefix + "/Magazine.kv6";
IModel *model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
}
break;
case RIFLE_WEAPON:
{
leftHand = (mat * MakeVector3(1.0f, 6.0f, 1.f)).GetXYZ();
rightHand = (mat * MakeVector3(0.f, -8.0f, 2.f)).GetXYZ();
Vector3 leftHand2 = (mat * MakeVector3(5.0f, -10.0f, 4.f)).GetXYZ();
Vector3 rightHand3 = (mat * MakeVector3(-2.0f, -7.0f, -4.f)).GetXYZ();
Vector3 rightHand4 = (mat * MakeVector3(-3.0f, -4.0f, -6.f)).GetXYZ();
ModelRenderParam param;
param.depthHack = true;
param.matrix = eyeMatrix * mat;
{
std::string path = weapPrefix + "/WeaponNoMagazine.kv6";
IModel *model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
mat = mat * Matrix4::Translate(0.f, 1.f, 1.f);
reload *= 2.5f;
if(reloading) {
if(reload < 0.1f){
float per = reload / .1f;
leftHand = Mix(leftHand,
(mat * MakeVector3(0.f, 0.f, 4.f)).GetXYZ(),
SmoothStep(per));
}else if(reload < 0.7f){
float per = (reload-.1f) / .6f;
if(per < .2f) {
per *= 4.f;
per -= .4f;
per = std::max(std::min(per, .2f), 0.f);
}
if(per > .5f){
per += per - .5f;
}
mat = mat * Matrix4::Translate(0.f, 0.f, per * per * 10.f);
leftHand = (mat * MakeVector3(0.f, 0.f, 4.f)).GetXYZ();
if(per > .5f){
per = (per - .5f);
leftHand = Mix(leftHand, leftHand2, SmoothStep(per));
}
}else if(reload < 1.4f){
float per = (1.4f - reload) / .7f;
if(per < .3f) {
per *= 4.f;
per -= .4f;
per = std::max(std::min(per, .3f), 0.f);
}
mat = mat * Matrix4::Translate(0.f, 0.f, per * per * 10.f);
leftHand = (mat * MakeVector3(0.f, 0.f, 4.f)).GetXYZ();
}else if(reload < 1.9f){
float per = (reload - 1.4f) / .5f;
Vector3 orig = leftHand;
leftHand = (mat * MakeVector3(0.f, 0.f, 4.f)).GetXYZ();
leftHand = Mix(leftHand, orig, SmoothStep(per));
rightHand = Mix(rightHand, rightHand3, SmoothStep(per));
}else if(reload < 2.2f){
float per = (reload - 1.9f) / .3f;
rightHand = Mix(rightHand3, rightHand4, SmoothStep(per));
}else {
float per = (reload - 2.2f) / .3f;
rightHand = Mix(rightHand4, rightHand, SmoothStep(per));
}
}
param.matrix = eyeMatrix * mat;
{
std::string path = weapPrefix + "/Magazine.kv6";
IModel *model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
}
break;
case SHOTGUN_WEAPON:
{
rightHand = (mat * MakeVector3(0.f, -8.0f, 2.f)).GetXYZ();
Vector3 leftHand2 = (mat * MakeVector3(5.0f, -10.0f, 4.f)).GetXYZ();
Vector3 leftHand3 = (mat * MakeVector3(1.0f, 1.0f, 2.f)).GetXYZ();
ModelRenderParam param;
param.depthHack = true;
param.matrix = eyeMatrix * mat;
{
std::string path = weapPrefix + "/WeaponNoPump.kv6";
IModel *model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
//mat = mat * Matrix4::Translate(0.f, 0.f, 0.f);
reload *= .5f;
leftHand = (mat * MakeVector3(0.f, 4.f, 2.f)).GetXYZ();
if(reloading) {
if(reload < 0.2f){
float per = reload / .2f;
leftHand = Mix(leftHand,
leftHand2,
SmoothStep(per));
}else if(reload < 0.35f){
float per = (reload-.2f) / .15f;
leftHand = Mix(leftHand2,
leftHand3,
SmoothStep(per));
}else if(reload < .5f){
float per = (reload-.35f) / .15f;
leftHand = Mix(leftHand3,
leftHand,
SmoothStep(per));
}
}
float cockFade = 1.f;
if(reloading) {
if(reload < .25f ||
p->GetWeapon()->GetAmmo() <
p->GetWeapon()->GetClipSize() - 1){
cockFade = 0.f;
}else{
cockFade = (reload - .25f) * 10.f;
if(cockFade > 1.f) cockFade = 1.f;
}
}
if(cockFade > 0.f) {
float cock = 0.f;
float tim = p->GetWeapon()->TimeToNextFire();
if(tim < 0.f){
// might be right after reloading...
if(p->GetWeapon()->GetAmmo() ==
p->GetWeapon()->GetClipSize() &&
reload > .5f && reload < 1.f) {
tim = reload - .4f;
if(tim < .05f) {
cock = 0.f;
}else if(tim < .12f){
cock = (tim - .05f) / .07f;
}else if(tim < .26f) {
cock = 1.f;
}else if(tim < .36f){
cock = 1.f - (tim - .26f) / .1f;
}
}
}if(tim < .2f) {
cock = 0.f;
}else if(tim < .3f) {
cock = (tim - .2f) / .1f;
}else if(tim < .42f) {
cock = 1.f;
}else if(tim < .52f) {
cock = 1.f - (tim - .42f) / .1f;
}else{
cock = 0.f;
}
cock *= cockFade;
mat = mat * Matrix4::Translate(0.f, -cock * 1.5f, 0.f);
leftHand = Mix(leftHand,
(mat * MakeVector3(0.f, 4.f, 2.f)).GetXYZ(),
cockFade);
}
param.matrix = eyeMatrix * mat;
{
std::string path = weapPrefix + "/Pump.kv6";
IModel *model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
}
break;
default:
abort();
}
}
// view hands
{
ModelRenderParam param;
param.depthHack = true;
IModel *model = renderer->RegisterModel
("Models/Player/Arm.kv6");
IModel *model2 = renderer->RegisterModel
("Models/Player/UpperArm.kv6");
IntVector3 col = p->GetColor();
param.customColor = MakeVector3(col.x/255.f,
col.y/255.f,
col.z/255.f);
const float armlen = 0.5f;
Vector3 shoulders[] = {{0.4f, 0.0f, 0.25f},
{-0.4f, 0.0f, 0.25f}};
Vector3 hands[] = {leftHand, rightHand};
Vector3 benddirs[] = {0.5f, 0.2f, 0.f,
-0.5f, 0.2f, 0.f};
for(int i = 0; i < 2; i++){
Vector3 shoulder = shoulders[i];
Vector3 hand = hands[i];
Vector3 benddir = benddirs[i];
float len2 = (hand - shoulder).GetPoweredLength();
// len2/4 + x^2 = armlen^2
float bendlen = sqrtf(std::max(armlen*armlen - len2*.25f, 0.f));
Vector3 bend = Vector3::Cross(benddir, hand-shoulder);
bend = bend.Normalize();
if(bend.z < 0.f) bend.z = -bend.z;
Vector3 elbow = (hand + shoulder) * .5f;
elbow += bend * bendlen;
{
Vector3 axises[3];
axises[2] = (hand - elbow).Normalize();
axises[0] = MakeVector3(0, 0, 1);
axises[1] = Vector3::Cross(axises[2], axises[0]).Normalize();
axises[0] = Vector3::Cross(axises[1], axises[2]).Normalize();
Matrix4 mat = Matrix4::Scale(.05f);
mat = Matrix4::FromAxis(axises[0],
axises[1],
axises[2],
elbow) * mat;
mat = eyeMatrix * mat;
param.matrix = mat;
renderer->RenderModel(model, param);
}
{
Vector3 axises[3];
axises[2] = (elbow - shoulder).Normalize();
axises[0] = MakeVector3(0, 0, 1);
axises[1] = Vector3::Cross(axises[2], axises[0]).Normalize();
axises[0] = Vector3::Cross(axises[1], axises[2]).Normalize();
Matrix4 mat = Matrix4::Scale(.05f);
mat = Matrix4::FromAxis(axises[0],
axises[1],
axises[2],
shoulder) * mat;
mat = eyeMatrix * mat;
param.matrix = mat;
renderer->RenderModel(model2, param);
}
}
}
// --- local view ends
} else {
ModelRenderParam param;
IModel *model;
Vector3 front = p->GetFront();
IntVector3 col = p->GetColor();
param.customColor = MakeVector3(col.x/255.f,
col.y/255.f,
col.z/255.f);
float yaw = atan2(front.y, front.x) + M_PI * .5f;
float pitch = -atan2(front.z, sqrt(front.x * front.x + front.y * front.y));
// lower axis
Matrix4 lower = Matrix4::Translate(p->GetOrigin());
lower = lower * Matrix4::Rotate(MakeVector3(0,0,1),
yaw);
Matrix4 scaler = Matrix4::Scale(0.1f);
scaler = scaler * Matrix4::Scale(-1,-1,1);
PlayerInput inp = p->GetInput();
// lower
Matrix4 torso, head, arms;
if(inp.crouch){
Matrix4 leg1 = Matrix4::Translate(-0.25f, 0.2f, -0.1f);
Matrix4 leg2 = Matrix4::Translate( 0.25f, 0.2f, -0.1f);
leg1 = lower * leg1;
leg2 = lower * leg2;
model = renderer->RegisterModel
("Models/Player/LegCrouch.kv6");
param.matrix = leg1 * scaler;
renderer->RenderModel(model, param);
param.matrix = leg2 * scaler;
renderer->RenderModel(model, param);
torso = Matrix4::Translate(0.f,0.f,-0.55f);
torso = lower * torso;
model = renderer->RegisterModel
("Models/Player/TorsoCrouch.kv6");
param.matrix = torso * scaler;
renderer->RenderModel(model, param);
head = Matrix4::Translate(0.f,0.f,-0.0f);
head = torso * head;
arms = Matrix4::Translate(0.f,0.f,-0.0f);
arms = torso * arms;
}else{
Matrix4 leg1 = Matrix4::Translate(-0.25f, 0.f, -0.1f);
Matrix4 leg2 = Matrix4::Translate( 0.25f, 0.f, -0.1f);
float ang = sinf(p->GetWalkAnimationProgress() * M_PI * 2.f) * 0.6f;
float walkVel = Vector3::Dot(p->GetVelocty(), p->GetFront2D()) * 2.f;
ang *= walkVel;
leg1 = leg1 * Matrix4::Rotate(MakeVector3(1,0,0),
ang);
leg2 = leg2 * Matrix4::Rotate(MakeVector3(1,0,0),
-ang);
leg1 = lower * leg1;
leg2 = lower * leg2;
model = renderer->RegisterModel
("Models/Player/Leg.kv6");
param.matrix = leg1 * scaler;
renderer->RenderModel(model, param);
param.matrix = leg2 * scaler;
renderer->RenderModel(model, param);
torso = Matrix4::Translate(0.f,0.f,-1.0f);
torso = lower * torso;
model = renderer->RegisterModel
("Models/Player/Torso.kv6");
param.matrix = torso * scaler;
renderer->RenderModel(model, param);
head = Matrix4::Translate(0.f,0.f,-0.0f);
head = torso * head;
arms = Matrix4::Translate(0.f,0.f,0.1f);
arms = torso * arms;
}
arms = arms * Matrix4::Rotate(MakeVector3(1,0,0),
pitch);
model = renderer->RegisterModel
("Models/Player/Arms.kv6");
param.matrix = arms * scaler;
renderer->RenderModel(model, param);
head = head * Matrix4::Rotate(MakeVector3(1,0,0),
pitch);
model = renderer->RegisterModel
("Models/Player/Head.kv6");
param.matrix = head * scaler;
renderer->RenderModel(model, param);
// draw tool
if(p->GetTool() == Player::ToolSpade){
WeaponInput inp = p->GetWeaponInput();
Matrix4 mat = Matrix4::Scale(0.05f);
if(inp.primary) {
float per = p->GetSpadeAnimationProgress();
per = 1.f - per;
mat = Matrix4::Rotate(MakeVector3(1, 0, 0),
-per * 0.7f) * mat;
}
mat = Matrix4::Translate(0.35f, -1.0f, 0.0f) * mat;
mat = arms * mat;
param.matrix = mat;
model = renderer->RegisterModel("Models/Weapons/Spade/Spade.kv6");
renderer->RenderModel(model, param);
}else if(p->GetTool() == Player::ToolBlock){
Matrix4 mat = Matrix4::Scale(0.05f);
mat = Matrix4::Translate(0.35f, -1.0f, 0.0f) * mat;
mat = arms * mat;
param.matrix = mat;
param.customColor = MakeVector3(p->GetBlockColor()) / 255.f;
model = renderer->RegisterModel("Models/Weapons/Block/Block2.kv6");
renderer->RenderModel(model, param);
}else if(p->GetTool() == Player::ToolGrenade){
Matrix4 mat = Matrix4::Scale(0.05f);
mat = Matrix4::Translate(0.35f, -1.0f, 0.0f) * mat;
mat = arms * mat;
param.matrix = mat;
model = renderer->RegisterModel("Models/Weapons/Grenade/Grenade.kv6");
renderer->RenderModel(model, param);
}else if(p->GetTool() == Player::ToolWeapon){
Matrix4 mat = Matrix4::Scale(0.05f);
mat = mat * Matrix4::Scale(-1, -1, 1);
mat = Matrix4::Translate(0.35f, -1.0f, 0.0f) * mat;
mat = arms * mat;
ModelRenderParam param;
param.matrix = mat;
std::string path = weapPrefix + "/Weapon.kv6";
model = renderer->RegisterModel(path.c_str());
renderer->RenderModel(model, param);
}
// draw intel in ctf
CTFGameMode *ctfMode = dynamic_cast<CTFGameMode *>(world->GetMode());
if(ctfMode){
int tId = p->GetTeamId();
if(tId < 3){
CTFGameMode::Team& team = ctfMode->GetTeam(p->GetTeamId());
if(team.hasIntel && team.carrier == p->GetId()){
IntVector3 col2 = world->GetTeam(1-p->GetTeamId()).color;
param.customColor = MakeVector3(col2.x/255.f,
col2.y/255.f,
col2.z/255.f);
Matrix4 mIntel = torso * Matrix4::Translate(0,0.6f,0.5f);
model = renderer->RegisterModel
("Models/MapObjects/Intel.kv6");
param.matrix = mIntel * scaler;
renderer->RenderModel(model, param);
param.customColor = MakeVector3(col.x/255.f,
col.y/255.f,
col.z/255.f);
}
}
}
if(false){
// draw hitbox
AddDebugObjectToScene(p->GetHitBoxes().torso);
AddDebugObjectToScene(p->GetHitBoxes().head);
AddDebugObjectToScene(p->GetHitBoxes().limbs[0]);
AddDebugObjectToScene(p->GetHitBoxes().limbs[1]);
AddDebugObjectToScene(p->GetHitBoxes().limbs[2]);
}
// third person player rendering, done
}
}
void Client::AddDebugObjectToScene(const spades::OBB3 &obb,
const Vector4& color) {
const Matrix4& mat = obb.m;
Vector3 v[2][2][2];
v[0][0][0] = (mat * MakeVector3(0,0,0)).GetXYZ();
v[0][0][1] = (mat * MakeVector3(0,0,1)).GetXYZ();
v[0][1][0] = (mat * MakeVector3(0,1,0)).GetXYZ();
v[0][1][1] = (mat * MakeVector3(0,1,1)).GetXYZ();
v[1][0][0] = (mat * MakeVector3(1,0,0)).GetXYZ();
v[1][0][1] = (mat * MakeVector3(1,0,1)).GetXYZ();
v[1][1][0] = (mat * MakeVector3(1,1,0)).GetXYZ();
v[1][1][1] = (mat * MakeVector3(1,1,1)).GetXYZ();
renderer->AddDebugLine(v[0][0][0], v[1][0][0], color);
renderer->AddDebugLine(v[0][0][1], v[1][0][1], color);
renderer->AddDebugLine(v[0][1][0], v[1][1][0], color);
renderer->AddDebugLine(v[0][1][1], v[1][1][1], color);
renderer->AddDebugLine(v[0][0][0], v[0][1][0], color);
renderer->AddDebugLine(v[0][0][1], v[0][1][1], color);
renderer->AddDebugLine(v[1][0][0], v[1][1][0], color);
renderer->AddDebugLine(v[1][0][1], v[1][1][1], color);
renderer->AddDebugLine(v[0][0][0], v[0][0][1], color);
renderer->AddDebugLine(v[0][1][0], v[0][1][1], color);
renderer->AddDebugLine(v[1][0][0], v[1][0][1], color);
renderer->AddDebugLine(v[1][1][0], v[1][1][1], color);
}
void Client::DrawCTFObjects() {
SPADES_MARK_FUNCTION();
CTFGameMode *mode = dynamic_cast<CTFGameMode *>(world->GetMode());
int tId;
IModel *base = renderer->RegisterModel("Models/MapObjects/CheckPoint.kv6");
IModel *intel = renderer->RegisterModel("Models/MapObjects/Intel.kv6");
for(tId = 0; tId < 2; tId++){
CTFGameMode::Team& team = mode->GetTeam(tId);
IntVector3 col = world->GetTeam(tId).color;
Vector3 color = {
col.x / 255.f, col.y / 255.f, col.z / 255.f
};
ModelRenderParam param;
param.customColor = color;
// draw base
param.matrix = Matrix4::Translate(team.basePos);
param.matrix = param.matrix * Matrix4::Scale(.3f);
renderer->RenderModel(base, param);
// draw flag
if(!mode->GetTeam(1-tId).hasIntel){
param.matrix = Matrix4::Translate(team.flagPos);
param.matrix = param.matrix * Matrix4::Scale(.1f);
renderer->RenderModel(intel, param);
}
}
}
void Client::DrawTCObjects() {
SPADES_MARK_FUNCTION();
TCGameMode *mode = dynamic_cast<TCGameMode *>(world->GetMode());
int tId;
IModel *base = renderer->RegisterModel("Models/MapObjects/CheckPoint.kv6");
int cnt = mode->GetNumTerritories();
for(tId = 0; tId < cnt; tId++){
TCGameMode::Territory *t = mode->GetTerritory(tId);
IntVector3 col;
if(t->ownerTeamId == 2){
col = IntVector3::Make(255, 255, 255);
}else{
col = world->GetTeam(t->ownerTeamId).color;
}
Vector3 color = {
col.x / 255.f, col.y / 255.f, col.z / 255.f
};
ModelRenderParam param;
param.customColor = color;
// draw base
param.matrix = Matrix4::Translate(t->pos);
param.matrix = param.matrix * Matrix4::Scale(.3f);
renderer->RenderModel(base, param);
}
}
void Client::RemoveAllCorpses(){
SPADES_MARK_FUNCTION();
std::list<Corpse *>::iterator it;
for(it = corpses.begin(); it != corpses.end(); it++)
delete *it;
corpses.clear();
lastMyCorpse = NULL;
}
void Client::RemoveAllLocalEntities(){
SPADES_MARK_FUNCTION();
std::list<ILocalEntity *>::iterator it;
for(it = localEntities.begin(); it != localEntities.end(); it++)
delete *it;
localEntities.clear();
}
void Client::RemoveInvisibleCorpses(){
SPADES_MARK_FUNCTION();
std::vector<std::list<Corpse *>::iterator> its;
std::list<Corpse *>::iterator it;
int cnt = (int)corpses.size() - corpseSoftLimit;
for(it = corpses.begin(); it != corpses.end(); it++){
if(cnt <= 0)
break;
Corpse *c = *it;
if(!c->IsVisibleFrom(lastSceneDef.viewOrigin)){
if(c == lastMyCorpse)
lastMyCorpse = NULL;
delete c;
its.push_back(it);
}
cnt--;
}
for(size_t i = 0; i < its.size(); i++)
corpses.erase(its[i]);
}
void Client::DrawScene(){
SPADES_MARK_FUNCTION();
renderer->StartScene(lastSceneDef);
if(world){
Player *p = world->GetLocalPlayer();
for(int i = 0; i < world->GetNumPlayerSlots(); i++)
if(world->GetPlayer(i))
AddPlayerToScene(world->GetPlayer(i));
std::vector<Grenade *> nades = world->GetAllGrenades();
for(size_t i = 0; i < nades.size(); i++){
AddGrenadeToScene(nades[i]);
}
{
std::list<Corpse *>::iterator it;
for(it = corpses.begin(); it != corpses.end(); it++){
Vector3 center = (*it)->GetCenter();
if((center - lastSceneDef.viewOrigin).GetPoweredLength() > 150.f * 150.f)
continue;
(*it)->AddToScene();
}
}
if(dynamic_cast<CTFGameMode *>(world->GetMode())){
DrawCTFObjects();
}
if(dynamic_cast<TCGameMode *>(world->GetMode())){
DrawTCObjects();
}
{
std::list<ILocalEntity *>::iterator it;
for(it = localEntities.begin(); it != localEntities.end(); it++){
(*it)->Render3D();
}
}
// draw block cursor
// FIXME: don't use debug line
if(p){
if(p->IsBlockCursorActive()){
std::vector<IntVector3> blocks;
if(p->IsBlockCursorDragging()){
blocks = world->CubeLine(p->GetBlockCursorDragPos(),
p->GetBlockCursorPos(),
256);
}else{
blocks.push_back(p->GetBlockCursorPos());
}
Vector4 color = {1,1,1,1};
if(blocks.size() > (int)p->GetNumBlocks())
color = MakeVector4(1,0,0,1);
for(size_t i = 0; i < blocks.size(); i++){
IntVector3& v = blocks[i];
renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z),
MakeVector3(v.x+1, v.y, v.z),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y+1, v.z),
MakeVector3(v.x+1, v.y+1, v.z),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z+1),
MakeVector3(v.x+1, v.y, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y+1, v.z+1),
MakeVector3(v.x+1, v.y+1, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z),
MakeVector3(v.x+1, v.y, v.z),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z),
MakeVector3(v.x, v.y+1, v.z),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z+1),
MakeVector3(v.x, v.y+1, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x+1, v.y, v.z),
MakeVector3(v.x+1, v.y+1, v.z),
color);
renderer->AddDebugLine(MakeVector3(v.x+1, v.y, v.z+1),
MakeVector3(v.x+1, v.y+1, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y, v.z),
MakeVector3(v.x, v.y, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x, v.y+1, v.z),
MakeVector3(v.x, v.y+1, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x+1, v.y, v.z),
MakeVector3(v.x+1, v.y, v.z+1),
color);
renderer->AddDebugLine(MakeVector3(v.x+1, v.y+1, v.z),
MakeVector3(v.x+1, v.y+1, v.z+1),
color);
}
}
}
}
for(size_t i = 0; i < flashDlights.size(); i++)
renderer->AddLight(flashDlights[i]);
flashDlightsOld.clear();
flashDlightsOld.swap(flashDlights);
// draw player hottrack
// FIXME: don't use debug line
{
Player *hottracked = HotTrackedPlayer();
if(hottracked){
IntVector3 col = world->GetTeam(hottracked->GetTeamId()).color;
Vector4 color;
color.x = col.x / 255.f;
color.y = col.y / 255.f;
color.z = col.z / 255.f;
color.w = 1.f;
Player::HitBoxes hb = hottracked->GetHitBoxes();
AddDebugObjectToScene(hb.head, color);
AddDebugObjectToScene(hb.torso, color);
AddDebugObjectToScene(hb.limbs[0], color);
AddDebugObjectToScene(hb.limbs[1], color);
AddDebugObjectToScene(hb.limbs[2], color);
}
}
renderer->EndScene();
}
Vector3 Client::Project(spades::Vector3 v){
v -= lastSceneDef.viewOrigin;
// transform to NDC
Vector3 v2;
v2.x = Vector3::Dot(v, lastSceneDef.viewAxis[0]);
v2.y = Vector3::Dot(v, lastSceneDef.viewAxis[1]);
v2.z = Vector3::Dot(v, lastSceneDef.viewAxis[2]);
float tanX = tanf(lastSceneDef.fovX * .5f);
float tanY = tanf(lastSceneDef.fovY * .5f);
v2.x /= tanX * v2.z;
v2.y /= tanY * v2.z;
// transform to IRenderer 2D coord
v2.x = (v2.x + 1.f) / 2.f * renderer->ScreenWidth();
v2.y = (-v2.y + 1.f) / 2.f * renderer->ScreenHeight();
return v2;
}
void Client::Draw2D(){
SPADES_MARK_FUNCTION();
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
IFont *font;
if(GetWorld()){
float wTime = world->GetTime();
{
std::list<ILocalEntity *>::iterator it;
for(it = localEntities.begin(); it != localEntities.end(); it++){
(*it)->Render2D();
}
}
Player *p = GetWorld()->GetLocalPlayer();
if(p){
if(wTime < lastHurtTime + .2f){
float per = (wTime - lastHurtTime) / .2f;
Vector3 color = {1.f, per, per};
renderer->MultiplyScreenColor(color);
}
Player *hottracked = HotTrackedPlayer();
if(hottracked){
Vector3 posxyz = Project(hottracked->GetEye());
Vector2 pos = {posxyz.x, posxyz.y};
float dist = (hottracked->GetEye() - p->GetEye()).GetLength();
int idist = (int)floorf(dist + .5f);
char buf[64];
sprintf(buf, "%s [%d%s]", hottracked->GetName().c_str(),
idist, (idist == 1) ? "block":"blocks");
font = textFont;
Vector2 size = font->Measure(buf);
pos.x -= size.x * .5f;
pos.y -= size.y;
font->Draw(buf,
pos + MakeVector2(1,1),
1.f, MakeVector4(0,0,0,0.5));
font->Draw(buf,
pos,
1.f, MakeVector4(1,1,1,1));
}
tcView->Draw();
if(p->IsAlive() && p->GetTeamId() < 2){
// draw damage ring
hurtRingView->Draw();
// draw sight
IImage *sight = renderer->RegisterImage("Gfx/Sight.tga");
renderer->SetColor(MakeVector4(1,1,1,1));
renderer->DrawImage(sight,
MakeVector2((scrWidth - sight->GetWidth()) * .5f,
(scrHeight - sight->GetHeight()) * .5f));
// draw ammo
Weapon *weap = p->GetWeapon();
IImage *ammoIcon;
float iconWidth, iconHeight;
float spacing = 2.f;
int stockNum;
int warnLevel;
if(p->IsToolWeapon()){
switch(weap->GetWeaponType()){
case RIFLE_WEAPON:
ammoIcon = renderer->RegisterImage("Gfx/Bullet/7.62mm.tga");
iconWidth = 6.f;
iconHeight = iconWidth * 4.f;
break;
case SMG_WEAPON:
ammoIcon = renderer->RegisterImage("Gfx/Bullet/9mm.tga");
iconWidth = 4.f;
iconHeight = iconWidth * 4.f;
break;
case SHOTGUN_WEAPON:
ammoIcon = renderer->RegisterImage("Gfx/Bullet/12gauge.tga");
iconWidth = 30.f;
iconHeight = iconWidth / 4.f;
spacing = -6.f;
break;
default:
SPInvalidEnum("weap->GetWeaponType()", weap->GetWeaponType());
}
int clipSize = weap->GetClipSize();
int clip = weap->GetAmmo();
for(int i = 0; i < clipSize; i++){
float x = scrWidth - 16.f - (float)(i+1) *
(iconWidth + spacing);
float y = scrHeight - 16.f - iconHeight;
if(clip >= i + 1){
renderer->SetColor(MakeVector4(1,1,1,1));
}else{
renderer->SetColor(MakeVector4(0.4,0.4,0.4,1));
}
renderer->DrawImage(ammoIcon,
AABB2(x,y,iconWidth,iconHeight));
}
stockNum = weap->GetStock();
warnLevel = weap->GetMaxStock() / 3;
}else{
iconHeight = 0.f;
warnLevel = 0;
switch(p->GetTool()){
case Player::ToolSpade:
case Player::ToolBlock:
stockNum = p->GetNumBlocks();
break;
case Player::ToolGrenade:
stockNum = p->GetNumGrenades();
break;
default:
SPInvalidEnum("p->GetTool()", p->GetTool());
}
}
Vector4 numberColor = {1, 1, 1, 1};
if(stockNum == 0){
numberColor.y = 0.3f;
numberColor.z = 0.3f;
}else if(stockNum <= warnLevel){
numberColor.z = 0.3f;
}
char buf[64];
sprintf(buf, "%d", stockNum);
font = designFont;
std::string stockStr = buf;
Vector2 size = font->Measure(stockStr);
Vector2 pos = MakeVector2(scrWidth - 16.f, scrHeight - 16.f - iconHeight);
pos -= size;
font->Draw(stockStr,
pos + MakeVector2(1,1),
1.f, MakeVector4(0,0,0,0.5));
font->Draw(stockStr,
pos,
1.f, numberColor);
// draw "press ... to reload"
{
std::string msg = "";
switch(p->GetTool()){
case Player::ToolBlock:
if(p->GetNumBlocks() == 0){
msg = "Out of Block";
}
break;
case Player::ToolGrenade:
if(p->GetNumGrenades() == 0){
msg = "Out of Grenade";
}
break;
case Player::ToolWeapon:
{
Weapon *weap = p->GetWeapon();
if(weap->IsReloading()){
msg = "Reloading";
}else if(weap->GetAmmo() == 0 &&
weap->GetStock() == 0){
msg = "Out of Ammo";
}else if(weap->GetStock() > 0 &&
weap->GetAmmo() < weap->GetClipSize() / 4){
msg = "Press [" + (std::string)cg_keyReloadWeapon + "] to Reload";
}
}
break;
default:;
// no message
}
if(!msg.empty()){
font = textFont;
Vector2 size = font->Measure(msg);
Vector2 pos = MakeVector2((scrWidth - size.x) * .5f,
scrHeight * 2.f / 3.f);
font->Draw(msg,
pos + MakeVector2(1,1),
1.f, MakeVector4(0,0,0,0.5));
font->Draw(msg,
pos,
1.f, MakeVector4(1,1,1,1));
}
}
if(p->GetTool() == Player::ToolBlock) {
paletteView->Draw();
}
// draw map
mapView->Draw();
// --- end of "player is alive" drawing
}else {
// draw respawn tme
if(!p->IsAlive()){
std::string msg;
float secs = p->GetRespawnTime() - world->GetTime();
char buf[64];
if(secs > 0.f)
sprintf(buf, "You will respawn in: %d", (int)ceilf(secs));
else
strcpy(buf, "Waiting for respawn");
msg = buf;
if(!msg.empty()){
font = textFont;
Vector2 size = font->Measure(msg);
Vector2 pos = MakeVector2((scrWidth - size.x) * .5f,
scrHeight / 3.f);
font->Draw(msg,
pos + MakeVector2(1,1),
1.f, MakeVector4(0,0,0,0.5));
font->Draw(msg,
pos,
1.f, MakeVector4(1,1,1,1));
}
}
// draw map
mapView->Draw();
}
// draw health
if(p->GetTeamId() < 2){
char buf[64];
sprintf(buf, "%d", p->GetHealth());
Vector4 numberColor = {1, 1, 1, 1};
if(p->GetHealth() == 0){
numberColor.y = 0.3f;
numberColor.z = 0.3f;
}else if(p->GetHealth() <= 50){
numberColor.z = 0.3f;
}
font = designFont;
std::string stockStr = buf;
Vector2 size = font->Measure(stockStr);
Vector2 pos = MakeVector2(16.f, scrHeight - 16.f);
pos.y -= size.y;
font->Draw(stockStr,
pos + MakeVector2(1,1),
1.f, MakeVector4(0,0,0,0.5));
font->Draw(stockStr,
pos,
1.f, numberColor);
}
if(IsFollowing()){
if(followingPlayerId == p->GetId()){
// just spectating
}else{
font = textFont;
std::string msg;
msg = "Following " + world->GetPlayerPersistent(followingPlayerId).name;
Vector2 size = font->Measure(msg);
Vector2 pos = MakeVector2(scrWidth - 8.f, scrHeight/3.f);
pos.x -= size.x;
font->Draw(msg,
pos + MakeVector2(1,1),
1.f, MakeVector4(0,0,0,0.5));
font->Draw(msg,
pos,
1.f, MakeVector4(1, 1, 1, 1));
}
}
chatWindow->Draw();
if(scoreboardVisible)
scoreboard->Draw();
// --- end "player is there" render
}else{
// world exists, but no local player: not joined
scoreboard->Draw();
}
if(IsLimboViewActive())
limbo->Draw();
// FIXME: when to draw chat editor?
if(chatEditing){
Vector2 pos;
pos.x = 8.f;
pos.y = scrHeight - 24.f;
std::string str;
if(chatGlobal)
str = "Global Chat: ";
else
str = "Team Chat: ";
str += chatText;
str += "_";
textFont->Draw(str, pos+MakeVector2(1, 1), 1.f,
MakeVector4(0,0,0,0.5));
textFont->Draw(str, pos, 1.f, MakeVector4(1,1,1,1));
}
}else{
// no world; loading?
// background
IImage *img;
float bgSize = std::max(scrWidth, scrHeight);
renderer->SetColor(MakeVector4(1, 1, 1, .1));
img = renderer->RegisterImage("Gfx/CircleGradient.png");
renderer->DrawImage(img, AABB2((scrWidth - bgSize) * .5f,
(scrHeight - bgSize) * .5f,
bgSize, bgSize));
renderer->SetColor(MakeVector4(1, 1, 1, .1));
img = renderer->RegisterImage("Gfx/White.tga");
renderer->DrawImage(img, AABB2(0,0,scrWidth,scrHeight));
// loading window
renderer->SetColor(MakeVector4(1, 1, 1, 1));
float wndX, wndY;
img = renderer->RegisterImage("Gfx/LoadingWindow.png");
wndX = (scrWidth - img->GetWidth()) * .5f;
wndY = (scrHeight - img->GetHeight()) * .5f;
wndX = floorf(wndX); wndY = floorf(wndY);
renderer->DrawImage(img, MakeVector2(wndX, wndY));
renderer->SetColor(MakeVector4(1, 1, 1, .2));
img = renderer->RegisterImage("Gfx/LoadingWindowGlow.png");
renderer->DrawImage(img, AABB2((scrWidth - 512.f) * .5f,
(scrHeight - 256.f) * .5f,
512.f, 256.f));
renderer->SetColor(MakeVector4(1, 1, 1, 1));
img = renderer->RegisterImage("Gfx/LoadingStripe.png");
float scrX = time * 32.f;
scrX = fmodf(scrX, 16.f);
renderer->DrawImage(img,
MakeVector2(wndX+11, wndY+37),
AABB2(-scrX, 0, 218,14));
std::string msg = net->GetStatusString();
font = textFont;
font->Draw(msg,
MakeVector2(wndX + 8.f,
wndY + 8.f),
1.f,
MakeVector4(0,0,0,1.f));
}
centerMessageView->Draw();
}
#pragma mark - Chat Messages
void Client::PlayerSentChatMessage(spades::client::Player *p,
bool global,
const std::string &msg){
std::string s;
if(global)
s = "[Global] ";
s += ChatWindow::TeamColorMessage(p->GetName(), p->GetTeamId());
s += ": ";
s += msg;
chatWindow->AddMessage(s);
if(!IsMuted()) {
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/Chat.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}
}
void Client::ServerSentMessage(const std::string &msg) {
chatWindow->AddMessage(msg);
if(msg.find(playerName) != std::string::npos){
printf("Mention: %s\n", msg.c_str());
}
}
void Client::ActivateChatTextEditor(bool global) {
chatEditing = true;
chatGlobal = global;
playerInput = PlayerInput();
weapInput = WeaponInput();
keypadInput = KeypadInput();
}
void Client::CloseChatTextEditor() {
chatEditing = false;
chatText.clear();
}
void Client::ChatCharEvent(const std::string& ch){
SPAssert(chatEditing);
chatText += ch;
}
void Client::ChatKeyEvent(const std::string &key) {
SPAssert(chatEditing);
if(key == "BackSpace"){
if(!chatText.empty())
chatText = chatText.substr(0, chatText.size() - 1);
}else if(key == "Enter"){
if(!chatText.empty()){
net->SendChat(chatText, chatGlobal);
}
CloseChatTextEditor();
}else if(key == "Left"){
// TODO: cursor
}else if(key == "Right"){
// TODO: cursor
}else if(key == "Escape"){
CloseChatTextEditor();
}
}
#pragma mark - Follow / Spectate
void Client::FollowNextPlayer() {
int myTeam = 2;
if(world->GetLocalPlayer()){
myTeam = world->GetLocalPlayer()->GetTeamId();
}
int nextId = followingPlayerId;
do{
nextId++;
if(nextId >= world->GetNumPlayerSlots())
nextId = 0;
Player *p = world->GetPlayer(nextId);
if(p == NULL)
continue;
if(myTeam < 2 && p->GetTeamId() != myTeam)
continue;
if(p->GetFront().GetPoweredLength() < .01f)
continue;
break;
}while(nextId != followingPlayerId);
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;
}
#pragma mark - Effects
Player *Client::HotTrackedPlayer(){
if(!world)
return NULL;
Player *p = world->GetLocalPlayer();
if(!p || !p->IsAlive())
return NULL;
if(ShouldRenderInThirdPersonView())
return NULL;
Vector3 origin = p->GetEye();
Vector3 dir = p->GetFront();
World::WeaponRayCastResult result = world->WeaponRayCast(origin,
dir,
p);
if(result.hit == false || result.player == NULL)
return NULL;
// don't hot track enemies (non-spectator only)
if(result.player->GetTeamId() != p->GetTeamId() &&
p->GetTeamId() < 2)
return NULL;
return result.player;
}
bool Client::IsMuted() {
// prevent to play loud sound at connection
return time < worldSetTime + .05f;
}
void Client::Bleed(spades::Vector3 v){
SPADES_MARK_FUNCTION();
if(!cg_blood)
return;
// distance cull
if((v - lastSceneDef.viewOrigin).GetPoweredLength() >
150.f * 150.f)
return;
//IImage *img = renderer->RegisterImage("Textures/SoftBall.tga");
IImage *img = renderer->RegisterImage("Gfx/White.tga");
Vector4 color = {0.5f, 0.02f, 0.04f, 1.f};
for(int i = 0; i < 10; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(v,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * 10.f,
1.f, 0.7f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + GetRandom()*GetRandom()*0.2f);
ent->SetLifeTime(3.f, 0.f, 1.f);
localEntities.push_back(ent);
}
color = MakeVector4(.7f, .35f, .37f, .6f);
for(int i = 0; i < 2; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 100.f);
ent->SetTrajectory(v,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * .7f,
.8f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.5f + GetRandom()*GetRandom()*0.2f,
2.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(.20f + GetRandom() * .2f, 0.06f, .20f);
localEntities.push_back(ent);
}
}
void Client::EmitBlockFragments(Vector3 origin,
IntVector3 c){
SPADES_MARK_FUNCTION();
// distance cull
float distPowered = (origin - lastSceneDef.viewOrigin).GetPoweredLength();
if(distPowered >
150.f * 150.f)
return;
IImage *img = renderer->RegisterImage("Gfx/White.tga");
Vector4 color = {c.x / 255.f,
c.y / 255.f, c.z / 255.f, 1.f};
for(int i = 0; i < 7; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * 7.f,
1.f, .9f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.2f + GetRandom()*GetRandom()*0.1f);
ent->SetLifeTime(2.f, 0.f, 1.f);
if(distPowered < 16.f * 16.f)
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
localEntities.push_back(ent);
}
if(distPowered <
32.f * 32.f){
for(int i = 0; i < 16; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * 12.f,
1.f, .9f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + GetRandom()*GetRandom()*0.14f);
ent->SetLifeTime(2.f, 0.f, 1.f);
if(distPowered < 16.f * 16.f)
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
localEntities.push_back(ent);
}
}
color += (MakeVector4(1, 1, 1, 1) - color) * .2f;
color.w *= .2f;
for(int i = 0; i < 2; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 100.f);
ent->SetTrajectory(origin,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * .7f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.6f + GetRandom()*GetRandom()*0.2f,
0.8f);
ent->SetLifeTime(.3f + GetRandom() * .3f, 0.06f, .4f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
localEntities.push_back(ent);
}
}
void Client::EmitBlockDestroyFragments(IntVector3 blk,
IntVector3 c){
SPADES_MARK_FUNCTION();
Vector3 origin = {blk.x + .5f, blk.y + .5f, blk.z + .5f};
// distance cull
if((origin - lastSceneDef.viewOrigin).GetPoweredLength() >
150.f * 150.f)
return;
IImage *img = renderer->RegisterImage("Gfx/White.tga");
Vector4 color = {c.x / 255.f,
c.y / 255.f, c.z / 255.f, 1.f};
for(int i = 0; i < 8; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom()) * 7.f,
1.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.3f + GetRandom()*GetRandom()*0.2f);
ent->SetLifeTime(2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
localEntities.push_back(ent);
}
}
void Client::MuzzleFire(spades::Vector3 origin,
spades::Vector3 dir,
bool local) {
DynamicLightParam l;
l.origin = origin;
l.radius = 5.f;
l.type = DynamicLightTypePoint;
l.color = MakeVector3(3.f, 1.6f, 0.5f);
flashDlights.push_back(l);
Vector4 color;
Vector3 velBias = {0, 0, -0.5f};
color = MakeVector4( .8f, .8f, .8f, .3f);
// rapid smoke
for(int i = 0; i < 2; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 120.f);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom())+velBias*.5f) * 0.3f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.2f,
7.f, 0.0000005f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(0.2f + GetRandom()*0.1f, 0.f, .30f);
localEntities.push_back(ent);
}
}
void Client::GrenadeExplosion(spades::Vector3 origin){
float dist = (origin - lastSceneDef.viewOrigin).GetLength();
if(dist > 170.f)
return;
grenadeVibration += 2.f / (dist + 5.f);
if(grenadeVibration > 1.f)
grenadeVibration = 1.f;
DynamicLightParam l;
l.origin = origin;
l.radius = 16.f;
l.type = DynamicLightTypePoint;
l.color = MakeVector3(3.f, 1.6f, 0.5f);
flashDlights.push_back(l);
Vector3 velBias = {0,0,0};
if(!map->ClipBox(origin.x, origin.y, origin.z)){
if(map->ClipBox(origin.x + 1.f, origin.y, origin.z)){
velBias.x -= 1.f;
}
if(map->ClipBox(origin.x - 1.f, origin.y, origin.z)){
velBias.x += 1.f;
}
if(map->ClipBox(origin.x, origin.y + 1.f, origin.z)){
velBias.y -= 1.f;
}
if(map->ClipBox(origin.x, origin.y - 1.f, origin.z)){
velBias.y += 1.f;
}
if(map->ClipBox(origin.x, origin.y , origin.z + 1.f)){
velBias.z -= 1.f;
}
if(map->ClipBox(origin.x, origin.y , origin.z - 1.f)){
velBias.z += 1.f;
}
}
Vector4 color;
color = MakeVector4( .8f, .8f, .8f, .6f);
// rapid smoke
for(int i = 0; i < 4; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 60.f);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom())+velBias*.5f) * 4.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.f + GetRandom()*GetRandom()*0.4f,
10.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(.1f + GetRandom()*0.02f, 0.f, .10f);
localEntities.push_back(ent);
}
// slow smoke
color.w = .25f;
for(int i = 0; i < 8; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 20.f);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
(GetRandom()-GetRandom()) * .2f)) * 2.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.4f + GetRandom()*GetRandom()*0.8f,
0.2f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(6.f + GetRandom() * 5.f, 0.1f, 8.f);
localEntities.push_back(ent);
}
// fragments
IImage *img = renderer->RegisterImage("Gfx/White.tga");
color = MakeVector4(0.01, 0.03, 0, 1.f);
for(int i = 0; i < 42; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
Vector3 dir = MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom());
dir += velBias * .5f;
float radius = 0.1f + GetRandom()*GetRandom()*0.2f;
ent->SetTrajectory(origin + dir * .2f,
dir * 20.f,
.1f + radius * 3.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(radius);
ent->SetLifeTime(3.5f + GetRandom() * 2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
localEntities.push_back(ent);
}
// fire smoke
color= MakeVector4(1.f, .6f, .2f, 1.f);
for(int i = 0; i < 4; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 60.f);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
GetRandom()-GetRandom())+velBias) * 12.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.f + GetRandom()*GetRandom()*0.4f,
6.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(.08f + GetRandom()*0.03f, 0.f, .10f);
ent->SetAdditive(true);
localEntities.push_back(ent);
}
}
void Client::GrenadeExplosionUnderwater(spades::Vector3 origin){
float dist = (origin - lastSceneDef.viewOrigin).GetLength();
if(dist > 170.f)
return;
grenadeVibration += 1.5f / (dist + 5.f);
if(grenadeVibration > 1.f)
grenadeVibration = 1.f;
Vector3 velBias = {0,0,0};
Vector4 color;
color = MakeVector4( .95f, .95f, .95f, .6f);
// water1
IImage *img = renderer->RegisterImage("Textures/WaterExpl.png");
for(int i = 0; i < 7; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
-GetRandom()*7.f)) * 2.5f,
.3f, .6f);
ent->SetRotation(0.f);
ent->SetRadius(1.5f + GetRandom()*GetRandom()*0.4f,
1.3f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(3.f + GetRandom()*0.3f, 0.f, .60f);
localEntities.push_back(ent);
}
// water2
img = renderer->RegisterImage("Textures/Fluid.png");
color.w = .9f;
for(int i = 0; i < 16; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
-GetRandom()*10.f)) * 3.5f,
1.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.9f + GetRandom()*GetRandom()*0.4f,
0.7f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(3.f + GetRandom()*0.3f, .7f, .60f);
localEntities.push_back(ent);
}
// slow smoke
color.w = .4f;
for(int i = 0; i < 8; i++){
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 20.f);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
(GetRandom()-GetRandom()) * .2f)) * 2.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.4f + GetRandom()*GetRandom()*0.8f,
0.2f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(6.f + GetRandom() * 5.f, 0.1f, 8.f);
localEntities.push_back(ent);
}
// fragments
img = renderer->RegisterImage("Gfx/White.tga");
color = MakeVector4(1,1,1, 0.7f);
for(int i = 0; i < 42; i++){
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(this, img, color);
Vector3 dir = MakeVector3(GetRandom()-GetRandom(),
GetRandom()-GetRandom(),
-GetRandom() * 3.f);
dir += velBias * .5f;
float radius = 0.1f + GetRandom()*GetRandom()*0.2f;
ent->SetTrajectory(origin + dir * .2f +
MakeVector3(0, 0, -1.2f),
dir * 13.f,
.1f + radius * 3.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(radius);
ent->SetLifeTime(3.5f + GetRandom() * 2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Delete);
localEntities.push_back(ent);
}
// TODO: wave?
}
#pragma mark - Server Packet Handlers
void Client::LocalPlayerCreated(){
followPos = world->GetLocalPlayer()->GetEye();
weapInput = WeaponInput();
playerInput = PlayerInput();
keypadInput = KeypadInput();
}
void Client::JoinedGame() {
// note: local player doesn't exist yet now
// tune for spectate mode
followingPlayerId = world->GetLocalPlayerIndex();
followPos = MakeVector3(256, 256, 30);
followVel = MakeVector3(0, 0, 0);
}
void Client::PlayerCreatedBlock(spades::client::Player *p){
SPADES_MARK_FUNCTION();
if(!IsMuted()) {
IAudioChunk *c = audioDevice->RegisterSound("Sounds/Weapons/Block/Build.wav");
audioDevice->Play(c, p->GetEye() + p->GetFront(),
AudioParam());
}
}
void Client::TeamCapturedTerritory(int teamId,
int terId) {
TCGameMode::Territory *ter = static_cast<TCGameMode *>(world->GetMode())->GetTerritory(terId);
int old = ter->ownerTeamId;
std::string msg;
msg = chatWindow->TeamColorMessage(world->GetTeam(teamId).name,
teamId);
msg += " captured ";
if(old < 2){
msg += chatWindow->TeamColorMessage(world->GetTeam(old).name,
old);
msg += "'s territory";
}else{
msg += "an neutral territory";
}
chatWindow->AddMessage(msg);
msg = world->GetTeam(teamId).name;
msg += " captured ";
if(old < 2){
msg += world->GetTeam(old).name;
msg += "'s Territory";
}else{
msg += "an Neutral Territory";
}
centerMessageView->AddMessage(msg);
if(world->GetLocalPlayer() && !IsMuted()){
if(teamId == world->GetLocalPlayer()->GetTeamId()){
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/TC/YourTeamCaptured.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}else{
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/TC/EnemyCaptured.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}
}
}
void Client::PlayerCapturedIntel(spades::client::Player *p){
std::string msg;
msg = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
msg += " captured ";
msg += chatWindow->TeamColorMessage(world->GetTeam(1 - p->GetTeamId()).name,
1 - p->GetTeamId());
msg += "'s intel";
chatWindow->AddMessage(msg);
msg = p->GetName();
msg += " captured ";
msg += world->GetTeam(1 - p->GetTeamId()).name;
msg += "'s Intel.";
centerMessageView->AddMessage(msg);
if(world->GetLocalPlayer() && !IsMuted()){
if(p->GetTeamId() == world->GetLocalPlayer()->GetTeamId()){
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/CTF/YourTeamCaptured.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}else{
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/CTF/EnemyCaptured.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}
}
}
void Client::PlayerPickedIntel(spades::client::Player *p) {
std::string msg;
msg = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
msg += " picked up ";
msg += chatWindow->TeamColorMessage(world->GetTeam(1 - p->GetTeamId()).name,
1 - p->GetTeamId());
msg += "'s intel";
chatWindow->AddMessage(msg);
msg = p->GetName();
msg += " picked up ";
msg += world->GetTeam(1 - p->GetTeamId()).name;
msg += "'s intel.";
centerMessageView->AddMessage(msg);
if(!IsMuted()) {
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/CTF/PickedUp.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}
}
void Client::PlayerDropIntel(spades::client::Player *p) {
std::string msg;
msg = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
msg += " dropped ";
msg += chatWindow->TeamColorMessage(world->GetTeam(1 - p->GetTeamId()).name,
1 - p->GetTeamId());
msg += "'s intel";
chatWindow->AddMessage(msg);
msg = p->GetName();
msg += " dropped ";
msg += world->GetTeam(1 - p->GetTeamId()).name;
msg += "'s intel.";
centerMessageView->AddMessage(msg);
}
void Client::PlayerDestroyedBlockWithWeaponOrTool(spades::IntVector3 blk){
Vector3 origin = {blk.x + .5f, blk.y + .5f, blk.z + .5f};
if(!map->IsSolid(blk.x, blk.y, blk.z))
return;;
IAudioChunk *c = audioDevice->RegisterSound("Sounds/Misc/BlockDestroy.wav");
if(!IsMuted()){
audioDevice->Play(c, origin,
AudioParam());
}
uint32_t col = map->GetColor(blk.x, blk.y, blk.z);
IntVector3 colV = {(uint8_t)col,
(uint8_t)(col >> 8), (uint8_t)(col >> 16)};
EmitBlockDestroyFragments(blk, colV);
}
void Client::PlayerDiggedBlock(spades::IntVector3 blk){
Vector3 origin = {blk.x + .5f, blk.y + .5f, blk.z + .5f};
IAudioChunk *c = audioDevice->RegisterSound("Sounds/Misc/BlockDestroy.wav");
if(!IsMuted()){
audioDevice->Play(c, origin,
AudioParam());
}
for(int z = blk.z - 1 ; z <= blk.z + 1; z++){
if(z < 0 || z > 61)
continue;
if(!map->IsSolid(blk.x, blk.y, z))
continue;
uint32_t col = map->GetColor(blk.x, blk.y, z);
IntVector3 colV = {(uint8_t)col,
(uint8_t)(col >> 8), (uint8_t)(col >> 16)};
EmitBlockDestroyFragments(IntVector3::Make(blk.x, blk.y, z), colV);
}
}
void Client::GrenadeDestroyedBlock(spades::IntVector3 blk){
for(int x = blk.x - 1; x <= blk.x + 1; x++)
for(int y = blk.y - 1; y <= blk.y + 1; y++)
for(int z = blk.z - 1 ; z <= blk.z + 1; z++){
if(z < 0 || z > 61 || x < 0 || x >= 512 ||
y < 0 || y >= 512)
continue;
if(!map->IsSolid(x, y, z))
continue;
uint32_t col = map->GetColor(x, y, z);
IntVector3 colV = {(uint8_t)col,
(uint8_t)(col >> 8), (uint8_t)(col >> 16)};
EmitBlockDestroyFragments(IntVector3::Make(x, y, z), colV);
}
}
void Client::TeamWon(int teamId){
std::string msg;
msg = chatWindow->TeamColorMessage(world->GetTeam(teamId).name,
teamId);
msg += " wins!";
chatWindow->AddMessage(msg);
msg = world->GetTeam(teamId).name;
msg += " wins!";
centerMessageView->AddMessage(msg);
if(world->GetLocalPlayer()){
if(teamId == world->GetLocalPlayer()->GetTeamId()){
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/Win.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}else{
IAudioChunk *chunk = audioDevice->RegisterSound("Sounds/Feedback/Lose.wav");
audioDevice->PlayLocal(chunk, AudioParam());
}
}
}
#pragma mark - IWorldListener Handlers
void Client::PlayerJumped(spades::client::Player *p){
SPADES_MARK_FUNCTION();
if(!IsMuted()){
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()){
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"
};
const char *wsnds[] = {
"Sounds/Player/Wade1.wav",
"Sounds/Player/Wade2.wav",
"Sounds/Player/Wade3.wav",
"Sounds/Player/Wade4.wav"
};
IAudioChunk *c = p->GetWade() ?
audioDevice->RegisterSound(wsnds[rand() % 4]):
audioDevice->RegisterSound(snds[rand() % 4]);
audioDevice->Play(c, p->GetOrigin(),
AudioParam());
}
}
void Client::PlayerFiredWeapon(spades::client::Player *p) {
SPADES_MARK_FUNCTION();
Vector3 muzzle;
// make dlight
{
Vector3 vec;
Matrix4 eyeMatrix = Matrix4::FromAxis(-p->GetRight(),
p->GetFront(),
-p->GetUp(),
p->GetEye());
Matrix4 mat;
mat = Matrix4::Translate(-0.13f,
.5f,
0.2f);
mat = eyeMatrix * mat;
vec = (mat * MakeVector3(0, 1, 0)).GetXYZ();
muzzle = vec;
MuzzleFire(vec, p->GetFront(), p == world->GetLocalPlayer());
}
if(cg_ejectBrass){
float dist = (p->GetOrigin() - lastSceneDef.viewOrigin).GetPoweredLength();
if(dist < 130.f * 130.f) {
IModel *model = NULL;
IAudioChunk *snd = NULL;
IAudioChunk *snd2 = NULL;
switch(p->GetWeapon()->GetWeaponType()){
case RIFLE_WEAPON:
model = renderer->RegisterModel("Models/Weapons/Rifle/Casing.kv6");
snd = (rand()&0x1000)?
audioDevice->RegisterSound("Sounds/Weapons/Rifle/ShellDrop1.wav"):
audioDevice->RegisterSound("Sounds/Weapons/Rifle/ShellDrop2.wav");
snd2 =
audioDevice->RegisterSound("Sounds/Weapons/Rifle/ShellWater.wav");
break;
case SHOTGUN_WEAPON:
// FIXME: don't want to show shotgun't casing
// because it isn't ejected when firing
//model = renderer->RegisterModel("Models/Weapons/Shotgun/Casing.kv6");
break;
case SMG_WEAPON:
model = renderer->RegisterModel("Models/Weapons/SMG/Casing.kv6");
snd = (rand()&0x1000)?
audioDevice->RegisterSound("Sounds/Weapons/SMG/ShellDrop1.wav"):
audioDevice->RegisterSound("Sounds/Weapons/SMG/ShellDrop2.wav");
snd2 =
audioDevice->RegisterSound("Sounds/Weapons/SMG/ShellWater.wav");
break;
}
if(model){
Vector3 origin;
origin = muzzle - p->GetFront() * 0.5f;
Vector3 vel;
vel = p->GetFront() * 0.5f + p->GetRight() +
p->GetUp() * 0.2f;
switch(p->GetWeapon()->GetWeaponType()){
case SMG_WEAPON:
vel -= p->GetFront() * 0.7f;
break;
case SHOTGUN_WEAPON:
vel *= .5f;
break;
default:
break;
}
ILocalEntity *ent;
ent = new GunCasing(this, model, snd, snd2,
origin, p->GetFront(),
vel);
AddLocalEntity(ent);
}
}
}
if(!IsMuted()){
IAudioChunk *c;
std::string weapPrefix = GetWeaponPrefix("Sounds", p->GetWeapon()->GetWeaponType());
bool isLocal = p == world->GetLocalPlayer();
c = isLocal ?
audioDevice->RegisterSound((weapPrefix + "/FireLocal.wav").c_str()):
audioDevice->RegisterSound((weapPrefix + "/Fire.wav").c_str());
AudioParam param;
param.volume = 8.f;
if(isLocal)
audioDevice->PlayLocal(c,MakeVector3(.4f, -.3f, .5f),
param);
else
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
- p->GetUp() * .3f
+ p->GetRight() * .4f,
param);
if(isLocal)
localFireVibrationTime = time;
// play far sound
c = audioDevice->RegisterSound((weapPrefix + "/FireFar.wav").c_str());
param.volume = 1.0f;
//if(p->GetWeapon()->GetWeaponType() == SMG_WEAPON)
// param.volume *= .3f;
param.referenceDistance = 10.f;
if(isLocal)
audioDevice->PlayLocal(c,MakeVector3(.4f, -.3f, .5f),
param);
else
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
- p->GetUp() * .3f
+ p->GetRight() * .4f,
param);
c = audioDevice->RegisterSound((weapPrefix + "/FireStereo.wav").c_str());
if(isLocal)
audioDevice->PlayLocal(c,MakeVector3(.4f, -.3f, .5f),
param);
else
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
- p->GetUp() * .3f
+ p->GetRight() * .4f,
param);
if(p->GetWeapon()->GetWeaponType() == SMG_WEAPON){
switch((rand() >> 8) & 3) {
case 0:
c = audioDevice->RegisterSound((weapPrefix + "/Mech1.wav").c_str());
break;
case 1:
c = audioDevice->RegisterSound((weapPrefix + "/Mech2.wav").c_str());
break;
case 2:
c = audioDevice->RegisterSound((weapPrefix + "/Mech3.wav").c_str());
break;
case 3:
c = audioDevice->RegisterSound((weapPrefix + "/Mech4.wav").c_str());
break;
}
param.volume = 1.6f;
if(isLocal)
audioDevice->PlayLocal(c,MakeVector3(.4f, -.3f, .5f),
param);
else
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
- p->GetUp() * .3f
+ p->GetRight() * .4f,
param);
}
}
}
void Client::PlayerDryFiredWeapon(spades::client::Player *p) {
SPADES_MARK_FUNCTION();
if(!IsMuted()){
bool isLocal = p == world->GetLocalPlayer();
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();
if(!IsMuted()){
IAudioChunk *c;
std::string weapPrefix = GetWeaponPrefix("Sounds", p->GetWeapon()->GetWeaponType());
bool isLocal = p == world->GetLocalPlayer();
c = isLocal ?
audioDevice->RegisterSound((weapPrefix+"/ReloadLocal.wav").c_str()):
audioDevice->RegisterSound((weapPrefix+"/Reload.wav").c_str());
AudioParam param;
param.volume = 0.2f;
if(isLocal)
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
param);
else
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
- p->GetUp() * .3f
+ p->GetRight() * .2f,
param);
}
}
void Client::PlayerReloadedWeapon(spades::client::Player *p){
SPADES_MARK_FUNCTION();
bool plays = false;
if(p->GetWeapon()->IsReloadSlow()){
Weapon *w = p->GetWeapon();
if(w->GetAmmo() == w->GetClipSize() ||
w->GetStock() == 0)
plays = true;
}
if(!plays)
return;
if(!IsMuted()){
IAudioChunk *c;
std::string weapPrefix = GetWeaponPrefix("Sounds", p->GetWeapon()->GetWeaponType());
bool isLocal = p == world->GetLocalPlayer();
c = isLocal ?
audioDevice->RegisterSound((weapPrefix+"/CockLocal.wav").c_str()):
audioDevice->RegisterSound((weapPrefix+"/Cock.wav").c_str());
AudioParam param;
param.volume = 0.2f;
if(isLocal)
audioDevice->PlayLocal(c, MakeVector3(.4f, -.3f, .5f),
param);
else
audioDevice->Play(c, p->GetEye() + p->GetFront() * 0.5f
- p->GetUp() * .3f
+ p->GetRight() * .2f,
param);
}
}
void Client::PlayerChangedTool(spades::client::Player *p){
SPADES_MARK_FUNCTION();
if(!IsMuted()){
bool isLocal = p == world->GetLocalPlayer();
IAudioChunk *c = isLocal ?
audioDevice->RegisterSound("Sounds/Weapons/SwitchLocal.wav"):
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();
IAudioChunk *c = isLocal ?
audioDevice->RegisterSound("Sounds/Weapons/SwitchLocal.wav"):
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::PlayerThrownGrenade(spades::client::Player *p, Grenade *g){
SPADES_MARK_FUNCTION();
if(!IsMuted()){
bool isLocal = p == world->GetLocalPlayer();
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();
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::PlayerHitPlayerWithSpade(spades::client::Player *p){
SPADES_MARK_FUNCTION();
SPRaise("Obsolete function called");
}
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);
if(!IsMuted()){
bool isLocal = p == world->GetLocalPlayer();
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()){
IAudioChunk *c =
audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh.wav");
audioDevice->Play(c, victim->GetEye(),
AudioParam());
}
}
}
// 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());
corpses.push_back(corp);
if(corpses.size() > corpseHardLimit){
corp = corpses.front();
delete corp;
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 = " [";
switch(kt){
case KillTypeClassChange:
cause += "Weapon Change";
break;
case KillTypeFall:
cause += "Fall";
break;
case KillTypeGrenade:
cause += "Grenade";
break;
case KillTypeHeadshot:
cause += "Headshot";
break;
case KillTypeMelee:
cause += "Spade";
break;
case KillTypeTeamChange:
cause += "Team Change";
break;
case KillTypeWeapon:
cause += killer->GetWeapon()->GetName();
break;
}
cause += "] ";
if(ff)
s += ChatWindow::ColoredMessage(cause, MsgColorRed);
else
s += cause;
if(killer != victim){
s += ChatWindow::TeamColorMessage(victim->GetName(),
victim->GetTeamId());
}
chatWindow->AddMessage(s);
// show big message if player is involved
if(victim != killer){
if(killer == world->GetLocalPlayer()){
char buf[256];
sprintf(buf, "You have killed %s", victim->GetName().c_str());
centerMessageView->AddMessage(buf);
}
}
}
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){
IAudioChunk *c =
audioDevice->RegisterSound("Sounds/Weapons/Spade/HitPlayer.wav");
audioDevice->Play(c, hitPos,
AudioParam());
}else{
IAudioChunk *c =
audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh.wav");
audioDevice->Play(c, hitPos,
AudioParam());
}
}
if(by == world->GetLocalPlayer() &&
hurtPlayer){
net->SendHit(hurtPlayer->GetId(), type);
}
}
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;
EmitBlockFragments(shiftedHitPos, colV);
if(!IsMuted()){
IAudioChunk *c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Hit.wav");
audioDevice->Play(c, shiftedHitPos,
AudioParam());
}
}
void Client::BlocksFell(std::vector<IntVector3> blocks) {
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;
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()){
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()){
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()){
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 = 40.f;
audioDevice->Play(c, g->GetPosition(),
param);
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/WaterExplodeStereo.wav");
param.volume = 40.f;
audioDevice->Play(c, g->GetPosition(),
param);
}
GrenadeExplosionUnderwater(g->GetPosition());
}else{
GrenadeExplosion(g->GetPosition());
if(!IsMuted()){
IAudioChunk *c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Explode.wav");
AudioParam param;
param.volume = 10.f;
audioDevice->Play(c, g->GetPosition(),
param);
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/ExplodeFar.wav");
param.volume = 40.f;
audioDevice->Play(c, g->GetPosition(),
param);
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/ExplodeStereo.wav");
param.volume = 40.f;
audioDevice->Play(c, g->GetPosition(),
param);
// debri sound
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Debris.wav");
param.volume = 5.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()){
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){
net->SendBlockAction(v, type);
}
void Client::LocalPlayerCreatedLineBlock(spades::IntVector3 v1, spades::IntVector3 v2) {
net->SendBlockLine(v1, v2);
}
void Client::LocalPlayerHurt(HurtType type,
bool sourceGiven,
spades::Vector3 source) {
SPADES_MARK_FUNCTION();
if(sourceGiven){
Player * p = world->GetLocalPlayer();
if(!p)
return;
Vector3 rel = source - p->GetEye();
rel.z = 0.f; rel = rel.Normalize();
hurtRingView->Add(rel);
}
}
}
}