Handle death and respawn better

This commit is contained in:
Perttu Ahola 2011-10-15 14:46:59 +03:00
parent 857fe0551c
commit 74febd5c31
9 changed files with 476 additions and 89 deletions

@ -152,6 +152,7 @@ set(minetest_SRCS
guiInventoryMenu.cpp
guiPauseMenu.cpp
guiPasswordChange.cpp
guiDeathScreen.cpp
client.cpp
tile.cpp
game.cpp

@ -431,7 +431,7 @@ void Client::step(float dtime)
snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
// This should be incremented in each version
writeU16(&data[51], 2);
writeU16(&data[51], 3);
// Send as unreliable
Send(0, data, false);
@ -1477,6 +1477,22 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
}
}
}
else if(command == TOCLIENT_DEATHSCREEN)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
bool set_camera_point_target = readU8(is);
v3f camera_point_target = readV3F1000(is);
ClientEvent event;
event.type = CE_DEATHSCREEN;
event.deathscreen.set_camera_point_target = set_camera_point_target;
event.deathscreen.camera_point_target_x = camera_point_target.X;
event.deathscreen.camera_point_target_y = camera_point_target.Y;
event.deathscreen.camera_point_target_z = camera_point_target.Z;
m_client_event_queue.push_back(event);
}
else
{
dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
@ -1712,6 +1728,20 @@ void Client::sendDamage(u8 damage)
Send(0, data, true);
}
void Client::sendRespawn()
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_RESPAWN);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
Send(0, data, true);
}
void Client::sendPlayerPos()
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

@ -113,7 +113,8 @@ enum ClientEventType
{
CE_NONE,
CE_PLAYER_DAMAGE,
CE_PLAYER_FORCE_MOVE
CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN,
};
struct ClientEvent
@ -129,6 +130,12 @@ struct ClientEvent
f32 pitch;
f32 yaw;
} player_force_move;
struct{
bool set_camera_point_target;
f32 camera_point_target_x;
f32 camera_point_target_y;
f32 camera_point_target_z;
} deathscreen;
};
};
@ -191,6 +198,7 @@ public:
void sendChangePassword(const std::wstring oldpassword,
const std::wstring newpassword);
void sendDamage(u8 damage);
void sendRespawn();
// locks envlock
void removeNode(v3s16 p);

@ -172,6 +172,13 @@ enum ToClientCommand
string serialized item
}
*/
TOCLIENT_DEATHSCREEN = 0x37,
/*
u16 command
u8 bool set camera point target
v3f1000 camera point target (to point the death cause or whatever)
*/
};
enum ToServerCommand
@ -321,6 +328,10 @@ enum ToServerCommand
[2] u16 item
*/
TOSERVER_RESPAWN=0x38,
/*
u16 TOSERVER_RESPAWN
*/
};
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiPasswordChange.h"
#include "guiInventoryMenu.h"
#include "guiTextInputMenu.h"
#include "guiDeathScreen.h"
#include "materials.h"
#include "config.h"
#include "clouds.h"
@ -39,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "profiler.h"
#include "mainmenumanager.h"
#include "gettext.h"
/*
TODO: Move content-aware stuff to separate file by adding properties
@ -129,6 +131,26 @@ struct TextDestSignNode : public TextDest
Client *m_client;
};
/* Respawn menu callback */
class MainRespawnInitiator: public IRespawnInitiator
{
public:
MainRespawnInitiator(bool *active, Client *client):
m_active(active), m_client(client)
{
*m_active = true;
}
void respawn()
{
*m_active = false;
m_client->sendRespawn();
}
private:
bool *m_active;
Client *m_client;
};
/*
Hotbar draw routine
*/
@ -916,6 +938,8 @@ void the_game(
bool invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false;
/*
Main loop
*/
@ -1388,9 +1412,24 @@ void the_game(
/*
Player speed control
TODO: Cache the keycodes from getKeySetting
*/
if(!noMenuActive() || !device->isWindowActive())
{
PlayerControl control(
false,
false,
false,
false,
false,
false,
false,
camera_pitch,
camera_yaw
);
client.setPlayerControl(control);
}
else
{
/*bool a_up,
bool a_down,
@ -1435,6 +1474,7 @@ void the_game(
//client.step(dtime_avg1);
}
{
// Read client events
for(;;)
{
@ -1448,12 +1488,43 @@ void the_game(
//u16 damage = event.player_damage.amount;
//dstream<<"Player damage: "<<damage<<std::endl;
damage_flash_timer = 0.05;
if(event.player_damage.amount >= 2){
damage_flash_timer += 0.05 * event.player_damage.amount;
}
}
else if(event.type == CE_PLAYER_FORCE_MOVE)
{
camera_yaw = event.player_force_move.yaw;
camera_pitch = event.player_force_move.pitch;
}
else if(event.type == CE_DEATHSCREEN)
{
if(respawn_menu_active)
continue;
/*bool set_camera_point_target =
event.deathscreen.set_camera_point_target;
v3f camera_point_target;
camera_point_target.X = event.deathscreen.camera_point_target_x;
camera_point_target.Y = event.deathscreen.camera_point_target_y;
camera_point_target.Z = event.deathscreen.camera_point_target_z;*/
MainRespawnInitiator *respawner =
new MainRespawnInitiator(
&respawn_menu_active, &client);
GUIDeathScreen *menu =
new GUIDeathScreen(guienv, guiroot, -1,
&g_menumgr, respawner);
menu->drop();
/* Handle visualization */
damage_flash_timer = 0;
/*LocalPlayer* player = client.getLocalPlayer();
player->setPosition(player->getPosition() + v3f(0,-BS,0));
camera.update(player, busytime, screensize);*/
}
}
}
//TimeTaker //timer2("//timer2");
@ -1468,14 +1539,10 @@ void the_game(
f32 camera_fov = camera.getFovMax();
if(FIELD_OF_VIEW_TEST)
{
client.updateCamera(v3f(0,0,0), v3f(0,0,1), camera_fov);
}
else
{
client.updateCamera(camera_position,
camera_direction, camera_fov);
}
//timer2.stop();
//TimeTaker //timer3("//timer3");

179
src/guiDeathScreen.cpp Normal file

@ -0,0 +1,179 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "guiDeathScreen.h"
#include "debug.h"
#include "serialization.h"
#include <string>
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "gettext.h"
#include "client.h"
GUIDeathScreen::GUIDeathScreen(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IRespawnInitiator *respawner
):
GUIModalMenu(env, parent, id, menumgr),
m_respawner(respawner),
m_screensize(1,1)
{
}
GUIDeathScreen::~GUIDeathScreen()
{
removeChildren();
delete m_respawner;
}
void GUIDeathScreen::removeChildren()
{
const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy;
for(core::list<gui::IGUIElement*>::ConstIterator
i = children.begin(); i != children.end(); i++)
{
children_copy.push_back(*i);
}
for(core::list<gui::IGUIElement*>::Iterator
i = children_copy.begin();
i != children_copy.end(); i++)
{
(*i)->remove();
}
}
void GUIDeathScreen::regenerateGui(v2u32 screensize)
{
m_screensize = screensize;
/*
Remove stuff
*/
removeChildren();
/*
Calculate new sizes and positions
*/
core::rect<s32> rect(
screensize.X/2 - 500/2,
screensize.Y/2 - 200/2,
screensize.X/2 + 500/2,
screensize.Y/2 + 200/2
);
DesiredRect = rect;
recalculateAbsolutePosition(false);
v2s32 size = rect.getSize();
/*
Add stuff
*/
changeCtype("");
{
core::rect<s32> rect(0, 0, 400, 50);
rect = rect + v2s32(size.X/2-400/2, size.Y/2-50/2-25);
Environment->addStaticText(wgettext("You died."), rect, false,
true, this, 256);
}
{
core::rect<s32> rect(0, 0, 140, 30);
rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+25);
gui::IGUIElement *e =
Environment->addButton(rect, this, 257,
wgettext("Respawn"));
Environment->setFocus(e);
}
changeCtype("C");
}
void GUIDeathScreen::drawMenu()
{
gui::IGUISkin* skin = Environment->getSkin();
if (!skin)
return;
video::IVideoDriver* driver = Environment->getVideoDriver();
{
video::SColor color(180,50,0,0);
driver->draw2DRectangle(color,
core::rect<s32>(0,0,m_screensize.X,m_screensize.Y), NULL);
}
{
video::SColor bgcolor(50,0,0,0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
}
gui::IGUIElement::draw();
}
bool GUIDeathScreen::OnEvent(const SEvent& event)
{
if(event.EventType==EET_KEY_INPUT_EVENT)
{
if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
{
respawn();
quitMenu();
return true;
}
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
{
respawn();
quitMenu();
return true;
}
}
if(event.EventType==EET_GUI_EVENT)
{
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible())
{
if(!canTakeFocus(event.GUIEvent.Element))
{
dstream<<"GUIDeathScreen: Not allowing focus change."
<<std::endl;
// Returning true disables focus change
return true;
}
}
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
{
switch(event.GUIEvent.Caller->getID())
{
case 257:
respawn();
quitMenu();
return true;
}
}
}
return Parent ? Parent->OnEvent(event) : false;
}
void GUIDeathScreen::respawn()
{
m_respawner->respawn();
}

60
src/guiDeathScreen.h Normal file

@ -0,0 +1,60 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef GUIMESSAGEMENU_HEADER
#define GUIMESSAGEMENU_HEADER
#include "common_irrlicht.h"
#include "modalMenu.h"
#include "utility.h"
#include <string>
class IRespawnInitiator
{
public:
virtual void respawn() = 0;
};
class GUIDeathScreen : public GUIModalMenu
{
public:
GUIDeathScreen(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IRespawnInitiator *respawner);
~GUIDeathScreen();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/
void regenerateGui(v2u32 screensize);
void drawMenu();
bool OnEvent(const SEvent& event);
void respawn();
private:
IRespawnInitiator *m_respawner;
v2u32 m_screensize;
};
#endif

@ -2026,16 +2026,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Get player
Player *player = emergePlayer(playername, password, peer_id);
/*{
// DEBUG: Test serialization
std::ostringstream test_os;
player->serialize(test_os);
dstream<<"Player serialization test: \""<<test_os.str()
<<"\""<<std::endl;
std::istringstream test_is(test_os.str());
player->deSerialize(test_is);
}*/
// If failed, cancel
if(player == NULL)
{
@ -2044,32 +2034,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return;
}
/*
// If a client is already connected to the player, cancel
if(player->peer_id != 0)
{
derr_server<<DTIME<<"Server: peer_id="<<peer_id
<<" tried to connect to "
"an already connected player (peer_id="
<<player->peer_id<<")"<<std::endl;
return;
}
// Set client of player
player->peer_id = peer_id;
*/
// Check if player doesn't exist
if(player == NULL)
throw con::InvalidIncomingDataException
("Server::ProcessData(): INIT: Player doesn't exist");
/*// update name if it was supplied
if(datasize >= 20+3)
{
data[20+3-1] = 0;
player->updateName((const char*)&data[3]);
}*/
/*
Answer with a TOCLIENT_INIT
*/
@ -2146,10 +2110,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
// Warnings about protocol version can be issued here
/*if(getClient(peer->id)->net_proto_version == 0)
if(getClient(peer->id)->net_proto_version < 3)
{
SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
}*/
SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
}
/*
Check HP, respawn if necessary
*/
{
Player *player = m_env.getPlayer(peer_id);
HandlePlayerHP(player, 0);
}
return;
}
@ -3207,35 +3179,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
}
else if(command == TOSERVER_DAMAGE)
{
if(g_settings->getBool("enable_damage"))
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u8 damage = readU8(is);
if(player->hp > damage)
if(g_settings->getBool("enable_damage"))
{
player->hp -= damage;
HandlePlayerHP(player, damage);
}
else
{
player->hp = 0;
dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
<<std::endl;
v3f pos = findSpawnPos(m_env.getServerMap());
player->setPosition(pos);
player->hp = 20;
SendMovePlayer(player);
SendPlayerHP(player);
//TODO: Throw items around
}
}
SendPlayerHP(player);
}
else if(command == TOSERVER_PASSWORD)
{
/*
@ -3305,6 +3262,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
player->wieldItem(item);
SendWieldedItem(player);
}
else if(command == TOSERVER_RESPAWN)
{
if(player->hp != 0)
return;
RespawnPlayer(player);
}
else
{
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
@ -3501,6 +3465,23 @@ void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
con.Send(peer_id, 0, data, true);
}
void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DEATHSCREEN);
writeU8(os, set_camera_point_target);
writeV3F1000(os, camera_point_target);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
/*
Non-static send methods
*/
@ -3955,6 +3936,51 @@ void Server::SendBlocks(float dtime)
Something random
*/
void Server::HandlePlayerHP(Player *player, s16 damage)
{
if(player->hp > damage)
{
player->hp -= damage;
SendPlayerHP(player);
}
else
{
dstream<<"Server::HandlePlayerHP(): Player "
<<player->getName()<<" dies"<<std::endl;
player->hp = 0;
//TODO: Throw items around
// Handle players that are not connected
if(player->peer_id == PEER_ID_INEXISTENT){
RespawnPlayer(player);
return;
}
SendPlayerHP(player);
RemoteClient *client = getClient(player->peer_id);
if(client->net_proto_version >= 3)
{
SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
}
else
{
RespawnPlayer(player);
}
}
}
void Server::RespawnPlayer(Player *player)
{
v3f pos = findSpawnPos(m_env.getServerMap());
player->setPosition(pos);
player->hp = 20;
SendMovePlayer(player);
SendPlayerHP(player);
}
void Server::UpdateCrafting(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);

@ -488,6 +488,8 @@ private:
static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason);
static void SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target);
/*
Non-static send methods
@ -526,6 +528,9 @@ private:
Something random
*/
void HandlePlayerHP(Player *player, s16 damage);
void RespawnPlayer(Player *player);
void UpdateCrafting(u16 peer_id);
// When called, connection mutex should be locked