diff --git a/projectfiles/VC14/wesnoth.vcxproj b/projectfiles/VC14/wesnoth.vcxproj
index 7d56e95abea9..b29784718fce 100644
--- a/projectfiles/VC14/wesnoth.vcxproj
+++ b/projectfiles/VC14/wesnoth.vcxproj
@@ -2624,6 +2624,9 @@
$(IntDir)Scripting\
$(IntDir)Scripting\
+
+ $(IntDir)Scripting\
+
$(IntDir)Scripting\
$(IntDir)Scripting\
diff --git a/projectfiles/VC16/wesnoth.vcxproj b/projectfiles/VC16/wesnoth.vcxproj
index 94e3391281ce..4d85939345d5 100644
--- a/projectfiles/VC16/wesnoth.vcxproj
+++ b/projectfiles/VC16/wesnoth.vcxproj
@@ -2634,6 +2634,9 @@
$(IntDir)Scripting\
$(IntDir)Scripting\
+
+ $(IntDir)Scripting\
+
$(IntDir)Scripting\
$(IntDir)Scripting\
@@ -3981,6 +3984,7 @@
+
diff --git a/source_lists/wesnoth b/source_lists/wesnoth
index 088e5c3bdc71..f9629aa70e00 100644
--- a/source_lists/wesnoth
+++ b/source_lists/wesnoth
@@ -310,6 +310,7 @@ scripting/application_lua_kernel.cpp
scripting/debug_lua.cpp
scripting/game_lua_kernel.cpp
scripting/lua_audio.cpp
+scripting/lua_color.cpp
scripting/lua_common.cpp
scripting/lua_cpp_function.cpp
scripting/lua_fileops.cpp
diff --git a/src/scripting/lua_color.cpp b/src/scripting/lua_color.cpp
new file mode 100644
index 000000000000..f44ebb98eb0c
--- /dev/null
+++ b/src/scripting/lua_color.cpp
@@ -0,0 +1,174 @@
+/*
+Copyright (C) 2020-2020 by the Battle for Wesnoth Project https://www.wesnoth.org/
+
+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.
+
+See the COPYING file for more details.
+*/
+
+#include "color_range.hpp"
+#include "scripting/lua_color.hpp"
+#include "scripting/lua_common.hpp"
+#include "scripting/push_check.hpp"
+#include "lua/lauxlib.h"
+#include "lua/lua.h" // for lua_State, lua_settop, etc
+#include "log.hpp"
+#include "game_config.hpp"
+
+static lg::log_domain log_scripting_lua("scripting/lua");
+#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
+#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
+
+static const char colorKey[] = "color range";
+
+bool luaW_iscolor(lua_State* L, int index)
+{
+ return luaL_testudata(L, index, colorKey) != nullptr;
+}
+
+static color_range& LuaW_checkcolor(lua_State *L, int index)
+{
+ if(!luaW_iscolor(L, index)) {
+ luaW_type_error(L, index, "color");
+ throw "luaW_type_error returned";
+ }
+ return *static_cast(lua_touserdata(L, index));
+}
+
+
+color_range* luaW_pushcolor(lua_State *L, const color_range& color)
+{
+ color_range* res = new(L) color_range(color);
+ luaL_setmetatable(L, colorKey);
+ return res;
+}
+
+int luaW_pushsinglecolor(lua_State *L, const color_t& color)
+{
+ lua_createtable(L, 0, 4);
+ luaW_table_set(L, -1, "r", color.r);
+ luaW_table_set(L, -1, "g", color.g);
+ luaW_table_set(L, -1, "b", color.b);
+ luaW_table_set(L, -1, "a", color.a);
+ return 1;
+}
+
+static int impl_color_collect(lua_State *L)
+{
+ color_range *c = static_cast(lua_touserdata(L, 1));
+ c->color_range::~color_range();
+ return 0;
+}
+
+/**
+ * Checks two color units for equality. (__eq metamethod)
+ */
+static int impl_color_equality(lua_State* L)
+{
+ color_range& left = LuaW_checkcolor(L, 1);
+ color_range& right = LuaW_checkcolor(L, 2);
+ const bool equal = left == right;
+ lua_pushboolean(L, equal);
+ return 1;
+}
+
+/**
+ * Turns a lua color to string. (__tostring metamethod)
+ */
+static int impl_color_tostring(lua_State* L)
+{
+ color_range& c = LuaW_checkcolor(L, 1);
+ //TODO: is this the best way to call tostring?
+ lua_push(L, c.debug());
+ return 1;
+}
+
+
+/**
+ * - Arg 1: userdata (ignored).
+ * - Arg 2: string containing the name of the color.
+ * - Ret 1: color_range containing the color.
+ */
+static int impl_get_color(lua_State *L)
+{
+ std::string color_id = luaL_checkstring(L, 2);
+ luaW_pushcolor(L, game_config::color_info(color_id));
+ return 1;
+}
+
+static int impl_color_get(lua_State *L)
+{
+ color_range& c = LuaW_checkcolor(L, 1);
+ char const *m = luaL_checkstring(L, 2);
+
+ if(strcmp(m, "min") == 0) {
+ return luaW_pushsinglecolor(L, c.min());
+ }
+ if(strcmp(m, "max") == 0) {
+ return luaW_pushsinglecolor(L, c.max());
+ }
+ if(strcmp(m, "mid") == 0) {
+ return luaW_pushsinglecolor(L, c.mid());
+ }
+ if(strcmp(m, "minimap") == 0) {
+ return luaW_pushsinglecolor(L, c.rep());
+ }
+ // TODO: i think this shortcut would be useful, but im not sure yet on the best attribute name.
+ //if(strcmp(m, "pango_hex") == 0) {
+ // lua_push(L, c.mid().to_hex_string());
+ // return 1;
+ //}
+ return 0;
+}
+
+static int impl_color_set(lua_State *L)
+{
+ return luaL_argerror(L, 2, "color objects canot be modified");
+}
+
+namespace lua_colors {
+ std::string register_metatables(lua_State* L)
+ {
+ std::ostringstream cmd_out;
+
+ // Create the getunit metatable.
+ cmd_out << "Adding color metatable...\n";
+
+ luaL_newmetatable(L, colorKey);
+ lua_pushcfunction(L, impl_color_collect);
+ lua_setfield(L, -2, "__gc");
+ lua_pushcfunction(L, impl_color_equality);
+ lua_setfield(L, -2, "__eq");
+ lua_pushcfunction(L, impl_color_tostring);
+ lua_setfield(L, -2, "__tostring");
+ lua_pushcfunction(L, impl_color_get);
+ lua_setfield(L, -2, "__index");
+ lua_pushcfunction(L, impl_color_set);
+ lua_setfield(L, -2, "__newindex");
+ lua_pushstring(L, "color range");
+ lua_setfield(L, -2, "__metatable");
+
+
+ // Create the current variable with its metatable.
+ cmd_out << "Adding wesnoth.colors table...\n";
+
+ lua_getglobal(L, "wesnoth");
+ lua_newuserdata(L, 0);
+ lua_createtable(L, 0, 2);
+ lua_pushcfunction(L, impl_get_color);
+ lua_setfield(L, -2, "__index");
+ lua_pushstring(L, "colors table");
+ lua_setfield(L, -2, "__metatable");
+ lua_setmetatable(L, -2);
+ lua_setfield(L, -2, "colors");
+ lua_pop(L, 1);
+
+ return cmd_out.str();
+ }
+}
+
diff --git a/src/scripting/lua_color.hpp b/src/scripting/lua_color.hpp
new file mode 100644
index 000000000000..fe5c0a438bf2
--- /dev/null
+++ b/src/scripting/lua_color.hpp
@@ -0,0 +1,19 @@
+/*
+Copyright (C) 2020-2020 by the Battle for Wesnoth Project https://www.wesnoth.org/
+
+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.
+
+See the COPYING file for more details.
+*/
+
+#include
+struct lua_State;
+
+namespace lua_colors {
+ std::string register_metatables(lua_State* L);
+}
\ No newline at end of file
diff --git a/src/scripting/lua_kernel_base.cpp b/src/scripting/lua_kernel_base.cpp
index 76ac91412b43..431c2566d4ee 100644
--- a/src/scripting/lua_kernel_base.cpp
+++ b/src/scripting/lua_kernel_base.cpp
@@ -28,6 +28,7 @@
#include "scripting/debug_lua.hpp"
#endif
+#include "scripting/lua_color.hpp"
#include "scripting/lua_common.hpp"
#include "scripting/lua_cpp_function.hpp"
#include "scripting/lua_fileops.hpp"
@@ -570,6 +571,8 @@ lua_kernel_base::lua_kernel_base()
// Create formula bridge metatables
cmd_log_ << lua_formula_bridge::register_metatables(L);
+ cmd_log_ << lua_colors::register_metatables(L);
+
// Create the Lua interpreter table
cmd_log_ << "Sandboxing Lua interpreter...\nTo make variables visible outside the interpreter, assign to _G.variable.\n";
cmd_log_ << "The special variable _ holds the result of the last expression (if any).\n";
diff --git a/src/scripting/push_check.hpp b/src/scripting/push_check.hpp
index 6c0f6a0bc6b3..5691bda9f476 100644
--- a/src/scripting/push_check.hpp
+++ b/src/scripting/push_check.hpp
@@ -397,3 +397,17 @@ lua_check_impl::remove_constref luaW_table_get_def(lua_State *L, int index, u
lua_pop(L, 1);
return res;
}
+
+
+template
+void luaW_table_set(lua_State *L, int index, utils::string_view k, const T& value)
+{
+ if(!lua_istable(L, index)) {
+ luaL_argerror(L, index, "table expected");
+ }
+
+ index = lua_absindex(L, index);
+ lua_pushlstring(L, k.data(), k.size());
+ lua_push(L, value);
+ lua_settable(L, index);
+}