Client::~Client() { SPADES_MARK_FUNCTION(); NetLog("Disconnecting"); if(logStream) { SPLog("Closing netlog"); logStream.reset(); } if(net){ SPLog("Disconnecting"); net->Disconnect(); net.reset(); } SPLog("Disconnected"); RemoveAllLocalEntities(); RemoveAllCorpses(); renderer->SetGameMap(nullptr); audioDevice->SetGameMap(nullptr); for(size_t i = 0; i < clientPlayers.size(); i++) { if(clientPlayers[i]) { clientPlayers[i]->Invalidate(); } } clientPlayers.clear(); scriptedUI->ClientDestroyed(); tcView.reset(); limbo.reset(); scoreboard.reset(); mapView.reset(); largeMapView.reset(); chatWindow.reset(); killfeedWindow.reset(); paletteView.reset(); centerMessageView.reset(); hurtRingView.reset(); designFont.Set(nullptr); textFont.Set(nullptr); bigTextFont.Set(nullptr); world.reset(); }
void Client::PlayerDropIntel(spades::client::Player *p) { std::string msg; { std::string holderName = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId()); std::string otherTeamName = chatWindow->TeamColorMessage(world->GetTeam(1 - p->GetTeamId()).name, 1 - p->GetTeamId()); msg = _Tr("Client", "{0} dropped {1}'s intel", holderName, otherTeamName); chatWindow->AddMessage(msg); } if ((int)cg_centerMessage != 0){ std::string holderName = p->GetName(); std::string otherTeamName = world->GetTeam(1 - p->GetTeamId()).name; msg = _Tr("Client", "{0} dropped {1}'s Intel", holderName, otherTeamName); NetLog("%s", msg.c_str()); centerMessageView->AddMessage(msg); } }
void Client::PlayerLeaving(spades::client::Player *p) { { std::string msg; msg = _Tr("Client", "Player {0} has left", chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId())); chatWindow->AddMessage(msg); } { std::string msg; msg = _Tr("Client", "Player {0} has left", p->GetName()); auto col = p->GetTeamId() < 2 ? world->GetTeam(p->GetTeamId()).color : IntVector3::Make(255, 255, 255); NetLog("%s", msg.c_str()); scriptedUI->RecordChatLog(msg, MakeVector4(col.x / 255.f, col.y / 255.f, col.z / 255.f, 0.8f)); } }
void Client::ServerSentMessage(const std::string &msg) { NetLog("%s", msg.c_str()); scriptedUI->RecordChatLog(msg, Vector4::Make(1.f, 1.f, 1.f, 0.8f)); if(cg_serverAlert) { if(msg.substr(0, 3) == "N% ") { ShowAlert(msg.substr(3), AlertType::Notice); return; } if(msg.substr(0, 3) == "!% ") { ShowAlert(msg.substr(3), AlertType::Error); return; } if(msg.substr(0, 3) == "%% ") { ShowAlert(msg.substr(3), AlertType::Warning); return; } } chatWindow->AddMessage(msg); }
void Client::TeamCapturedTerritory(int teamId, int terId) { TCGameMode::Territory *ter = static_cast<TCGameMode *>(world->GetMode())->GetTerritory(terId); int old = ter->ownerTeamId; std::string msg; std::string teamName = chatWindow->TeamColorMessage(world->GetTeam(teamId).name, teamId); if(old < 2){ std::string otherTeam = chatWindow->TeamColorMessage(world->GetTeam(old).name, old); msg = _Tr("Client", "{0} captured {1}'s territory", teamName, otherTeam); }else{ msg = _Tr("Client", "{0} captured an neutral territory", teamName); } chatWindow->AddMessage(msg); if ((int)cg_centerMessage != 0){ teamName = world->GetTeam(teamId).name; if (old < 2){ std::string otherTeam = world->GetTeam(old).name; msg = _Tr("Client", "{0} captured {1}'s Territory", teamName, otherTeam); } else{ msg = _Tr("Client", "{0} captured an Neutral Territory", teamName); } NetLog("%s", msg.c_str()); centerMessageView->AddMessage(msg); } if(world->GetLocalPlayer() && !IsMuted()){ if(teamId == world->GetLocalPlayer()->GetTeamId()){ Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/TC/YourTeamCaptured.wav"); audioDevice->PlayLocal(chunk, AudioParam()); }else{ Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/TC/EnemyCaptured.wav"); audioDevice->PlayLocal(chunk, AudioParam()); } } }
void Client::PlayerPickedIntel(spades::client::Player *p) { std::string msg; { std::string holderName = chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId()); std::string otherTeamName = chatWindow->TeamColorMessage(world->GetTeam(1 - p->GetTeamId()).name, 1 - p->GetTeamId()); msg = _Tr("Client", "{0} picked up {1}'s intel", holderName, otherTeamName); chatWindow->AddMessage(msg); } if ((int)cg_centerMessage != 0){ std::string holderName = p->GetName(); std::string otherTeamName = world->GetTeam(1 - p->GetTeamId()).name; msg = _Tr("Client", "{0} picked up {1}'s Intel.", holderName, otherTeamName); NetLog("%s", msg.c_str()); centerMessageView->AddMessage(msg); } if(!IsMuted()) { Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/CTF/PickedUp.wav"); audioDevice->PlayLocal(chunk, AudioParam()); } }
void Client::TeamWon(int teamId){ std::string msg; msg = chatWindow->TeamColorMessage(world->GetTeam(teamId).name, teamId); msg = _Tr("Client", "{0} wins!", msg); chatWindow->AddMessage(msg); msg = world->GetTeam(teamId).name; msg = _Tr("Client", "{0} Wins!", msg); NetLog("%s", msg.c_str()); centerMessageView->AddMessage(msg); scriptedUI->RecordChatLog(msg, MakeVector4(1.f, 1.f, 1.f, 0.8f)); if(world->GetLocalPlayer()){ if(teamId == world->GetLocalPlayer()->GetTeamId()){ Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Win.wav"); audioDevice->PlayLocal(chunk, AudioParam()); }else{ Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Lose.wav"); audioDevice->PlayLocal(chunk, AudioParam()); } } }
void Client::RunFrame(float dt) { SPADES_MARK_FUNCTION(); fpsCounter.MarkFrame(); if(frameToRendererInit > 0){ // waiting for renderer initialization DrawStartupScreen(); frameToRendererInit--; if(frameToRendererInit == 0){ DoInit(); }else{ return; } } timeSinceInit += std::min(dt, .03f); // update network try{ if(net->GetStatus() == NetClientStatusConnected) net->DoEvents(0); else net->DoEvents(10); }catch(const std::exception& ex){ if(net->GetStatus() == NetClientStatusNotConnected){ SPLog("Disconnected because of error:\n%s", ex.what()); NetLog("Disconnected because of error:\n%s", ex.what()); throw; }else{ SPLog("Exception while processing network packets (ignored):\n%s", ex.what()); } } hurtRingView->Update(dt); centerMessageView->Update(dt); mapView->Update(dt); largeMapView->Update(dt); UpdateAutoFocus(dt); if(world){ UpdateWorld(dt); }else{ renderer->SetFogColor(MakeVector3(0.f, 0.f, 0.f)); } chatWindow->Update(dt); killfeedWindow->Update(dt); limbo->Update(dt); // CreateSceneDefinition also can be used for sounds SceneDefinition sceneDef = CreateSceneDefinition(); lastSceneDef = sceneDef; // Update sounds try{ audioDevice->Respatialize(sceneDef.viewOrigin, sceneDef.viewAxis[2], sceneDef.viewAxis[1]); }catch(const std::exception& ex){ SPLog("Audio subsystem returned error (ignored):\n%s", ex.what()); } // render scene DrawScene(); // draw 2d Draw2D(); // draw scripted GUI scriptedUI->RunFrame(dt); if(scriptedUI->WantsClientToBeClosed()) readyToClose = true; // Well done! renderer->FrameDone(); renderer->Flip(); // reset all "delayed actions" (in case we forget to reset these) hasDelayedReload = false; time += dt; }
void Client::SetWorld(spades::client::World *w) { SPADES_MARK_FUNCTION(); if(world.get() == w){ return; } scriptedUI->CloseUI(); RemoveAllCorpses(); RemoveAllLocalEntities(); lastHealth = 0; lastHurtTime = -100.f; hurtRingView->ClearAll(); scoreboardVisible = false; flashlightOn = false; for(size_t i = 0; i < clientPlayers.size(); i++) { if(clientPlayers[i]) { clientPlayers[i]->Invalidate(); } } clientPlayers.clear(); if(world){ world->SetListener(nullptr); renderer->SetGameMap(nullptr); audioDevice->SetGameMap(nullptr); world = nullptr; map = nullptr; } world.reset(w); if(world){ SPLog("World set"); // initialize player view objects clientPlayers.resize(world->GetNumPlayerSlots()); for(size_t i = 0; i < world->GetNumPlayerSlots(); i++) { Player *p = world->GetPlayer(i); if(p){ clientPlayers[i] = new ClientPlayer(p, this); }else{ clientPlayers[i] = nullptr; } } world->SetListener(this); map = world->GetMap(); renderer->SetGameMap(map); audioDevice->SetGameMap(map); NetLog("------ World Loaded ------"); }else{ SPLog("World removed"); NetLog("------ World Unloaded ------"); } limbo->SetSelectedTeam(2); limbo->SetSelectedWeapon(RIFLE_WEAPON); worldSubFrame = 0.f; worldSetTime = time; inGameLimbo = false; }
static int luasrc_NetLog (lua_State *L) { NetLog(luaL_checkint(L, 1), luaL_checkstring(L, 2)); return 0; }
void Client::PlayerKilledPlayer(spades::client::Player *killer, spades::client::Player *victim, KillType kt) { // play hit sound if (kt == KillTypeWeapon || kt == KillTypeHeadshot) { // don't play on local: see BullethitPlayer if (victim != world->GetLocalPlayer()) { if (!IsMuted()) { Handle<IAudioChunk> c; switch (SampleRandomInt(0, 2)) { case 0: c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh1.opus"); break; case 1: c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh2.opus"); break; case 2: c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh3.opus"); break; } AudioParam param; param.volume = 4.f; audioDevice->Play(c, victim->GetEye(), param); } } } // The local player is dead; initialize the look-you-are-dead cam if (victim == world->GetLocalPlayer()) { followCameraState.enabled = false; Vector3 v = -victim->GetFront(); followAndFreeCameraState.yaw = atan2(v.y, v.x); followAndFreeCameraState.pitch = 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 - SampleRandomFloat() * 4.f)); } corp->AddImpulse(victim->GetVelocty() * 32.f); corpses.emplace_back(corp); if (corpses.size() > corpseHardLimit) { corpses.pop_front(); } else if (corpses.size() > corpseSoftLimit) { RemoveInvisibleCorpses(); } } // add chat message std::string s; s = ChatWindow::TeamColorMessage(killer->GetName(), killer->GetTeamId()); std::string cause; bool isFriendlyFire = killer->GetTeamId() == victim->GetTeamId(); if (killer == victim) isFriendlyFire = false; Weapon *w = killer ? killer->GetWeapon() : nullptr; // only used in case of KillTypeWeapon switch (kt) { case KillTypeWeapon: switch (w ? w->GetWeaponType() : RIFLE_WEAPON) { case RIFLE_WEAPON: cause += _Tr("Client", "Rifle"); break; case SMG_WEAPON: cause += _Tr("Client", "SMG"); break; case SHOTGUN_WEAPON: cause += _Tr("Client", "Shotgun"); break; } break; case KillTypeFall: //! A cause of death shown in the kill feed. cause += _Tr("Client", "Fall"); break; case KillTypeMelee: //! A cause of death shown in the kill feed. cause += _Tr("Client", "Melee"); break; case KillTypeGrenade: cause += _Tr("Client", "Grenade"); break; case KillTypeHeadshot: //! A cause of death shown in the kill feed. cause += _Tr("Client", "Headshot"); break; case KillTypeTeamChange: //! A cause of death shown in the kill feed. cause += _Tr("Client", "Team Change"); break; case KillTypeClassChange: //! A cause of death shown in the kill feed. cause += _Tr("Client", "Weapon Change"); break; default: cause += "???"; break; } s += " ["; if (isFriendlyFire) s += ChatWindow::ColoredMessage(cause, MsgColorFriendlyFire); else if (killer == world->GetLocalPlayer() || victim == world->GetLocalPlayer()) s += ChatWindow::ColoredMessage(cause, MsgColorGray); else s += cause; s += "] "; if (killer != victim) { s += ChatWindow::TeamColorMessage(victim->GetName(), victim->GetTeamId()); } killfeedWindow->AddMessage(s); // log to netlog if (killer != victim) { NetLog("%s (%s) [%s] %s (%s)", killer->GetName().c_str(), world->GetTeam(killer->GetTeamId()).name.c_str(), cause.c_str(), victim->GetName().c_str(), world->GetTeam(victim->GetTeamId()).name.c_str()); } else { NetLog("%s (%s) [%s]", killer->GetName().c_str(), world->GetTeam(killer->GetTeamId()).name.c_str(), cause.c_str()); } // show big message if player is involved if (victim != killer) { Player *local = world->GetLocalPlayer(); if (killer == local || victim == local) { std::string msg; if (killer == local) { if ((int)cg_centerMessage == 2) msg = _Tr("Client", "You have killed {0}", victim->GetName()); } else { msg = _Tr("Client", "You were killed by {0}", killer->GetName()); } centerMessageView->AddMessage(msg); } } }