/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "common_irrlicht.h"
#include "game.h"
#include "client.h"
#include "server.h"
#include "guiPauseMenu.h"
#include "guiPasswordChange.h"
#include "guiInventoryMenu.h"
#include "guiTextInputMenu.h"
#include "materials.h"
#include "config.h"
#include "clouds.h"
#include "farmesh.h"
#include "mapblock.h"
/*
TODO: Move content-aware stuff to separate file by adding properties
and virtual interfaces
*/
#include "content_mapnode.h"
#include "content_nodemeta.h"
/*
Setting this to 1 enables a special camera mode that forces
the renderers to think that the camera statically points from
the starting place to a static direction.
This allows one to move around with the player and see what
is actually drawn behind solid things and behind the player.
*/
#define FIELD_OF_VIEW_TEST 0
MapDrawControl draw_control;
// Chat data
struct ChatLine
{
ChatLine():
age(0.0)
{
}
ChatLine(const std::wstring &a_text):
age(0.0),
text(a_text)
{
}
float age;
std::wstring text;
};
/*
Inventory stuff
*/
// Inventory actions from the menu are buffered here before sending
Queue<InventoryAction*> inventory_action_queue;
// This is a copy of the inventory that the client's environment has
Inventory local_inventory;
u16 g_selected_item = 0;
/*
Text input system
*/
struct TextDestSign : public TextDest
{
TextDestSign(v3s16 blockpos, s16 id, Client *client)
{
m_blockpos = blockpos;
m_id = id;
m_client = client;
}
void gotText(std::wstring text)
{
std::string ntext = wide_to_narrow(text);
dstream<<"Changing text of a sign object: "
<<ntext<<std::endl;
m_client->sendSignText(m_blockpos, m_id, ntext);
}
v3s16 m_blockpos;
s16 m_id;
Client *m_client;
};
struct TextDestChat : public TextDest
{
TextDestChat(Client *client)
{
m_client = client;
}
void gotText(std::wstring text)
{
// Discard empty line
if(text == L"")
return;
// Send to others
m_client->sendChatMessage(text);
// Show locally
m_client->addChatMessage(text);
}
Client *m_client;
};
struct TextDestSignNode : public TextDest
{
TextDestSignNode(v3s16 p, Client *client)
{
m_p = p;
m_client = client;
}
void gotText(std::wstring text)
{
std::string ntext = wide_to_narrow(text);
dstream<<"Changing text of a sign node: "
<<ntext<<std::endl;
m_client->sendSignNodeText(m_p, ntext);
}
v3s16 m_p;
Client *m_client;
};
/*
Render distance feedback loop
*/
void updateViewingRange(f32 frametime_in, Client *client)
{
if(draw_control.range_all == true)
return;
static f32 added_frametime = 0;
static s16 added_frames = 0;
added_frametime += frametime_in;
added_frames += 1;
// Actually this counter kind of sucks because frametime is busytime
static f32 counter = 0;
counter -= frametime_in;
if(counter > 0)
return;
//counter = 0.1;
counter = 0.2;
/*dstream<<__FUNCTION_NAME
<<": Collected "<<added_frames<<" frames, total of "
<<added_frametime<<"s."<<std::endl;*/
/*dstream<<"draw_control.blocks_drawn="
<<draw_control.blocks_drawn
<<", draw_control.blocks_would_have_drawn="
<<draw_control.blocks_would_have_drawn
<<std::endl;*/
float range_min = g_settings.getS16("viewing_range_nodes_min");
float range_max = g_settings.getS16("viewing_range_nodes_max");
// Limit minimum to keep the feedback loop stable
if(range_min < 5)
range_min = 5;
draw_control.wanted_min_range = range_min;
//draw_control.wanted_max_blocks = (1.5*draw_control.blocks_drawn)+1;
draw_control.wanted_max_blocks = (1.5*draw_control.blocks_would_have_drawn)+1;
if(draw_control.wanted_max_blocks < 10)
draw_control.wanted_max_blocks = 10;
float block_draw_ratio = 1.0;
if(draw_control.blocks_would_have_drawn != 0)
{
block_draw_ratio = (float)draw_control.blocks_drawn
/ (float)draw_control.blocks_would_have_drawn;
}
// Calculate the average frametime in the case that all wanted
// blocks had been drawn
f32 frametime = added_frametime / added_frames / block_draw_ratio;
added_frametime = 0.0;
added_frames = 0;
float wanted_fps = g_settings.getFloat("wanted_fps");
float wanted_frametime = 1.0 / wanted_fps;
f32 wanted_frametime_change = wanted_frametime - frametime;
//dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
// If needed frametime change is small, just return
if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
{
//dstream<<"ignoring small wanted_frametime_change"<<std::endl;
return;
}
float range = draw_control.wanted_range;
float new_range = range;
static s16 range_old = 0;
static f32 frametime_old = 0;
float d_range = range - range_old;
f32 d_frametime = frametime - frametime_old;
// A sane default of 30ms per 50 nodes of range
static f32 time_per_range = 30. / 50;
if(d_range != 0)
{
time_per_range = d_frametime / d_range;
}
// The minimum allowed calculated frametime-range derivative:
// Practically this sets the maximum speed of changing the range.
// The lower this value, the higher the maximum changing speed.
// A low value here results in wobbly range (0.001)
// A high value here results in slow changing range (0.0025)
// SUGG: This could be dynamically adjusted so that when
// the camera is turning, this is lower
//float min_time_per_range = 0.0015;
float min_time_per_range = 0.0010;
//float min_time_per_range = 0.05 / range;
if(time_per_range < min_time_per_range)
{
time_per_range = min_time_per_range;
//dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
}
else
{
//dstream<<"time_per_range="<<time_per_range<<std::endl;
}
f32 wanted_range_change = wanted_frametime_change / time_per_range;
// Dampen the change a bit to kill oscillations
//wanted_range_change *= 0.9;
//wanted_range_change *= 0.75;
wanted_range_change *= 0.5;
//dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
// If needed range change is very small, just return
if(fabs(wanted_range_change) < 0.001)
{
//dstream<<"ignoring small wanted_range_change"<<std::endl;
return;
}
new_range += wanted_range_change;
//float new_range_unclamped = new_range;
if(new_range < range_min)
new_range = range_min;
if(new_range > range_max)
new_range = range_max;
/*dstream<<"new_range="<<new_range_unclamped
<<", clamped to "<<new_range<<std::endl;*/
draw_control.wanted_range = new_range;
range_old = new_range;
frametime_old = frametime;
}
/*
Hotbar draw routine
*/
void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
Inventory *inventory, s32 halfheartcount)
{
InventoryList *mainlist = inventory->getList("main");
if(mainlist == NULL)
{
dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
return;
}
s32 padding = imgsize/12;
//s32 height = imgsize + padding*2;
s32 width = itemcount*(imgsize+padding*2);
// Position of upper left corner of bar
v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
// Draw background color
/*core::rect<s32> barrect(0,0,width,height);
barrect += pos;
video::SColor bgcolor(255,128,128,128);
driver->draw2DRectangle(bgcolor, barrect, NULL);*/
core::rect<s32> imgrect(0,0,imgsize,imgsize);
for(s32 i=0; i<itemcount; i++)
{
InventoryItem *item = mainlist->getItem(i);
core::rect<s32> rect = imgrect + pos
+ v2s32(padding+i*(imgsize+padding*2), padding);
if(g_selected_item == i)
{
video::SColor c_outside(255,255,0,0);
//video::SColor c_outside(255,0,0,0);
//video::SColor c_inside(255,192,192,192);
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
// Black base borders
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - padding, y1 - padding),
v2s32(x2 + padding, y1)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - padding, y2),
v2s32(x2 + padding, y2 + padding)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - padding, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + padding, y2)
), NULL);
/*// Light inside borders
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1 - padding/2),
v2s32(x2 + padding/2, y1)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y2),
v2s32(x2 + padding/2, y2 + padding/2)
), NULL);
driver->draw2DRectangle(c_inside,
|