diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client/clientlauncher.cpp | 27 | ||||
-rw-r--r-- | src/client/inputhandler.h | 50 | ||||
-rw-r--r-- | src/client/joystick_controller.cpp | 179 | ||||
-rw-r--r-- | src/client/joystick_controller.h | 163 | ||||
-rw-r--r-- | src/client/keys.h | 86 | ||||
-rw-r--r-- | src/client/tile.cpp | 134 |
7 files changed, 612 insertions, 28 deletions
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index a1ec37fe3..5faa186a7 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,6 +1,7 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.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 404a16310..6145e3dde 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -32,7 +32,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiEngine.h" #include "player.h" #include "fontengine.h" +#include "joystick_controller.h" #include "clientlauncher.h" +#include "version.h" /* mainmenumanager.h */ @@ -112,6 +114,8 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) porting::setXorgClassHint(video_driver->getExposedVideoData(), PROJECT_NAME_C); + porting::setXorgWindowIcon(device); + /* This changes the minimum allowed number of vertices in a VBO. Default is 500. @@ -184,7 +188,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) { // Set the window caption const wchar_t *text = wgettext("Main Menu"); - device->setWindowCaption((utf8_to_wide(PROJECT_NAME_C) + L" [" + text + L"]").c_str()); + device->setWindowCaption((utf8_to_wide(PROJECT_NAME_C) + + L" " + utf8_to_wide(g_version_hash) + + L" [" + text + L"]").c_str()); delete[] text; try { // This is used for catching disconnects @@ -499,7 +505,8 @@ void ClientLauncher::main_menu(MainMenuData *menudata) #endif /* show main menu */ - GUIEngine mymenu(device, guiroot, &g_menumgr, smgr, menudata, *kill); + GUIEngine mymenu(device, &input->joystick, guiroot, + &g_menumgr, smgr, menudata, *kill); smgr->clear(); /* leave scene manager in a clean state */ } @@ -558,6 +565,22 @@ 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; + } + } porting::initIrrlicht(device); } diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 69e4b25fa..824b0da2e 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define INPUT_HANDLER_H #include "irrlichttypes_extrabloated.h" +#include "joystick_controller.h" class MyEventReceiver : public IEventReceiver { @@ -42,11 +43,15 @@ public: // Remember whether each key is down or up if (event.EventType == irr::EET_KEY_INPUT_EVENT) { - if (event.KeyInput.PressedDown) { - keyIsDown.set(event.KeyInput); - keyWasDown.set(event.KeyInput); - } else { - keyIsDown.unset(event.KeyInput); + const KeyPress &keyCode = event.KeyInput; + if (keysListenedFor[keyCode]) { + if (event.KeyInput.PressedDown) { + keyIsDown.set(keyCode); + keyWasDown.set(keyCode); + } else { + keyIsDown.unset(keyCode); + } + return true; } } @@ -58,6 +63,14 @@ public: 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 (noMenuActive() == false) { @@ -116,6 +129,15 @@ public: return b; } + void listenForKey(const KeyPress &keyCode) + { + keysListenedFor.set(keyCode); + } + void dontListenForKeys() + { + keysListenedFor.clear(); + } + s32 getMouseWheel() { s32 a = mouse_wheel; @@ -159,6 +181,8 @@ public: s32 mouse_wheel; + JoystickController *joystick; + #ifdef HAVE_TOUCHSCREENGUI TouchScreenGUI* m_touchscreengui; #endif @@ -168,6 +192,12 @@ private: KeyList keyIsDown; // Whether a key has been pressed or not KeyList keyWasDown; + // List of keys we listen for + // TODO perhaps the type of this is not really + // performant as KeyList is designed for few but + // often changing keys, and keysListenedFor is expected + // to change seldomly but contain lots of keys. + KeyList keysListenedFor; }; @@ -183,6 +213,7 @@ public: m_receiver(receiver), m_mousepos(0,0) { + m_receiver->joystick = &joystick; } virtual bool isKeyDown(const KeyPress &keyCode) { @@ -192,6 +223,14 @@ public: { return m_receiver->WasKeyDown(keyCode); } + virtual void listenForKey(const KeyPress &keyCode) + { + m_receiver->listenForKey(keyCode); + } + virtual void dontListenForKeys() + { + m_receiver->dontListenForKeys(); + } virtual v2s32 getMousePos() { if (m_device->getCursorControl()) { @@ -261,6 +300,7 @@ public: void clear() { + joystick.clear(); m_receiver->clearInput(); } private: diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp new file mode 100644 index 000000000..ef8d18ab0 --- /dev/null +++ b/src/client/joystick_controller.cpp @@ -0,0 +1,179 @@ +/* +Minetest +Copyright (C) 2016 est31, <MTest31@outlook.com> + +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 "joystick_controller.h" +#include "irrlichttypes_extrabloated.h" +#include "keys.h" +#include "settings.h" +#include "gettime.h" + +bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const +{ + u32 buttons = ev.ButtonStates; + + buttons &= filter_mask; + return buttons == compare_mask; +} + +bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const +{ + s16 ax_val = ev.Axis[axis_to_compare]; + + return (ax_val * direction < 0) && (thresh * direction > ax_val * direction); +} + +// spares many characters +#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 jlo; + + jlo.axes_dead_border = 1024; + + const JoystickAxisLayout axes[JA_COUNT] = { + {0, 1}, // JA_SIDEWARD_MOVE + {1, 1}, // JA_FORWARD_MOVE + {3, 1}, // JA_FRUSTUM_HORIZONTAL + {4, 1}, // JA_FRUSTUM_VERTICAL + }; + memcpy(jlo.axes, axes, sizeof(jlo.axes)); + + u32 sb = 1 << 7; // START button mask + u32 fb = 1 << 3; // FOUR button mask + u32 bm = sb | fb; // Mask for Both Modifiers + + // The back button means "ESC". + JLO_B_PB(KeyType::ESC, 1 << 6, 1 << 6); + + // The start button counts as modifier as well as use key. + // JLO_B_PB(KeyType::USE, sb, sb)); + + // Accessible without start modifier button pressed + // regardless whether four is pressed or not + JLO_B_PB(KeyType::SNEAK, sb | 1 << 2, 1 << 2); + + // Accessible without four modifier button pressed + // regardless whether start is pressed or not + JLO_B_PB(KeyType::MOUSE_L, fb | 1 << 4, 1 << 4); + JLO_B_PB(KeyType::MOUSE_R, fb | 1 << 5, 1 << 5); + + // Accessible without any modifier pressed + JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); + JLO_B_PB(KeyType::SPECIAL1, bm | 1 << 1, 1 << 1); + + // Accessible with start button not pressed, but four pressed + // TODO find usage for button 0 + JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); + JLO_B_PB(KeyType::SCROLL_UP, bm | 1 << 4, fb | 1 << 4); + JLO_B_PB(KeyType::SCROLL_DOWN,bm | 1 << 5, fb | 1 << 5); + + // Accessible with start button and four pressed + // TODO find usage for buttons 0, 1 and 4, 5 + + // Now about the buttons simulated by the axes + + // 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); + + // Scroll buttons + JLO_A_PB(KeyType::SCROLL_UP, 2, -1, 1024); + JLO_A_PB(KeyType::SCROLL_DOWN, 5, -1, 1024); + + return jlo; +} + +static const JoystickLayout default_layout = create_default_layout(); + +JoystickController::JoystickController() +{ + m_layout = &default_layout; + doubling_dtime = g_settings->getFloat("repeat_joystick_button_time"); + + for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { + m_past_pressed_time[i] = 0; + } + clear(); +} + +bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev) +{ + m_internal_time = 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->axis_keys.size(); i++) { + if (m_layout->axis_keys[i].isTriggered(ev)) { + keys_pressed.set(m_layout->axis_keys[i].key); + } + } + + // Then update the values + + for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { + if (keys_pressed[i]) { + if (!m_past_pressed_keys[i] && + m_past_pressed_time[i] < m_internal_time - doubling_dtime) { + m_past_pressed_keys[i] = true; + m_past_pressed_time[i] = m_internal_time; + } + } else if (m_pressed_keys[i]) { + m_past_released_keys[i] = true; + } + + m_pressed_keys[i] = keys_pressed[i]; + } + + for (size_t i = 0; i < JA_COUNT; i++) { + const JoystickAxisLayout &ax_la = m_layout->axes[i]; + m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id]; + } + + + return true; +} + +void JoystickController::clear() +{ + m_pressed_keys.reset(); + m_past_pressed_keys.reset(); + m_past_released_keys.reset(); + memset(m_axes_vals, 0, sizeof(m_axes_vals)); +} + +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))) + return 0; + return v; +} diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h new file mode 100644 index 000000000..ed0ee4068 --- /dev/null +++ b/src/client/joystick_controller.h @@ -0,0 +1,163 @@ +/* +Minetest +Copyright (C) 2016 est31, <MTest31@outlook.com> + +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. +*/ + +#ifndef JOYSTICK_HEADER +#define JOYSTICK_HEADER + +#include "irrlichttypes_extrabloated.h" +#include "keys.h" +#include <bitset> +#include <vector> + +enum JoystickAxis { + JA_SIDEWARD_MOVE, + JA_FORWARD_MOVE, + + JA_FRUSTUM_HORIZONTAL, + JA_FRUSTUM_VERTICAL, + + // To know the count of enum values + JA_COUNT, +}; + +struct JoystickAxisLayout { + u16 axis_id; + // -1 if to invert, 1 if to keep it. + int invert; +}; + + +struct JoystickCombination { + + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const=0; + + GameKeyType key; +}; + +struct JoystickButtonCmb : public JoystickCombination { + + JoystickButtonCmb() {} + JoystickButtonCmb(GameKeyType key, u32 filter_mask, u32 compare_mask) : + filter_mask(filter_mask), + compare_mask(compare_mask) + { + this->key = key; + } + + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const; + + u32 filter_mask; + u32 compare_mask; +}; + +struct JoystickAxisCmb : public JoystickCombination { + + JoystickAxisCmb() {} + JoystickAxisCmb(GameKeyType key, u16 axis_to_compare, int direction, s16 thresh) : + axis_to_compare(axis_to_compare), + direction(direction), + thresh(thresh) + { + this->key = key; + } + + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const; + + u16 axis_to_compare; + + // if -1, thresh must be smaller than the axis value in order to trigger + // if 1, thresh must be bigger than the axis value in order to trigger + int direction; + s16 thresh; +}; + +struct JoystickLayout { + std::vector<JoystickButtonCmb> button_keys; + std::vector<JoystickAxisCmb> axis_keys; + JoystickAxisLayout axes[JA_COUNT]; + s16 axes_dead_border; +}; + +class JoystickController { + +public: + JoystickController(); + bool handleEvent(const irr::SEvent::SJoystickEvent &ev); + void clear(); + + bool wasKeyDown(GameKeyType b) + { + bool r = m_past_pressed_keys[b]; + m_past_pressed_keys[b] = false; + return r; + } + bool getWasKeyDown(GameKeyType b) + { + return m_past_pressed_keys[b]; + } + void clearWasKeyDown(GameKeyType b) + { + m_past_pressed_keys[b] = false; + } + + bool wasKeyReleased(GameKeyType b) + { + bool r = m_past_released_keys[b]; + m_past_released_keys[b] = false; + return r; + } + bool getWasKeyReleased(GameKeyType b) + { + return m_past_pressed_keys[b]; + } + void clearWasKeyReleased(GameKeyType b) + { + m_past_pressed_keys[b] = false; + } + + bool isKeyDown(GameKeyType b) + { + return m_pressed_keys[b]; + } + + s16 getAxis(JoystickAxis axis) + { + return m_axes_vals[axis]; + } + + s16 getAxisWithoutDead(JoystickAxis axis); + + f32 doubling_dtime; + +private: + const JoystickLayout *m_layout; + + s16 m_axes_vals[JA_COUNT]; + + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_pressed_keys; + + f32 m_internal_time; + + f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT]; + + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_pressed_keys; + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_released_keys; +}; + +#endif diff --git a/src/client/keys.h b/src/client/keys.h new file mode 100644 index 000000000..6467c443e --- /dev/null +++ b/src/client/keys.h @@ -0,0 +1,86 @@ +/* +Minetest +Copyright (C) 2016 est31, <MTest31@outlook.com> + +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. +*/ + +#ifndef KEYS_HEADER +#define KEYS_HEADER + +#include<list> + +class KeyType { +public: + enum T { + // Player movement + FORWARD, + BACKWARD, + LEFT, + RIGHT, + JUMP, + SPECIAL1, + SNEAK, + AUTORUN, + + ESC, + + // Other + DROP, + INVENTORY, + CHAT, + CMD, + CONSOLE, + MINIMAP, + FREEMOVE, + FASTMOVE, + NOCLIP, + CINEMATIC, + SCREENSHOT, + TOGGLE_HUD, + TOGGLE_CHAT, + TOGGLE_FORCE_FOG_OFF, + TOGGLE_UPDATE_CAMERA, + TOGGLE_DEBUG, + TOGGLE_PROFILER, + CAMERA_MODE, + INCREASE_VIEWING_RANGE, + DECREASE_VIEWING_RANGE, + RANGESELECT, + ZOOM, + + QUICKTUNE_NEXT, + QUICKTUNE_PREV, + QUICKTUNE_INC, + QUICKTUNE_DEC, + + DEBUG_STACKS, + + // joystick specific keys + MOUSE_L, + MOUSE_R, + SCROLL_UP, + SCROLL_DOWN, + + // Fake keycode for array size and internal checks + INTERNAL_ENUM_COUNT + + }; +}; + +typedef KeyType::T GameKeyType; + + +#endif diff --git a/src/client/tile.cpp b/src/client/tile.cpp index ec8c95f02..8f0c39465 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -948,11 +948,10 @@ video::ITexture* TextureSource::generateTextureFromMesh( video::IImage* TextureSource::generateImage(const std::string &name) { - /* - Get the base image - */ + // Get the base image const char separator = '^'; + const char escape = '\\'; const char paren_open = '('; const char paren_close = ')'; @@ -960,7 +959,9 @@ video::IImage* TextureSource::generateImage(const std::string &name) s32 last_separator_pos = -1; u8 paren_bal = 0; for (s32 i = name.size() - 1; i >= 0; i--) { - switch(name[i]) { + if (i > 0 && name[i-1] == escape) + continue; + switch (name[i]) { case separator: if (paren_bal == 0) { last_separator_pos = i; @@ -1028,10 +1029,12 @@ video::IImage* TextureSource::generateImage(const std::string &name) return NULL; } core::dimension2d<u32> dim = tmp->getDimension(); - if (!baseimg) - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); - blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim); - tmp->drop(); + if (baseimg) { + blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim); + tmp->drop(); + } else { + baseimg = tmp; + } } else if (!generateImagePart(last_part_of_name, baseimg)) { // Generate image according to part of name errorstream << "generateImage(): " @@ -1099,9 +1102,27 @@ video::IImage * Align2Npot2(video::IImage * image, #endif +static std::string unescape_string(const std::string &str, const char esc = '\\') +{ + std::string out; + size_t pos = 0, cpos; + out.reserve(str.size()); + while (1) { + cpos = str.find_first_of(esc, pos); + if (cpos == std::string::npos) { + out += str.substr(pos); + break; + } + out += str.substr(pos, cpos - pos) + str[cpos + 1]; + pos = cpos + 2; + } + return out; +} + bool TextureSource::generateImagePart(std::string part_of_name, video::IImage *& baseimg) { + const char escape = '\\'; // same as in generateImage() video::IVideoDriver* driver = m_device->getVideoDriver(); sanity_check(driver); @@ -1251,7 +1272,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } /* [combine:WxH:X,Y=filename:X,Y=filename2 - Creates a bigger texture from an amount of smaller ones + Creates a bigger texture from any amount of smaller ones */ else if (str_starts_with(part_of_name, "[combine")) { @@ -1259,7 +1280,6 @@ bool TextureSource::generateImagePart(std::string part_of_name, sf.next(":"); u32 w0 = stoi(sf.next("x")); u32 h0 = stoi(sf.next(":")); - //infostream<<"combined w="<<w0<<" h="<<h0<<std::endl; core::dimension2d<u32> dim(w0,h0); if (baseimg == NULL) { baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); @@ -1268,11 +1288,11 @@ bool TextureSource::generateImagePart(std::string part_of_name, while (sf.at_end() == false) { u32 x = stoi(sf.next(",")); u32 y = stoi(sf.next("=")); - std::string filename = sf.next(":"); + std::string filename = unescape_string(sf.next_esc(":", escape), escape); infostream<<"Adding \""<<filename <<"\" to combined ("<<x<<","<<y<<")" <<std::endl; - video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); + video::IImage *img = generateImage(filename); if (img) { core::dimension2d<u32> dim = img->getDimension(); infostream<<"Size "<<dim.Width @@ -1295,7 +1315,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } } /* - "[brighten" + [brighten */ else if (str_starts_with(part_of_name, "[brighten")) { @@ -1309,7 +1329,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, brighten(baseimg); } /* - "[noalpha" + [noalpha Make image completely opaque. Used for the leaves texture when in old leaves mode, so that the transparent parts don't look completely black @@ -1336,7 +1356,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } } /* - "[makealpha:R,G,B" + [makealpha:R,G,B Convert one color to transparent. */ else if (str_starts_with(part_of_name, "[makealpha:")) @@ -1375,7 +1395,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } } /* - "[transformN" + [transformN Rotates and/or flips the image. N can be a number (between 0 and 7) or a transform name. @@ -1543,12 +1563,11 @@ bool TextureSource::generateImagePart(std::string part_of_name, Strfnd sf(part_of_name); sf.next(":"); u32 percent = stoi(sf.next(":")); - std::string filename = sf.next(":"); - //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl; + std::string filename = unescape_string(sf.next_esc(":", escape), escape); if (baseimg == NULL) baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16)); - video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); + video::IImage *img = generateImage(filename); if (img) { core::dimension2d<u32> dim = img->getDimension(); @@ -1628,9 +1647,9 @@ bool TextureSource::generateImagePart(std::string part_of_name, } Strfnd sf(part_of_name); sf.next(":"); - std::string filename = sf.next(":"); + std::string filename = unescape_string(sf.next_esc(":", escape), escape); - video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); + video::IImage *img = generateImage(filename); if (img) { apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0), img->getDimension()); @@ -1673,6 +1692,10 @@ bool TextureSource::generateImagePart(std::string part_of_name, apply_colorize(baseimg, v2u32(0, 0), baseimg->getDimension(), color, ratio, keep_alpha); } + /* + [applyfiltersformesh + Internal modifier + */ else if (str_starts_with(part_of_name, "[applyfiltersformesh")) { // Apply the "clean transparent" filter, if configured. @@ -1735,6 +1758,75 @@ bool TextureSource::generateImagePart(std::string part_of_name, baseimg->drop(); baseimg = image; } + /* + [opacity:R + Makes the base image transparent according to the given ratio. + R must be between 0 and 255. + 0 means totally transparent. + 255 means totally opaque. + */ + else if (str_starts_with(part_of_name, "[opacity:")) { + 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 ratio = mystoi(sf.next(""), 0, 255); + + core::dimension2d<u32> dim = baseimg->getDimension(); + + for (u32 y = 0; y < dim.Height; y++) + for (u32 x = 0; x < dim.Width; x++) + { + video::SColor c = baseimg->getPixel(x, y); + c.setAlpha(floor((c.getAlpha() * ratio) / 255 + 0.5)); + baseimg->setPixel(x, y, c); + } + } + /* + [invert:mode + Inverts the given channels of the base image. + Mode may contain the characters "r", "g", "b", "a". + Only the channels that are mentioned in the mode string + will be inverted. + */ + else if (str_starts_with(part_of_name, "[invert:")) { + 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(":"); + + std::string mode = sf.next(""); + u32 mask = 0; + if (mode.find("a") != std::string::npos) + mask |= 0xff000000UL; + if (mode.find("r") != std::string::npos) + mask |= 0x00ff0000UL; + if (mode.find("g") != std::string::npos) + mask |= 0x0000ff00UL; + if (mode.find("b") != std::string::npos) + mask |= 0x000000ffUL; + + core::dimension2d<u32> dim = baseimg->getDimension(); + + for (u32 y = 0; y < dim.Height; y++) + for (u32 x = 0; x < dim.Width; x++) + { + video::SColor c = baseimg->getPixel(x, y); + c.color ^= mask; + baseimg->setPixel(x, y, c); + } + } else { errorstream << "generateImagePart(): Invalid " |