diff options
author | ShadowNinja <shadowninja@minetest.net> | 2017-06-03 14:55:10 -0400 |
---|---|---|
committer | ShadowNinja <shadowninja@minetest.net> | 2017-06-03 14:55:10 -0400 |
commit | caecdb681c428c1aab9c0f7eec2570c0460f995c (patch) | |
tree | e5115982ea59bbf2343ba9b35bc4a0cfbb56f407 /src/client | |
parent | 81d56b94919dceb7b2e51d70b21a7ca22f852bd5 (diff) | |
parent | 80dc961d24e1964e25d57039ddb2ba639f9f4d22 (diff) | |
download | minetest-caecdb681c428c1aab9c0f7eec2570c0460f995c.tar.gz minetest-caecdb681c428c1aab9c0f7eec2570c0460f995c.tar.bz2 minetest-caecdb681c428c1aab9c0f7eec2570c0460f995c.zip |
Merge 0.4.16 into stable-0.4
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client/clientlauncher.cpp | 108 | ||||
-rw-r--r-- | src/client/clientlauncher.h | 37 | ||||
-rw-r--r-- | src/client/inputhandler.cpp | 119 | ||||
-rw-r--r-- | src/client/inputhandler.h | 360 | ||||
-rw-r--r-- | src/client/joystick_controller.cpp | 108 | ||||
-rw-r--r-- | src/client/joystick_controller.h | 9 | ||||
-rw-r--r-- | src/client/keys.h | 15 | ||||
-rw-r--r-- | src/client/tile.cpp | 224 | ||||
-rw-r--r-- | src/client/tile.h | 137 |
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 ¶ms); - // 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 |