This commit is contained in:
Maksim Gamarnik 2016-03-04 16:30:16 +02:00
commit 3505f8982f
17 changed files with 432 additions and 242 deletions

View File

@ -2337,7 +2337,7 @@ These functions return the leftover itemstack.
otherwise returns `nil`. otherwise returns `nil`.
* The returned table contains the functions `fetch`, `fetch_async` and `fetch_async_get` * The returned table contains the functions `fetch`, `fetch_async` and `fetch_async_get`
described below. described below.
* Only works at init time. * Only works at init time and must be called from the mod's main scope (not from a function).
* Function only exists if minetest server was built with cURL support. * Function only exists if minetest server was built with cURL support.
* **DO NOT ALLOW ANY OTHER MODS TO ACCESS THE RETURNED TABLE, STORE IT IN * **DO NOT ALLOW ANY OTHER MODS TO ACCESS THE RETURNED TABLE, STORE IT IN
A LOCAL VARIABLE!** A LOCAL VARIABLE!**
@ -3698,11 +3698,12 @@ Definition tables
-- ^ Minimum and maximum `y` positions these decorations can be generated at. -- ^ Minimum and maximum `y` positions these decorations can be generated at.
-- ^ This parameter refers to the `y` position of the decoration base, so -- ^ This parameter refers to the `y` position of the decoration base, so
-- the actual maximum height would be `height_max + size.Y`. -- the actual maximum height would be `height_max + size.Y`.
flags = "liquid_surface", flags = "liquid_surface, force_placement",
-- ^ Flags for all decoration types. -- ^ Flags for all decoration types.
-- ^ "liquid_surface": Instead of placement on the highest solid surface -- ^ "liquid_surface": Instead of placement on the highest solid surface
-- ^ in a mapchunk column, placement is on the highest liquid surface. -- ^ in a mapchunk column, placement is on the highest liquid surface.
-- ^ Placement is disabled if solid nodes are found above the liquid surface. -- ^ Placement is disabled if solid nodes are found above the liquid surface.
-- ^ "force_placement": Nodes other than "air" and "ignore" are replaced by the decoration.
----- Simple-type parameters ----- Simple-type parameters
decoration = "default:grass", decoration = "default:grass",
@ -3746,7 +3747,7 @@ Definition tables
}, },
-- ^ See 'Schematic specifier' for details. -- ^ See 'Schematic specifier' for details.
replacements = {["oldname"] = "convert_to", ...}, replacements = {["oldname"] = "convert_to", ...},
flags = "place_center_x, place_center_y, place_center_z, force_placement", flags = "place_center_x, place_center_y, place_center_z",
-- ^ Flags for schematic decorations. See 'Schematic attributes'. -- ^ Flags for schematic decorations. See 'Schematic attributes'.
rotation = "90" -- rotate schematic 90 degrees on placement rotation = "90" -- rotate schematic 90 degrees on placement
-- ^ Rotation can be "0", "90", "180", "270", or "random". -- ^ Rotation can be "0", "90", "180", "270", or "random".

View File

@ -316,7 +316,7 @@ if map format version >= 23:
u8[key_len] key u8[key_len] key
u32 val_len u32 val_len
u8[val_len] value u8[val_len] value
serialized inventory serialized inventory
- Node timers - Node timers
if map format version == 23: if map format version == 23:

View File

@ -390,6 +390,7 @@ ChatPrompt::ChatPrompt(std::wstring prompt, u32 history_limit):
m_cols(0), m_cols(0),
m_view(0), m_view(0),
m_cursor(0), m_cursor(0),
m_cursor_len(0),
m_nick_completion_start(0), m_nick_completion_start(0),
m_nick_completion_end(0) m_nick_completion_end(0)
{ {
@ -417,20 +418,13 @@ void ChatPrompt::input(const std::wstring &str)
m_nick_completion_end = 0; m_nick_completion_end = 0;
} }
std::wstring ChatPrompt::submit() void ChatPrompt::addToHistory(std::wstring line)
{ {
std::wstring line = m_line;
m_line.clear();
if (!line.empty()) if (!line.empty())
m_history.push_back(line); m_history.push_back(line);
if (m_history.size() > m_history_limit) if (m_history.size() > m_history_limit)
m_history.erase(m_history.begin()); m_history.erase(m_history.begin());
m_history_index = m_history.size(); m_history_index = m_history.size();
m_view = 0;
m_cursor = 0;
m_nick_completion_start = 0;
m_nick_completion_end = 0;
return line;
} }
void ChatPrompt::clear() void ChatPrompt::clear()
@ -442,13 +436,15 @@ void ChatPrompt::clear()
m_nick_completion_end = 0; m_nick_completion_end = 0;
} }
void ChatPrompt::replace(std::wstring line) std::wstring ChatPrompt::replace(std::wstring line)
{ {
std::wstring old_line = m_line;
m_line = line; m_line = line;
m_view = m_cursor = line.size(); m_view = m_cursor = line.size();
clampView(); clampView();
m_nick_completion_start = 0; m_nick_completion_start = 0;
m_nick_completion_end = 0; m_nick_completion_end = 0;
return old_line;
} }
void ChatPrompt::historyPrev() void ChatPrompt::historyPrev()
@ -590,14 +586,12 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
s32 length = m_line.size(); s32 length = m_line.size();
s32 increment = (dir == CURSOROP_DIR_RIGHT) ? 1 : -1; s32 increment = (dir == CURSOROP_DIR_RIGHT) ? 1 : -1;
if (scope == CURSOROP_SCOPE_CHARACTER) switch (scope) {
{ case CURSOROP_SCOPE_CHARACTER:
new_cursor += increment; new_cursor += increment;
} break;
else if (scope == CURSOROP_SCOPE_WORD) case CURSOROP_SCOPE_WORD:
{ if (dir == CURSOROP_DIR_RIGHT) {
if (increment > 0)
{
// skip one word to the right // skip one word to the right
while (new_cursor < length && isspace(m_line[new_cursor])) while (new_cursor < length && isspace(m_line[new_cursor]))
new_cursor++; new_cursor++;
@ -605,39 +599,47 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
new_cursor++; new_cursor++;
while (new_cursor < length && isspace(m_line[new_cursor])) while (new_cursor < length && isspace(m_line[new_cursor]))
new_cursor++; new_cursor++;
} } else {
else
{
// skip one word to the left // skip one word to the left
while (new_cursor >= 1 && isspace(m_line[new_cursor - 1])) while (new_cursor >= 1 && isspace(m_line[new_cursor - 1]))
new_cursor--; new_cursor--;
while (new_cursor >= 1 && !isspace(m_line[new_cursor - 1])) while (new_cursor >= 1 && !isspace(m_line[new_cursor - 1]))
new_cursor--; new_cursor--;
} }
} break;
else if (scope == CURSOROP_SCOPE_LINE) case CURSOROP_SCOPE_LINE:
{
new_cursor += increment * length; new_cursor += increment * length;
break;
case CURSOROP_SCOPE_SELECTION:
break;
} }
new_cursor = MYMAX(MYMIN(new_cursor, length), 0); new_cursor = MYMAX(MYMIN(new_cursor, length), 0);
if (op == CURSOROP_MOVE) switch (op) {
{ case CURSOROP_MOVE:
m_cursor = new_cursor; m_cursor = new_cursor;
} m_cursor_len = 0;
else if (op == CURSOROP_DELETE) break;
{ case CURSOROP_DELETE:
if (new_cursor < old_cursor) if (m_cursor_len > 0) { // Delete selected text first
{ m_line.erase(m_cursor, m_cursor_len);
m_line.erase(new_cursor, old_cursor - new_cursor); } else {
m_cursor = new_cursor; m_cursor = MYMIN(new_cursor, old_cursor);
m_line.erase(m_cursor, abs(new_cursor - old_cursor));
} }
else if (new_cursor > old_cursor) m_cursor_len = 0;
{ break;
m_line.erase(old_cursor, new_cursor - old_cursor); case CURSOROP_SELECT:
m_cursor = old_cursor; if (scope == CURSOROP_SCOPE_LINE) {
m_cursor = 0;
m_cursor_len = length;
} else {
m_cursor = MYMIN(new_cursor, old_cursor);
m_cursor_len += abs(new_cursor - old_cursor);
m_cursor_len = MYMIN(m_cursor_len, length - m_cursor);
} }
break;
} }
clampView(); clampView();

View File

@ -146,14 +146,21 @@ public:
void input(wchar_t ch); void input(wchar_t ch);
void input(const std::wstring &str); void input(const std::wstring &str);
// Submit, clear and return current line // Add a string to the history
std::wstring submit(); void addToHistory(std::wstring line);
// Get current line
std::wstring getLine() const { return m_line; }
// Get section of line that is currently selected
std::wstring getSelection() const
{ return m_line.substr(m_cursor, m_cursor_len); }
// Clear the current line // Clear the current line
void clear(); void clear();
// Replace the current line with the given text // Replace the current line with the given text
void replace(std::wstring line); std::wstring replace(std::wstring line);
// Select previous command from history // Select previous command from history
void historyPrev(); void historyPrev();
@ -169,10 +176,13 @@ public:
std::wstring getVisiblePortion() const; std::wstring getVisiblePortion() const;
// Get cursor position (relative to visible portion). -1 if invalid // Get cursor position (relative to visible portion). -1 if invalid
s32 getVisibleCursorPosition() const; s32 getVisibleCursorPosition() const;
// Get length of cursor selection
s32 getCursorLength() const { return m_cursor_len; }
// Cursor operations // Cursor operations
enum CursorOp { enum CursorOp {
CURSOROP_MOVE, CURSOROP_MOVE,
CURSOROP_SELECT,
CURSOROP_DELETE CURSOROP_DELETE
}; };
@ -186,7 +196,8 @@ public:
enum CursorOpScope { enum CursorOpScope {
CURSOROP_SCOPE_CHARACTER, CURSOROP_SCOPE_CHARACTER,
CURSOROP_SCOPE_WORD, CURSOROP_SCOPE_WORD,
CURSOROP_SCOPE_LINE CURSOROP_SCOPE_LINE,
CURSOROP_SCOPE_SELECTION
}; };
// Cursor operation // Cursor operation
@ -224,6 +235,8 @@ private:
s32 m_view; s32 m_view;
// Cursor (index into m_line) // Cursor (index into m_line)
s32 m_cursor; s32 m_cursor;
// Cursor length (length of selected portion of line)
s32 m_cursor_len;
// Last nick completion start (index into m_line) // Last nick completion start (index into m_line)
s32 m_nick_completion_start; s32 m_nick_completion_start;

View File

@ -248,9 +248,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
bool any_position_valid = false; bool any_position_valid = false;
// The order is important here, must be y first
for(s16 y = max_y; y >= min_y; y--)
for(s16 x = min_x; x <= max_x; x++) for(s16 x = min_x; x <= max_x; x++)
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++) for(s16 z = min_z; z <= max_z; z++)
{ {
v3s16 p(x,y,z); v3s16 p(x,y,z);
@ -404,17 +403,15 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
Go through every nodebox, find nearest collision Go through every nodebox, find nearest collision
*/ */
for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) { for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) {
// Ignore if already stepped up this nodebox.
if(is_step_up[boxindex])
continue;
// Find nearest collision of the two boxes (raytracing-like) // Find nearest collision of the two boxes (raytracing-like)
f32 dtime_tmp; f32 dtime_tmp;
int collided = axisAlignedCollision( int collided = axisAlignedCollision(
cboxes[boxindex], movingbox, *speed_f, d, &dtime_tmp); cboxes[boxindex], movingbox, *speed_f, d, &dtime_tmp);
// Ignore if already stepped up this nodebox.
if (is_step_up[boxindex]) {
pos_f->Y += (cboxes[boxindex].MaxEdge.Y - movingbox.MinEdge.Y);
continue;
}
if (collided == -1 || dtime_tmp >= nearest_dtime) if (collided == -1 || dtime_tmp >= nearest_dtime)
continue; continue;
@ -464,12 +461,10 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
is_collision = false; is_collision = false;
CollisionInfo info; CollisionInfo info;
if (is_object[nearest_boxindex]) { if (is_object[nearest_boxindex])
info.type = COLLISION_OBJECT; info.type = COLLISION_OBJECT;
result.standing_on_object = true; else
} else {
info.type = COLLISION_NODE; info.type = COLLISION_NODE;
}
info.node_p = node_positions[nearest_boxindex]; info.node_p = node_positions[nearest_boxindex];
info.bouncy = bouncy; info.bouncy = bouncy;
@ -487,13 +482,12 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
speed_f->X = 0; speed_f->X = 0;
result.collides = true; result.collides = true;
result.collides_xz = true; result.collides_xz = true;
} else if(nearest_collided == 1) { // Y }
if (fabs(speed_f->Y) > BS * 3) { else if(nearest_collided == 1) { // Y
if (fabs(speed_f->Y) > BS * 3)
speed_f->Y *= bounce; speed_f->Y *= bounce;
} else { else
speed_f->Y = 0; speed_f->Y = 0;
result.touching_ground = true;
}
result.collides = true; result.collides = true;
} else if(nearest_collided == 2) { // Z } else if(nearest_collided == 2) { // Z
if (fabs(speed_f->Z) > BS * 3) if (fabs(speed_f->Z) > BS * 3)
@ -514,5 +508,43 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
} }
} }
/*
Final touches: Check if standing on ground, step up stairs.
*/
aabb3f box = box_0;
box.MinEdge += *pos_f;
box.MaxEdge += *pos_f;
for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) {
const aabb3f& cbox = cboxes[boxindex];
/*
See if the object is touching ground.
Object touches ground if object's minimum Y is near node's
maximum Y and object's X-Z-area overlaps with the node's
X-Z-area.
Use 0.15*BS so that it is easier to get on a node.
*/
if (cbox.MaxEdge.X - d > box.MinEdge.X && cbox.MinEdge.X + d < box.MaxEdge.X &&
cbox.MaxEdge.Z - d > box.MinEdge.Z &&
cbox.MinEdge.Z + d < box.MaxEdge.Z) {
if (is_step_up[boxindex]) {
pos_f->Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
box = box_0;
box.MinEdge += *pos_f;
box.MaxEdge += *pos_f;
}
if (fabs(cbox.MaxEdge.Y - box.MinEdge.Y) < 0.15 * BS) {
result.touching_ground = true;
if (is_object[boxindex])
result.standing_on_object = true;
if (is_unloaded[boxindex])
result.standing_on_unloaded = true;
}
}
}
return result; return result;
} }

View File

@ -176,19 +176,6 @@ struct LocalFormspecHandler : public TextDest {
} }
} }
if (m_formname == "MT_CHAT_MENU") {
assert(m_client != 0);
if ((fields.find("btn_send") != fields.end()) ||
(fields.find("quit") != fields.end())) {
StringMap::const_iterator it = fields.find("f_text");
if (it != fields.end())
m_client->typeChatMessage(utf8_to_wide(it->second));
return;
}
}
if (m_formname == "MT_DEATH_SCREEN") { if (m_formname == "MT_DEATH_SCREEN") {
assert(m_client != 0); assert(m_client != 0);
@ -1097,27 +1084,6 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop #define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
#endif #endif
static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
IWritableTextureSource *tsrc, IrrlichtDevice *device,
Client *client, std::string text)
{
std::string formspec =
FORMSPEC_VERSION_STRING
SIZE_TAG
"field[3,2.35;6,0.5;f_text;;" + text + "]"
"button_exit[4,3;3,0.5;btn_send;" + strgettext("Proceed") + "]"
;
/* Create menu */
/* Note: FormspecFormSource and LocalFormspecHandler
* are deleted by guiFormSpecMenu */
FormspecFormSource *fs_src = new FormspecFormSource(formspec);
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client);
create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
}
static void show_deathscreen(GUIFormSpecMenu **cur_formspec, static void show_deathscreen(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef, InventoryManager *invmgr, IGameDef *gamedef,
IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client) IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client)
@ -1518,7 +1484,7 @@ protected:
void dropSelectedItem(); void dropSelectedItem();
void openInventory(); void openInventory();
void openConsole(); void openConsole(float height, const wchar_t *line=NULL);
void toggleFreeMove(float *statustext_time); void toggleFreeMove(float *statustext_time);
void toggleFreeMoveAlt(float *statustext_time, float *jump_timer); void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
void toggleFast(float *statustext_time); void toggleFast(float *statustext_time);
@ -1578,6 +1544,10 @@ protected:
static void settingChangedCallback(const std::string &setting_name, void *data); static void settingChangedCallback(const std::string &setting_name, void *data);
void readSettings(); void readSettings();
#ifdef __ANDROID__
void handleAndroidChatInput();
#endif
private: private:
InputHandler *input; InputHandler *input;
@ -1664,8 +1634,8 @@ private:
#ifdef __ANDROID__ #ifdef __ANDROID__
bool m_cache_hold_aux1; bool m_cache_hold_aux1;
bool m_android_chat_open;
#endif #endif
}; };
Game::Game() : Game::Game() :
@ -2179,7 +2149,7 @@ bool Game::initGui()
// Chat backend and console // Chat backend and console
gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
-1, chat_backend, client); -1, chat_backend, client, &g_menumgr);
if (!gui_chat_console) { if (!gui_chat_console) {
*error_message = "Could not allocate memory for chat console"; *error_message = "Could not allocate memory for chat console";
errorstream << *error_message << std::endl; errorstream << *error_message << std::endl;
@ -2617,10 +2587,10 @@ void Game::processUserInput(VolatileRunFlags *flags,
input->step(dtime); input->step(dtime);
#ifdef __ANDROID__ #ifdef __ANDROID__
if (current_formspec != NULL)
if (current_formspec != 0)
current_formspec->getAndroidUIInput(); current_formspec->getAndroidUIInput();
else
handleAndroidChatInput();
#endif #endif
// Increase timer for double tap of "keymap_jump" // Increase timer for double tap of "keymap_jump"
@ -2656,16 +2626,16 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) {
openInventory(); openInventory();
} else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
show_pause_menu(&current_formspec, client, gamedef, texture_src, device, if (!gui_chat_console->isOpenInhibited()) {
simple_singleplayer_mode); show_pause_menu(&current_formspec, client, gamedef,
texture_src, device, simple_singleplayer_mode);
}
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) {
show_chat_menu(&current_formspec, client, gamedef, texture_src, device, openConsole(0.2, L"");
client, "");
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) {
show_chat_menu(&current_formspec, client, gamedef, texture_src, device, openConsole(0.2, L"/");
client, "/");
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) {
openConsole(); openConsole(1);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) {
toggleFreeMove(statustext_time); toggleFreeMove(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) {
@ -2700,15 +2670,15 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
decreaseViewRange(statustext_time); decreaseViewRange(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) {
toggleFullViewRange(statustext_time); toggleFullViewRange(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) {
quicktune->next(); quicktune->next();
else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) {
quicktune->prev(); quicktune->prev();
else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) {
quicktune->inc(); quicktune->inc();
else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) {
quicktune->dec(); quicktune->dec();
else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) { } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) {
// Print debug stacks // Print debug stacks
dstream << "-----------------------------------------" dstream << "-----------------------------------------"
<< std::endl; << std::endl;
@ -2808,14 +2778,31 @@ void Game::openInventory()
} }
void Game::openConsole() void Game::openConsole(float height, const wchar_t *line)
{ {
if (!gui_chat_console->isOpenInhibited()) { #ifdef __ANDROID__
// Open up to over half of the screen porting::showInputDialog(gettext("ok"), "", "", 2);
gui_chat_console->openConsole(0.6); m_android_chat_open = true;
guienv->setFocus(gui_chat_console); #else
if (gui_chat_console->isOpenInhibited())
return;
gui_chat_console->openConsole(height);
if (line) {
gui_chat_console->setCloseOnEnter(true);
gui_chat_console->replaceAndAddToHistory(line);
}
#endif
}
#ifdef __ANDROID__
void Game::handleAndroidChatInput()
{
if (m_android_chat_open && porting::getInputDialogState() == 0) {
std::string text = porting::getInputDialogValue();
client->typeChatMessage(utf8_to_wide(text));
} }
} }
#endif
void Game::toggleFreeMove(float *statustext_time) void Game::toggleFreeMove(float *statustext_time)

View File

@ -46,15 +46,18 @@ GUIChatConsole::GUIChatConsole(
gui::IGUIElement* parent, gui::IGUIElement* parent,
s32 id, s32 id,
ChatBackend* backend, ChatBackend* backend,
Client* client Client* client,
IMenuManager* menumgr
): ):
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
core::rect<s32>(0,0,100,100)), core::rect<s32>(0,0,100,100)),
m_chat_backend(backend), m_chat_backend(backend),
m_client(client), m_client(client),
m_menumgr(menumgr),
m_screensize(v2u32(0,0)), m_screensize(v2u32(0,0)),
m_animate_time_old(0), m_animate_time_old(0),
m_open(false), m_open(false),
m_close_on_enter(false),
m_height(0), m_height(0),
m_desired_height(0), m_desired_height(0),
m_desired_height_fraction(0.0), m_desired_height_fraction(0.0),
@ -119,6 +122,8 @@ void GUIChatConsole::openConsole(f32 height)
m_desired_height_fraction = height; m_desired_height_fraction = height;
m_desired_height = height * m_screensize.Y; m_desired_height = height * m_screensize.Y;
reformatConsole(); reformatConsole();
Environment->setFocus(this);
m_menumgr->createdMenu(this);
} }
bool GUIChatConsole::isOpen() const bool GUIChatConsole::isOpen() const
@ -134,11 +139,13 @@ bool GUIChatConsole::isOpenInhibited() const
void GUIChatConsole::closeConsole() void GUIChatConsole::closeConsole()
{ {
m_open = false; m_open = false;
Environment->removeFocus(this);
m_menumgr->deletingMenu(this);
} }
void GUIChatConsole::closeConsoleAtOnce() void GUIChatConsole::closeConsoleAtOnce()
{ {
m_open = false; closeConsole();
m_height = 0; m_height = 0;
recalculateConsolePosition(); recalculateConsolePosition();
} }
@ -148,6 +155,14 @@ f32 GUIChatConsole::getDesiredHeight() const
return m_desired_height_fraction; return m_desired_height_fraction;
} }
void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
{
ChatPrompt& prompt = m_chat_backend->getPrompt();
prompt.addToHistory(prompt.getLine());
prompt.replace(line);
}
void GUIChatConsole::setCursor( void GUIChatConsole::setCursor(
bool visible, bool blinking, f32 blink_speed, f32 relative_height) bool visible, bool blinking, f32 blink_speed, f32 relative_height)
{ {
@ -362,13 +377,15 @@ void GUIChatConsole::drawPrompt()
s32 cursor_pos = prompt.getVisibleCursorPosition(); s32 cursor_pos = prompt.getVisibleCursorPosition();
if (cursor_pos >= 0) if (cursor_pos >= 0)
{ {
s32 cursor_len = prompt.getCursorLength();
video::IVideoDriver* driver = Environment->getVideoDriver(); video::IVideoDriver* driver = Environment->getVideoDriver();
s32 x = (1 + cursor_pos) * m_fontsize.X; s32 x = (1 + cursor_pos) * m_fontsize.X;
core::rect<s32> destrect( core::rect<s32> destrect(
x, x,
y + (1.0-m_cursor_height) * m_fontsize.Y, y + m_fontsize.Y * (1.0 - m_cursor_height),
x + m_fontsize.X, x + m_fontsize.X * MYMAX(cursor_len, 1),
y + m_fontsize.Y); y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
);
video::SColor cursor_color(255,255,255,255); video::SColor cursor_color(255,255,255,255);
driver->draw2DRectangle( driver->draw2DRectangle(
cursor_color, cursor_color,
@ -381,23 +398,27 @@ void GUIChatConsole::drawPrompt()
bool GUIChatConsole::OnEvent(const SEvent& event) bool GUIChatConsole::OnEvent(const SEvent& event)
{ {
ChatPrompt &prompt = m_chat_backend->getPrompt();
if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
{ {
// Key input // Key input
if(KeyPress(event.KeyInput) == getKeySetting("keymap_console")) if(KeyPress(event.KeyInput) == getKeySetting("keymap_console"))
{ {
closeConsole(); closeConsole();
Environment->removeFocus(this);
// inhibit open so the_game doesn't reopen immediately // inhibit open so the_game doesn't reopen immediately
m_open_inhibited = 50; m_open_inhibited = 50;
m_close_on_enter = false;
return true; return true;
} }
else if(event.KeyInput.Key == KEY_ESCAPE) else if(event.KeyInput.Key == KEY_ESCAPE)
{ {
closeConsoleAtOnce(); closeConsoleAtOnce();
Environment->removeFocus(this); m_close_on_enter = false;
// the_game will open the pause menu // inhibit open so the_game doesn't reopen immediately
m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu"
return true; return true;
} }
else if(event.KeyInput.Key == KEY_PRIOR) else if(event.KeyInput.Key == KEY_PRIOR)
@ -412,57 +433,50 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
else if(event.KeyInput.Key == KEY_RETURN) else if(event.KeyInput.Key == KEY_RETURN)
{ {
std::wstring text = m_chat_backend->getPrompt().submit(); prompt.addToHistory(prompt.getLine());
std::wstring text = prompt.replace(L"");
m_client->typeChatMessage(text); m_client->typeChatMessage(text);
if (m_close_on_enter) {
closeConsoleAtOnce();
m_close_on_enter = false;
}
return true; return true;
} }
else if(event.KeyInput.Key == KEY_UP) else if(event.KeyInput.Key == KEY_UP)
{ {
// Up pressed // Up pressed
// Move back in history // Move back in history
m_chat_backend->getPrompt().historyPrev(); prompt.historyPrev();
return true; return true;
} }
else if(event.KeyInput.Key == KEY_DOWN) else if(event.KeyInput.Key == KEY_DOWN)
{ {
// Down pressed // Down pressed
// Move forward in history // Move forward in history
m_chat_backend->getPrompt().historyNext(); prompt.historyNext();
return true; return true;
} }
else if(event.KeyInput.Key == KEY_LEFT) else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
{ {
// Left or Ctrl-Left pressed // Left/right pressed
// move character / word to the left // Move/select character/word to the left depending on control and shift keys
ChatPrompt::CursorOpScope scope = ChatPrompt::CursorOp op = event.KeyInput.Shift ?
event.KeyInput.Control ? ChatPrompt::CURSOROP_SELECT :
ChatPrompt::CURSOROP_MOVE;
ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
ChatPrompt::CURSOROP_DIR_LEFT :
ChatPrompt::CURSOROP_DIR_RIGHT;
ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD : ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER; ChatPrompt::CURSOROP_SCOPE_CHARACTER;
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(op, dir, scope);
ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT,
scope);
return true;
}
else if(event.KeyInput.Key == KEY_RIGHT)
{
// Right or Ctrl-Right pressed
// move character / word to the right
ChatPrompt::CursorOpScope scope =
event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER;
m_chat_backend->getPrompt().cursorOperation(
ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT,
scope);
return true; return true;
} }
else if(event.KeyInput.Key == KEY_HOME) else if(event.KeyInput.Key == KEY_HOME)
{ {
// Home pressed // Home pressed
// move to beginning of line // move to beginning of line
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -472,7 +486,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
{ {
// End pressed // End pressed
// move to end of line // move to end of line
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -486,7 +500,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
event.KeyInput.Control ? event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD : ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER; ChatPrompt::CURSOROP_SCOPE_CHARACTER;
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
scope); scope);
@ -500,30 +514,72 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
event.KeyInput.Control ? event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD : ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER; ChatPrompt::CURSOROP_SCOPE_CHARACTER;
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
scope); scope);
return true; return true;
} }
else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
{
// Ctrl-A pressed
// Select all text
prompt.cursorOperation(
ChatPrompt::CURSOROP_SELECT,
ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
ChatPrompt::CURSOROP_SCOPE_LINE);
return true;
}
else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
{
// Ctrl-C pressed
// Copy text to clipboard
if (prompt.getCursorLength() <= 0)
return true;
std::wstring wselected = prompt.getSelection();
std::string selected(wselected.begin(), wselected.end());
Environment->getOSOperator()->copyToClipboard(selected.c_str());
return true;
}
else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control) else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
{ {
// Ctrl-V pressed // Ctrl-V pressed
// paste text from clipboard // paste text from clipboard
if (prompt.getCursorLength() > 0) {
// Delete selected section of text
prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
ChatPrompt::CURSOROP_SCOPE_SELECTION);
}
IOSOperator *os_operator = Environment->getOSOperator(); IOSOperator *os_operator = Environment->getOSOperator();
const c8 *text = os_operator->getTextFromClipboard(); const c8 *text = os_operator->getTextFromClipboard();
if (text) if (!text)
{ return true;
std::wstring wtext = narrow_to_wide(text); std::basic_string<unsigned char> str((const unsigned char*)text);
m_chat_backend->getPrompt().input(wtext); prompt.input(std::wstring(str.begin(), str.end()));
} return true;
}
else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
{
// Ctrl-X pressed
// Cut text to clipboard
if (prompt.getCursorLength() <= 0)
return true;
std::wstring wselected = prompt.getSelection();
std::string selected(wselected.begin(), wselected.end());
Environment->getOSOperator()->copyToClipboard(selected.c_str());
prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
ChatPrompt::CURSOROP_SCOPE_SELECTION);
return true; return true;
} }
else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control) else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
{ {
// Ctrl-U pressed // Ctrl-U pressed
// kill line to left end // kill line to left end
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -533,7 +589,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
{ {
// Ctrl-K pressed // Ctrl-K pressed
// kill line to right end // kill line to right end
m_chat_backend->getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -545,7 +601,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
// Nick completion // Nick completion
std::list<std::string> names = m_client->getConnectedPlayerNames(); std::list<std::string> names = m_client->getConnectedPlayerNames();
bool backwards = event.KeyInput.Shift; bool backwards = event.KeyInput.Shift;
m_chat_backend->getPrompt().nickCompletion(names, backwards); prompt.nickCompletion(names, backwards);
return true; return true;
} }
else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) else if(event.KeyInput.Char != 0 && !event.KeyInput.Control)
@ -553,9 +609,9 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
#if (defined(linux) || defined(__linux)) #if (defined(linux) || defined(__linux))
wchar_t wc = L'_'; wchar_t wc = L'_';
mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
m_chat_backend->getPrompt().input(wc); prompt.input(wc);
#else #else
m_chat_backend->getPrompt().input(event.KeyInput.Char); prompt.input(event.KeyInput.Char);
#endif #endif
return true; return true;
} }

View File

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define GUICHATCONSOLE_HEADER #define GUICHATCONSOLE_HEADER
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "modalMenu.h"
#include "chat.h" #include "chat.h"
#include "config.h" #include "config.h"
@ -33,7 +34,8 @@ public:
gui::IGUIElement* parent, gui::IGUIElement* parent,
s32 id, s32 id,
ChatBackend* backend, ChatBackend* backend,
Client* client); Client* client,
IMenuManager* menumgr);
virtual ~GUIChatConsole(); virtual ~GUIChatConsole();
// Open the console (height = desired fraction of screen size) // Open the console (height = desired fraction of screen size)
@ -51,11 +53,16 @@ public:
void closeConsole(); void closeConsole();
// Close the console immediately, without animation. // Close the console immediately, without animation.
void closeConsoleAtOnce(); void closeConsoleAtOnce();
// Set whether to close the console after the user presses enter.
void setCloseOnEnter(bool close) { m_close_on_enter = close; }
// Return the desired height (fraction of screen size) // Return the desired height (fraction of screen size)
// Zero if the console is closed or getting closed // Zero if the console is closed or getting closed
f32 getDesiredHeight() const; f32 getDesiredHeight() const;
// Replace actual line when adding the actual to the history (if there is any)
void replaceAndAddToHistory(std::wstring line);
// Change how the cursor looks // Change how the cursor looks
void setCursor( void setCursor(
bool visible, bool visible,
@ -81,11 +88,9 @@ private:
void drawPrompt(); void drawPrompt();
private: private:
// pointer to the chat backend
ChatBackend* m_chat_backend; ChatBackend* m_chat_backend;
// pointer to the client
Client* m_client; Client* m_client;
IMenuManager* m_menumgr;
// current screen size // current screen size
v2u32 m_screensize; v2u32 m_screensize;
@ -95,6 +100,8 @@ private:
// should the console be opened or closed? // should the console be opened or closed?
bool m_open; bool m_open;
// should it close after you press enter?
bool m_close_on_enter;
// current console height [pixels] // current console height [pixels]
s32 m_height; s32 m_height;
// desired height [pixels] // desired height [pixels]

View File

@ -47,9 +47,9 @@ extern gui::IGUIStaticText *guiroot;
class MainMenuManager : public IMenuManager class MainMenuManager : public IMenuManager
{ {
public: public:
virtual void createdMenu(GUIModalMenu *menu) virtual void createdMenu(gui::IGUIElement *menu)
{ {
for(std::list<GUIModalMenu*>::iterator for(std::list<gui::IGUIElement*>::iterator
i = m_stack.begin(); i = m_stack.begin();
i != m_stack.end(); ++i) i != m_stack.end(); ++i)
{ {
@ -61,13 +61,13 @@ public:
m_stack.push_back(menu); m_stack.push_back(menu);
} }
virtual void deletingMenu(GUIModalMenu *menu) virtual void deletingMenu(gui::IGUIElement *menu)
{ {
// Remove all entries if there are duplicates // Remove all entries if there are duplicates
bool removed_entry; bool removed_entry;
do{ do{
removed_entry = false; removed_entry = false;
for(std::list<GUIModalMenu*>::iterator for(std::list<gui::IGUIElement*>::iterator
i = m_stack.begin(); i = m_stack.begin();
i != m_stack.end(); ++i) i != m_stack.end(); ++i)
{ {
@ -91,10 +91,10 @@ public:
// Returns true to prevent further processing // Returns true to prevent further processing
virtual bool preprocessEvent(const SEvent& event) virtual bool preprocessEvent(const SEvent& event)
{ {
if(!m_stack.empty()) if (m_stack.empty())
return m_stack.back()->preprocessEvent(event);
else
return false; return false;
GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(m_stack.back());
return mm && mm->preprocessEvent(event);
} }
u32 menuCount() u32 menuCount()
@ -104,16 +104,17 @@ public:
bool pausesGame() bool pausesGame()
{ {
for(std::list<GUIModalMenu*>::iterator for(std::list<gui::IGUIElement*>::iterator
i = m_stack.begin(); i != m_stack.end(); ++i) i = m_stack.begin(); i != m_stack.end(); ++i)
{ {
if((*i)->pausesGame()) GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(*i);
if (mm && mm->pausesGame())
return true; return true;
} }
return false; return false;
} }
std::list<GUIModalMenu*> m_stack; std::list<gui::IGUIElement*> m_stack;
}; };
extern MainMenuManager g_menumgr; extern MainMenuManager g_menumgr;

View File

@ -399,8 +399,21 @@ void MapgenValleys::calculateNoise()
//mapgen_profiler->avg("noisemaps", tcn.stop() / 1000.f); //mapgen_profiler->avg("noisemaps", tcn.stop() / 1000.f);
float heat_offset = 0.f;
float humidity_scale = 1.f;
// Altitude chill tends to reduce the average heat.
if (use_altitude_chill)
heat_offset = 5.f;
// River humidity tends to increase the humidity range.
if (humid_rivers) {
humidity_scale = 0.8f;
}
for (s32 index = 0; index < csize.X * csize.Z; index++) { for (s32 index = 0; index < csize.X * csize.Z; index++) {
noise_heat->result[index] += noise_heat_blend->result[index]; noise_heat->result[index] += noise_heat_blend->result[index] + heat_offset;
noise_humidity->result[index] *= humidity_scale;
noise_humidity->result[index] += noise_humidity_blend->result[index]; noise_humidity->result[index] += noise_humidity_blend->result[index];
} }
@ -481,9 +494,10 @@ float MapgenValleys::terrainLevelFromNoise(TerrainNoise *tn)
} }
// base - depth : height of the bottom of the river // base - depth : height of the bottom of the river
// water_level - 6 : don't make rivers below 6 nodes under the surface // water_level - 3 : don't make rivers below 3 nodes under the surface
// We use three because that's as low as the swamp biomes go.
// There is no logical equivalent to this using rangelim. // There is no logical equivalent to this using rangelim.
mount = MYMIN(MYMAX(base - depth, (float) (water_level - 6)), mount); mount = MYMIN(MYMAX(base - depth, (float)(water_level - 3)), mount);
// Slope has no influence on rivers. // Slope has no influence on rivers.
*tn->slope = 0.f; *tn->slope = 0.f;
@ -503,7 +517,7 @@ float MapgenValleys::adjustedTerrainLevelFromNoise(TerrainNoise *tn)
for (s16 y = y_start; y <= y_start + 1000; y++) { for (s16 y = y_start; y <= y_start + 1000; y++) {
float fill = NoisePerlin3D(&noise_inter_valley_fill->np, tn->x, y, tn->z, seed); float fill = NoisePerlin3D(&noise_inter_valley_fill->np, tn->x, y, tn->z, seed);
if (fill * *tn->slope <= y - mount) { if (fill * *tn->slope < y - mount) {
mount = MYMAX(y - 1, mount); mount = MYMAX(y - 1, mount);
break; break;
} }
@ -522,7 +536,7 @@ int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
s16 level_at_point = terrainLevelAtPoint(p.X, p.Y); s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
if (level_at_point <= water_level || if (level_at_point <= water_level ||
level_at_point > water_level + 16) level_at_point > water_level + 32)
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
else else
return level_at_point; return level_at_point;
@ -552,6 +566,15 @@ float MapgenValleys::terrainLevelAtPoint(s16 x, s16 z)
int MapgenValleys::generateTerrain() int MapgenValleys::generateTerrain()
{ {
// Raising this reduces the rate of evaporation.
static const float evaporation = 300.f;
// from the lua
static const float humidity_dropoff = 4.f;
// constant to convert altitude chill (compatible with lua) to heat
static const float alt_to_heat = 20.f;
// humidity reduction by altitude
static const float alt_to_humid = 10.f;
MapNode n_air(CONTENT_AIR); MapNode n_air(CONTENT_AIR);
MapNode n_river_water(c_river_water_source); MapNode n_river_water(c_river_water_source);
MapNode n_sand(c_sand); MapNode n_sand(c_sand);
@ -564,42 +587,56 @@ int MapgenValleys::generateTerrain()
for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) { for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
s16 river_y = floor(noise_rivers->result[index_2d]); float river_y = noise_rivers->result[index_2d];
s16 surface_y = floor(noise_terrain_height->result[index_2d]); float surface_y = noise_terrain_height->result[index_2d];
float slope = noise_inter_valley_slope->result[index_2d]; float slope = noise_inter_valley_slope->result[index_2d];
float t_heat = noise_heat->result[index_2d];
heightmap[index_2d] = surface_y; heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT;
if (surface_y > surface_max_y) if (surface_y > surface_max_y)
surface_max_y = surface_y; surface_max_y = ceil(surface_y);
if (humid_rivers) {
// Derive heat from (base) altitude. This will be most correct
// at rivers, since other surface heights may vary below.
if (use_altitude_chill && (surface_y > 0.f || river_y > 0.f))
t_heat -= alt_to_heat * MYMAX(surface_y, river_y) / altitude_chill;
// If humidity is low or heat is high, lower the water table.
float delta = noise_humidity->result[index_2d] - 50.f;
if (delta < 0.f) {
float t_evap = (t_heat - 32.f) / evaporation;
river_y += delta * MYMAX(t_evap, 0.08f);
}
}
u32 index_3d = (z - node_min.Z) * zstride + (x - node_min.X); u32 index_3d = (z - node_min.Z) * zstride + (x - node_min.X);
u32 index_data = vm->m_area.index(x, node_min.Y - 1, z); u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
// Mapgens concern themselves with stone and water. // Mapgens concern themselves with stone and water.
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
float fill = 0.f;
fill = noise_inter_valley_fill->result[index_3d];
if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) { if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
bool river = (river_y > surface_y); float fill = noise_inter_valley_fill->result[index_3d];
float surface_delta = (float)y - surface_y;
bool river = y + 1 < river_y;
if (river && y == surface_y) { if (fabs(surface_delta) <= 0.5f && y > water_level && river) {
// river bottom // river bottom
vm->m_data[index_data] = n_sand; vm->m_data[index_data] = n_sand;
} else if (river && y <= surface_y) { } else if (slope * fill > surface_delta) {
// ground // ground
vm->m_data[index_data] = n_stone; vm->m_data[index_data] = n_stone;
} else if (river && y < river_y) { if (y > heightmap[index_2d])
// river heightmap[index_2d] = y;
vm->m_data[index_data] = n_river_water; if (y > surface_max_y)
} else if ((!river) && myround(fill * slope) >= y - surface_y) { surface_max_y = y;
// ground
vm->m_data[index_data] = n_stone;
heightmap[index_2d] = surface_max_y = y;
} else if (y <= water_level) { } else if (y <= water_level) {
// sea // sea
vm->m_data[index_data] = n_water; vm->m_data[index_data] = n_water;
} else if (river) {
// river
vm->m_data[index_data] = n_river_water;
} else { } else {
vm->m_data[index_data] = n_air; vm->m_data[index_data] = n_air;
} }
@ -609,18 +646,51 @@ int MapgenValleys::generateTerrain()
index_3d += ystride; index_3d += ystride;
} }
// Although the original valleys adjusts humidity by distance // This happens if we're generating a chunk that doesn't
// from seawater, this causes problems with the default biomes. // contain the terrain surface, in which case, we need
// Adjust only by freshwater proximity. // to set heightmap to a value outside of the chunk,
const float humidity_offset = 0.8f; // derived by testing // to avoid confusing lua mods that use heightmap.
if (humid_rivers) if (heightmap[index_2d] == -MAX_MAP_GENERATION_LIMIT) {
noise_humidity->result[index_2d] *= (1 + pow(0.5f, MYMAX((surface_max_y s16 surface_y_int = myround(surface_y);
- noise_rivers->result[index_2d]) / 3.f, 0.f))) * humidity_offset; if (surface_y_int > node_max.Y + 1 || surface_y_int < node_min.Y - 1) {
// If surface_y is outside the chunk, it's good enough.
heightmap[index_2d] = surface_y_int;
} else {
// If the ground is outside of this chunk, but surface_y
// is within the chunk, give a value outside.
heightmap[index_2d] = node_min.Y - 2;
}
}
// Assign the heat adjusted by altitude. if (humid_rivers) {
if (use_altitude_chill && surface_max_y > 0) // Use base ground (water table) in a riverbed, to
noise_heat->result[index_2d] *= // avoid an unnatural rise in humidity.
pow(0.5f, (surface_max_y - altitude_chill / 3.f) / altitude_chill); float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
float humid = noise_humidity->result[index_2d];
float water_depth = (t_alt - river_y) / humidity_dropoff;
humid *= 1.f + pow(0.5f, MYMAX(water_depth, 1.f));
// Reduce humidity with altitude (ignoring riverbeds).
// This is similar to the lua version's seawater adjustment,
// but doesn't increase the base humidity, which causes
// problems with the default biomes.
if (t_alt > 0.f)
humid -= alt_to_humid * t_alt / altitude_chill;
noise_humidity->result[index_2d] = humid;
}
// Assign the heat adjusted by any changed altitudes.
// The altitude will change about half the time.
if (use_altitude_chill) {
// ground height ignoring riverbeds
float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
if (humid_rivers && heightmap[index_2d] == (s16)myround(surface_y))
// The altitude hasn't changed. Use the first result.
noise_heat->result[index_2d] = t_heat;
else if (t_alt > 0.f)
noise_heat->result[index_2d] -= alt_to_heat * t_alt / altitude_chill;
}
} }
return surface_max_y; return surface_max_y;
@ -645,7 +715,7 @@ MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map)
// generated mapchunk or if not, a node of overgenerated base terrain. // generated mapchunk or if not, a node of overgenerated base terrain.
content_t c_above = vm->m_data[vi + em.X].getContent(); content_t c_above = vm->m_data[vi + em.X].getContent();
bool air_above = c_above == CONTENT_AIR; bool air_above = c_above == CONTENT_AIR;
bool water_above = (c_above == c_water_source); bool water_above = (c_above == c_water_source || c_above == c_river_water_source);
// If there is air or water above enable top/filler placement, otherwise force // If there is air or water above enable top/filler placement, otherwise force
// nplaced to stone level by setting a number exceeding any possible filler depth. // nplaced to stone level by setting a number exceeding any possible filler depth.
@ -714,7 +784,7 @@ MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map)
water_above = true; water_above = true;
} else if (c == c_river_water_source) { } else if (c == c_river_water_source) {
vm->m_data[vi] = MapNode(biome->c_river_water); vm->m_data[vi] = MapNode(biome->c_river_water);
nplaced = U16_MAX; // Sand was already placed under rivers. nplaced = depth_top; // Enable filler placement for next surface
air_above = false; air_above = false;
water_above = true; water_above = true;
} else if (c == CONTENT_AIR) { } else if (c == CONTENT_AIR) {

View File

@ -304,13 +304,16 @@ size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p)
s16 height = (deco_height_max > 0) ? s16 height = (deco_height_max > 0) ?
pr->range(deco_height, deco_height_max) : deco_height; pr->range(deco_height, deco_height_max) : deco_height;
bool force_placement = (flags & DECO_FORCE_PLACEMENT);
v3s16 em = vm->m_area.getExtent(); v3s16 em = vm->m_area.getExtent();
u32 vi = vm->m_area.index(p); u32 vi = vm->m_area.index(p);
for (int i = 0; i < height; i++) { for (int i = 0; i < height; i++) {
vm->m_area.add_y(em, vi, 1); vm->m_area.add_y(em, vi, 1);
content_t c = vm->m_data[vi].getContent(); content_t c = vm->m_data[vi].getContent();
if (c != CONTENT_AIR && c != CONTENT_IGNORE) if (c != CONTENT_AIR && c != CONTENT_IGNORE &&
!force_placement)
break; break;
vm->m_data[vi] = MapNode(c_place); vm->m_data[vi] = MapNode(c_place);

View File

@ -31,8 +31,8 @@ class IMenuManager
{ {
public: public:
// A GUIModalMenu calls these when this class is passed as a parameter // A GUIModalMenu calls these when this class is passed as a parameter
virtual void createdMenu(GUIModalMenu *menu) = 0; virtual void createdMenu(gui::IGUIElement *menu) = 0;
virtual void deletingMenu(GUIModalMenu *menu) = 0; virtual void deletingMenu(gui::IGUIElement *menu) = 0;
}; };
/* /*

View File

@ -853,7 +853,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
assert(f->liquid_type == LIQUID_SOURCE); assert(f->liquid_type == LIQUID_SOURCE);
if (opaque_water) if (opaque_water)
f->alpha = 255; f->alpha = 255;
f->solidness = 0; f->solidness = 1;
is_liquid = true; is_liquid = true;
break; break;
case NDT_FLOWINGLIQUID: case NDT_FLOWINGLIQUID:

View File

@ -116,7 +116,6 @@ void ScriptApiSecurity::initializeSecurity()
"upvaluejoin", "upvaluejoin",
"sethook", "sethook",
"debug", "debug",
"getupvalue",
"setlocal", "setlocal",
}; };
static const char *package_whitelist[] = { static const char *package_whitelist[] = {

View File

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_http.h" #include "lua_api/l_http.h"
#include "httpfetch.h" #include "httpfetch.h"
#include "settings.h" #include "settings.h"
#include "debug.h"
#include "log.h" #include "log.h"
#include <algorithm> #include <algorithm>
@ -130,11 +131,27 @@ int ModApiHttp::l_request_http_api(lua_State *L)
{ {
NO_MAP_LOCK_REQUIRED; NO_MAP_LOCK_REQUIRED;
// We have to make sure that this function is being called directly by
// a mod, otherwise a malicious mod could override this function and
// steal its return value.
lua_Debug info;
// Make sure there's only one item below this function on the stack...
if (lua_getstack(L, 2, &info)) {
return 0;
}
FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
// ...and that that item is the main file scope.
if (strcmp(info.what, "main") != 0) {
return 0;
}
// Mod must be listed in secure.http_mods or secure.trusted_mods // Mod must be listed in secure.http_mods or secure.trusted_mods
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
if (!lua_isstring(L, -1)) { if (!lua_isstring(L, -1)) {
lua_pushnil(L); return 0;
return 1;
} }
const char *mod_name = lua_tostring(L, -1); const char *mod_name = lua_tostring(L, -1);

View File

@ -75,9 +75,10 @@ int ModApiUtil::l_get_us_time(lua_State *L)
} }
#define CHECK_SECURE_SETTING(L, name) \ #define CHECK_SECURE_SETTING(L, name) \
if (name.compare(0, 7, "secure.") == 0) {\ if (ScriptApiSecurity::isSecure(L) && \
lua_pushliteral(L, "Attempt to set secure setting.");\ name.compare(0, 7, "secure.") == 0) { \
lua_error(L);\ lua_pushliteral(L, "Attempt to set secure setting."); \
lua_error(L); \
} }
// setting_set(name, value) // setting_set(name, value)

View File

@ -146,6 +146,7 @@ void TerminalChatConsole::typeChatMessage(const std::wstring &msg)
void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed) void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
{ {
ChatPrompt &prompt = m_chat_backend.getPrompt();
// Helpful if you want to collect key codes that aren't documented // Helpful if you want to collect key codes that aren't documented
/*if (ch != ERR) { /*if (ch != ERR) {
m_chat_backend.addMessage(L"", m_chat_backend.addMessage(L"",
@ -177,20 +178,20 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case KEY_ENTER: case KEY_ENTER:
case '\r': case '\r':
case '\n': { case '\n': {
std::wstring text = m_chat_backend.getPrompt().submit(); prompt.addToHistory(prompt.getLine());
typeChatMessage(text); typeChatMessage(prompt.replace(L""));
break; break;
} }
case KEY_UP: case KEY_UP:
m_chat_backend.getPrompt().historyPrev(); prompt.historyPrev();
break; break;
case KEY_DOWN: case KEY_DOWN:
m_chat_backend.getPrompt().historyNext(); prompt.historyNext();
break; break;
case KEY_LEFT: case KEY_LEFT:
// Left pressed // Left pressed
// move character to the left // move character to the left
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_CHARACTER); ChatPrompt::CURSOROP_SCOPE_CHARACTER);
@ -198,7 +199,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case 545: case 545:
// Ctrl-Left pressed // Ctrl-Left pressed
// move word to the left // move word to the left
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_WORD); ChatPrompt::CURSOROP_SCOPE_WORD);
@ -206,7 +207,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case KEY_RIGHT: case KEY_RIGHT:
// Right pressed // Right pressed
// move character to the right // move character to the right
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_CHARACTER); ChatPrompt::CURSOROP_SCOPE_CHARACTER);
@ -214,7 +215,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case 560: case 560:
// Ctrl-Right pressed // Ctrl-Right pressed
// move word to the right // move word to the right
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_WORD); ChatPrompt::CURSOROP_SCOPE_WORD);
@ -222,7 +223,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case KEY_HOME: case KEY_HOME:
// Home pressed // Home pressed
// move to beginning of line // move to beginning of line
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -230,7 +231,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case KEY_END: case KEY_END:
// End pressed // End pressed
// move to end of line // move to end of line
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -240,7 +241,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case 127: case 127:
// Backspace pressed // Backspace pressed
// delete character to the left // delete character to the left
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_CHARACTER); ChatPrompt::CURSOROP_SCOPE_CHARACTER);
@ -248,7 +249,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case KEY_DC: case KEY_DC:
// Delete pressed // Delete pressed
// delete character to the right // delete character to the right
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_CHARACTER); ChatPrompt::CURSOROP_SCOPE_CHARACTER);
@ -256,7 +257,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case 519: case 519:
// Ctrl-Delete pressed // Ctrl-Delete pressed
// delete word to the right // delete word to the right
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_WORD); ChatPrompt::CURSOROP_SCOPE_WORD);
@ -264,7 +265,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case 21: case 21:
// Ctrl-U pressed // Ctrl-U pressed
// kill line to left end // kill line to left end
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -272,7 +273,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case 11: case 11:
// Ctrl-K pressed // Ctrl-K pressed
// kill line to right end // kill line to right end
m_chat_backend.getPrompt().cursorOperation( prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE); ChatPrompt::CURSOROP_SCOPE_LINE);
@ -280,7 +281,7 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
case KEY_TAB: case KEY_TAB:
// Tab pressed // Tab pressed
// Nick completion // Nick completion
m_chat_backend.getPrompt().nickCompletion(m_nicks, false); prompt.nickCompletion(m_nicks, false);
break; break;
default: default:
// Add character to the prompt, // Add character to the prompt,
@ -296,11 +297,11 @@ void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
m_pending_utf8_bytes = ""; m_pending_utf8_bytes = "";
// hopefully only one char in the wstring... // hopefully only one char in the wstring...
for (size_t i = 0; i < w.size(); i++) { for (size_t i = 0; i < w.size(); i++) {
m_chat_backend.getPrompt().input(w.c_str()[i]); prompt.input(w.c_str()[i]);
} }
} }
} else if (IS_ASCII_PRINTABLE_CHAR(ch)) { } else if (IS_ASCII_PRINTABLE_CHAR(ch)) {
m_chat_backend.getPrompt().input(ch); prompt.input(ch);
} else { } else {
// Silently ignore characters we don't handle // Silently ignore characters we don't handle