Alerts
This commit is contained in:
parent
eea46be61d
commit
cf8d4f3cb0
@ -670,9 +670,11 @@ namespace spades {
|
||||
layouter.AddHeading(_Tr("Preferences", "Feedbacks"));
|
||||
layouter.AddToggleField(_Tr("Preferences", "Chat Notify Sounds"), "cg_chatBeep");
|
||||
layouter.AddToggleField(_Tr("Preferences", "Hit Indicator"), "cg_hitIndicator");
|
||||
layouter.AddToggleField(_Tr("Preferences", "Show Alerts"), "cg_alerts");
|
||||
|
||||
layouter.AddHeading(_Tr("Preferences", "AoS 0.75/0.76 Compatibility"));
|
||||
layouter.AddToggleField(_Tr("Preferences", "Allow Unicode"), "cg_unicode");
|
||||
layouter.AddToggleField(_Tr("Preferences", "Server Alert"), "cg_serverAlert");
|
||||
|
||||
layouter.AddHeading(_Tr("Preferences", "Misc"));
|
||||
layouter.AddSliderField(_Tr("Preferences", "Field of View"), "cg_fov", 30, 90, 1,
|
||||
|
@ -60,6 +60,8 @@ static float nextRandom() {
|
||||
SPADES_SETTING(cg_chatBeep, "1");
|
||||
|
||||
|
||||
SPADES_SETTING(cg_serverAlert, "1");
|
||||
|
||||
|
||||
|
||||
|
||||
@ -98,6 +100,8 @@ namespace spades {
|
||||
nextScreenShotIndex(0),
|
||||
nextMapShotIndex(0),
|
||||
|
||||
alertDisappearTime(-10000.f),
|
||||
|
||||
// FIXME: preferences?
|
||||
corpseSoftTimeLimit(30.f), // FIXME: this is not used
|
||||
corpseSoftLimit(6),
|
||||
@ -458,6 +462,43 @@ namespace spades {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Client::ShowAlert(const std::string &contents,
|
||||
AlertType type) {
|
||||
float timeout;
|
||||
switch(type) {
|
||||
case AlertType::Notice:
|
||||
timeout = 2.5f;
|
||||
break;
|
||||
case AlertType::Warning:
|
||||
timeout = 3.f;
|
||||
break;
|
||||
case AlertType::Error:
|
||||
timeout = 3.f;
|
||||
break;
|
||||
}
|
||||
ShowAlert(contents, type, timeout);
|
||||
}
|
||||
|
||||
void Client::ShowAlert(const std::string &contents,
|
||||
AlertType type,
|
||||
float timeout,
|
||||
bool quiet) {
|
||||
alertType = type;
|
||||
alertContents = contents;
|
||||
alertDisappearTime = time + timeout;
|
||||
alertAppearTime = time;
|
||||
|
||||
if(type != AlertType::Notice && !quiet) {
|
||||
PlayAlertSound();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::PlayAlertSound() {
|
||||
Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Alert.wav");
|
||||
audioDevice->PlayLocal(chunk, AudioParam());
|
||||
}
|
||||
|
||||
/** Records chat message/game events to the log file. */
|
||||
void Client::NetLog(const char *format, ...) {
|
||||
char buf[4096];
|
||||
@ -512,15 +553,19 @@ namespace spades {
|
||||
|
||||
std::string msg;
|
||||
msg = _Tr("Client", "Map saved: {0}", name);
|
||||
msg = ChatWindow::ColoredMessage(msg, MsgColorSysInfo);
|
||||
chatWindow->AddMessage(msg);
|
||||
ShowAlert(msg, AlertType::Notice);
|
||||
}catch(const Exception& ex){
|
||||
std::string msg;
|
||||
msg = _Tr("Client", "Saving map failed: ");
|
||||
msg += ex.GetShortMessage();
|
||||
ShowAlert(msg, AlertType::Error);
|
||||
SPLog("Saving map failed: %s", ex.what());
|
||||
}catch(const std::exception& ex){
|
||||
std::string msg;
|
||||
msg = _Tr("Client", "Saving map failed: ");
|
||||
std::vector<std::string> lines = SplitIntoLines(ex.what());
|
||||
msg += lines[0];
|
||||
msg = ChatWindow::ColoredMessage(msg, MsgColorRed);
|
||||
chatWindow->AddMessage(msg);
|
||||
msg += ex.what();
|
||||
ShowAlert(msg, AlertType::Error);
|
||||
SPLog("Saving map failed: %s", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@ -593,9 +638,25 @@ namespace spades {
|
||||
}
|
||||
|
||||
void Client::ServerSentMessage(const std::string &msg) {
|
||||
chatWindow->AddMessage(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);
|
||||
}
|
||||
|
||||
#pragma mark - Follow / Spectate
|
||||
|
@ -190,6 +190,16 @@ namespace spades {
|
||||
Handle<IFont> textFont;
|
||||
Handle<IFont> bigTextFont;
|
||||
|
||||
enum class AlertType {
|
||||
Notice,
|
||||
Warning,
|
||||
Error
|
||||
};
|
||||
AlertType alertType;
|
||||
std::string alertContents;
|
||||
float alertDisappearTime;
|
||||
float alertAppearTime;
|
||||
|
||||
std::list<std::unique_ptr<ILocalEntity>> localEntities;
|
||||
std::list<std::unique_ptr<Corpse>> corpses;
|
||||
Corpse *lastMyCorpse;
|
||||
@ -209,6 +219,14 @@ namespace spades {
|
||||
void DrawStartupScreen();
|
||||
void DoInit();
|
||||
|
||||
void ShowAlert(const std::string& contents,
|
||||
AlertType type);
|
||||
void ShowAlert(const std::string& contents,
|
||||
AlertType type,
|
||||
float timeout,
|
||||
bool quiet = false);
|
||||
void PlayAlertSound();
|
||||
|
||||
void UpdateWorld(float dt);
|
||||
void UpdateLocalSpectator(float dt);
|
||||
void UpdateLocalPlayer(float dt);
|
||||
@ -226,6 +244,7 @@ namespace spades {
|
||||
void DrawHurtScreenEffect();
|
||||
void DrawHurtSprites();
|
||||
void DrawHealth();
|
||||
void DrawAlert();
|
||||
void DrawDebugAim();
|
||||
|
||||
void DrawScene();
|
||||
@ -340,6 +359,7 @@ namespace spades {
|
||||
virtual void LocalPlayerCreatedLineBlock(IntVector3, IntVector3);
|
||||
virtual void LocalPlayerHurt(HurtType type, bool sourceGiven,
|
||||
Vector3 source);
|
||||
virtual void LocalPlayerBuildError(BuildFailureReason reason);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -97,13 +97,18 @@ namespace spades {
|
||||
msg = _Tr("Client", "Screenshot saved: {0}", name);
|
||||
msg = ChatWindow::ColoredMessage(msg, MsgColorSysInfo);
|
||||
chatWindow->AddMessage(msg);
|
||||
}catch(const Exception& ex){
|
||||
std::string msg;
|
||||
msg = _Tr("Client", "Screenshot failed: ");
|
||||
msg += ex.GetShortMessage();
|
||||
ShowAlert(msg, AlertType::Error);
|
||||
SPLog("Screenshot failed: %s", ex.what());
|
||||
}catch(const std::exception& ex){
|
||||
std::string msg;
|
||||
msg = _Tr("Client", "Screenshot failed: ");
|
||||
std::vector<std::string> lines = SplitIntoLines(ex.what());
|
||||
msg += lines[0];
|
||||
msg = ChatWindow::ColoredMessage(msg, MsgColorRed);
|
||||
chatWindow->AddMessage(msg);
|
||||
msg += ex.what();
|
||||
ShowAlert(msg, AlertType::Error);
|
||||
SPLog("Screenshot failed: %s", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +129,6 @@ namespace spades {
|
||||
SPRaise("No free file name");
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - 2D Drawings
|
||||
|
||||
void Client::DrawSplash() {
|
||||
@ -549,6 +553,108 @@ namespace spades {
|
||||
mapView->Draw();
|
||||
}
|
||||
|
||||
void Client::DrawAlert() {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
IFont *font = textFont;
|
||||
float scrWidth = renderer->ScreenWidth();
|
||||
float scrHeight = renderer->ScreenHeight();
|
||||
auto& r = renderer;
|
||||
|
||||
const float fadeOutTime = 1.f;
|
||||
|
||||
float fade = 1.f - (time - alertDisappearTime) / fadeOutTime;
|
||||
fade = std::min(fade, 1.f);
|
||||
if(fade <= 0.f) {
|
||||
return;
|
||||
}
|
||||
|
||||
float borderFade = 1.f - (time - alertAppearTime) * 1.5f;
|
||||
borderFade = std::max(std::min(borderFade, 1.f), 0.f);
|
||||
borderFade *= fade;
|
||||
|
||||
Handle<IImage> alertIcon(renderer->RegisterImage("Gfx/AlertIcon.png"), false);
|
||||
|
||||
Vector2 textSize = font->Measure(alertContents);
|
||||
Vector2 contentsSize = textSize;
|
||||
contentsSize.y = std::max(contentsSize.y, 16.f);
|
||||
if(alertType != AlertType::Notice) {
|
||||
contentsSize.x += 22.f;
|
||||
}
|
||||
|
||||
// add margin
|
||||
const float margin = 8.f;
|
||||
contentsSize.x += margin * 2.f;
|
||||
contentsSize.y += margin * 2.f;
|
||||
|
||||
contentsSize.x = floorf(contentsSize.x);
|
||||
contentsSize.y = floorf(contentsSize.y);
|
||||
|
||||
Vector2 pos = (Vector2(scrWidth, scrHeight) - contentsSize) * Vector2(0.5f, 0.7f);
|
||||
pos.y += 40.f;
|
||||
|
||||
pos.x = floorf(pos.x);
|
||||
pos.y = floorf(pos.y);
|
||||
|
||||
Vector4 color;
|
||||
|
||||
// draw border
|
||||
switch(alertType) {
|
||||
case AlertType::Notice:
|
||||
color = Vector4(0.f, 0.f, 0.f, 0.f);
|
||||
break;
|
||||
case AlertType::Warning:
|
||||
color = Vector4(1.f, 1.f, 0.f, .7f);
|
||||
break;
|
||||
case AlertType::Error:
|
||||
color = Vector4(1.f, 0.f, 0.f, .7f);
|
||||
break;
|
||||
}
|
||||
color *= borderFade;
|
||||
r->SetColorAlphaPremultiplied(color);
|
||||
|
||||
const float border = 1.f;
|
||||
r->DrawImage(nullptr, AABB2(pos.x-border, pos.y-border, contentsSize.x+border*2.f, border));
|
||||
r->DrawImage(nullptr, AABB2(pos.x-border, pos.y+contentsSize.y, contentsSize.x+border*2.f, border));
|
||||
|
||||
r->DrawImage(nullptr, AABB2(pos.x-border, pos.y, border, contentsSize.y));
|
||||
r->DrawImage(nullptr, AABB2(pos.x+contentsSize.x, pos.y, border, contentsSize.y));
|
||||
|
||||
// fill background
|
||||
color = Vector4(0.f, 0.f, 0.f, fade * 0.5f);
|
||||
r->SetColorAlphaPremultiplied(color);
|
||||
r->DrawImage(nullptr, AABB2(pos.x, pos.y, contentsSize.x, contentsSize.y));
|
||||
|
||||
// draw icon
|
||||
switch(alertType) {
|
||||
case AlertType::Notice:
|
||||
color = Vector4(0.f, 0.f, 0.f, 0.f);
|
||||
break;
|
||||
case AlertType::Warning:
|
||||
color = Vector4(1.f, 1.f, 0.f, 1.f);
|
||||
break;
|
||||
case AlertType::Error:
|
||||
color = Vector4(1.f, 0.f, 0.f, 1.f);
|
||||
break;
|
||||
}
|
||||
color *= fade;
|
||||
r->SetColorAlphaPremultiplied(color);
|
||||
|
||||
r->DrawImage(alertIcon, Vector2(pos.x + margin, pos.y + (contentsSize.y - 16.f) * 0.5f));
|
||||
|
||||
// draw text
|
||||
color = Vector4(1.f, 1.f, 1.f, 1.f);
|
||||
color *= fade;
|
||||
|
||||
font->DrawShadow(alertContents,
|
||||
Vector2(pos.x + contentsSize.x - textSize.x - margin,
|
||||
pos.y + (contentsSize.y - textSize.y) * 0.5f),
|
||||
1.f, color,
|
||||
Vector4(0.f, 0.f, 0.f, fade * 0.5f));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Client::DrawHealth() {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
@ -618,12 +724,16 @@ namespace spades {
|
||||
}
|
||||
}
|
||||
|
||||
DrawAlert();
|
||||
|
||||
chatWindow->Draw();
|
||||
killfeedWindow->Draw();
|
||||
|
||||
|
||||
// large map view should come in front
|
||||
largeMapView->Draw();
|
||||
|
||||
|
||||
if(scoreboardVisible)
|
||||
scoreboard->Draw();
|
||||
|
||||
@ -632,6 +742,8 @@ namespace spades {
|
||||
// world exists, but no local player: not joined
|
||||
|
||||
scoreboard->Draw();
|
||||
|
||||
DrawAlert();
|
||||
}
|
||||
|
||||
if(IsLimboViewActive())
|
||||
@ -669,6 +781,8 @@ namespace spades {
|
||||
renderer->SetColorAlphaPremultiplied(MakeVector4(op, op, op, op));
|
||||
renderer->DrawImage(img, AABB2(scrWidth - 236.f + p * 234.f, scrHeight - 18.f, 4.f, 4.f));
|
||||
}
|
||||
|
||||
DrawAlert();
|
||||
}
|
||||
|
||||
void Client::Draw2D(){
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "Client.h"
|
||||
|
||||
#include <Core/Settings.h>
|
||||
#include <Core/Strings.h>
|
||||
|
||||
#include "IAudioChunk.h"
|
||||
#include "IAudioDevice.h"
|
||||
@ -80,6 +81,9 @@ SPADES_SETTING(cg_switchToolByWheel, "1");
|
||||
|
||||
SPADES_SETTING(cg_debugCorpse, "0");
|
||||
|
||||
SPADES_SETTING(cg_alerts, "1");
|
||||
|
||||
|
||||
namespace spades {
|
||||
namespace client {
|
||||
|
||||
@ -422,21 +426,40 @@ namespace spades {
|
||||
}
|
||||
}else if(CheckKey(cg_keyToolBlock, name) && down){
|
||||
if(world->GetLocalPlayer()->GetTeamId() < 2 &&
|
||||
world->GetLocalPlayer()->IsAlive() &&
|
||||
world->GetLocalPlayer()->IsToolSelectable(Player::ToolBlock)){
|
||||
SetSelectedTool(Player::ToolBlock);
|
||||
world->GetLocalPlayer()->IsAlive()
|
||||
){
|
||||
if(world->GetLocalPlayer()->IsToolSelectable(Player::ToolBlock)) {
|
||||
SetSelectedTool(Player::ToolBlock);
|
||||
}else{
|
||||
if(cg_alerts)
|
||||
ShowAlert(_Tr("Client", "Out of Blocks"), AlertType::Error);
|
||||
else
|
||||
PlayAlertSound();
|
||||
}
|
||||
}
|
||||
}else if(CheckKey(cg_keyToolWeapon, name) && down){
|
||||
if(world->GetLocalPlayer()->GetTeamId() < 2 &&
|
||||
world->GetLocalPlayer()->IsAlive() &&
|
||||
world->GetLocalPlayer()->IsToolSelectable(Player::ToolWeapon)){
|
||||
SetSelectedTool(Player::ToolWeapon);
|
||||
world->GetLocalPlayer()->IsAlive()){
|
||||
if(world->GetLocalPlayer()->IsToolSelectable(Player::ToolWeapon)) {
|
||||
SetSelectedTool(Player::ToolWeapon);
|
||||
}else{
|
||||
if(cg_alerts)
|
||||
ShowAlert(_Tr("Client", "Out of Ammo"), AlertType::Error);
|
||||
else
|
||||
PlayAlertSound();
|
||||
}
|
||||
}
|
||||
}else if(CheckKey(cg_keyToolGrenade, name) && down){
|
||||
if(world->GetLocalPlayer()->GetTeamId() < 2 &&
|
||||
world->GetLocalPlayer()->IsAlive() &&
|
||||
world->GetLocalPlayer()->IsToolSelectable(Player::ToolGrenade)){
|
||||
SetSelectedTool(Player::ToolGrenade);
|
||||
world->GetLocalPlayer()->IsAlive()){
|
||||
if(world->GetLocalPlayer()->IsToolSelectable(Player::ToolGrenade)) {
|
||||
SetSelectedTool(Player::ToolGrenade);
|
||||
}else{
|
||||
if(cg_alerts)
|
||||
ShowAlert(_Tr("Client", "Out of Grenades"), AlertType::Error);
|
||||
else
|
||||
PlayAlertSound();
|
||||
}
|
||||
}
|
||||
}else if(CheckKey(cg_keyGlobalChat, name) && down){
|
||||
// global chat
|
||||
|
@ -545,68 +545,132 @@ namespace spades {
|
||||
// draw block cursor
|
||||
// FIXME: don't use debug line
|
||||
if(p){
|
||||
if(p->IsBlockCursorActive() &&
|
||||
p->IsReadyToUseTool()){
|
||||
if(p->IsReadyToUseTool() &&
|
||||
p->GetTool() == Player::ToolBlock){
|
||||
std::vector<IntVector3> blocks;
|
||||
if(p->IsBlockCursorDragging()){
|
||||
blocks = world->CubeLine(p->GetBlockCursorDragPos(),
|
||||
p->GetBlockCursorPos(),
|
||||
256);
|
||||
blocks = std::move
|
||||
(world->CubeLine(p->GetBlockCursorDragPos(),
|
||||
p->GetBlockCursorPos(),
|
||||
256));
|
||||
}else{
|
||||
blocks.push_back(p->GetBlockCursorPos());
|
||||
}
|
||||
|
||||
bool active = p->IsBlockCursorActive();
|
||||
|
||||
Vector4 color = {1,1,1,1};
|
||||
if(!active)
|
||||
color = Vector4(1,1,0,1);
|
||||
if((int)blocks.size() > p->GetNumBlocks())
|
||||
color = MakeVector4(1,0,0,1);
|
||||
if(!active)
|
||||
color.w *= 0.5f;
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
if(active) {
|
||||
|
||||
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);
|
||||
}else{
|
||||
// not active
|
||||
|
||||
const float ln = 0.1f;
|
||||
{
|
||||
float xx = v.x, yy = v.y, zz = v.z;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx+ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy+ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz+ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x + 1, yy = v.y, zz = v.z;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx-ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy+ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz+ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x, yy = v.y + 1, zz = v.z;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx+ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy-ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz+ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x + 1, yy = v.y + 1, zz = v.z;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx-ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy-ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz+ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x, yy = v.y, zz = v.z + 1;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx+ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy+ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz-ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x + 1, yy = v.y, zz = v.z + 1;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx-ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy+ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz-ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x, yy = v.y + 1, zz = v.z + 1;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx+ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy-ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz-ln), color);
|
||||
}
|
||||
{
|
||||
float xx = v.x + 1, yy = v.y + 1, zz = v.z + 1;
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx-ln, yy, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy-ln, zz), color);
|
||||
renderer->AddDebugLine(Vector3(xx, yy, zz), Vector3(xx, yy, zz-ln), color);
|
||||
}
|
||||
|
||||
}
|
||||
// --- one block drawn
|
||||
} // end for
|
||||
|
||||
} // p->IsReadyToUseTool
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,8 @@ SPADES_SETTING(cg_ragdoll, "1");
|
||||
SPADES_SETTING(cg_blood, "1");
|
||||
SPADES_SETTING(cg_ejectBrass, "1");
|
||||
|
||||
SPADES_SETTING(cg_alerts, "");
|
||||
|
||||
|
||||
namespace spades {
|
||||
namespace client {
|
||||
@ -93,6 +95,8 @@ namespace spades {
|
||||
if(!world->GetMap()->CastRay(p->GetEye(),
|
||||
p->GetFront(),
|
||||
256.f, outBlockCoord)){
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -439,6 +443,31 @@ namespace spades {
|
||||
|
||||
lastKills = world->GetPlayerPersistent(player->GetId()).kills;
|
||||
|
||||
// show block count when building block lines.
|
||||
if(player->IsAlive() &&
|
||||
player->GetTool() == Player::ToolBlock &&
|
||||
player->GetWeaponInput().secondary &&
|
||||
player->IsBlockCursorDragging()) {
|
||||
if(player->IsBlockCursorActive()) {
|
||||
auto blocks = std::move
|
||||
(world->CubeLine(player->GetBlockCursorDragPos(),
|
||||
player->GetBlockCursorPos(),
|
||||
256));
|
||||
auto msg = _TrN("Client",
|
||||
"{0} block", "{0} blocks",
|
||||
blocks.size());
|
||||
AlertType type =
|
||||
static_cast<int>(blocks.size()) > player->GetNumBlocks() ?
|
||||
AlertType::Warning : AlertType::Notice;
|
||||
ShowAlert(msg, type, 0.f, true);
|
||||
}else{
|
||||
// invalid
|
||||
auto msg = _Tr("Client", "-- blocks");
|
||||
AlertType type = AlertType::Warning;
|
||||
ShowAlert(msg, type, 0.f, true);
|
||||
}
|
||||
}
|
||||
|
||||
if(player->IsAlive())
|
||||
lastAliveTime = time;
|
||||
|
||||
@ -975,6 +1004,8 @@ namespace spades {
|
||||
void Client::AddBulletTracer(spades::client::Player *player,
|
||||
spades::Vector3 muzzlePos,
|
||||
spades::Vector3 hitPos) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
Tracer *t;
|
||||
float vel;
|
||||
switch(player->GetWeapon()->GetWeaponType()) {
|
||||
@ -992,6 +1023,8 @@ namespace spades {
|
||||
}
|
||||
|
||||
void Client::BlocksFell(std::vector<IntVector3> blocks) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
if(blocks.empty())
|
||||
return;
|
||||
FallingBlock *b = new FallingBlock(this, blocks);
|
||||
@ -1129,9 +1162,11 @@ namespace spades {
|
||||
}
|
||||
|
||||
void Client::LocalPlayerBlockAction(spades::IntVector3 v, BlockActionType type){
|
||||
SPADES_MARK_FUNCTION();
|
||||
net->SendBlockAction(v, type);
|
||||
}
|
||||
void Client::LocalPlayerCreatedLineBlock(spades::IntVector3 v1, spades::IntVector3 v2) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
net->SendBlockLine(v1, v2);
|
||||
}
|
||||
|
||||
@ -1139,6 +1174,7 @@ namespace spades {
|
||||
bool sourceGiven,
|
||||
spades::Vector3 source) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
if(sourceGiven){
|
||||
Player * p = world->GetLocalPlayer();
|
||||
if(!p)
|
||||
@ -1149,5 +1185,24 @@ namespace spades {
|
||||
}
|
||||
}
|
||||
|
||||
void Client::LocalPlayerBuildError(BuildFailureReason reason) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
|
||||
if(!cg_alerts) {
|
||||
PlayAlertSound();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(reason) {
|
||||
case BuildFailureReason::InsufficientBlocks:
|
||||
ShowAlert(_Tr("Client", "Insufficient Blocks."),
|
||||
AlertType::Error);
|
||||
break;
|
||||
case BuildFailureReason::InvalidPosition:
|
||||
ShowAlert(_Tr("Client", "You cannot place a block there."),
|
||||
AlertType::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,12 @@ namespace spades {
|
||||
namespace client {
|
||||
class Player;
|
||||
class Grenade;
|
||||
|
||||
enum class BuildFailureReason {
|
||||
InsufficientBlocks,
|
||||
InvalidPosition
|
||||
};
|
||||
|
||||
class IWorldListener{
|
||||
public:
|
||||
virtual void PlayerObjectSet(int playerId) = 0;
|
||||
@ -70,6 +76,7 @@ namespace spades {
|
||||
virtual void LocalPlayerCreatedLineBlock(IntVector3, IntVector3) = 0;
|
||||
virtual void LocalPlayerHurt(HurtType type, bool sourceGiven,
|
||||
Vector3 source) = 0;
|
||||
virtual void LocalPlayerBuildError(BuildFailureReason reason) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +113,7 @@ namespace spades {
|
||||
|
||||
void Player::SetWeaponInput(WeaponInput newInput){
|
||||
SPADES_MARK_FUNCTION();
|
||||
auto *listener = GetWorld()->GetListener();
|
||||
|
||||
if(!IsAlive())
|
||||
return;
|
||||
@ -151,11 +152,11 @@ namespace spades {
|
||||
}else{
|
||||
holdingGrenade = true;
|
||||
grenadeTime = world->GetTime();
|
||||
if(world->GetListener() &&
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer())
|
||||
// playing other's grenade sound
|
||||
// is cheating
|
||||
world->GetListener()->LocalPlayerPulledGrenadePin();
|
||||
listener->LocalPlayerPulledGrenadePin();
|
||||
}
|
||||
}
|
||||
}else if(tool == ToolBlock){
|
||||
@ -180,34 +181,58 @@ namespace spades {
|
||||
if(IsBlockCursorActive()){
|
||||
blockCursorDragging = true;
|
||||
blockCursorDragPos = blockCursorPos;
|
||||
}else{
|
||||
// cannot build; invalid position.
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer()) {
|
||||
listener->
|
||||
LocalPlayerBuildError(BuildFailureReason::InvalidPosition);
|
||||
}
|
||||
}
|
||||
}else {
|
||||
if(IsBlockCursorActive() &&
|
||||
IsBlockCursorDragging()){
|
||||
std::vector<IntVector3> blocks = GetWorld()->CubeLine(blockCursorDragPos,
|
||||
blockCursorPos, 256);
|
||||
if((int)blocks.size() <= blockStocks){
|
||||
if(GetWorld()->GetListener() &&
|
||||
this == world->GetLocalPlayer())
|
||||
GetWorld()->GetListener()->LocalPlayerCreatedLineBlock(blockCursorDragPos, blockCursorPos);
|
||||
//blockStocks -= blocks.size(); decrease when created
|
||||
}
|
||||
if(blockCursorActive){
|
||||
if(IsBlockCursorDragging()) {
|
||||
if(IsBlockCursorActive()){
|
||||
std::vector<IntVector3> blocks = GetWorld()->CubeLine(blockCursorDragPos,
|
||||
blockCursorPos, 256);
|
||||
if((int)blocks.size() <= blockStocks){
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer())
|
||||
listener->LocalPlayerCreatedLineBlock(blockCursorDragPos, blockCursorPos);
|
||||
//blockStocks -= blocks.size(); decrease when created
|
||||
}else{
|
||||
// cannot build; insufficient blocks.
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer()) {
|
||||
listener->
|
||||
LocalPlayerBuildError(BuildFailureReason::InsufficientBlocks);
|
||||
}
|
||||
}
|
||||
nextBlockTime = world->GetTime() + GetToolSecondaryDelay();
|
||||
} else {
|
||||
// cannot build; invalid position.
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer()) {
|
||||
listener->
|
||||
LocalPlayerBuildError(BuildFailureReason::InvalidPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockCursorDragging = false;
|
||||
blockCursorActive = false;
|
||||
}
|
||||
}
|
||||
if(newInput.primary != weapInput.primary){
|
||||
if(newInput.primary != weapInput.primary ||
|
||||
(newInput.primary && weapInput.primary &&
|
||||
canPending)){
|
||||
if(newInput.primary){
|
||||
|
||||
if(IsBlockCursorActive() && blockStocks > 0){
|
||||
if(GetWorld()->GetListener() &&
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer())
|
||||
GetWorld()->GetListener()->LocalPlayerBlockAction(blockCursorPos, BlockActionCreate);
|
||||
|
||||
listener->LocalPlayerBlockAction(blockCursorPos, BlockActionCreate);
|
||||
|
||||
lastSingleBlockBuildSeqDone = true;
|
||||
// blockStocks--; decrease when created
|
||||
|
||||
nextBlockTime = world->GetTime() + GetToolPrimaryDelay();
|
||||
@ -215,13 +240,34 @@ namespace spades {
|
||||
this == world->GetLocalPlayer()) {
|
||||
pendingPlaceBlock = true;
|
||||
pendingPlaceBlockPos = blockCursorPos;
|
||||
}
|
||||
if(!blockCursorActive) {
|
||||
newInput.primary = false;
|
||||
lastSingleBlockBuildSeqDone = false;
|
||||
}else if(!IsBlockCursorActive()) {
|
||||
lastSingleBlockBuildSeqDone = false;
|
||||
if(canPending) {
|
||||
// Delayed block placement can be activated,
|
||||
// so don't show alert
|
||||
}else{
|
||||
lastSingleBlockBuildSeqDone = true;
|
||||
// cannot build; invalid position.
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer()) {
|
||||
listener->
|
||||
LocalPlayerBuildError(BuildFailureReason::InvalidPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockCursorDragging = false;
|
||||
blockCursorActive = false;
|
||||
}else{
|
||||
if(!lastSingleBlockBuildSeqDone) {
|
||||
// cannot build; invalid position.
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer()) {
|
||||
listener->
|
||||
LocalPlayerBuildError(BuildFailureReason::InvalidPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else if(IsToolWeapon()){
|
||||
@ -355,6 +401,7 @@ namespace spades {
|
||||
|
||||
void Player::Update(float dt) {
|
||||
SPADES_MARK_FUNCTION();
|
||||
auto* listener = world->GetListener();
|
||||
|
||||
MovePlayer(dt);
|
||||
if(tool == ToolSpade){
|
||||
@ -370,13 +417,15 @@ namespace spades {
|
||||
firstDig = false;
|
||||
}
|
||||
}
|
||||
}else if(tool == ToolBlock){
|
||||
}else if(tool == ToolBlock && IsLocalPlayer()){
|
||||
GameMap::RayCastResult result;
|
||||
result = GetWorld()->GetMap()->CastRay2(GetEye(),
|
||||
GetFront(),
|
||||
12);
|
||||
canPending = false;
|
||||
|
||||
|
||||
|
||||
if(result.hit &&
|
||||
(result.hitBlock + result.normal).z < 62 &&
|
||||
(!OverlapsWithOneBlock(result.hitBlock + result.normal)) &&
|
||||
@ -395,6 +444,15 @@ namespace spades {
|
||||
if(airborne == false || blockStocks <= 0){
|
||||
// player is no longer airborne, or doesn't have a block to place.
|
||||
pendingPlaceBlock = false;
|
||||
lastSingleBlockBuildSeqDone = true;
|
||||
if(blockStocks > 0) {
|
||||
// cannot build; invalid position.
|
||||
if(listener &&
|
||||
this == world->GetLocalPlayer()) {
|
||||
listener->
|
||||
LocalPlayerBuildError(BuildFailureReason::InvalidPosition);
|
||||
}
|
||||
}
|
||||
}else if((!OverlapsWithOneBlock(pendingPlaceBlockPos)) &&
|
||||
BoxDistanceToBlock(pendingPlaceBlockPos) < 3.f){
|
||||
// now building became possible.
|
||||
@ -404,6 +462,7 @@ namespace spades {
|
||||
GetWorld()->GetListener()->LocalPlayerBlockAction(pendingPlaceBlockPos, BlockActionCreate);
|
||||
|
||||
pendingPlaceBlock = false;
|
||||
lastSingleBlockBuildSeqDone = true;
|
||||
// blockStocks--; decrease when created
|
||||
|
||||
nextBlockTime = world->GetTime() + GetToolPrimaryDelay();
|
||||
@ -416,6 +475,13 @@ namespace spades {
|
||||
(result.hitBlock + result.normal).z < 62 &&
|
||||
BoxDistanceToBlock(result.hitBlock + result.normal) < 3.f;
|
||||
blockCursorActive = false;
|
||||
for(int dist = 11; dist >= 1 &&
|
||||
BoxDistanceToBlock(result.hitBlock + result.normal) > 3.f ; dist--) {
|
||||
result = GetWorld()->GetMap()->CastRay2(GetEye(),
|
||||
GetFront(),
|
||||
dist);
|
||||
}
|
||||
|
||||
blockCursorPos = result.hitBlock + result.normal;
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,7 @@ namespace spades {
|
||||
bool blockCursorDragging;
|
||||
IntVector3 blockCursorPos;
|
||||
IntVector3 blockCursorDragPos;
|
||||
bool lastSingleBlockBuildSeqDone;
|
||||
float lastReloadingTime;
|
||||
|
||||
bool pendingPlaceBlock;
|
||||
@ -167,6 +168,7 @@ namespace spades {
|
||||
bool IsBlockCursorDragging();
|
||||
IntVector3 GetBlockCursorPos(){return blockCursorPos;}
|
||||
IntVector3 GetBlockCursorDragPos(){return blockCursorDragPos;}
|
||||
bool CanActivateDelayedBlockPlacement() { return canPending; }
|
||||
bool IsReadyToUseTool();
|
||||
|
||||
// ammo counts
|
||||
|
Loading…
x
Reference in New Issue
Block a user