diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 74b3d3ba..6a4d1983 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1547,6 +1547,11 @@ Player-only: (no-op for other objects) - get_look_yaw(): yaw in radians (wraps around pretty randomly as of now) - set_look_pitch(radians): sets look pitch - set_look_yaw(radians): sets look yaw +- get_breath() : returns players breath +- set_breath(value) : sets players breath + values: 0 player is drowning, + 1-10 number of bubbles remain, + 11 bubbles bar is not shown - set_inventory_formspec(formspec) ^ Redefine player's inventory form ^ Should usually be called in on_joinplayer diff --git a/src/client.cpp b/src/client.cpp index c458684f..e5ba1485 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -692,9 +692,14 @@ void Client::step(float dtime) m_client_event_queue.push_back(event); } } + else if(event.type == CEE_PLAYER_BREATH) + { + u16 breath = event.player_breath.amount; + sendBreath(breath); + } } } - + /* Print some info */ @@ -1579,6 +1584,15 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_client_event_queue.push_back(event); } } + else if(command == TOCLIENT_BREATH) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + u16 breath = readU16(is); + player->setBreath(breath) ; + } else if(command == TOCLIENT_MOVE_PLAYER) { std::string datastring((char*)&data[2], datasize-2); @@ -2359,6 +2373,20 @@ void Client::sendDamage(u8 damage) Send(0, data, true); } +void Client::sendBreath(u16 breath) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOSERVER_BREATH); + writeU16(os, breath); + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + void Client::sendRespawn() { DSTACK(__FUNCTION_NAME); @@ -2694,7 +2722,7 @@ u16 Client::getBreath() { Player *player = m_env.getLocalPlayer(); assert(player != NULL); - return player->breath; + return player->getBreath(); } bool Client::getChatMessage(std::wstring &message) diff --git a/src/client.h b/src/client.h index 0b69abf5..84cabf3f 100644 --- a/src/client.h +++ b/src/client.h @@ -306,6 +306,7 @@ public: void sendChangePassword(const std::wstring oldpassword, const std::wstring newpassword); void sendDamage(u8 damage); + void sendBreath(u16 breath); void sendRespawn(); ClientEnvironment& getEnv() diff --git a/src/clientserver.h b/src/clientserver.h index a43baab2..a84d29a9 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -488,6 +488,12 @@ enum ToClientCommand u16 len u8[len] value */ + + TOCLIENT_BREATH = 0x4e, + /* + u16 command + u16 breath + */ }; enum ToServerCommand @@ -711,6 +717,12 @@ enum ToServerCommand /* u16 command */ + + TOSERVER_BREATH = 0x42, + /* + u16 command + u16 breath + */ }; #endif diff --git a/src/content_sao.cpp b/src/content_sao.cpp index cc02a743..993859f1 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -935,6 +935,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, m_moved(false), m_inventory_not_sent(false), m_hp_not_sent(false), + m_breath_not_sent(false), m_wielded_item_not_sent(false), m_physics_override_speed(1), m_physics_override_jump(1), @@ -1370,6 +1371,16 @@ void PlayerSAO::setHP(s16 hp) } } +u16 PlayerSAO::getBreath() const +{ + return m_player->getBreath(); +} + +void PlayerSAO::setBreath(u16 breath) +{ + m_player->setBreath(breath); +} + void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) { m_armor_groups = armor_groups; diff --git a/src/content_sao.h b/src/content_sao.h index dca02bb0..bfce83d0 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -162,7 +162,8 @@ public: void rightClick(ServerActiveObject *clicker); s16 getHP() const; void setHP(s16 hp); - + u16 getBreath() const; + void setBreath(u16 breath); void setArmorGroups(const ItemGroupList &armor_groups); void setAnimation(v2f frame_range, float frame_speed, float frame_blend); void setBonePosition(std::string bone, v3f position, v3f rotation); @@ -282,6 +283,7 @@ public: bool m_moved; bool m_inventory_not_sent; bool m_hp_not_sent; + bool m_breath_not_sent; bool m_wielded_item_not_sent; float m_physics_override_speed; diff --git a/src/environment.cpp b/src/environment.cpp index cd878398..57fdfd7e 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2242,15 +2242,19 @@ void ClientEnvironment::step(float dtime) v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_gamedef->ndef()->get(n); - - if(c.isLiquid() && c.drowning){ - if(lplayer->breath > 10) - lplayer->breath = 11; - if(lplayer->breath > 0) - lplayer->breath -= 1; + if(c.isLiquid() && c.drowning && lplayer->hp > 0){ + u16 breath = lplayer->getBreath(); + if(breath > 10){ + breath = 11; + } + if(breath > 0){ + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); } - if(lplayer->breath == 0){ + if(lplayer->getBreath() == 0){ damageLocalPlayer(1, true); } } @@ -2262,10 +2266,16 @@ void ClientEnvironment::step(float dtime) v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_gamedef->ndef()->get(n); - - if(!c.isLiquid() || !c.drowning){ - if(lplayer->breath <= 10) - lplayer->breath += 1; + if (!lplayer->hp){ + lplayer->setBreath(11); + } + else if(!c.isLiquid() || !c.drowning){ + u16 breath = lplayer->getBreath(); + if(breath <= 10){ + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } } } @@ -2528,6 +2538,14 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) m_client_event_queue.push_back(event); } +void ClientEnvironment::updateLocalPlayerBreath(u16 breath) +{ + ClientEnvEvent event; + event.type = CEE_PLAYER_BREATH; + event.player_breath.amount = breath; + m_client_event_queue.push_back(event); +} + /* Client likes to call these */ diff --git a/src/environment.h b/src/environment.h index ac479999..e175d70d 100644 --- a/src/environment.h +++ b/src/environment.h @@ -395,7 +395,8 @@ class ClientSimpleObject; enum ClientEnvEventType { CEE_NONE, - CEE_PLAYER_DAMAGE + CEE_PLAYER_DAMAGE, + CEE_PLAYER_BREATH }; struct ClientEnvEvent @@ -408,6 +409,9 @@ struct ClientEnvEvent u8 amount; bool send_to_server; } player_damage; + struct{ + u16 amount; + } player_breath; }; }; @@ -462,6 +466,7 @@ public: */ void damageLocalPlayer(u8 damage, bool handle_hp=true); + void updateLocalPlayerBreath(u16 breath); /* Client likes to call these diff --git a/src/player.cpp b/src/player.cpp index 2a7a3084..8028fe6a 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -36,10 +36,10 @@ Player::Player(IGameDef *gamedef): camera_barely_in_ceiling(false), inventory(gamedef->idef()), hp(PLAYER_MAX_HP), - breath(-1), peer_id(PEER_ID_INEXISTENT), // protected m_gamedef(gamedef), + m_breath(-1), m_pitch(0), m_yaw(0), m_speed(0,0,0), @@ -177,11 +177,12 @@ void Player::serialize(std::ostream &os) args.setFloat("yaw", m_yaw); args.setV3F("position", m_position); args.setS32("hp", hp); + args.setS32("breath", m_breath); args.writeLines(os); os<<"PlayerArgsEnd\n"; - + inventory.serialize(os); } @@ -213,6 +214,11 @@ void Player::deSerialize(std::istream &is, std::string playername) }catch(SettingNotFoundException &e){ hp = 20; } + try{ + m_breath = args.getS32("breath"); + }catch(SettingNotFoundException &e){ + m_breath = 11; + } inventory.deSerialize(is); diff --git a/src/player.h b/src/player.h index 60645a60..7ddc40b3 100644 --- a/src/player.h +++ b/src/player.h @@ -160,6 +160,16 @@ public: return m_yaw; } + u16 getBreath() + { + return m_breath; + } + + virtual void setBreath(u16 breath) + { + m_breath = breath; + } + f32 getRadPitch() { return -1.0 * m_pitch * core::DEGTORAD; @@ -249,13 +259,12 @@ public: float physics_override_gravity; u16 hp; - u16 breath; float hurt_tilt_timer; float hurt_tilt_strength; u16 peer_id; - + std::string inventory_formspec; PlayerControl control; @@ -274,6 +283,7 @@ protected: IGameDef *m_gamedef; char m_name[PLAYERNAME_SIZE]; + u16 m_breath; f32 m_pitch; f32 m_yaw; v3f m_speed; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f90b5928..ee24789c 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -702,6 +702,33 @@ int ObjectRef::l_set_look_yaw(lua_State *L) return 1; } +// set_breath(self, breath) +int ObjectRef::l_set_breath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if(co == NULL) return 0; + u16 breath = luaL_checknumber(L, 2); + // Do it + co->setBreath(breath); + co->m_breath_not_sent = true; + return 0; +} + +// get_breath(self) +int ObjectRef::l_get_breath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if(co == NULL) return 0; + // Do it + u16 breath = co->getBreath(); + lua_pushinteger (L, breath); + return 1; +} + // set_inventory_formspec(self, formspec) int ObjectRef::l_set_inventory_formspec(lua_State *L) { @@ -1098,6 +1125,8 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_look_yaw), luamethod(ObjectRef, set_look_yaw), luamethod(ObjectRef, set_look_pitch), + luamethod(ObjectRef, get_breath), + luamethod(ObjectRef, set_breath), luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_player_control), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 57dac0e6..a8263844 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -179,6 +179,12 @@ private: // set_look_yaw(self, radians) static int l_set_look_yaw(lua_State *L); + // set_breath(self, breath) + static int l_set_breath(lua_State *L); + + // get_breath(self, breath) + static int l_get_breath(lua_State *L); + // set_inventory_formspec(self, formspec) static int l_set_inventory_formspec(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 955858c7..3bba193d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1139,6 +1139,13 @@ void Server::AsyncRunStep() SendPlayerHP(client->peer_id); } + /* + Send player breath if changed + */ + if(playersao->m_breath_not_sent){ + SendPlayerBreath(client->peer_id); + } + /* Send player inventories if necessary */ @@ -2105,6 +2112,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(g_settings->getBool("enable_damage")) SendPlayerHP(peer_id); + // Send Breath + SendPlayerBreath(peer_id); + // Send detached inventories sendDetachedInventories(peer_id); @@ -2583,6 +2593,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendPlayerHP(peer_id); } } + else if(command == TOSERVER_BREATH) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u16 breath = readU16(is); + playersao->setBreath(breath); + } else if(command == TOSERVER_PASSWORD) { /* @@ -3326,6 +3343,21 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) con.Send(peer_id, 0, data, true); } +void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_BREATH); + writeU16(os, breath); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + void Server::SendAccessDenied(con::Connection &con, u16 peer_id, const std::wstring &reason) { @@ -3755,6 +3787,15 @@ void Server::SendPlayerHP(u16 peer_id) SendHP(m_con, peer_id, playersao->getHP()); } +void Server::SendPlayerBreath(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + PlayerSAO *playersao = getPlayerSAO(peer_id); + assert(playersao); + playersao->m_breath_not_sent = false; + SendBreath(m_con, peer_id, playersao->getBreath()); +} + void Server::SendMovePlayer(u16 peer_id) { DSTACK(__FUNCTION_NAME); diff --git a/src/server.h b/src/server.h index edc5c489..65762d90 100644 --- a/src/server.h +++ b/src/server.h @@ -557,6 +557,7 @@ private: static void SendMovement(con::Connection &con, u16 peer_id); static void SendHP(con::Connection &con, u16 peer_id, u8 hp); + static void SendBreath(con::Connection &con, u16 peer_id, u16 breath); static void SendAccessDenied(con::Connection &con, u16 peer_id, const std::wstring &reason); static void SendDeathscreen(con::Connection &con, u16 peer_id, @@ -578,6 +579,7 @@ private: void SendChatMessage(u16 peer_id, const std::wstring &message); void BroadcastChatMessage(const std::wstring &message); void SendPlayerHP(u16 peer_id); + void SendPlayerBreath(u16 peer_id); void SendMovePlayer(u16 peer_id); void SendPlayerPrivileges(u16 peer_id); void SendPlayerInventoryFormspec(u16 peer_id);