summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/CMakeLists.txt1
-rw-r--r--src/client/clientlauncher.cpp108
-rw-r--r--src/client/clientlauncher.h37
-rw-r--r--src/client/inputhandler.cpp119
-rw-r--r--src/client/inputhandler.h360
-rw-r--r--src/client/joystick_controller.cpp108
-rw-r--r--src/client/joystick_controller.h9
-rw-r--r--src/client/keys.h15
-rw-r--r--src/client/tile.cpp224
-rw-r--r--src/client/tile.h137
10 files changed, 721 insertions, 397 deletions
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index 5faa186a7..2d274ae68 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -1,5 +1,6 @@
set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/inputhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp
PARENT_SCOPE
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index 6145e3dde..289d1537b 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -42,31 +42,15 @@ gui::IGUIEnvironment *guienv = NULL;
gui::IGUIStaticText *guiroot = NULL;
MainMenuManager g_menumgr;
-bool noMenuActive()
+bool isMenuActive()
{
- return g_menumgr.menuCount() == 0;
+ return g_menumgr.menuCount() != 0;
}
// Passed to menus to allow disconnecting and exiting
MainGameCallback *g_gamecallback = NULL;
-// Instance of the time getter
-static TimeGetter *g_timegetter = NULL;
-
-u32 getTimeMs()
-{
- if (g_timegetter == NULL)
- return 0;
- return g_timegetter->getTime(PRECISION_MILLI);
-}
-
-u32 getTime(TimePrecision prec) {
- if (g_timegetter == NULL)
- return 0;
- return g_timegetter->getTime(prec);
-}
-
ClientLauncher::~ClientLauncher()
{
if (receiver)
@@ -96,9 +80,6 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
return false;
}
- // Create time getter
- g_timegetter = new IrrlichtTimeGetter(device);
-
// Speed tests (done after irrlicht is loaded to get timer)
if (cmd_args.getFlag("speedtests")) {
dstream << "Running speed tests" << std::endl;
@@ -114,7 +95,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
porting::setXorgClassHint(video_driver->getExposedVideoData(), PROJECT_NAME_C);
- porting::setXorgWindowIcon(device);
+ porting::setWindowIcon(device);
/*
This changes the minimum allowed number of vertices in a VBO.
@@ -127,10 +108,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
device->setResizable(true);
- if (random_input)
- input = new RandomInputHandler();
- else
- input = new RealInputHandler(device, receiver);
+ init_input();
smgr = device->getSceneManager();
smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
@@ -337,6 +315,33 @@ bool ClientLauncher::init_engine()
return device != NULL;
}
+void ClientLauncher::init_input()
+{
+ if (random_input)
+ input = new RandomInputHandler();
+ else
+ input = new RealInputHandler(device, receiver);
+
+ if (g_settings->getBool("enable_joysticks")) {
+ irr::core::array<irr::SJoystickInfo> infos;
+ std::vector<irr::SJoystickInfo> joystick_infos;
+
+ // Make sure this is called maximum once per
+ // irrlicht device, otherwise it will give you
+ // multiple events for the same joystick.
+ if (device->activateJoysticks(infos)) {
+ infostream << "Joystick support enabled" << std::endl;
+ joystick_infos.reserve(infos.size());
+ for (u32 i = 0; i < infos.size(); i++) {
+ joystick_infos.push_back(infos[i]);
+ }
+ input->joystick.onJoystickConnect(joystick_infos);
+ } else {
+ errorstream << "Could not activate joystick support." << std::endl;
+ }
+ }
+}
+
bool ClientLauncher::launch_game(std::string &error_message,
bool reconnect_requested, GameParams &game_params,
const Settings &cmd_args)
@@ -403,15 +408,14 @@ bool ClientLauncher::launch_game(std::string &error_message,
return false;
}
- if (menudata.name == "")
- menudata.name = std::string("Guest") + itos(myrand_range(1000, 9999));
- else
- playername = menudata.name;
+ if (menudata.name == "" && !simple_singleplayer_mode) {
+ error_message = gettext("Please choose a name!");
+ return false;
+ }
+ playername = menudata.name;
password = menudata.password;
- g_settings->set("name", playername);
-
current_playername = playername;
current_password = password;
current_address = address;
@@ -424,13 +428,16 @@ bool ClientLauncher::launch_game(std::string &error_message,
current_password = "";
current_address = "";
current_port = myrand_range(49152, 65535);
- } else if (address != "") {
- ServerListSpec server;
- server["name"] = menudata.servername;
- server["address"] = menudata.address;
- server["port"] = menudata.port;
- server["description"] = menudata.serverdescription;
- ServerList::insert(server);
+ } else {
+ g_settings->set("name", playername);
+ if (address != "") {
+ ServerListSpec server;
+ server["name"] = menudata.servername;
+ server["address"] = menudata.address;
+ server["port"] = menudata.port;
+ server["description"] = menudata.serverdescription;
+ ServerList::insert(server);
+ }
}
infostream << "Selected world: " << worldspec.name
@@ -489,7 +496,7 @@ void ClientLauncher::main_menu(MainMenuData *menudata)
infostream << "Waiting for other menus" << std::endl;
while (device->run() && *kill == false) {
- if (noMenuActive())
+ if (!isMenuActive())
break;
driver->beginScene(true, true, video::SColor(255, 128, 128, 128));
guienv->drawAll();
@@ -528,7 +535,7 @@ bool ClientLauncher::create_engine_device()
// Determine driver
video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
- std::string driverstring = g_settings->get("video_driver");
+ const std::string &driverstring = g_settings->get("video_driver");
std::vector<video::E_DRIVER_TYPE> drivers
= porting::getSupportedVideoDrivers();
u32 i;
@@ -564,25 +571,8 @@ bool ClientLauncher::create_engine_device()
device = createDeviceEx(params);
- if (device) {
- if (g_settings->getBool("enable_joysticks")) {
- irr::core::array<irr::SJoystickInfo> infos;
- std::vector<irr::SJoystickInfo> joystick_infos;
- // Make sure this is called maximum once per
- // irrlicht device, otherwise it will give you
- // multiple events for the same joystick.
- if (device->activateJoysticks(infos)) {
- infostream << "Joystick support enabled" << std::endl;
- joystick_infos.reserve(infos.size());
- for (u32 i = 0; i < infos.size(); i++) {
- joystick_infos.push_back(infos[i]);
- }
- } else {
- errorstream << "Could not activate joystick support." << std::endl;
- }
- }
+ if (device)
porting::initIrrlicht(device);
- }
return device != NULL;
}
diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h
index b10bbebc9..4ff77bc03 100644
--- a/src/client/clientlauncher.h
+++ b/src/client/clientlauncher.h
@@ -24,42 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/inputhandler.h"
#include "gameparams.h"
-// A small helper class
-class TimeGetter
-{
-public:
- virtual u32 getTime(TimePrecision prec) = 0;
-};
-
-// A precise irrlicht one
-class IrrlichtTimeGetter: public TimeGetter
-{
-public:
- IrrlichtTimeGetter(IrrlichtDevice *device):
- m_device(device)
- {}
- u32 getTime(TimePrecision prec)
- {
- if (prec == PRECISION_MILLI) {
- if (m_device == NULL)
- return 0;
- return m_device->getTimer()->getRealTime();
- } else {
- return porting::getTime(prec);
- }
- }
-private:
- IrrlichtDevice *m_device;
-};
-// Not so precise one which works without irrlicht
-class SimpleTimeGetter: public TimeGetter
-{
-public:
- u32 getTime(TimePrecision prec)
- {
- return porting::getTime(prec);
- }
-};
class ClientLauncher
{
@@ -91,6 +55,7 @@ public:
protected:
void init_args(GameParams &game_params, const Settings &cmd_args);
bool init_engine();
+ void init_input();
bool launch_game(std::string &error_message, bool reconnect_requested,
GameParams &game_params, const Settings &cmd_args);
diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp
new file mode 100644
index 000000000..9c7a94c4e
--- /dev/null
+++ b/src/client/inputhandler.cpp
@@ -0,0 +1,119 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#include "util/numeric.h"
+#include "inputhandler.h"
+#include "mainmenumanager.h"
+
+bool MyEventReceiver::OnEvent(const SEvent &event)
+{
+ /*
+ React to nothing here if a menu is active
+ */
+ if (isMenuActive()) {
+#ifdef HAVE_TOUCHSCREENGUI
+ if (m_touchscreengui) {
+ m_touchscreengui->Toggle(false);
+ }
+#endif
+ return g_menumgr.preprocessEvent(event);
+ }
+
+ // Remember whether each key is down or up
+ if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
+ const KeyPress &keyCode = event.KeyInput;
+ if (keysListenedFor[keyCode]) {
+ if (event.KeyInput.PressedDown) {
+ keyIsDown.set(keyCode);
+ keyWasDown.set(keyCode);
+ } else {
+ keyIsDown.unset(keyCode);
+ }
+ return true;
+ }
+ }
+
+#ifdef HAVE_TOUCHSCREENGUI
+ // case of touchscreengui we have to handle different events
+ if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) {
+ m_touchscreengui->translateEvent(event);
+ return true;
+ }
+#endif
+
+ if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+ /* TODO add a check like:
+ if (event.JoystickEvent != joystick_we_listen_for)
+ return false;
+ */
+ return joystick->handleEvent(event.JoystickEvent);
+ }
+ // handle mouse events
+ if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
+ if (isMenuActive()) {
+ left_active = false;
+ middle_active = false;
+ right_active = false;
+ } else {
+ left_active = event.MouseInput.isLeftPressed();
+ middle_active = event.MouseInput.isMiddlePressed();
+ right_active = event.MouseInput.isRightPressed();
+
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ leftclicked = true;
+ }
+ if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
+ rightclicked = true;
+ }
+ if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
+ leftreleased = true;
+ }
+ if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
+ rightreleased = true;
+ }
+ if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+ mouse_wheel += event.MouseInput.Wheel;
+ }
+ }
+ } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
+ static const LogLevel irr_loglev_conv[] = {
+ LL_VERBOSE, // ELL_DEBUG
+ LL_INFO, // ELL_INFORMATION
+ LL_WARNING, // ELL_WARNING
+ LL_ERROR, // ELL_ERROR
+ LL_NONE, // ELL_NONE
+ };
+ assert(event.LogEvent.Level < ARRLEN(irr_loglev_conv));
+ g_logger.log(irr_loglev_conv[event.LogEvent.Level],
+ std::string("Irrlicht: ") +
+ (const char *)event.LogEvent.Text);
+ return true;
+ }
+ /* always return false in order to continue processing events */
+ return false;
+}
+
+/*
+ * RandomInputHandler
+ */
+s32 RandomInputHandler::Rand(s32 min, s32 max)
+{
+ return (myrand() % (max - min + 1)) + min;
+}
diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h
index 824b0da2e..7c422d189 100644
--- a/src/client/inputhandler.h
+++ b/src/client/inputhandler.h
@@ -22,104 +22,87 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "joystick_controller.h"
+#include <list>
+#include "keycode.h"
-class MyEventReceiver : public IEventReceiver
-{
-public:
- // This is the one method that we have to implement
- virtual bool OnEvent(const SEvent& event)
- {
- /*
- React to nothing here if a menu is active
- */
- if (noMenuActive() == false) {
#ifdef HAVE_TOUCHSCREENGUI
- if (m_touchscreengui != 0) {
- m_touchscreengui->Toggle(false);
- }
+#include "touchscreengui.h"
#endif
- return g_menumgr.preprocessEvent(event);
- }
- // Remember whether each key is down or up
- if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
- const KeyPress &keyCode = event.KeyInput;
- if (keysListenedFor[keyCode]) {
- if (event.KeyInput.PressedDown) {
- keyIsDown.set(keyCode);
- keyWasDown.set(keyCode);
- } else {
- keyIsDown.unset(keyCode);
- }
- return true;
- }
- }
+class KeyList : private std::list<KeyPress>
+{
+ typedef std::list<KeyPress> super;
+ typedef super::iterator iterator;
+ typedef super::const_iterator const_iterator;
-#ifdef HAVE_TOUCHSCREENGUI
- // case of touchscreengui we have to handle different events
- if ((m_touchscreengui != 0) &&
- (event.EventType == irr::EET_TOUCH_INPUT_EVENT)) {
- m_touchscreengui->translateEvent(event);
- return true;
- }
-#endif
+ virtual const_iterator find(const KeyPress &key) const
+ {
+ const_iterator f(begin());
+ const_iterator e(end());
+
+ while (f != e) {
+ if (*f == key)
+ return f;
- if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
- /* TODO add a check like:
- if (event.JoystickEvent != joystick_we_listen_for)
- return false;
- */
- return joystick->handleEvent(event.JoystickEvent);
+ ++f;
}
- // handle mouse events
- if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
- if (noMenuActive() == false) {
- left_active = false;
- middle_active = false;
- right_active = false;
- } else {
- left_active = event.MouseInput.isLeftPressed();
- middle_active = event.MouseInput.isMiddlePressed();
- right_active = event.MouseInput.isRightPressed();
-
- if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
- leftclicked = true;
- }
- if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
- rightclicked = true;
- }
- if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
- leftreleased = true;
- }
- if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
- rightreleased = true;
- }
- if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
- mouse_wheel += event.MouseInput.Wheel;
- }
- }
- } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
- static const LogLevel irr_loglev_conv[] = {
- LL_VERBOSE, // ELL_DEBUG
- LL_INFO, // ELL_INFORMATION
- LL_WARNING, // ELL_WARNING
- LL_ERROR, // ELL_ERROR
- LL_NONE, // ELL_NONE
- };
- assert(event.LogEvent.Level < ARRLEN(irr_loglev_conv));
- g_logger.log(irr_loglev_conv[event.LogEvent.Level],
- std::string("Irrlicht: ") + (const char*) event.LogEvent.Text);
- return true;
+
+ return e;
+ }
+
+ virtual iterator find(const KeyPress &key)
+ {
+ iterator f(begin());
+ iterator e(end());
+
+ while (f != e) {
+ if (*f == key)
+ return f;
+
+ ++f;
}
- /* always return false in order to continue processing events */
- return false;
+
+ return e;
+ }
+
+public:
+ void clear() { super::clear(); }
+
+ void set(const KeyPress &key)
+ {
+ if (find(key) == end())
+ push_back(key);
}
- bool IsKeyDown(const KeyPress &keyCode) const
+ void unset(const KeyPress &key)
{
- return keyIsDown[keyCode];
+ iterator p(find(key));
+
+ if (p != end())
+ erase(p);
}
+ void toggle(const KeyPress &key)
+ {
+ iterator p(this->find(key));
+
+ if (p != end())
+ erase(p);
+ else
+ push_back(key);
+ }
+
+ bool operator[](const KeyPress &key) const { return find(key) != end(); }
+};
+
+class MyEventReceiver : public IEventReceiver
+{
+public:
+ // This is the one method that we have to implement
+ virtual bool OnEvent(const SEvent &event);
+
+ bool IsKeyDown(const KeyPress &keyCode) const { return keyIsDown[keyCode]; }
+
// Checks whether a key was down and resets the state
bool WasKeyDown(const KeyPress &keyCode)
{
@@ -129,14 +112,8 @@ public:
return b;
}
- void listenForKey(const KeyPress &keyCode)
- {
- keysListenedFor.set(keyCode);
- }
- void dontListenForKeys()
- {
- keysListenedFor.clear();
- }
+ void listenForKey(const KeyPress &keyCode) { keysListenedFor.set(keyCode); }
+ void dontListenForKeys() { keysListenedFor.clear(); }
s32 getMouseWheel()
{
@@ -184,7 +161,7 @@ public:
JoystickController *joystick;
#ifdef HAVE_TOUCHSCREENGUI
- TouchScreenGUI* m_touchscreengui;
+ TouchScreenGUI *m_touchscreengui;
#endif
private:
@@ -200,7 +177,42 @@ private:
KeyList keysListenedFor;
};
+class InputHandler
+{
+public:
+ InputHandler() {}
+ virtual ~InputHandler() {}
+
+ virtual bool isKeyDown(const KeyPress &keyCode) = 0;
+ virtual bool wasKeyDown(const KeyPress &keyCode) = 0;
+
+ virtual void listenForKey(const KeyPress &keyCode) {}
+ virtual void dontListenForKeys() {}
+
+ virtual v2s32 getMousePos() = 0;
+ virtual void setMousePos(s32 x, s32 y) = 0;
+
+ virtual bool getLeftState() = 0;
+ virtual bool getRightState() = 0;
+
+ virtual bool getLeftClicked() = 0;
+ virtual bool getRightClicked() = 0;
+ virtual void resetLeftClicked() = 0;
+ virtual void resetRightClicked() = 0;
+
+ virtual bool getLeftReleased() = 0;
+ virtual bool getRightReleased() = 0;
+ virtual void resetLeftReleased() = 0;
+ virtual void resetRightReleased() = 0;
+ virtual s32 getMouseWheel() = 0;
+
+ virtual void step(float dtime) {}
+
+ virtual void clear() {}
+
+ JoystickController joystick;
+};
/*
Separated input handler
*/
@@ -208,10 +220,8 @@ private:
class RealInputHandler : public InputHandler
{
public:
- RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
- m_device(device),
- m_receiver(receiver),
- m_mousepos(0,0)
+ RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver)
+ : m_device(device), m_receiver(receiver), m_mousepos(0, 0)
{
m_receiver->joystick = &joystick;
}
@@ -227,16 +237,12 @@ public:
{
m_receiver->listenForKey(keyCode);
}
- virtual void dontListenForKeys()
- {
- m_receiver->dontListenForKeys();
- }
+ virtual void dontListenForKeys() { m_receiver->dontListenForKeys(); }
virtual v2s32 getMousePos()
{
if (m_device->getCursorControl()) {
return m_device->getCursorControl()->getPosition();
- }
- else {
+ } else {
return m_mousepos;
}
}
@@ -244,69 +250,36 @@ public:
{
if (m_device->getCursorControl()) {
m_device->getCursorControl()->setPosition(x, y);
- }
- else {
- m_mousepos = v2s32(x,y);
+ } else {
+ m_mousepos = v2s32(x, y);
}
}
- virtual bool getLeftState()
- {
- return m_receiver->left_active;
- }
- virtual bool getRightState()
- {
- return m_receiver->right_active;
- }
+ virtual bool getLeftState() { return m_receiver->left_active; }
+ virtual bool getRightState() { return m_receiver->right_active; }
- virtual bool getLeftClicked()
- {
- return m_receiver->leftclicked;
- }
- virtual bool getRightClicked()
- {
- return m_receiver->rightclicked;
- }
- virtual void resetLeftClicked()
- {
- m_receiver->leftclicked = false;
- }
- virtual void resetRightClicked()
- {
- m_receiver->rightclicked = false;
- }
+ virtual bool getLeftClicked() { return m_receiver->leftclicked; }
+ virtual bool getRightClicked() { return m_receiver->rightclicked; }
+ virtual void resetLeftClicked() { m_receiver->leftclicked = false; }
+ virtual void resetRightClicked() { m_receiver->rightclicked = false; }
- virtual bool getLeftReleased()
- {
- return m_receiver->leftreleased;
- }
- virtual bool getRightReleased()
- {
- return m_receiver->rightreleased;
- }
- virtual void resetLeftReleased()
- {
- m_receiver->leftreleased = false;
- }
- virtual void resetRightReleased()
- {
- m_receiver->rightreleased = false;
- }
+ virtual bool getLeftReleased() { return m_receiver->leftreleased; }
+ virtual bool getRightReleased() { return m_receiver->rightreleased; }
+ virtual void resetLeftReleased() { m_receiver->leftreleased = false; }
+ virtual void resetRightReleased() { m_receiver->rightreleased = false; }
- virtual s32 getMouseWheel()
- {
- return m_receiver->getMouseWheel();
- }
+ virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); }
void clear()
{
joystick.clear();
m_receiver->clearInput();
}
+
private:
- IrrlichtDevice *m_device;
+ IrrlichtDevice *m_device;
MyEventReceiver *m_receiver;
- v2s32 m_mousepos;
+ v2s32 m_mousepos;
};
class RandomInputHandler : public InputHandler
@@ -322,70 +295,25 @@ public:
rightreleased = false;
keydown.clear();
}
- virtual bool isKeyDown(const KeyPress &keyCode)
- {
- return keydown[keyCode];
- }
- virtual bool wasKeyDown(const KeyPress &keyCode)
- {
- return false;
- }
- virtual v2s32 getMousePos()
- {
- return mousepos;
- }
- virtual void setMousePos(s32 x, s32 y)
- {
- mousepos = v2s32(x, y);
- }
+ virtual bool isKeyDown(const KeyPress &keyCode) { return keydown[keyCode]; }
+ virtual bool wasKeyDown(const KeyPress &keyCode) { return false; }
+ virtual v2s32 getMousePos() { return mousepos; }
+ virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }
- virtual bool getLeftState()
- {
- return leftdown;
- }
- virtual bool getRightState()
- {
- return rightdown;
- }
+ virtual bool getLeftState() { return leftdown; }
+ virtual bool getRightState() { return rightdown; }
- virtual bool getLeftClicked()
- {
- return leftclicked;
- }
- virtual bool getRightClicked()
- {
- return rightclicked;
- }
- virtual void resetLeftClicked()
- {
- leftclicked = false;
- }
- virtual void resetRightClicked()
- {
- rightclicked = false;
- }
+ virtual bool getLeftClicked() { return leftclicked; }
+ virtual bool getRightClicked() { return rightclicked; }
+ virtual void resetLeftClicked() { leftclicked = false; }
+ virtual void resetRightClicked() { rightclicked = false; }
- virtual bool getLeftReleased()
- {
- return leftreleased;
- }
- virtual bool getRightReleased()
- {
- return rightreleased;
- }
- virtual void resetLeftReleased()
- {
- leftreleased = false;
- }
- virtual void resetRightReleased()
- {
- rightreleased = false;
- }
+ virtual bool getLeftReleased() { return leftreleased; }
+ virtual bool getRightReleased() { return rightreleased; }
+ virtual void resetLeftReleased() { leftreleased = false; }
+ virtual void resetRightReleased() { rightreleased = false; }
- virtual s32 getMouseWheel()
- {
- return 0;
- }
+ virtual s32 getMouseWheel() { return 0; }
virtual void step(float dtime)
{
@@ -456,10 +384,8 @@ public:
mousepos += mousespeed;
}
- s32 Rand(s32 min, s32 max)
- {
- return (myrand()%(max-min+1))+min;
- }
+ s32 Rand(s32 min, s32 max);
+
private:
KeyList keydown;
v2s32 mousepos;
diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp
index ef8d18ab0..905ca6420 100644
--- a/src/client/joystick_controller.cpp
+++ b/src/client/joystick_controller.cpp
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "keys.h"
#include "settings.h"
#include "gettime.h"
+#include "porting.h"
+#include "../util/string.h"
bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
{
@@ -42,7 +44,7 @@ bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
#define JLO_B_PB(A, B, C) jlo.button_keys.push_back(JoystickButtonCmb(A, B, C))
#define JLO_A_PB(A, B, C, D) jlo.axis_keys.push_back(JoystickAxisCmb(A, B, C, D))
-static JoystickLayout create_default_layout()
+JoystickLayout create_default_layout()
{
JoystickLayout jlo;
@@ -103,11 +105,59 @@ static JoystickLayout create_default_layout()
return jlo;
}
-static const JoystickLayout default_layout = create_default_layout();
+JoystickLayout create_xbox_layout()
+{
+ JoystickLayout jlo;
+
+ jlo.axes_dead_border = 7000;
+
+ const JoystickAxisLayout axes[JA_COUNT] = {
+ {0, 1}, // JA_SIDEWARD_MOVE
+ {1, 1}, // JA_FORWARD_MOVE
+ {2, 1}, // JA_FRUSTUM_HORIZONTAL
+ {3, 1}, // JA_FRUSTUM_VERTICAL
+ };
+ memcpy(jlo.axes, axes, sizeof(jlo.axes));
+
+ // The back button means "ESC".
+ JLO_B_PB(KeyType::ESC, 1 << 8, 1 << 8); // back
+ JLO_B_PB(KeyType::ESC, 1 << 9, 1 << 9); // start
+
+ // 4 Buttons
+ JLO_B_PB(KeyType::JUMP, 1 << 0, 1 << 0); // A/green
+ JLO_B_PB(KeyType::ESC, 1 << 1, 1 << 1); // B/red
+ JLO_B_PB(KeyType::SPECIAL1, 1 << 2, 1 << 2); // X/blue
+ JLO_B_PB(KeyType::INVENTORY, 1 << 3, 1 << 3); // Y/yellow
+
+ // Analog Sticks
+ JLO_B_PB(KeyType::SPECIAL1, 1 << 11, 1 << 11); // left
+ JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right
+
+ // Triggers
+ JLO_B_PB(KeyType::MOUSE_L, 1 << 6, 1 << 6); // lt
+ JLO_B_PB(KeyType::MOUSE_R, 1 << 7, 1 << 7); // rt
+ JLO_B_PB(KeyType::SCROLL_UP, 1 << 4, 1 << 4); // lb
+ JLO_B_PB(KeyType::SCROLL_DOWN, 1 << 5, 1 << 5); // rb
+
+ // D-PAD
+ JLO_B_PB(KeyType::ZOOM, 1 << 15, 1 << 15); // up
+ JLO_B_PB(KeyType::DROP, 1 << 13, 1 << 13); // left
+ JLO_B_PB(KeyType::SCREENSHOT, 1 << 14, 1 << 14); // right
+ JLO_B_PB(KeyType::FREEMOVE, 1 << 16, 1 << 16); // down
+
+ // Movement buttons, important for vessels
+ JLO_A_PB(KeyType::FORWARD, 1, 1, 1024);
+ JLO_A_PB(KeyType::BACKWARD, 1, -1, 1024);
+ JLO_A_PB(KeyType::LEFT, 0, 1, 1024);
+ JLO_A_PB(KeyType::RIGHT, 0, -1, 1024);
+
+ return jlo;
+}
JoystickController::JoystickController()
{
- m_layout = &default_layout;
+ m_joystick_id = 0;
+
doubling_dtime = g_settings->getFloat("repeat_joystick_button_time");
for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) {
@@ -116,23 +166,55 @@ JoystickController::JoystickController()
clear();
}
+void JoystickController::onJoystickConnect(const std::vector<irr::SJoystickInfo> &joystick_infos)
+{
+ s32 id = g_settings->getS32("joystick_id");
+ std::string layout = g_settings->get("joystick_type");
+
+ if (id < 0 || (u16)id >= joystick_infos.size()) {
+ // TODO: auto detection
+ id = 0;
+ }
+
+ if (id >= 0 && (u16)id < joystick_infos.size()) {
+ if (layout.empty() || layout == "auto")
+ setLayoutFromControllerName(joystick_infos[id].Name.c_str());
+ else
+ setLayoutFromControllerName(layout);
+ }
+
+ m_joystick_id = id;
+}
+
+void JoystickController::setLayoutFromControllerName(const std::string &name)
+{
+ if (lowercase(name).find("xbox") != std::string::npos) {
+ m_layout = create_xbox_layout();
+ } else {
+ m_layout = create_default_layout();
+ }
+}
+
bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev)
{
- m_internal_time = getTimeMs() / 1000.f;
+ if (ev.Joystick != m_joystick_id)
+ return false;
+
+ m_internal_time = porting::getTimeMs() / 1000.f;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> keys_pressed;
// First generate a list of keys pressed
- for (size_t i = 0; i < m_layout->button_keys.size(); i++) {
- if (m_layout->button_keys[i].isTriggered(ev)) {
- keys_pressed.set(m_layout->button_keys[i].key);
+ for (size_t i = 0; i < m_layout.button_keys.size(); i++) {
+ if (m_layout.button_keys[i].isTriggered(ev)) {
+ keys_pressed.set(m_layout.button_keys[i].key);
}
}
- for (size_t i = 0; i < m_layout->axis_keys.size(); i++) {
- if (m_layout->axis_keys[i].isTriggered(ev)) {
- keys_pressed.set(m_layout->axis_keys[i].key);
+ for (size_t i = 0; i < m_layout.axis_keys.size(); i++) {
+ if (m_layout.axis_keys[i].isTriggered(ev)) {
+ keys_pressed.set(m_layout.axis_keys[i].key);
}
}
@@ -153,7 +235,7 @@ bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev)
}
for (size_t i = 0; i < JA_COUNT; i++) {
- const JoystickAxisLayout &ax_la = m_layout->axes[i];
+ const JoystickAxisLayout &ax_la = m_layout.axes[i];
m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id];
}
@@ -172,8 +254,8 @@ void JoystickController::clear()
s16 JoystickController::getAxisWithoutDead(JoystickAxis axis)
{
s16 v = m_axes_vals[axis];
- if (((v > 0) && (v < m_layout->axes_dead_border)) ||
- ((v < 0) && (v > -m_layout->axes_dead_border)))
+ if (((v > 0) && (v < m_layout.axes_dead_border)) ||
+ ((v < 0) && (v > -m_layout.axes_dead_border)))
return 0;
return v;
}
diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h
index ed0ee4068..2c0e7b90a 100644
--- a/src/client/joystick_controller.h
+++ b/src/client/joystick_controller.h
@@ -98,6 +98,9 @@ class JoystickController {
public:
JoystickController();
+
+ void onJoystickConnect(const std::vector<irr::SJoystickInfo> &joystick_infos);
+
bool handleEvent(const irr::SEvent::SJoystickEvent &ev);
void clear();
@@ -146,10 +149,14 @@ public:
f32 doubling_dtime;
private:
- const JoystickLayout *m_layout;
+ void setLayoutFromControllerName(const std::string &name);
+
+ JoystickLayout m_layout;
s16 m_axes_vals[JA_COUNT];
+ u8 m_joystick_id;
+
std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_pressed_keys;
f32 m_internal_time;
diff --git a/src/client/keys.h b/src/client/keys.h
index 6467c443e..9478737f6 100644
--- a/src/client/keys.h
+++ b/src/client/keys.h
@@ -20,11 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef KEYS_HEADER
#define KEYS_HEADER
-#include<list>
+#include <list>
-class KeyType {
+class KeyType
+{
public:
- enum T {
+ enum T
+ {
// Player movement
FORWARD,
BACKWARD,
@@ -42,11 +44,17 @@ public:
INVENTORY,
CHAT,
CMD,
+ CMD_LOCAL,
CONSOLE,
MINIMAP,
FREEMOVE,
FASTMOVE,
NOCLIP,
+ HOTBAR_PREV,
+ HOTBAR_NEXT,
+ MUTE,
+ INC_VOLUME,
+ DEC_VOLUME,
CINEMATIC,
SCREENSHOT,
TOGGLE_HUD,
@@ -82,5 +90,4 @@ public:
typedef KeyType::T GameKeyType;
-
#endif
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index 8f0c39465..99495132b 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -134,9 +134,8 @@ std::string getTexturePath(const std::string &filename)
/*
Check from texture_path
*/
- std::string texture_path = g_settings->get("texture_path");
- if (texture_path != "")
- {
+ const std::string &texture_path = g_settings->get("texture_path");
+ if (texture_path != "") {
std::string testpath = texture_path + DIR_DELIM + filename;
// Check all filename extensions. Returns "" if not found.
fullpath = getImagePath(testpath);
@@ -342,6 +341,8 @@ public:
*/
video::ITexture* getTextureForMesh(const std::string &name, u32 *id);
+ virtual Palette* getPalette(const std::string &name);
+
// Returns a pointer to the irrlicht device
virtual IrrlichtDevice* getDevice()
{
@@ -378,11 +379,6 @@ public:
video::ITexture* generateTextureFromMesh(
const TextureFromMeshParams &params);
- // Generates an image from a full string like
- // "stone.png^mineral_coal.png^[crack:1:0".
- // Shall be called from the main thread.
- video::IImage* generateImage(const std::string &name);
-
video::ITexture* getNormalTexture(const std::string &name);
video::SColor getTextureAverageColor(const std::string &name);
video::ITexture *getShaderFlagsTexture(bool normamap_present);
@@ -405,6 +401,13 @@ private:
// if baseimg is NULL, it is created. Otherwise stuff is made on it.
bool generateImagePart(std::string part_of_name, video::IImage *& baseimg);
+ /*! Generates an image from a full string like
+ * "stone.png^mineral_coal.png^[crack:1:0".
+ * Shall be called from the main thread.
+ * The returned Image should be dropped.
+ */
+ video::IImage* generateImage(const std::string &name);
+
// Thread-safe cache of what source images are known (true = known)
MutexedMap<std::string, bool> m_source_image_existence;
@@ -423,6 +426,9 @@ private:
// but can't be deleted because the ITexture* might still be used
std::vector<video::ITexture*> m_texture_trash;
+ // Maps image file names to loaded palettes.
+ UNORDERED_MAP<std::string, Palette> m_palettes;
+
// Cached settings needed for making textures from meshes
bool m_setting_trilinear_filter;
bool m_setting_bilinear_filter;
@@ -558,7 +564,11 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
// color alpha with the destination alpha.
// Otherwise, any pixels that are not fully transparent get the color alpha.
static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
- video::SColor color, int ratio, bool keep_alpha);
+ const video::SColor &color, int ratio, bool keep_alpha);
+
+// paint a texture using the given color
+static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size,
+ const video::SColor &color);
// Apply a mask to an image
static void apply_mask(video::IImage *mask, video::IImage *dst,
@@ -682,6 +692,61 @@ video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *
return getTexture(name + "^[applyfiltersformesh", id);
}
+Palette* TextureSource::getPalette(const std::string &name)
+{
+ // Only the main thread may load images
+ sanity_check(thr_is_current_thread(m_main_thread));
+
+ if (name == "")
+ return NULL;
+
+ UNORDERED_MAP<std::string, Palette>::iterator it = m_palettes.find(name);
+ if (it == m_palettes.end()) {
+ // Create palette
+ video::IImage *img = generateImage(name);
+ if (!img) {
+ warningstream << "TextureSource::getPalette(): palette \"" << name
+ << "\" could not be loaded." << std::endl;
+ return NULL;
+ }
+ Palette new_palette;
+ u32 w = img->getDimension().Width;
+ u32 h = img->getDimension().Height;
+ // Real area of the image
+ u32 area = h * w;
+ if (area == 0)
+ return NULL;
+ if (area > 256) {
+ warningstream << "TextureSource::getPalette(): the specified"
+ << " palette image \"" << name << "\" is larger than 256"
+ << " pixels, using the first 256." << std::endl;
+ area = 256;
+ } else if (256 % area != 0)
+ warningstream << "TextureSource::getPalette(): the "
+ << "specified palette image \"" << name << "\" does not "
+ << "contain power of two pixels." << std::endl;
+ // We stretch the palette so it will fit 256 values
+ // This many param2 values will have the same color
+ u32 step = 256 / area;
+ // For each pixel in the image
+ for (u32 i = 0; i < area; i++) {
+ video::SColor c = img->getPixel(i % w, i / w);
+ // Fill in palette with 'step' colors
+ for (u32 j = 0; j < step; j++)
+ new_palette.push_back(c);
+ }
+ img->drop();
+ // Fill in remaining elements
+ while (new_palette.size() < 256)
+ new_palette.push_back(video::SColor(0xFFFFFFFF));
+ m_palettes[name] = new_palette;
+ it = m_palettes.find(name);
+ }
+ if (it != m_palettes.end())
+ return &((*it).second);
+ return NULL;
+}
+
void TextureSource::processQueue()
{
/*
@@ -725,8 +790,6 @@ void TextureSource::rebuildImagesAndTextures()
video::IImage *img = generateImage(ti->name);
#ifdef __ANDROID__
img = Align2Npot2(img, driver);
- sanity_check(img->getDimension().Height == npot2(img->getDimension().Height));
- sanity_check(img->getDimension().Width == npot2(img->getDimension().Width));
#endif
// Create texture from resulting image
video::ITexture *t = NULL;
@@ -938,7 +1001,7 @@ video::ITexture* TextureSource::generateTextureFromMesh(
smgr->drop();
// Unset render target
- driver->setRenderTarget(0, false, true, 0);
+ driver->setRenderTarget(0, false, true, video::SColor(0,0,0,0));
if (params.delete_texture_on_shutdown)
m_texture_trash.push_back(rtt);
@@ -1059,6 +1122,13 @@ video::IImage* TextureSource::generateImage(const std::string &name)
* @param driver driver to use for image operations
* @return image or copy of image aligned to npot2
*/
+
+inline u16 get_GL_major_version()
+{
+ const GLubyte *gl_version = glGetString(GL_VERSION);
+ return (u16) (gl_version[0] - '0');
+}
+
video::IImage * Align2Npot2(video::IImage * image,
video::IVideoDriver* driver)
{
@@ -1069,7 +1139,10 @@ video::IImage * Align2Npot2(video::IImage * image,
core::dimension2d<u32> dim = image->getDimension();
std::string extensions = (char*) glGetString(GL_EXTENSIONS);
- if (extensions.find("GL_OES_texture_npot") != std::string::npos) {
+
+ // Only GLES2 is trusted to correctly report npot support
+ if (get_GL_major_version() > 1 &&
+ extensions.find("GL_OES_texture_npot") != std::string::npos) {
return image;
}
@@ -1135,17 +1208,17 @@ bool TextureSource::generateImagePart(std::string part_of_name,
#endif
if (image == NULL) {
if (part_of_name != "") {
- if (part_of_name.find("_normal.png") == std::string::npos){
- errorstream<<"generateImage(): Could not load image \""
- <<part_of_name<<"\""<<" while building texture"<<std::endl;
- errorstream<<"generateImage(): Creating a dummy"
- <<" image for \""<<part_of_name<<"\""<<std::endl;
- } else {
- infostream<<"generateImage(): Could not load normal map \""
- <<part_of_name<<"\""<<std::endl;
- infostream<<"generateImage(): Creating a dummy"
- <<" normal map for \""<<part_of_name<<"\""<<std::endl;
+
+ // Do not create normalmap dummies
+ if (part_of_name.find("_normal.png") != std::string::npos) {
+ warningstream << "generateImage(): Could not load normal map \""
+ << part_of_name << "\"" << std::endl;
+ return true;
}
+
+ errorstream << "generateImage(): Could not load image \""
+ << part_of_name << "\" while building texture; "
+ "Creating a dummy image" << std::endl;
}
// Just create a dummy image
@@ -1660,6 +1733,30 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
}
/*
+ [multiply:color
+ multiplys a given color to any pixel of an image
+ color = color as ColorString
+ */
+ else if (str_starts_with(part_of_name, "[multiply:")) {
+ Strfnd sf(part_of_name);
+ sf.next(":");
+ std::string color_str = sf.next(":");
+
+ if (baseimg == NULL) {
+ errorstream << "generateImagePart(): baseimg != NULL "
+ << "for part_of_name=\"" << part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
+
+ video::SColor color;
+
+ if (!parseColorString(color_str, color, false))
+ return false;
+
+ apply_multiplication(baseimg, v2u32(0, 0), baseimg->getDimension(), color);
+ }
+ /*
[colorize:color
Overlays image with given color
color = color as ColorString
@@ -1716,6 +1813,12 @@ bool TextureSource::generateImagePart(std::string part_of_name,
* equal to the target minimum. If e.g. this is a vertical frames
* animation, the short dimension will be the real size.
*/
+ if ((dim.Width == 0) || (dim.Height == 0)) {
+ errorstream << "generateImagePart(): Illegal 0 dimension "
+ << "for part_of_name=\""<< part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
u32 xscale = scaleto / dim.Width;
u32 yscale = scaleto / dim.Height;
u32 scale = (xscale > yscale) ? xscale : yscale;
@@ -1823,10 +1926,53 @@ bool TextureSource::generateImagePart(std::string part_of_name,
for (u32 x = 0; x < dim.Width; x++)
{
video::SColor c = baseimg->getPixel(x, y);
- c.color ^= mask;
+ c.color ^= mask;
baseimg->setPixel(x, y, c);
}
}
+ /*
+ [sheet:WxH:X,Y
+ Retrieves a tile at position X,Y (in tiles)
+ from the base image it assumes to be a
+ tilesheet with dimensions W,H (in tiles).
+ */
+ else if (part_of_name.substr(0,7) == "[sheet:") {
+ if (baseimg == NULL) {
+ errorstream << "generateImagePart(): baseimg != NULL "
+ << "for part_of_name=\"" << part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
+
+ Strfnd sf(part_of_name);
+ sf.next(":");
+ u32 w0 = stoi(sf.next("x"));
+ u32 h0 = stoi(sf.next(":"));
+ u32 x0 = stoi(sf.next(","));
+ u32 y0 = stoi(sf.next(":"));
+
+ core::dimension2d<u32> img_dim = baseimg->getDimension();
+ core::dimension2d<u32> tile_dim(v2u32(img_dim) / v2u32(w0, h0));
+
+ video::IImage *img = driver->createImage(
+ video::ECF_A8R8G8B8, tile_dim);
+ if (!img) {
+ errorstream << "generateImagePart(): Could not create image "
+ << "for part_of_name=\"" << part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
+
+ img->fill(video::SColor(0,0,0,0));
+ v2u32 vdim(tile_dim);
+ core::rect<s32> rect(v2s32(x0 * vdim.X, y0 * vdim.Y), tile_dim);
+ baseimg->copyToWithAlpha(img, v2s32(0), rect,
+ video::SColor(255,255,255,255), NULL);
+
+ // Replace baseimg
+ baseimg->drop();
+ baseimg = img;
+ }
else
{
errorstream << "generateImagePart(): Invalid "
@@ -1920,7 +2066,7 @@ static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst
Apply color to destination
*/
static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
- video::SColor color, int ratio, bool keep_alpha)
+ const video::SColor &color, int ratio, bool keep_alpha)
{
u32 alpha = color.getAlpha();
video::SColor dst_c;
@@ -1955,6 +2101,27 @@ static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
}
/*
+ Apply color to destination
+*/
+static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size,
+ const video::SColor &color)
+{
+ video::SColor dst_c;
+
+ for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+ for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
+ dst_c = dst->getPixel(x, y);
+ dst_c.set(
+ dst_c.getAlpha(),
+ (dst_c.getRed() * color.getRed()) / 255,
+ (dst_c.getGreen() * color.getGreen()) / 255,
+ (dst_c.getBlue() * color.getBlue()) / 255
+ );
+ dst->setPixel(x, y, dst_c);
+ }
+}
+
+/*
Apply mask to destination
*/
static void apply_mask(video::IImage *mask, video::IImage *dst,
@@ -2171,7 +2338,8 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name)
if (isKnownSourceImage("override_normal.png"))
return getTexture("override_normal.png");
std::string fname_base = name;
- std::string normal_ext = "_normal.png";
+ static const char *normal_ext = "_normal.png";
+ static const u32 normal_ext_size = strlen(normal_ext);
size_t pos = fname_base.find(".");
std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
if (isKnownSourceImage(fname_normal)) {
@@ -2179,10 +2347,10 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name)
size_t i = 0;
while ((i = fname_base.find(".", i)) != std::string::npos) {
fname_base.replace(i, 4, normal_ext);
- i += normal_ext.length();
+ i += normal_ext_size;
}
return getTexture(fname_base);
- }
+ }
return NULL;
}
diff --git a/src/client/tile.h b/src/client/tile.h
index b75916841..15854fb71 100644
--- a/src/client/tile.h
+++ b/src/client/tile.h
@@ -33,6 +33,8 @@ class IGameDef;
struct TileSpec;
struct TileDef;
+typedef std::vector<video::SColor> Palette;
+
/*
tile.{h,cpp}: Texture handling stuff.
*/
@@ -106,6 +108,13 @@ public:
const std::string &name, u32 *id = NULL)=0;
virtual video::ITexture* getTextureForMesh(
const std::string &name, u32 *id = NULL) = 0;
+ /*!
+ * Returns a palette from the given texture name.
+ * The pointer is valid until the texture source is
+ * destructed.
+ * Should be called from the main thread.
+ */
+ virtual Palette* getPalette(const std::string &name) = 0;
virtual IrrlichtDevice* getDevice()=0;
virtual bool isKnownSourceImage(const std::string &name)=0;
virtual video::ITexture* generateTextureFromMesh(
@@ -161,9 +170,7 @@ enum MaterialType{
// Should the crack be drawn on transparent pixels (unset) or not (set)?
// Ignored if MATERIAL_FLAG_CRACK is not set.
#define MATERIAL_FLAG_CRACK_OVERLAY 0x04
-// Animation made up by splitting the texture to vertical frames, as
-// defined by extra parameters
-#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08
+#define MATERIAL_FLAG_ANIMATION 0x08
#define MATERIAL_FLAG_HIGHLIGHTED 0x10
#define MATERIAL_FLAG_TILEABLE_HORIZONTAL 0x20
#define MATERIAL_FLAG_TILEABLE_VERTICAL 0x40
@@ -187,65 +194,67 @@ struct FrameSpec
video::ITexture *flags_texture;
};
-struct TileSpec
+#define MAX_TILE_LAYERS 2
+
+//! Defines a layer of a tile.
+struct TileLayer
{
- TileSpec():
- texture_id(0),
+ TileLayer():
texture(NULL),
normal_texture(NULL),
flags_texture(NULL),
- alpha(255),
+ shader_id(0),
+ texture_id(0),
+ animation_frame_length_ms(0),
+ animation_frame_count(1),
material_type(TILE_MATERIAL_BASIC),
material_flags(
//0 // <- DEBUG, Use the one below
- MATERIAL_FLAG_BACKFACE_CULLING
+ MATERIAL_FLAG_BACKFACE_CULLING |
+ MATERIAL_FLAG_TILEABLE_HORIZONTAL|
+ MATERIAL_FLAG_TILEABLE_VERTICAL
),
- shader_id(0),
- animation_frame_count(1),
- animation_frame_length_ms(0),
- rotation(0)
+ has_color(false),
+ color()
{
}
- bool operator==(const TileSpec &other) const
+ /*!
+ * Two layers are equal if they can be merged.
+ */
+ bool operator==(const TileLayer &other) const
{
- return (
+ return
texture_id == other.texture_id &&
- /* texture == other.texture && */
- alpha == other.alpha &&
material_type == other.material_type &&
material_flags == other.material_flags &&
- rotation == other.rotation
- );
+ color == other.color;
}
- bool operator!=(const TileSpec &other) const
+ /*!
+ * Two tiles are not equal if they must have different vertices.
+ */
+ bool operator!=(const TileLayer &other) const
{
return !(*this == other);
}
-
+
// Sets everything else except the texture in the material
void applyMaterialOptions(video::SMaterial &material) const
{
switch (material_type) {
case TILE_MATERIAL_BASIC:
+ case TILE_MATERIAL_WAVING_LEAVES:
+ case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_ALPHA:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
- material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
- case TILE_MATERIAL_WAVING_LEAVES:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
- case TILE_MATERIAL_WAVING_PLANTS:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;
@@ -270,23 +279,73 @@ struct TileSpec
material.TextureLayer[1].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
}
}
-
- u32 texture_id;
+
+ bool isTileable() const
+ {
+ return (material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
+ && (material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL);
+ }
+
+ // Ordered for size, please do not reorder
+
video::ITexture *texture;
video::ITexture *normal_texture;
video::ITexture *flags_texture;
-
- // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used)
- u8 alpha;
- // Material parameters
- u8 material_type;
- u8 material_flags;
+
u32 shader_id;
- // Animation parameters
- u8 animation_frame_count;
+
+ u32 texture_id;
+
u16 animation_frame_length_ms;
+ u8 animation_frame_count;
+
+ u8 material_type;
+ u8 material_flags;
+
+ //! If true, the tile has its own color.
+ bool has_color;
+
std::vector<FrameSpec> frames;
+ /*!
+ * The color of the tile, or if the tile does not own
+ * a color then the color of the node owning this tile.
+ */
+ video::SColor color;
+};
+
+/*!
+ * Defines a face of a node. May have up to two layers.
+ */
+struct TileSpec
+{
+ TileSpec():
+ rotation(0),
+ emissive_light(0)
+ {
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
+ layers[layer] = TileLayer();
+ }
+
+ /*!
+ * Returns true if this tile can be merged with the other tile.
+ */
+ bool isTileable(const TileSpec &other) const {
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ if (layers[layer] != other.layers[layer])
+ return false;
+ if (!layers[layer].isTileable())
+ return false;
+ }
+ return rotation == 0
+ && rotation == other.rotation
+ && emissive_light == other.emissive_light;
+ }
+
u8 rotation;
+ //! This much light does the tile emit.
+ u8 emissive_light;
+ //! The first is base texture, the second is overlay.
+ TileLayer layers[MAX_TILE_LAYERS];
};
#endif