Browse Source

Add nametag background setting and object property (#10937)

rubenwardy 3 years ago
parent
commit
f85e9ab925

+ 1 - 0
.clang-format

@@ -29,3 +29,4 @@ AlignAfterOpenBracket: DontAlign
 ContinuationIndentWidth: 16
 ConstructorInitializerIndentWidth: 16
 BreakConstructorInitializers: AfterColon
+AlwaysBreakTemplateDeclarations: Yes

+ 4 - 0
builtin/settingtypes.txt

@@ -451,6 +451,10 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
 
 [**Basic]
 
+#    Whether nametag backgrounds should be shown by default.
+#    Mods may still set a background.
+show_nametag_backgrounds (Show nametag backgrounds by default) bool true
+
 #    Enable vertex buffer objects.
 #    This should greatly improve graphics performance.
 enable_vbo (VBO) bool true

+ 14 - 4
doc/lua_api.txt

@@ -6274,15 +6274,21 @@ object you are working with still exists.
 * `get_nametag_attributes()`
     * returns a table with the attributes of the nametag of an object
     * {
-        color = {a=0..255, r=0..255, g=0..255, b=0..255},
         text = "",
+        color = {a=0..255, r=0..255, g=0..255, b=0..255},
+        bgcolor = {a=0..255, r=0..255, g=0..255, b=0..255},
       }
 * `set_nametag_attributes(attributes)`
     * sets the attributes of the nametag of an object
     * `attributes`:
       {
-        color = ColorSpec,
         text = "My Nametag",
+        color = ColorSpec,
+        -- ^ Text color
+        bgcolor = ColorSpec or false,
+        -- ^ Sets background color of nametag
+        -- `false` will cause the background to be set automatically based on user settings
+        -- Default: false
       }
 
 #### Lua entity only (no-op for other objects)
@@ -6956,9 +6962,13 @@ Player properties need to be saved manually.
         -- For all other objects, a nil or empty string removes the nametag.
         -- To hide a nametag, set its color alpha to zero. That will disable it entirely.
 
-
         nametag_color = <ColorSpec>,
-        -- Sets color of nametag
+        -- Sets text color of nametag
+
+        nametag_bgcolor = <ColorSpec>,
+        -- Sets background color of nametag
+        -- `false` will cause the background to be set automatically based on user settings.
+        -- Default: false
 
         infotext = "",
         -- By default empty, text to be shown when pointed at object

+ 14 - 2
games/devtest/mods/testentities/visuals.lua

@@ -103,23 +103,35 @@ minetest.register_entity("testentities:nametag", {
 
 	on_activate = function(self, staticdata)
 		if staticdata ~= "" then
-			self.color = minetest.deserialize(staticdata).color
+			local data = minetest.deserialize(staticdata)
+			self.color = data.color
+			self.bgcolor = data.bgcolor
 		else
 			self.color = {
 				r = math.random(0, 255),
 				g = math.random(0, 255),
 				b = math.random(0, 255),
 			}
+
+			if math.random(0, 10) > 5 then
+				self.bgcolor = {
+					r = math.random(0, 255),
+					g = math.random(0, 255),
+					b = math.random(0, 255),
+					a = math.random(0, 255),
+				}
+			end
 		end
 
 		assert(self.color)
 		self.object:set_properties({
 			nametag = tostring(math.random(1000, 10000)),
 			nametag_color = self.color,
+			nametag_bgcolor = self.bgcolor,
 		})
 	end,
 
 	get_staticdata = function(self)
-		return minetest.serialize({ color = self.color })
+		return minetest.serialize({ color = self.color, bgcolor = self.bgcolor })
 	end,
 })

+ 2 - 1
src/activeobjectmgr.h

@@ -25,7 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class TestClientActiveObjectMgr;
 class TestServerActiveObjectMgr;
 
-template <typename T> class ActiveObjectMgr
+template <typename T>
+class ActiveObjectMgr
 {
 	friend class ::TestClientActiveObjectMgr;
 	friend class ::TestServerActiveObjectMgr;

+ 13 - 20
src/client/camera.cpp

@@ -79,6 +79,7 @@ Camera::Camera(MapDrawControl &draw_control, Client *client):
 	m_cache_fov                 = std::fmax(g_settings->getFloat("fov"), 45.0f);
 	m_arm_inertia               = g_settings->getBool("arm_inertia");
 	m_nametags.clear();
+	m_show_nametag_backgrounds  = g_settings->getBool("show_nametag_backgrounds");
 }
 
 Camera::~Camera()
@@ -696,18 +697,14 @@ void Camera::drawNametags()
 	v2u32 screensize = driver->getScreenSize();
 
 	for (const Nametag *nametag : m_nametags) {
-		if (nametag->nametag_color.getAlpha() == 0) {
-			// Enforce hiding nametag,
-			// because if freetype is enabled, a grey
-			// shadow can remain.
-			continue;
-		}
-		v3f pos = nametag->parent_node->getAbsolutePosition() + nametag->nametag_pos * BS;
+		// Nametags are hidden in GenericCAO::updateNametag()
+
+		v3f pos = nametag->parent_node->getAbsolutePosition() + nametag->pos * BS;
 		f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f };
 		trans.multiplyWith1x4Matrix(transformed_pos);
 		if (transformed_pos[3] > 0) {
 			std::wstring nametag_colorless =
-				unescape_translate(utf8_to_wide(nametag->nametag_text));
+				unescape_translate(utf8_to_wide(nametag->text));
 			core::dimension2d<u32> textsize = font->getDimension(
 				nametag_colorless.c_str());
 			f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
@@ -720,26 +717,22 @@ void Camera::drawNametags()
 			core::rect<s32> size(0, 0, textsize.Width, textsize.Height);
 			core::rect<s32> bg_size(-2, 0, textsize.Width+2, textsize.Height);
 
-			video::SColor textColor = nametag->nametag_color;
-
-			bool darkBackground = textColor.getLuminance() > 186;
-			video::SColor backgroundColor = darkBackground
-					? video::SColor(50, 50, 50, 50)
-					: video::SColor(50, 255, 255, 255);
-			driver->draw2DRectangle(backgroundColor, bg_size + screen_pos);
+			auto bgcolor = nametag->getBgColor(m_show_nametag_backgrounds);
+			if (bgcolor.getAlpha() != 0)
+				driver->draw2DRectangle(bgcolor, bg_size + screen_pos);
 
 			font->draw(
-				translate_string(utf8_to_wide(nametag->nametag_text)).c_str(),
-				size + screen_pos, textColor);
+				translate_string(utf8_to_wide(nametag->text)).c_str(),
+				size + screen_pos, nametag->textcolor);
 		}
 	}
 }
 
 Nametag *Camera::addNametag(scene::ISceneNode *parent_node,
-		const std::string &nametag_text, video::SColor nametag_color,
-		const v3f &pos)
+		const std::string &text, video::SColor textcolor,
+		Optional<video::SColor> bgcolor, const v3f &pos)
 {
-	Nametag *nametag = new Nametag(parent_node, nametag_text, nametag_color, pos);
+	Nametag *nametag = new Nametag(parent_node, text, textcolor, bgcolor, pos);
 	m_nametags.push_back(nametag);
 	return nametag;
 }

+ 34 - 13
src/client/camera.h

@@ -25,27 +25,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <ICameraSceneNode.h>
 #include <ISceneNode.h>
 #include <list>
+#include "util/Optional.h"
 
 class LocalPlayer;
 struct MapDrawControl;
 class Client;
 class WieldMeshSceneNode;
 
-struct Nametag {
+struct Nametag
+{
+	scene::ISceneNode *parent_node;
+	std::string text;
+	video::SColor textcolor;
+	Optional<video::SColor> bgcolor;
+	v3f pos;
+
 	Nametag(scene::ISceneNode *a_parent_node,
-			const std::string &a_nametag_text,
-			const video::SColor &a_nametag_color,
-			const v3f &a_nametag_pos):
+			const std::string &text,
+			const video::SColor &textcolor,
+			const Optional<video::SColor> &bgcolor,
+			const v3f &pos):
 		parent_node(a_parent_node),
-		nametag_text(a_nametag_text),
-		nametag_color(a_nametag_color),
-		nametag_pos(a_nametag_pos)
+		text(text),
+		textcolor(textcolor),
+		bgcolor(bgcolor),
+		pos(pos)
 	{
 	}
-	scene::ISceneNode *parent_node;
-	std::string nametag_text;
-	video::SColor nametag_color;
-	v3f nametag_pos;
+
+	video::SColor getBgColor(bool use_fallback) const
+	{
+		if (bgcolor)
+			return bgcolor.value();
+		else if (!use_fallback)
+			return video::SColor(0, 0, 0, 0);
+		else if (textcolor.getLuminance() > 186)
+			// Dark background for light text
+			return video::SColor(50, 50, 50, 50);
+		else
+			// Light background for dark text
+			return video::SColor(50, 255, 255, 255);
+	}
 };
 
 enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT};
@@ -164,8 +184,8 @@ public:
 	}
 
 	Nametag *addNametag(scene::ISceneNode *parent_node,
-		const std::string &nametag_text, video::SColor nametag_color,
-		const v3f &pos);
+		const std::string &text, video::SColor textcolor,
+		Optional<video::SColor> bgcolor, const v3f &pos);
 
 	void removeNametag(Nametag *nametag);
 
@@ -245,4 +265,5 @@ private:
 	bool m_arm_inertia;
 
 	std::list<Nametag *> m_nametags;
+	bool m_show_nametag_backgrounds;
 };

+ 7 - 5
src/client/content_cao.cpp

@@ -934,7 +934,7 @@ void GenericCAO::updateNametag()
 	if (m_is_local_player) // No nametag for local player
 		return;
 
-	if (m_prop.nametag.empty()) {
+	if (m_prop.nametag.empty() || m_prop.nametag_color.getAlpha() == 0) {
 		// Delete nametag
 		if (m_nametag) {
 			m_client->getCamera()->removeNametag(m_nametag);
@@ -952,12 +952,14 @@ void GenericCAO::updateNametag()
 	if (!m_nametag) {
 		// Add nametag
 		m_nametag = m_client->getCamera()->addNametag(node,
-			m_prop.nametag, m_prop.nametag_color, pos);
+			m_prop.nametag, m_prop.nametag_color,
+			m_prop.nametag_bgcolor, pos);
 	} else {
 		// Update nametag
-		m_nametag->nametag_text = m_prop.nametag;
-		m_nametag->nametag_color = m_prop.nametag_color;
-		m_nametag->nametag_pos = pos;
+		m_nametag->text = m_prop.nametag;
+		m_nametag->textcolor = m_prop.nametag_color;
+		m_nametag->bgcolor = m_prop.nametag_bgcolor;
+		m_nametag->pos = pos;
 	}
 }
 

+ 1 - 0
src/defaultsettings.cpp

@@ -240,6 +240,7 @@ void set_default_settings()
 #endif
 	settings->setDefault("enable_particles", "true");
 	settings->setDefault("arm_inertia", "true");
+	settings->setDefault("show_nametag_backgrounds", "true");
 
 	settings->setDefault("enable_minimap", "true");
 	settings->setDefault("minimap_shape_round", "true");

+ 22 - 0
src/object_properties.cpp

@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/basic_macros.h"
 #include <sstream>
 
+static const video::SColor NULL_BGCOLOR{0, 1, 1, 1};
+
 ObjectProperties::ObjectProperties()
 {
 	textures.emplace_back("unknown_object.png");
@@ -62,6 +64,13 @@ std::string ObjectProperties::dump()
 	os << ", nametag=" << nametag;
 	os << ", nametag_color=" << "\"" << nametag_color.getAlpha() << "," << nametag_color.getRed()
 			<< "," << nametag_color.getGreen() << "," << nametag_color.getBlue() << "\" ";
+
+	if (nametag_bgcolor)
+		os << ", nametag_bgcolor=" << "\"" << nametag_color.getAlpha() << "," << nametag_color.getRed()
+		   << "," << nametag_color.getGreen() << "," << nametag_color.getBlue() << "\" ";
+	else
+		os << ", nametag_bgcolor=null ";
+
 	os << ", selectionbox=" << PP(selectionbox.MinEdge) << "," << PP(selectionbox.MaxEdge);
 	os << ", pointable=" << pointable;
 	os << ", static_save=" << static_save;
@@ -121,6 +130,13 @@ void ObjectProperties::serialize(std::ostream &os) const
 	writeU8(os, shaded);
 	writeU8(os, show_on_minimap);
 
+	if (!nametag_bgcolor)
+		writeARGB8(os, NULL_BGCOLOR);
+	else if (nametag_bgcolor.value().getAlpha() == 0)
+		writeARGB8(os, video::SColor(0, 0, 0, 0));
+	else
+		writeARGB8(os, nametag_bgcolor.value());
+
 	// Add stuff only at the bottom.
 	// Never remove anything, because we don't want new versions of this
 }
@@ -182,5 +198,11 @@ void ObjectProperties::deSerialize(std::istream &is)
 		if (is.eof())
 			return;
 		show_on_minimap = tmp;
+
+		auto bgcolor = readARGB8(is);
+		if (bgcolor != NULL_BGCOLOR)
+			nametag_bgcolor = bgcolor;
+		else
+			nametag_bgcolor = nullopt;
 	} catch (SerializationError &e) {}
 }

+ 2 - 0
src/object_properties.h

@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include <map>
 #include <vector>
+#include "util/Optional.h"
 
 struct ObjectProperties
 {
@@ -53,6 +54,7 @@ struct ObjectProperties
 	s8 glow = 0;
 	std::string nametag = "";
 	video::SColor nametag_color = video::SColor(255, 255, 255, 255);
+	Optional<video::SColor> nametag_bgcolor = nullopt;
 	f32 automatic_face_movement_max_rotation_per_sec = -1.0f;
 	std::string infotext;
 	//! For dropped items, this contains item information.

+ 18 - 0
src/script/common/c_content.cpp

@@ -312,6 +312,17 @@ void read_object_properties(lua_State *L, int index,
 			prop->nametag_color = color;
 	}
 	lua_pop(L, 1);
+	lua_getfield(L, -1, "nametag_bgcolor");
+	if (!lua_isnil(L, -1)) {
+		if (lua_toboolean(L, -1)) {
+			video::SColor color;
+			if (read_color(L, -1, &color))
+				prop->nametag_bgcolor = color;
+		} else {
+			prop->nametag_bgcolor = nullopt;
+		}
+	}
+	lua_pop(L, 1);
 
 	lua_getfield(L, -1, "automatic_face_movement_max_rotation_per_sec");
 	if (lua_isnumber(L, -1)) {
@@ -403,6 +414,13 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
 	lua_setfield(L, -2, "nametag");
 	push_ARGB8(L, prop->nametag_color);
 	lua_setfield(L, -2, "nametag_color");
+	if (prop->nametag_bgcolor) {
+		push_ARGB8(L, prop->nametag_bgcolor.value());
+		lua_setfield(L, -2, "nametag_bgcolor");
+	} else {
+		lua_pushboolean(L, false);
+		lua_setfield(L, -2, "nametag_bgcolor");
+	}
 	lua_pushnumber(L, prop->automatic_face_movement_max_rotation_per_sec);
 	lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec");
 	lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size());

+ 16 - 8
src/script/common/helper.cpp

@@ -50,22 +50,26 @@ bool LuaHelper::isNaN(lua_State *L, int idx)
 /*
  * Read template functions
  */
-template <> bool LuaHelper::readParam(lua_State *L, int index)
+template <>
+bool LuaHelper::readParam(lua_State *L, int index)
 {
 	return lua_toboolean(L, index) != 0;
 }
 
-template <> s16 LuaHelper::readParam(lua_State *L, int index)
+template <>
+s16 LuaHelper::readParam(lua_State *L, int index)
 {
 	return lua_tonumber(L, index);
 }
 
-template <> int LuaHelper::readParam(lua_State *L, int index)
+template <>
+int LuaHelper::readParam(lua_State *L, int index)
 {
 	return luaL_checkint(L, index);
 }
 
-template <> float LuaHelper::readParam(lua_State *L, int index)
+template <>
+float LuaHelper::readParam(lua_State *L, int index)
 {
 	if (isNaN(L, index))
 		throw LuaError("NaN value is not allowed.");
@@ -73,7 +77,8 @@ template <> float LuaHelper::readParam(lua_State *L, int index)
 	return (float)luaL_checknumber(L, index);
 }
 
-template <> v2s16 LuaHelper::readParam(lua_State *L, int index)
+template <>
+v2s16 LuaHelper::readParam(lua_State *L, int index)
 {
 	v2s16 p;
 	CHECK_POS_TAB(index);
@@ -88,7 +93,8 @@ template <> v2s16 LuaHelper::readParam(lua_State *L, int index)
 	return p;
 }
 
-template <> v2f LuaHelper::readParam(lua_State *L, int index)
+template <>
+v2f LuaHelper::readParam(lua_State *L, int index)
 {
 	v2f p;
 	CHECK_POS_TAB(index);
@@ -103,7 +109,8 @@ template <> v2f LuaHelper::readParam(lua_State *L, int index)
 	return p;
 }
 
-template <> v3f LuaHelper::readParam(lua_State *L, int index)
+template <>
+v3f LuaHelper::readParam(lua_State *L, int index)
 {
 	v3f p;
 	CHECK_POS_TAB(index);
@@ -122,7 +129,8 @@ template <> v3f LuaHelper::readParam(lua_State *L, int index)
 	return p;
 }
 
-template <> std::string LuaHelper::readParam(lua_State *L, int index)
+template <>
+std::string LuaHelper::readParam(lua_State *L, int index)
 {
 	size_t length;
 	std::string result;

+ 2 - 1
src/script/common/helper.h

@@ -38,7 +38,8 @@ protected:
 	 * @param index Lua Index to read
 	 * @return read value from Lua
 	 */
-	template <typename T> static T readParam(lua_State *L, int index);
+	template <typename T>
+	static T readParam(lua_State *L, int index);
 
 	/**
 	 * Read a value using a template type T from Lua State L and index

+ 26 - 3
src/script/lua_api/l_object.cpp

@@ -737,6 +737,18 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L)
 	}
 	lua_pop(L, 1);
 
+	lua_getfield(L, -1, "bgcolor");
+	if (!lua_isnil(L, -1)) {
+		if (lua_toboolean(L, -1)) {
+			video::SColor color;
+			if (read_color(L, -1, &color))
+				prop->nametag_bgcolor = color;
+		} else {
+			prop->nametag_bgcolor = nullopt;
+		}
+	}
+	lua_pop(L, 1);
+
 	std::string nametag = getstringfield_default(L, 2, "text", "");
 	prop->nametag = nametag;
 
@@ -758,13 +770,24 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L)
 	if (!prop)
 		return 0;
 
-	video::SColor color = prop->nametag_color;
-
 	lua_newtable(L);
-	push_ARGB8(L, color);
+
+	push_ARGB8(L, prop->nametag_color);
 	lua_setfield(L, -2, "color");
+
+	if (prop->nametag_bgcolor) {
+		push_ARGB8(L, prop->nametag_bgcolor.value());
+		lua_setfield(L, -2, "bgcolor");
+	} else {
+		lua_pushboolean(L, false);
+		lua_setfield(L, -2, "bgcolor");
+	}
+
 	lua_pushstring(L, prop->nametag.c_str());
 	lua_setfield(L, -2, "text");
+
+
+
 	return 1;
 }
 

+ 77 - 0
src/util/Optional.h

@@ -0,0 +1,77 @@
+/*
+Minetest
+Copyright (C) 2021  rubenwardy
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "debug.h"
+
+struct nullopt_t
+{
+};
+constexpr nullopt_t nullopt{};
+
+/**
+ * An implementation of optional for C++11, which aims to be
+ * compatible with a subset of std::optional features.
+ *
+ * Unfortunately, Minetest doesn't use C++17 yet.
+ *
+ * @tparam T The type to be stored
+ */
+template <typename T>
+class Optional
+{
+	bool m_has_value = false;
+	T m_value;
+
+public:
+	Optional() noexcept {}
+	Optional(nullopt_t) noexcept {}
+	Optional(const T &value) noexcept : m_has_value(true), m_value(value) {}
+	Optional(const Optional<T> &other) noexcept :
+			m_has_value(other.m_has_value), m_value(other.m_value)
+	{
+	}
+
+	void operator=(nullopt_t) noexcept { m_has_value = false; }
+
+	void operator=(const Optional<T> &other) noexcept
+	{
+		m_has_value = other.m_has_value;
+		m_value = other.m_value;
+	}
+
+	T &value()
+	{
+		FATAL_ERROR_IF(!m_has_value, "optional doesn't have value");
+		return m_value;
+	}
+
+	const T &value() const
+	{
+		FATAL_ERROR_IF(!m_has_value, "optional doesn't have value");
+		return m_value;
+	}
+
+	const T &value_or(const T &def) const { return m_has_value ? m_value : def; }
+
+	bool has_value() const noexcept { return m_has_value; }
+
+	explicit operator bool() const { return m_has_value; }
+};

+ 1 - 1
src/util/serialize.h

@@ -290,7 +290,7 @@ inline void writeS8(u8 *data, s8 i)
 
 inline void writeS16(u8 *data, s16 i)
 {
-	writeU16(data, (u16)i); 
+	writeU16(data, (u16)i);
 }
 
 inline void writeS32(u8 *data, s32 i)