add suffocation and drowning, enable for survival mode

This commit is contained in:
darkrose 2014-09-22 21:14:18 +10:00
parent 27adeddd1f
commit f542cfa84e
15 changed files with 220 additions and 129 deletions

BIN
data/textures/bubble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

After

Width:  |  Height:  |  Size: 247 B

View File

@ -539,19 +539,14 @@ void Client::step(float dtime)
/*
Get events
*/
for(;;)
{
for (;;) {
ClientEnvEvent event = m_env.getClientEvent();
if(event.type == CEE_NONE)
{
if (event.type == CEE_NONE) {
break;
}
else if(event.type == CEE_PLAYER_DAMAGE)
{
if(m_ignore_damage_timer <= 0)
{
u8 damage = event.player_damage.amount;
sendDamage(damage);
}else if (event.type == CEE_PLAYER_DAMAGE) {
if (m_ignore_damage_timer <= 0) {
s8 damage = event.player_damage.amount;
sendDamage(damage,0);
// Add to ClientEvent queue
ClientEvent event;
@ -559,6 +554,19 @@ void Client::step(float dtime)
event.player_damage.amount = damage;
m_client_event_queue.push_back(event);
}
}else if (event.type == CEE_PLAYER_SUFFOCATE) {
if (m_ignore_damage_timer <= 0) {
s8 damage = event.player_damage.amount;
sendDamage(0,damage);
// Add to ClientEvent queue
ClientEvent event;
event.type = CE_PLAYER_SUFFOCATE;
if (getAir() < 1 && damage > 0)
event.type = CE_PLAYER_DAMAGE;
event.player_damage.amount = damage;
m_client_event_queue.push_back(event);
}
}
}
}
@ -1115,46 +1123,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
player->updateAnim(anim_id);
}
else if(command == TOCLIENT_SECTORMETA)
else if(command == TOCLIENT_PLAYERHP)
{
infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
#if 0
/*
[0] u16 command
[2] u8 sector count
[3...] v2s16 pos + sector metadata
*/
if(datasize < 3)
return;
//infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
{ //envlock
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u8 buf[4];
is.read((char*)buf, 1);
u16 sector_count = readU8(buf);
//infostream<<"sector_count="<<sector_count<<std::endl;
for(u16 i=0; i<sector_count; i++)
{
// Read position
is.read((char*)buf, 4);
v2s16 pos = readV2S16(buf);
/*infostream<<"Client: deserializing sector at "
<<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
// Create sector
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
}
} //envlock
#endif
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
Player *player = m_env.getLocalPlayer();
assert(player != NULL);
u8 hp = readU8(is);
u8 air = readU8(is);
player->hp = hp;
player->air = air;
}
else if(command == TOCLIENT_INVENTORY)
{
@ -1413,6 +1391,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
}
else if(command == TOCLIENT_HP)
{
infostream<<"Client received DEPRECATED TOCLIENT_HP"<<std::endl;
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
Player *player = m_env.getLocalPlayer();
@ -1753,13 +1732,14 @@ void Client::sendChangePassword(const std::wstring oldpassword,
}
void Client::sendDamage(u8 damage)
void Client::sendDamage(s8 damage,s8 suffocate)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_DAMAGE);
writeU8(os, damage);
writeU16(os, TOSERVER_PLAYERDAMAGE);
writeS8(os, damage);
writeS8(os, suffocate);
// Make data buffer
std::string s = os.str();
@ -2121,6 +2101,13 @@ u16 Client::getHP()
return player->hp;
}
u16 Client::getAir()
{
Player *player = m_env.getLocalPlayer();
assert(player != NULL);
return player->air;
}
void Client::setTempMod(v3s16 p, NodeMod mod)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

View File

@ -119,6 +119,7 @@ enum ClientEventType
{
CE_NONE,
CE_PLAYER_DAMAGE,
CE_PLAYER_SUFFOCATE,
CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN,
};
@ -130,7 +131,7 @@ struct ClientEvent
struct{
} none;
struct{
u8 amount;
s8 amount;
} player_damage;
struct{
f32 pitch;
@ -207,7 +208,7 @@ public:
void sendChatMessage(const std::wstring &message);
void sendChangePassword(const std::wstring oldpassword,
const std::wstring newpassword);
void sendDamage(u8 damage);
void sendDamage(s8 damage, s8 suffocate);
void sendRespawn();
void sendWantCookie();
@ -260,6 +261,7 @@ public:
u32 getDayNightRatio();
u16 getHP();
u16 getAir();
void setTempMod(v3s16 p, NodeMod mod);
void clearTempMod(v3s16 p);

View File

@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "utility.h"
#define PROTOCOL_VERSION 5
#define PROTOCOL_VERSION 6
/* the last protocol version used by 0.3.x minetest-c55 clients */
#define PROTOCOL_DOTTHREE 3
/* this is the oldest protocol that we will allow to connect
@ -82,11 +82,11 @@ enum ToClientCommand
[4] u8 animation_id
*/
TOCLIENT_SECTORMETA = 0x26, // Obsolete
TOCLIENT_PLAYERHP = 0x26,
/*
[0] u16 command
[2] u8 sector count
[3...] v2s16 pos + sector metadata
u16 command
u8 hp
*/
TOCLIENT_INVENTORY = 0x27,
@ -284,7 +284,12 @@ enum ToServerCommand
3: digging completed
*/
TOSERVER_RELEASE = 0x29, // Obsolete
TOSERVER_PLAYERDAMAGE = 0x29,
/*
u16 command
u8 damage_amount
u8 suffocate_amount
*/
TOSERVER_SIGNTEXT = 0x30, // Old signs
/*
@ -324,7 +329,7 @@ enum ToServerCommand
[5] u16 item
*/
TOSERVER_DAMAGE = 0x35,
TOSERVER_DAMAGE = 0x35, // obsolete, see TOSERVER_PLAYERDAMAGE
/*
u16 command
u8 amount

View File

@ -152,6 +152,7 @@ void set_creative_defaults(Settings *settings)
settings->setDefault("infinite_inventory", "true");
settings->setDefault("droppable_inventory", "false");
settings->setDefault("enable_damage", "false");
settings->setDefault("enable_suffocation", "false");
settings->setDefault("max_mob_level", "passive");
settings->setDefault("initial_inventory", "false");
settings->setDefault("tool_wear","false");
@ -162,6 +163,7 @@ void set_adventure_defaults(Settings *settings)
settings->setDefault("infinite_inventory", "false");
settings->setDefault("droppable_inventory", "true");
settings->setDefault("enable_damage", "true");
settings->setDefault("enable_suffocation", "false");
settings->setDefault("max_mob_level", "aggressive");
settings->setDefault("initial_inventory", "true");
settings->setDefault("tool_wear","true");
@ -172,6 +174,7 @@ void set_survival_defaults(Settings *settings)
settings->setDefault("infinite_inventory", "false");
settings->setDefault("droppable_inventory", "true");
settings->setDefault("enable_damage", "true");
settings->setDefault("enable_suffocation", "true");
settings->setDefault("max_mob_level", "aggressive");
settings->setDefault("initial_inventory", "false");
settings->setDefault("tool_wear","true");

View File

@ -4033,6 +4033,18 @@ void ClientEnvironment::step(float dtime)
event.type = CEE_PLAYER_DAMAGE;
event.player_damage.amount = damage_per_second;
m_client_event_queue.push_back(event);
}else if (!content_features(n3).air_equivalent) {
ClientEnvEvent event;
event.type = CEE_PLAYER_SUFFOCATE;
event.player_damage.amount = 2;
if (content_features(n3).liquid_type == LIQUID_NONE)
event.player_damage.amount = 4;
m_client_event_queue.push_back(event);
}else if (lplayer->air < 20) {
ClientEnvEvent event;
event.type = CEE_PLAYER_SUFFOCATE;
event.player_damage.amount = -20;
m_client_event_queue.push_back(event);
}
}

View File

@ -336,7 +336,8 @@ private:
enum ClientEnvEventType
{
CEE_NONE,
CEE_PLAYER_DAMAGE
CEE_PLAYER_DAMAGE,
CEE_PLAYER_SUFFOCATE
};
struct ClientEnvEvent

View File

@ -223,7 +223,7 @@ public:
*/
void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
Inventory *inventory, s32 halfheartcount)
Inventory *inventory, s32 halfheartcount, s32 halfbubblecount)
{
InventoryList *mainlist = inventory->getList("main");
if(mainlist == NULL)
@ -278,36 +278,74 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
/*
Draw hearts
*/
{
video::ITexture *heart_texture =
driver->getTexture(getTexturePath("heart.png").c_str());
v2s32 p = pos + v2s32(0, -25);
for(s32 i=0; i<halfheartcount/2; i++)
{
struct {
s32 count;
s32 halfcount;
const char* texture;
bool show_full;
} barData[3] = {
{halfheartcount/2,halfheartcount,"heart.png",true},
{halfbubblecount/2,halfbubblecount,"bubble.png",false},
{10,0,"heart.png",false},
};
v2s32 bar_base(0,-25);
for (s32 k=0; k<3; k++) {
if (barData[k].count == 10 && !barData[k].show_full)
continue;
video::ITexture *texture = driver->getTexture(getTexturePath(barData[k].texture).c_str());
v2s32 p = pos + bar_base;
for (s32 i=0; i<barData[k].count; i++) {
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
core::rect<s32> rect(0,0,16,16);
rect += p;
driver->draw2DImage(heart_texture, rect,
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(heart_texture->getOriginalSize())),
core::dimension2di(texture->getOriginalSize())),
NULL, colors, true);
p += v2s32(16,0);
}
if(halfheartcount % 2 == 1)
{
if (barData[k].halfcount % 2 == 1) {
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
core::rect<s32> rect(0,0,16/2,16);
rect += p;
core::dimension2di srcd(heart_texture->getOriginalSize());
core::dimension2di srcd(texture->getOriginalSize());
srcd.Width /= 2;
driver->draw2DImage(heart_texture, rect,
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), srcd),
NULL, colors, true);
p += v2s32(16,0);
}
bar_base.Y -= 20;
}
//if (halfbubblecount < 20) {
//video::ITexture *bubble_texture = driver->getTexture(getTexturePath("bubble.png").c_str());
//v2s32 p = pos + v2s32(0, -40);
//for (s32 i=0; i<halfbubblecount/2; i++) {
//const video::SColor color(255,255,255,255);
//const video::SColor colors[] = {color,color,color,color};
//core::rect<s32> rect(0,0,16,16);
//rect += p;
//driver->draw2DImage(bubble_texture, rect,
//core::rect<s32>(core::position2d<s32>(0,0),
//core::dimension2di(bubble_texture->getOriginalSize())),
//NULL, colors, true);
//p += v2s32(16,0);
//}
//if (halfbubblecount % 2 == 1) {
//const video::SColor color(255,255,255,255);
//const video::SColor colors[] = {color,color,color,color};
//core::rect<s32> rect(0,0,16/2,16);
//rect += p;
//core::dimension2di srcd(bubble_texture->getOriginalSize());
//srcd.Width /= 2;
//driver->draw2DImage(bubble_texture, rect,
//core::rect<s32>(core::position2d<s32>(0,0), srcd),
//NULL, colors, true);
//p += v2s32(16,0);
//}
//}
if (selected != "") {
v2u32 dim = font->getDimension(narrow_to_wide(selected).c_str());
v2s32 sdim(dim.X,dim.Y);
@ -1622,28 +1660,20 @@ void the_game(
{
// Read client events
for(;;)
{
for(;;) {
ClientEvent event = client.getClientEvent();
if(event.type == CE_NONE)
{
if (event.type == CE_NONE) {
break;
}
else if(event.type == CE_PLAYER_DAMAGE)
{
}else if (event.type == CE_PLAYER_DAMAGE) {
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)
{
}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)
}else if (event.type == CE_DEATHSCREEN) {
if (respawn_menu_active)
continue;
MainRespawnInitiator *respawner =
@ -2361,8 +2391,7 @@ void the_game(
/*
Draw crosshair
*/
if(show_hud)
{
if (show_hud) {
driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
displaycenter + core::vector2d<s32>(10,0),
video::SColor(255,255,255,255));
@ -2382,18 +2411,16 @@ void the_game(
/*
Draw hotbar
*/
if(show_hud)
{
if (show_hud) {
draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
hotbar_imagesize, hotbar_itemcount, &local_inventory,
client.getHP());
client.getHP(), client.getAir());
}
/*
Damage flash
*/
if(damage_flash_timer > 0.0)
{
if (damage_flash_timer > 0.0) {
damage_flash_timer -= dtime;
video::SColor color(128,255,0,0);

View File

@ -407,6 +407,8 @@ LocalPlayer::LocalPlayer():
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
hp = 0;
// Likewise, initialize air to 20, so that no bubbles will be shown
air = 20;
}
LocalPlayer::~LocalPlayer()

View File

@ -172,6 +172,7 @@ public:
bool craftresult_is_preview;
u16 hp;
u16 air;
u16 peer_id;

View File

@ -2130,7 +2130,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
/*
Check HP, respawn if necessary
*/
HandlePlayerHP(player, 0);
HandlePlayerHP(player, 0, 0);
/*
Print out action
@ -3489,7 +3489,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|| tool->getContent() != CONTENT_TOOLITEM_STEELBUCKET
) {
mlist->deleteItem(item_i);
HandlePlayerHP(player,4);
HandlePlayerHP(player,4,0);
}else{
std::string dug_s = std::string("ToolItem ") + tool->getToolName() + "_lava 1";
std::istringstream is(dug_s, std::ios::binary);
@ -4084,7 +4084,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
}else{
ilist->deleteItem(item_i);
HandlePlayerHP(player,4);
HandlePlayerHP(player,4,0);
UpdateCrafting(player->peer_id);
SendInventory(player->peer_id);
}
@ -4275,19 +4275,34 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
<<action<<std::endl;
}
}
#if 0
else if(command == TOSERVER_RELEASE)
else if(command == TOSERVER_PLAYERDAMAGE)
{
if(datasize < 3)
return;
/*
length: 3
[0] u16 command
[2] u8 button
*/
infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
s8 damage = readS8(is);
s8 suffocate = readS8(is);
if (damage && g_settings->getBool("enable_damage")) {
actionstream<<player->getName()<<" damaged by "
<<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
<<std::endl;
}else{
damage = 0;
}
if (suffocate && g_settings->getBool("enable_suffocation")) {
actionstream<<player->getName()<<" lost "
<<(int)suffocate<<" air at "<<PP(player->getPosition()/BS)
<<std::endl;
}else{
suffocate = 0;
}
if (!damage && !suffocate) {
SendPlayerHP(player);
}else{
HandlePlayerHP(player, damage, suffocate);
}
}
#endif
else if(command == TOSERVER_SIGNTEXT)
{
infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
@ -4590,17 +4605,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u8 damage = readU8(is);
infostream<<"TOSERVER_DAMAGE: using deprecated command"<<std::endl;
if(g_settings->getBool("enable_damage"))
{
if (g_settings->getBool("enable_damage")) {
actionstream<<player->getName()<<" damaged by "
<<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
<<std::endl;
HandlePlayerHP(player, damage);
}
else
{
HandlePlayerHP(player, damage, 0);
}else{
SendPlayerHP(player);
}
}
@ -4900,13 +4913,14 @@ void Server::deletingPeer(con::Peer *peer, bool timeout)
Static send methods
*/
void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp, u8 air)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_HP);
writeU16(os, TOCLIENT_PLAYERHP);
writeU8(os, hp);
writeU8(os, air);
// Make data buffer
std::string s = os.str();
@ -5157,7 +5171,7 @@ void Server::BroadcastChatMessage(const std::wstring &message)
void Server::SendPlayerHP(Player *player)
{
SendHP(m_con, player->peer_id, player->hp);
SendHP(m_con, player->peer_id, player->hp, player->air);
}
void Server::SendPlayerCookie(Player *player)
@ -5437,15 +5451,22 @@ void Server::SendBlocks(float dtime)
Something random
*/
void Server::HandlePlayerHP(Player *player, s16 damage)
void Server::HandlePlayerHP(Player *player, s16 damage, s16 suffocate)
{
if(player->hp > damage)
{
player->hp -= damage;
SendPlayerHP(player);
if (player->air > suffocate) {
player->air -= suffocate;
if (player->air > 20)
player->air = 20;
}else{
player->air = 0;
damage += suffocate-player->air;
}
else
{
if (player->hp > damage) {
player->hp -= damage;
if (player->hp > 20)
player->hp = 20;
SendPlayerHP(player);
}else{
infostream<<"Server::HandlePlayerHP(): Player "
<<player->getName()<<" dies"<<std::endl;
@ -5480,6 +5501,7 @@ void Server::RespawnPlayer(Player *player)
pos = findSpawnPos(m_env.getServerMap());
player->setPosition(pos);
player->hp = 20;
player->air = 20;
SendMovePlayer(player);
SendPlayerHP(player);
}

View File

@ -513,7 +513,7 @@ private:
Static send methods
*/
static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendHP(con::Connection &con, u16 peer_id, u8 hp, u8 air);
static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason);
static void SendDeathscreen(con::Connection &con, u16 peer_id,
@ -558,7 +558,7 @@ private:
Something random
*/
void HandlePlayerHP(Player *player, s16 damage);
void HandlePlayerHP(Player *player, s16 damage, s16 suffocate);
void RespawnPlayer(Player *player);
void UpdateCrafting(u16 peer_id);

View File

@ -76,6 +76,11 @@ inline void writeU8(u8 *data, u8 i)
data[0] = ((i>> 0)&0xff);
}
inline void writeS8(u8 *data, s8 i)
{
data[0] = ((i>> 0)&0xff);
}
inline uint64_t readU64(u8 *data)
{
return ((uint64_t)data[0]<<56) | ((uint64_t)data[1]<<48)
@ -99,6 +104,11 @@ inline u8 readU8(u8 *data)
return (data[0]<<0);
}
inline s8 readS8(u8 *data)
{
return ((s8)data[0]<<0);
}
inline void writeS32(u8 *data, s32 i){
writeU32(data, (u32)i);
}
@ -204,12 +214,24 @@ inline void writeU8(std::ostream &os, u8 p)
writeU8((u8*)buf, p);
os.write(buf, 1);
}
inline void writeS8(std::ostream &os, s8 p)
{
char buf[1];
writeS8((u8*)buf, p);
os.write(buf, 1);
}
inline u8 readU8(std::istream &is)
{
char buf[1];
is.read(buf, 1);
return readU8((u8*)buf);
}
inline s8 readS8(std::istream &is)
{
char buf[1];
is.read(buf, 1);
return readS8((u8*)buf);
}
inline void writeU16(std::ostream &os, u16 p)
{

View File

@ -135,6 +135,13 @@
#enable_damage = true
# survival
#enable_damage = true
# Enable players suffocating/drowning and dying
# creative
#enable_suffocation = false
# adventure
#enable_suffocation = false
# survival
#enable_suffocation = true
# The maximum 'level' of mobs to spawn: passive, aggressive, destructive
# roughly: passive = rats, aggressive = oerkki, destructive = DM
# creative