This commit is contained in:
yvt 2014-03-11 03:42:04 +09:00
parent eea46be61d
commit cf8d4f3cb0
10 changed files with 505 additions and 91 deletions

View File

@ -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,

View File

@ -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

View File

@ -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);
};
}
}

View File

@ -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(){

View File

@ -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

View File

@ -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
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
};
}
}

View File

@ -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;
}

View File

@ -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