Add nametag background setting and object property (#10937)

This commit is contained in:
rubenwardy 2021-02-17 19:51:28 +00:00 committed by GitHub
parent a8f6befd39
commit f85e9ab925
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 254 additions and 58 deletions

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

@ -451,6 +451,10 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
[**Basic] [**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. # Enable vertex buffer objects.
# This should greatly improve graphics performance. # This should greatly improve graphics performance.
enable_vbo (VBO) bool true enable_vbo (VBO) bool true

@ -6274,15 +6274,21 @@ object you are working with still exists.
* `get_nametag_attributes()` * `get_nametag_attributes()`
* returns a table with the attributes of the nametag of an object * 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 = "", 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)` * `set_nametag_attributes(attributes)`
* sets the attributes of the nametag of an object * sets the attributes of the nametag of an object
* `attributes`: * `attributes`:
{ {
color = ColorSpec,
text = "My Nametag", 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) #### 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. -- 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. -- To hide a nametag, set its color alpha to zero. That will disable it entirely.
nametag_color = <ColorSpec>, 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 = "", infotext = "",
-- By default empty, text to be shown when pointed at object -- By default empty, text to be shown when pointed at object

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

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

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

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

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

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

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

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

@ -312,6 +312,17 @@ void read_object_properties(lua_State *L, int index,
prop->nametag_color = color; prop->nametag_color = color;
} }
lua_pop(L, 1); 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"); lua_getfield(L, -1, "automatic_face_movement_max_rotation_per_sec");
if (lua_isnumber(L, -1)) { if (lua_isnumber(L, -1)) {
@ -403,6 +414,13 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
lua_setfield(L, -2, "nametag"); lua_setfield(L, -2, "nametag");
push_ARGB8(L, prop->nametag_color); push_ARGB8(L, prop->nametag_color);
lua_setfield(L, -2, "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_pushnumber(L, prop->automatic_face_movement_max_rotation_per_sec);
lua_setfield(L, -2, "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()); lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size());

@ -50,22 +50,26 @@ bool LuaHelper::isNaN(lua_State *L, int idx)
/* /*
* Read template functions * 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; 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); 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); 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)) if (isNaN(L, index))
throw LuaError("NaN value is not allowed."); 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); 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; v2s16 p;
CHECK_POS_TAB(index); CHECK_POS_TAB(index);
@ -88,7 +93,8 @@ template <> v2s16 LuaHelper::readParam(lua_State *L, int index)
return p; return p;
} }
template <> v2f LuaHelper::readParam(lua_State *L, int index) template <>
v2f LuaHelper::readParam(lua_State *L, int index)
{ {
v2f p; v2f p;
CHECK_POS_TAB(index); CHECK_POS_TAB(index);
@ -103,7 +109,8 @@ template <> v2f LuaHelper::readParam(lua_State *L, int index)
return p; return p;
} }
template <> v3f LuaHelper::readParam(lua_State *L, int index) template <>
v3f LuaHelper::readParam(lua_State *L, int index)
{ {
v3f p; v3f p;
CHECK_POS_TAB(index); CHECK_POS_TAB(index);
@ -122,7 +129,8 @@ template <> v3f LuaHelper::readParam(lua_State *L, int index)
return p; 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; size_t length;
std::string result; std::string result;

@ -38,7 +38,8 @@ protected:
* @param index Lua Index to read * @param index Lua Index to read
* @return read value from Lua * @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 * Read a value using a template type T from Lua State L and index

@ -737,6 +737,18 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L)
} }
lua_pop(L, 1); 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", ""); std::string nametag = getstringfield_default(L, 2, "text", "");
prop->nametag = nametag; prop->nametag = nametag;
@ -758,13 +770,24 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L)
if (!prop) if (!prop)
return 0; return 0;
video::SColor color = prop->nametag_color;
lua_newtable(L); lua_newtable(L);
push_ARGB8(L, color);
push_ARGB8(L, prop->nametag_color);
lua_setfield(L, -2, "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_pushstring(L, prop->nametag.c_str());
lua_setfield(L, -2, "text"); lua_setfield(L, -2, "text");
return 1; return 1;
} }

77
src/util/Optional.h Normal file

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

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