#include "sky.h"
#include "sound.h"
#if USE_SOUND
- #include "sound_openal.h"
+#include "sound_openal.h"
#endif
#include "event_manager.h"
#include <iomanip>
Text input system
*/
-struct TextDestNodeMetadata : public TextDest
-{
+struct TextDestNodeMetadata : public TextDest {
TextDestNodeMetadata(v3s16 p, Client *client)
{
m_p = p;
void gotText(std::wstring text)
{
std::string ntext = wide_to_narrow(text);
- infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
- <<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
+ infostream << "Submitting 'text' field of node at (" << m_p.X << ","
+ << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
std::map<std::string, std::string> fields;
fields["text"] = ntext;
m_client->sendNodemetaFields(m_p, "", fields);
Client *m_client;
};
-struct TextDestPlayerInventory : public TextDest
-{
+struct TextDestPlayerInventory : public TextDest {
TextDestPlayerInventory(Client *client)
{
m_client = client;
Client *m_client;
};
-struct LocalFormspecHandler : public TextDest
-{
+struct LocalFormspecHandler : public TextDest {
LocalFormspecHandler();
LocalFormspecHandler(std::string formname) :
m_client(0)
m_formname = formname;
}
- void gotText(std::wstring message) {
+ void gotText(std::wstring message)
+ {
errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
}
return;
}
}
+
if (m_formname == "MT_CHAT_MENU") {
assert(m_client != 0);
+
if ((fields.find("btn_send") != fields.end()) ||
(fields.find("quit") != fields.end())) {
if (fields.find("f_text") != fields.end()) {
m_client->typeChatMessage(narrow_to_wide(fields["f_text"]));
}
+
return;
}
}
if (m_formname == "MT_DEATH_SCREEN") {
assert(m_client != 0);
+
if ((fields.find("btn_respawn") != fields.end())) {
m_client->sendRespawn();
return;
}
// don't show error message for unhandled cursor keys
- if ( (fields.find("key_up") != fields.end()) ||
- (fields.find("key_down") != fields.end()) ||
- (fields.find("key_left") != fields.end()) ||
- (fields.find("key_right") != fields.end())) {
+ if ((fields.find("key_up") != fields.end()) ||
+ (fields.find("key_down") != fields.end()) ||
+ (fields.find("key_left") != fields.end()) ||
+ (fields.find("key_right") != fields.end())) {
return;
}
errorstream << "LocalFormspecHandler::gotText unhandled >" << m_formname << "< event" << std::endl;
int i = 0;
- for (std::map<std::string,std::string>::iterator iter = fields.begin();
+
+ for (std::map<std::string, std::string>::iterator iter = fields.begin();
iter != fields.end(); iter++) {
- errorstream << "\t"<< i << ": " << iter->first << "=" << iter->second << std::endl;
+ errorstream << "\t" << i << ": " << iter->first << "=" << iter->second << std::endl;
i++;
}
}
std::string getForm()
{
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
- if(!meta)
+
+ if (!meta)
return "";
+
return meta->getString("formspec");
}
std::string resolveText(std::string str)
{
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
- if(!meta)
+
+ if (!meta)
return str;
+
return meta->resolveString(str);
}
}
std::string getForm()
{
- LocalPlayer* player = m_client->getEnv().getLocalPlayer();
+ LocalPlayer *player = m_client->getEnv().getLocalPlayer();
return player->inventory_formspec;
}
/*
Check if a node is pointable
*/
-inline bool isPointableNode(const MapNode& n,
- Client *client, bool liquids_pointable)
+inline bool isPointableNode(const MapNode &n,
+ Client *client, bool liquids_pointable)
{
const ContentFeatures &features = client->getNodeDefManager()->get(n);
return features.pointable ||
- (liquids_pointable && features.isLiquid());
+ (liquids_pointable && features.isLiquid());
}
/*
f32 mindistance = BS * 1001;
// First try to find a pointed at active object
- if(look_for_object)
- {
- selected_object = client->getSelectedActiveObject(d*BS,
- camera_position, shootline);
+ if (look_for_object) {
+ selected_object = client->getSelectedActiveObject(d * BS,
+ camera_position, shootline);
- if(selected_object != NULL)
- {
- if(selected_object->doShowSelectionBox())
- {
+ if (selected_object != NULL) {
+ if (selected_object->doShowSelectionBox()) {
aabb3f *selection_box = selected_object->getSelectionBox();
// Box should exist because object was
// returned in the first place
v3f pos = selected_object->getPosition();
hilightboxes.push_back(aabb3f(
- selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
- selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
+ selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
+ selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
}
mindistance = (selected_object->getPosition() - camera_position).getLength();
<<std::endl;*/
s16 a = d;
- s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
- s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
- s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
- s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
- s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
- s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
+ s16 ystart = pos_i.Y + 0 - (camera_direction.Y < 0 ? a : 1);
+ s16 zstart = pos_i.Z - (camera_direction.Z < 0 ? a : 1);
+ s16 xstart = pos_i.X - (camera_direction.X < 0 ? a : 1);
+ s16 yend = pos_i.Y + 1 + (camera_direction.Y > 0 ? a : 1);
+ s16 zend = pos_i.Z + (camera_direction.Z > 0 ? a : 1);
+ s16 xend = pos_i.X + (camera_direction.X > 0 ? a : 1);
// Prevent signed number overflow
- if(yend==32767)
- yend=32766;
- if(zend==32767)
- zend=32766;
- if(xend==32767)
- xend=32766;
-
- for(s16 y = ystart; y <= yend; y++)
- for(s16 z = zstart; z <= zend; z++)
- for(s16 x = xstart; x <= xend; x++)
- {
- MapNode n;
- try
- {
- n = map.getNode(v3s16(x,y,z));
- }
- catch(InvalidPositionException &e)
- {
- continue;
- }
- if(!isPointableNode(n, client, liquids_pointable))
- continue;
-
- std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
-
- v3s16 np(x,y,z);
- v3f npf = intToFloat(np, BS);
-
- for(std::vector<aabb3f>::const_iterator
- i = boxes.begin();
- i != boxes.end(); i++)
- {
- aabb3f box = *i;
- box.MinEdge += npf;
- box.MaxEdge += npf;
-
- for(u16 j=0; j<6; j++)
- {
- v3s16 facedir = g_6dirs[j];
- aabb3f facebox = box;
-
- f32 d = 0.001*BS;
- if(facedir.X > 0)
- facebox.MinEdge.X = facebox.MaxEdge.X-d;
- else if(facedir.X < 0)
- facebox.MaxEdge.X = facebox.MinEdge.X+d;
- else if(facedir.Y > 0)
- facebox.MinEdge.Y = facebox.MaxEdge.Y-d;
- else if(facedir.Y < 0)
- facebox.MaxEdge.Y = facebox.MinEdge.Y+d;
- else if(facedir.Z > 0)
- facebox.MinEdge.Z = facebox.MaxEdge.Z-d;
- else if(facedir.Z < 0)
- facebox.MaxEdge.Z = facebox.MinEdge.Z+d;
-
- v3f centerpoint = facebox.getCenter();
- f32 distance = (centerpoint - camera_position).getLength();
- if(distance >= mindistance)
+ if (yend == 32767)
+ yend = 32766;
+
+ if (zend == 32767)
+ zend = 32766;
+
+ if (xend == 32767)
+ xend = 32766;
+
+ for (s16 y = ystart; y <= yend; y++)
+ for (s16 z = zstart; z <= zend; z++)
+ for (s16 x = xstart; x <= xend; x++) {
+ MapNode n;
+
+ try {
+ n = map.getNode(v3s16(x, y, z));
+ } catch (InvalidPositionException &e) {
continue;
- if(!facebox.intersectsWithLine(shootline))
+ }
+
+ if (!isPointableNode(n, client, liquids_pointable))
continue;
- v3s16 np_above = np + facedir;
-
- result.type = POINTEDTHING_NODE;
- result.node_undersurface = np;
- result.node_abovesurface = np_above;
- mindistance = distance;
-
- hilightboxes.clear();
- if (!g_settings->getBool("enable_node_highlighting")) {
- for(std::vector<aabb3f>::const_iterator
- i2 = boxes.begin();
- i2 != boxes.end(); i2++)
- {
- aabb3f box = *i2;
- box.MinEdge += npf + v3f(-d,-d,-d) - intToFloat(camera_offset, BS);
- box.MaxEdge += npf + v3f(d,d,d) - intToFloat(camera_offset, BS);
- hilightboxes.push_back(box);
+ std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
+
+ v3s16 np(x, y, z);
+ v3f npf = intToFloat(np, BS);
+
+ for (std::vector<aabb3f>::const_iterator
+ i = boxes.begin();
+ i != boxes.end(); i++) {
+ aabb3f box = *i;
+ box.MinEdge += npf;
+ box.MaxEdge += npf;
+
+ for (u16 j = 0; j < 6; j++) {
+ v3s16 facedir = g_6dirs[j];
+ aabb3f facebox = box;
+
+ f32 d = 0.001 * BS;
+
+ if (facedir.X > 0)
+ facebox.MinEdge.X = facebox.MaxEdge.X - d;
+ else if (facedir.X < 0)
+ facebox.MaxEdge.X = facebox.MinEdge.X + d;
+ else if (facedir.Y > 0)
+ facebox.MinEdge.Y = facebox.MaxEdge.Y - d;
+ else if (facedir.Y < 0)
+ facebox.MaxEdge.Y = facebox.MinEdge.Y + d;
+ else if (facedir.Z > 0)
+ facebox.MinEdge.Z = facebox.MaxEdge.Z - d;
+ else if (facedir.Z < 0)
+ facebox.MaxEdge.Z = facebox.MinEdge.Z + d;
+
+ v3f centerpoint = facebox.getCenter();
+ f32 distance = (centerpoint - camera_position).getLength();
+
+ if (distance >= mindistance)
+ continue;
+
+ if (!facebox.intersectsWithLine(shootline))
+ continue;
+
+ v3s16 np_above = np + facedir;
+
+ result.type = POINTEDTHING_NODE;
+ result.node_undersurface = np;
+ result.node_abovesurface = np_above;
+ mindistance = distance;
+
+ hilightboxes.clear();
+
+ if (!g_settings->getBool("enable_node_highlighting")) {
+ for (std::vector<aabb3f>::const_iterator
+ i2 = boxes.begin();
+ i2 != boxes.end(); i2++) {
+ aabb3f box = *i2;
+ box.MinEdge += npf + v3f(-d, -d, -d) - intToFloat(camera_offset, BS);
+ box.MaxEdge += npf + v3f(d, d, d) - intToFloat(camera_offset, BS);
+ hilightboxes.push_back(box);
+ }
+ }
}
}
- }
- }
- } // for coords
+ } // for coords
return result;
}
gui::IGUIFont *font, u32 text_height, u32 show_profiler,
u32 show_profiler_max)
{
- if(show_profiler == 0)
- {
+ if (show_profiler == 0) {
guitext_profiler->setVisible(false);
- }
- else
- {
+ } else {
std::ostringstream os(std::ios_base::binary);
g_profiler->printPage(os, show_profiler, show_profiler_max);
guitext_profiler->setVisible(true);
s32 w = font->getDimension(text.c_str()).Width;
- if(w < 400)
+
+ if (w < 400)
w = 400;
- core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w,
- 8+(text_height+5)*2 +
- font->getDimension(text.c_str()).Height);
+
+ core::rect<s32> rect(6, 4 + (text_height + 5) * 2, 12 + w,
+ 8 + (text_height + 5) * 2 +
+ font->getDimension(text.c_str()).Height);
guitext_profiler->setRelativePosition(rect);
guitext_profiler->setVisible(true);
}
class ProfilerGraph
{
private:
- struct Piece{
+ struct Piece {
Profiler::GraphValues values;
};
- struct Meta{
+ struct Meta {
float min;
float max;
video::SColor color;
- Meta(float initial=0, video::SColor color=
- video::SColor(255,255,255,255)):
+ Meta(float initial = 0,
+ video::SColor color = video::SColor(255, 255, 255, 255)):
min(initial),
max(initial),
color(color)
Piece piece;
piece.values = values;
m_log.push_back(piece);
- while(m_log.size() > m_log_max_size)
+
+ while (m_log.size() > m_log_max_size)
m_log.erase(m_log.begin());
}
void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
- gui::IGUIFont* font) const
+ gui::IGUIFont *font) const
{
std::map<std::string, Meta> m_meta;
- for(std::list<Piece>::const_iterator k = m_log.begin();
- k != m_log.end(); k++)
- {
+
+ for (std::list<Piece>::const_iterator k = m_log.begin();
+ k != m_log.end(); k++) {
const Piece &piece = *k;
- for(Profiler::GraphValues::const_iterator i = piece.values.begin();
- i != piece.values.end(); i++){
+
+ for (Profiler::GraphValues::const_iterator i = piece.values.begin();
+ i != piece.values.end(); i++) {
const std::string &id = i->first;
const float &value = i->second;
std::map<std::string, Meta>::iterator j =
- m_meta.find(id);
- if(j == m_meta.end()){
+ m_meta.find(id);
+
+ if (j == m_meta.end()) {
m_meta[id] = Meta(value);
continue;
}
- if(value < j->second.min)
+
+ if (value < j->second.min)
j->second.min = value;
- if(value > j->second.max)
+
+ if (value > j->second.max)
j->second.max = value;
}
}
// Assign colors
static const video::SColor usable_colors[] = {
- video::SColor(255,255,100,100),
- video::SColor(255,90,225,90),
- video::SColor(255,100,100,255),
- video::SColor(255,255,150,50),
- video::SColor(255,220,220,100)
+ video::SColor(255, 255, 100, 100),
+ video::SColor(255, 90, 225, 90),
+ video::SColor(255, 100, 100, 255),
+ video::SColor(255, 255, 150, 50),
+ video::SColor(255, 220, 220, 100)
};
static const u32 usable_colors_count =
- sizeof(usable_colors) / sizeof(*usable_colors);
+ sizeof(usable_colors) / sizeof(*usable_colors);
u32 next_color_i = 0;
- for(std::map<std::string, Meta>::iterator i = m_meta.begin();
- i != m_meta.end(); i++){
+
+ for (std::map<std::string, Meta>::iterator i = m_meta.begin();
+ i != m_meta.end(); i++) {
Meta &meta = i->second;
- video::SColor color(255,200,200,200);
- if(next_color_i < usable_colors_count)
+ video::SColor color(255, 200, 200, 200);
+
+ if (next_color_i < usable_colors_count)
color = usable_colors[next_color_i++];
+
meta.color = color;
}
}*/
s32 meta_i = 0;
- for(std::map<std::string, Meta>::const_iterator i = m_meta.begin();
- i != m_meta.end(); i++){
+
+ for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
+ i != m_meta.end(); i++) {
const std::string &id = i->first;
const Meta &meta = i->second;
s32 x = x_left;
s32 y = y_bottom - meta_i * 50;
float show_min = meta.min;
float show_max = meta.max;
- if(show_min >= -0.0001 && show_max >= -0.0001){
- if(show_min <= show_max * 0.5)
+
+ if (show_min >= -0.0001 && show_max >= -0.0001) {
+ if (show_min <= show_max * 0.5)
show_min = 0;
}
+
s32 texth = 15;
char buf[10];
snprintf(buf, 10, "%.3g", show_max);
font->draw(narrow_to_wide(buf).c_str(),
core::rect<s32>(textx, y - graphh,
- textx2, y - graphh + texth),
+ textx2, y - graphh + texth),
meta.color);
snprintf(buf, 10, "%.3g", show_min);
font->draw(narrow_to_wide(buf).c_str(),
core::rect<s32>(textx, y - texth,
- textx2, y),
+ textx2, y),
meta.color);
font->draw(narrow_to_wide(id).c_str(),
- core::rect<s32>(textx, y - graphh/2 - texth/2,
- textx2, y - graphh/2 + texth/2),
+ core::rect<s32>(textx, y - graphh / 2 - texth / 2,
+ textx2, y - graphh / 2 + texth / 2),
meta.color);
s32 graph1y = y;
s32 graph1h = graphh;
bool relativegraph = (show_min != 0 && show_min != show_max);
float lastscaledvalue = 0.0;
bool lastscaledvalue_exists = false;
- for(std::list<Piece>::const_iterator j = m_log.begin();
- j != m_log.end(); j++)
- {
+
+ for (std::list<Piece>::const_iterator j = m_log.begin();
+ j != m_log.end(); j++) {
const Piece &piece = *j;
float value = 0;
bool value_exists = false;
Profiler::GraphValues::const_iterator k =
- piece.values.find(id);
- if(k != piece.values.end()){
+ piece.values.find(id);
+
+ if (k != piece.values.end()) {
value = k->second;
value_exists = true;
}
- if(!value_exists){
+
+ if (!value_exists) {
x++;
lastscaledvalue_exists = false;
continue;
}
+
float scaledvalue = 1.0;
- if(show_max != show_min)
+
+ if (show_max != show_min)
scaledvalue = (value - show_min) / (show_max - show_min);
- if(scaledvalue == 1.0 && value == 0){
+
+ if (scaledvalue == 1.0 && value == 0) {
x++;
lastscaledvalue_exists = false;
continue;
}
- if(relativegraph){
- if(lastscaledvalue_exists){
+
+ if (relativegraph) {
+ if (lastscaledvalue_exists) {
s32 ivalue1 = lastscaledvalue * graph1h;
s32 ivalue2 = scaledvalue * graph1h;
- driver->draw2DLine(v2s32(x-1, graph1y - ivalue1),
- v2s32(x, graph1y - ivalue2), meta.color);
+ driver->draw2DLine(v2s32(x - 1, graph1y - ivalue1),
+ v2s32(x, graph1y - ivalue2), meta.color);
}
+
lastscaledvalue = scaledvalue;
lastscaledvalue_exists = true;
- } else{
+ } else {
s32 ivalue = scaledvalue * graph1h;
driver->draw2DLine(v2s32(x, graph1y),
- v2s32(x, graph1y - ivalue), meta.color);
+ v2s32(x, graph1y - ivalue), meta.color);
}
+
x++;
}
+
meta_i++;
}
}
p(p),
n(n)
{}
- const char* getType() const
- {return "NodeDug";}
+ const char *getType() const
+ {
+ return "NodeDug";
+ }
};
class SoundMaker
void playPlayerStep()
{
- if(m_player_step_timer <= 0 && m_player_step_sound.exists()){
+ if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
m_player_step_timer = 0.03;
m_sound->playSound(m_player_step_sound, false);
}
static void viewBobbingStep(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
sm->playPlayerStep();
}
static void playerRegainGround(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
sm->playPlayerStep();
}
static void cameraPunchLeft(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
}
static void cameraPunchRight(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
}
static void nodeDug(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
- NodeDugEvent *nde = (NodeDugEvent*)e;
+ SoundMaker *sm = (SoundMaker *)data;
+ NodeDugEvent *nde = (NodeDugEvent *)e;
sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
}
static void playerDamage(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
}
static void playerFallingDamage(MtEvent *e, void *data)
{
- SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
}
{
std::set<std::string> m_fetched;
public:
-
void fetchSounds(const std::string &name,
std::set<std::string> &dst_paths,
std::set<std::string> &dst_datas)
{
- if(m_fetched.count(name))
+ if (m_fetched.count(name))
return;
+
m_fetched.insert(name);
std::string base = porting::path_share + DIR_DELIM + "testsounds";
dst_paths.insert(base + DIR_DELIM + name + ".ogg");
public:
GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
- f32 *fog_range, Client *client):
+ f32 *fog_range, Client *client) :
m_sky(sky),
m_force_fog_off(force_fog_off),
m_fog_range(fog_range),
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel)
{
- if(!is_highlevel)
+ if (!is_highlevel)
return;
// Background color
services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4);
// Fog distance
- float fog_distance = 10000*BS;
- if(g_settings->getBool("enable_fog") && !*m_force_fog_off)
+ float fog_distance = 10000 * BS;
+
+ if (g_settings->getBool("enable_fog") && !*m_force_fog_off)
fog_distance = *m_fog_range;
+
services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
// Day-night ratio
services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1);
services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1);
- LocalPlayer* player = m_client->getEnv().getLocalPlayer();
+ LocalPlayer *player = m_client->getEnv().getLocalPlayer();
v3f eye_position = player->getEyePosition();
- services->setPixelShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
- services->setVertexShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
+ services->setPixelShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
+ services->setVertexShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
// Uniform sampler layers
int layer0 = 0;
int layer2 = 2;
// before 1.8 there isn't a "integer interface", only float
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
- services->setPixelShaderConstant("baseTexture" , (irr::f32*)&layer0, 1);
- services->setPixelShaderConstant("normalTexture" , (irr::f32*)&layer1, 1);
- services->setPixelShaderConstant("useNormalmap" , (irr::f32*)&layer2, 1);
+ services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1);
+ services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1);
+ services->setPixelShaderConstant("useNormalmap" , (irr::f32 *)&layer2, 1);
#else
- services->setPixelShaderConstant("baseTexture" , (irr::s32*)&layer0, 1);
- services->setPixelShaderConstant("normalTexture" , (irr::s32*)&layer1, 1);
- services->setPixelShaderConstant("useNormalmap" , (irr::s32*)&layer2, 1);
+ services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1);
+ services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1);
+ services->setPixelShaderConstant("useNormalmap" , (irr::s32 *)&layer2, 1);
#endif
}
};
INodeDefManager *nodedef = client.ndef();
ClientMap &map = client.getEnv().getClientMap();
- if(prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable)
- {
- verbosestream<<"Node placement prediction for "
- <<playeritem_def.name<<" is "
- <<prediction<<std::endl;
+ if (prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable) {
+ verbosestream << "Node placement prediction for "
+ << playeritem_def.name << " is "
+ << prediction << std::endl;
v3s16 p = neighbourpos;
+
// Place inside node itself if buildable_to
- try{
+ try {
MapNode n_under = map.getNode(nodepos);
- if(nodedef->get(n_under).buildable_to)
+
+ if (nodedef->get(n_under).buildable_to)
p = nodepos;
else if (!nodedef->get(map.getNode(p)).buildable_to)
return false;
- }catch(InvalidPositionException &e){}
+ } catch (InvalidPositionException &e) {}
+
// Find id of predicted node
content_t id;
bool found = nodedef->getId(prediction, id);
- if(!found){
- errorstream<<"Node placement prediction failed for "
- <<playeritem_def.name<<" (places "
- <<prediction
- <<") - Name not known"<<std::endl;
+
+ if (!found) {
+ errorstream << "Node placement prediction failed for "
+ << playeritem_def.name << " (places "
+ << prediction
+ << ") - Name not known" << std::endl;
return false;
}
+
// Predict param2 for facedir and wallmounted nodes
u8 param2 = 0;
- if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED){
+
+ if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) {
v3s16 dir = nodepos - neighbourpos;
- if(abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))){
+
+ if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
param2 = dir.Y < 0 ? 1 : 0;
- } else if(abs(dir.X) > abs(dir.Z)){
+ } else if (abs(dir.X) > abs(dir.Z)) {
param2 = dir.X < 0 ? 3 : 2;
} else {
param2 = dir.Z < 0 ? 5 : 4;
}
}
- if(nodedef->get(id).param_type_2 == CPT2_FACEDIR){
+
+ if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) {
v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
- if(abs(dir.X) > abs(dir.Z)){
+
+ if (abs(dir.X) > abs(dir.Z)) {
param2 = dir.X < 0 ? 3 : 1;
} else {
param2 = dir.Z < 0 ? 2 : 0;
}
}
- assert(param2 >= 0 && param2 <= 5);
+
+ assert(param2 <= 5);
+
//Check attachment if node is in group attached_node
- if(((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0){
+ if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) {
static v3s16 wallmounted_dirs[8] = {
- v3s16(0,1,0),
- v3s16(0,-1,0),
- v3s16(1,0,0),
- v3s16(-1,0,0),
- v3s16(0,0,1),
- v3s16(0,0,-1),
+ v3s16(0, 1, 0),
+ v3s16(0, -1, 0),
+ v3s16(1, 0, 0),
+ v3s16(-1, 0, 0),
+ v3s16(0, 0, 1),
+ v3s16(0, 0, -1),
};
v3s16 pp;
- if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED)
+
+ if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED)
pp = p + wallmounted_dirs[param2];
else
- pp = p + v3s16(0,-1,0);
- if(!nodedef->get(map.getNode(pp)).walkable)
+ pp = p + v3s16(0, -1, 0);
+
+ if (!nodedef->get(map.getNode(pp)).walkable)
return false;
}
+
// Add node to client map
MapNode n(id, 0, param2);
- try{
- LocalPlayer* player = client.getEnv().getLocalPlayer();
+
+ try {
+ LocalPlayer *player = client.getEnv().getLocalPlayer();
// Dont place node when player would be inside new node
// NOTE: This is to be eventually implemented by a mod as client-side Lua
if (!nodedef->get(n).walkable ||
- (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
- (nodedef->get(n).walkable &&
- neighbourpos != player->getStandingNodePos() + v3s16(0,1,0) &&
- neighbourpos != player->getStandingNodePos() + v3s16(0,2,0))) {
-
- // This triggers the required mesh update too
- client.addNode(p, n);
- return true;
- }
- }catch(InvalidPositionException &e){
- errorstream<<"Node placement prediction failed for "
- <<playeritem_def.name<<" (places "
- <<prediction
- <<") - Position not loaded"<<std::endl;
+ (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
+ (nodedef->get(n).walkable &&
+ neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
+ neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
+
+ // This triggers the required mesh update too
+ client.addNode(p, n);
+ return true;
+ }
+ } catch (InvalidPositionException &e) {
+ errorstream << "Node placement prediction failed for "
+ << playeritem_def.name << " (places "
+ << prediction
+ << ") - Position not loaded" << std::endl;
}
}
+
return false;
}
-static inline void create_formspec_menu(GUIFormSpecMenu** cur_formspec,
+static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
- IWritableTextureSource* tsrc, IrrlichtDevice * device,
- IFormSource* fs_src, TextDest* txt_dest, Client* client
- ) {
+ IWritableTextureSource *tsrc, IrrlichtDevice *device,
+ IFormSource *fs_src, TextDest *txt_dest, Client *client)
+{
if (*cur_formspec == 0) {
*cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr,
- invmgr, gamedef, tsrc, fs_src, txt_dest, client);
+ invmgr, gamedef, tsrc, fs_src, txt_dest, client);
(*cur_formspec)->doPause = false;
/*
remaining reference (i.e. the menu was removed)
and delete it in that case.
*/
- }
- else {
+
+ } else {
(*cur_formspec)->setFormSource(fs_src);
(*cur_formspec)->setTextDest(txt_dest);
}
#define SIZE_TAG "size[11,5.5,true]"
#endif
-static void show_chat_menu(GUIFormSpecMenu** cur_formspec,
+static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
- IWritableTextureSource* tsrc, IrrlichtDevice * device,
- Client* client, std::string text)
+ IWritableTextureSource *tsrc, IrrlichtDevice *device,
+ Client *client, std::string text)
{
std::string formspec =
FORMSPEC_VERSION_STRING
/* 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);
+ 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,
- IWritableTextureSource* tsrc, IrrlichtDevice * device, Client* client)
+ IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client)
{
std::string formspec =
std::string(FORMSPEC_VERSION_STRING) +
/* Create menu */
/* Note: FormspecFormSource and LocalFormspecHandler
* are deleted by guiFormSpecMenu */
- FormspecFormSource* fs_src = new FormspecFormSource(formspec);
- LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
+ FormspecFormSource *fs_src = new FormspecFormSource(formspec);
+ LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
}
/******************************************************************************/
-static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
+static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
- IWritableTextureSource* tsrc, IrrlichtDevice * device,
+ IWritableTextureSource *tsrc, IrrlichtDevice *device,
bool singleplayermode)
{
#ifdef __ANDROID__
std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
- "No menu visible:\n"
- "- single tap: button activate\n"
- "- double tap: place/use\n"
- "- slide finger: look around\n"
- "Menu/Inventory visible:\n"
- "- double tap (outside):\n"
- " -->close\n"
- "- touch stack, touch slot:\n"
- " --> move stack\n"
- "- touch&drag, tap 2nd finger\n"
- " --> place single item to slot\n"
- ));
+ "No menu visible:\n"
+ "- single tap: button activate\n"
+ "- double tap: place/use\n"
+ "- slide finger: look around\n"
+ "Menu/Inventory visible:\n"
+ "- double tap (outside):\n"
+ " -->close\n"
+ "- touch stack, touch slot:\n"
+ " --> move stack\n"
+ "- touch&drag, tap 2nd finger\n"
+ " --> place single item to slot\n"
+ ));
#else
std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
- "- WASD: move\n"
- "- Space: jump/climb\n"
- "- Shift: sneak/go down\n"
- "- Q: drop item\n"
- "- I: inventory\n"
- "- Mouse: turn/look\n"
- "- Mouse left: dig/punch\n"
- "- Mouse right: place/use\n"
- "- Mouse wheel: select item\n"
- "- T: chat\n"
- ));
+ "- WASD: move\n"
+ "- Space: jump/climb\n"
+ "- Shift: sneak/go down\n"
+ "- Q: drop item\n"
+ "- I: inventory\n"
+ "- Mouse: turn/look\n"
+ "- Mouse left: dig/punch\n"
+ "- Mouse right: place/use\n"
+ "- Mouse wheel: select item\n"
+ "- T: chat\n"
+ ));
#endif
float ypos = singleplayermode ? 0.5 : 0.1;
std::ostringstream os;
os << FORMSPEC_VERSION_STRING << SIZE_TAG
- << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
- << wide_to_narrow(wstrgettext("Continue")) << "]";
+ << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
+ << wide_to_narrow(wstrgettext("Continue")) << "]";
if (!singleplayermode) {
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
- << wide_to_narrow(wstrgettext("Change Password")) << "]";
- }
+ << wide_to_narrow(wstrgettext("Change Password")) << "]";
+ }
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
- << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
+ << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
- << wide_to_narrow(wstrgettext("Change Keys")) << "]";
+ << wide_to_narrow(wstrgettext("Change Keys")) << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
- << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
+ << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
- << wide_to_narrow(wstrgettext("Exit to OS")) << "]"
+ << wide_to_narrow(wstrgettext("Exit to OS")) << "]"
<< "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
<< "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
<< minetest_build_info << "\n"
/* Create menu */
/* Note: FormspecFormSource and LocalFormspecHandler *
* are deleted by guiFormSpecMenu */
- FormspecFormSource* fs_src = new FormspecFormSource(os.str());
- LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
+ FormspecFormSource *fs_src = new FormspecFormSource(os.str());
+ LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
}
/******************************************************************************/
-static void updateChat(Client& client, f32 dtime, bool show_debug,
- const v2u32& screensize, bool show_chat, u32 show_profiler,
- ChatBackend& chat_backend, gui::IGUIStaticText* guitext_chat,
- gui::IGUIFont* font)
+static void updateChat(Client &client, f32 dtime, bool show_debug,
+ const v2u32 &screensize, bool show_chat, u32 show_profiler,
+ ChatBackend &chat_backend, gui::IGUIStaticText *guitext_chat,
+ gui::IGUIFont *font)
{
// Add chat log output for errors to be shown in chat
static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
// Get new messages from error log buffer
- while(!chat_log_error_buf.empty()) {
+ while (!chat_log_error_buf.empty()) {
chat_backend.addMessage(L"", narrow_to_wide(chat_log_error_buf.get()));
}
// Get new messages from client
std::wstring message;
+
while (client.getChatMessage(message)) {
chat_backend.addUnparsedMessage(message);
}
// Update gui element size and position
s32 chat_y = 5 + line_height;
+
if (show_debug)
chat_y += line_height;
// first pass to calculate height of text to be set
s32 width = std::min(font->getDimension(recent_chat.c_str()).Width + 10,
- porting::getWindowSize().X - 20);
+ porting::getWindowSize().X - 20);
core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
guitext_chat->setRelativePosition(rect);
- //now use real height of text and adjust rect according to this size
+ //now use real height of text and adjust rect according to this size
rect = core::rect<s32>(10, chat_y, width,
- chat_y + guitext_chat->getTextHeight());
+ chat_y + guitext_chat->getTextHeight());
guitext_chat->setRelativePosition(rect);
// Don't show chat if disabled or empty or profiler is enabled
guitext_chat->setVisible(
- show_chat && recent_chat_count != 0 && !show_profiler);
+ show_chat && recent_chat_count != 0 && !show_profiler);
}
-/******************************************************************************/
-void the_game(bool &kill, bool random_input, InputHandler *input,
- IrrlichtDevice *device, gui::IGUIFont* font, std::string map_dir,
- std::string playername, std::string password,
- std::string address /* If "", local server is used */,
- u16 port, std::wstring &error_message, ChatBackend &chat_backend,
- const SubgameSpec &gamespec /* Used for local game */,
- bool simple_singleplayer_mode)
-{
- GUIFormSpecMenu* current_formspec = 0;
- video::IVideoDriver* driver = device->getVideoDriver();
- scene::ISceneManager* smgr = device->getSceneManager();
- // Calculate text height using the font
- u32 text_height = font->getDimension(L"Random test string").Height;
- /*
- Draw "Loading" screen
- */
+/****************************************************************************
+ THE GAME
+ ****************************************************************************/
- {
- wchar_t* text = wgettext("Loading...");
- draw_load_screen(text, device, guienv, font, 0, 0);
- delete[] text;
- }
+const float object_hit_delay = 0.2;
- // Create texture source
- IWritableTextureSource *tsrc = createTextureSource(device);
+struct FpsControl {
+ u32 last_time, busy_time, sleep_time;
+};
- // Create shader source
- IWritableShaderSource *shsrc = createShaderSource(device);
- // These will be filled by data received from the server
- // Create item definition manager
- IWritableItemDefManager *itemdef = createItemDefManager();
- // Create node definition manager
- IWritableNodeDefManager *nodedef = createNodeDefManager();
+/* The reason the following structs are not anonymous structs within the
+ * class is that they are not used by the majority of member functions and
+ * many functions that do require objects of thse types do not modify them
+ * (so they can be passed as a const qualified parameter)
+ */
- // Sound fetcher (useful when testing)
- GameOnDemandSoundFetcher soundfetcher;
+struct CameraOrientation {
+ f32 camera_yaw; // "right/left"
+ f32 camera_pitch; // "up/down"
+};
- // Sound manager
- ISoundManager *sound = NULL;
- bool sound_is_dummy = false;
-#if USE_SOUND
- if(g_settings->getBool("enable_sound")){
- infostream<<"Attempting to use OpenAL audio"<<std::endl;
- sound = createOpenALSoundManager(&soundfetcher);
- if(!sound)
- infostream<<"Failed to initialize OpenAL audio"<<std::endl;
- } else {
- infostream<<"Sound disabled."<<std::endl;
- }
-#endif
- if(!sound){
- infostream<<"Using dummy audio."<<std::endl;
- sound = &dummySoundManager;
- sound_is_dummy = true;
- }
+//TODO: Needs a better name because fog_range etc are included
+struct InteractParams {
+ u16 dig_index;
+ u16 new_playeritem;
+ PointedThing pointed_old;
+ bool digging;
+ bool ldown_for_dig;
+ bool left_punch;
+ float nodig_delay_timer;
+ float dig_time;
+ float dig_time_complete;
+ float repeat_rightclick_timer;
+ float object_hit_delay_timer;
+ float time_from_last_punch;
+ ClientActiveObject *selected_object;
+
+ float jump_timer;
+ float damage_flash;
+ float update_draw_list_timer;
+ float statustext_time;
+
+ f32 fog_range;
- Server *server = NULL;
+ v3f update_draw_list_last_cam_dir;
- try{
- // Event manager
- EventManager eventmgr;
+ u32 profiler_current_page;
+ u32 profiler_max_page; // Number of pages
+};
- // Sound maker
- SoundMaker soundmaker(sound, nodedef);
- soundmaker.registerReceiver(&eventmgr);
+struct Jitter {
+ f32 max, min, avg, counter, max_sample, min_sample, max_fraction;
+};
- // Create UI for modifying quicktune values
- QuicktuneShortcutter quicktune;
+struct RunStats {
+ u32 drawtime;
+ u32 beginscenetime;
+ u32 endscenetime;
- /*
- Create server.
- */
+ Jitter dtime_jitter, busy_time_jitter;
+};
- if(address == ""){
- wchar_t* text = wgettext("Creating server....");
- draw_load_screen(text, device, guienv, font, 0, 25);
- delete[] text;
- infostream<<"Creating server"<<std::endl;
+/* Flags that can, or may, change during main game loop
+ */
+struct VolatileRunFlags {
+ bool invert_mouse;
+ bool show_chat;
+ bool show_hud;
+ bool force_fog_off;
+ bool show_debug;
+ bool show_profiler_graph;
+ bool disable_camera_update;
+ bool first_loop_after_window_activation;
+ bool camera_offset_changed;
+};
- std::string bind_str = g_settings->get("bind_address");
- Address bind_addr(0,0,0,0, port);
- if (g_settings->getBool("ipv6_server")) {
- bind_addr.setAddress((IPv6AddressBytes*) NULL);
- }
- try {
- bind_addr.Resolve(bind_str.c_str());
- address = bind_str;
- } catch (ResolveError &e) {
- infostream << "Resolving bind address \"" << bind_str
- << "\" failed: " << e.what()
- << " -- Listening on all addresses." << std::endl;
- }
+/* This is not intended to be a public class. If a public class becomes
+ * desirable then it may be better to create another 'wrapper' class that
+ * hides most of the stuff in this class (nothing in this class is required
+ * by any other file) but exposes the public methods/data only.
+ */
+class MinetestApp
+{
+public:
+ MinetestApp();
+ ~MinetestApp();
+
+ bool startup(bool *kill,
+ bool random_input,
+ InputHandler *input,
+ IrrlichtDevice *device,
+ gui::IGUIFont *font,
+ const std::string &map_dir,
+ const std::string &playername,
+ const std::string &password,
+ // If address is "", local server is used and address is updated
+ std::string *address,
+ u16 port,
+ std::wstring *error_message,
+ ChatBackend *chat_backend,
+ const SubgameSpec &gamespec, // Used for local game
+ bool simple_singleplayer_mode);
+
+ void run();
+ void shutdown();
+
+protected:
+
+ void extendedResourceCleanup();
+
+ // Basic initialisation
+ bool init(const std::string &map_dir, std::string *address,
+ u16 port,
+ const SubgameSpec &gamespec);
+ bool initSound();
+ bool createSingleplayerServer(const std::string map_dir,
+ const SubgameSpec &gamespec, u16 port, std::string *address);
+
+ // Client creation
+ bool createClient(const std::string &playername,
+ const std::string &password, std::string *address, u16 port,
+ std::wstring *error_message);
+ bool initGui(std::wstring *error_message);
+
+ // Client connection
+ bool connectToServer(const std::string &playername,
+ const std::string &password, std::string *address, u16 port,
+ bool *connect_ok, bool *aborted);
+ bool getServerContent(bool *aborted);
+
+ // Main loop
+
+ void updateInteractTimers(InteractParams *args, f32 dtime);
+ bool checkConnection();
+ bool handleCallbacks();
+ void processQueues();
+ void addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times,
+ f32 dtime);
+ void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
+
+ void processUserInput(VolatileRunFlags *flags, InteractParams *interact_args,
+ f32 dtime);
+ void processKeyboardInput(VolatileRunFlags *flags,
+ float *statustext_time,
+ float *jump_timer,
+ u32 *profiler_current_page,
+ u32 profiler_max_page);
+ void processItemSelection(u16 *new_playeritem);
+
+ void dropSelectedItem();
+ void openInventory();
+ void openConsole();
+ void toggleFreeMove(float *statustext_time);
+ void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
+ void toggleFast(float *statustext_time);
+ void toggleNoClip(float *statustext_time);
+
+ void toggleChat(float *statustext_time, bool *flag);
+ void toggleHud(float *statustext_time, bool *flag);
+ void toggleFog(float *statustext_time, bool *flag);
+ void toggleDebug(float *statustext_time, bool *show_debug,
+ bool *show_profiler_graph);
+ void toggleUpdateCamera(float *statustext_time, bool *flag);
+ void toggleProfiler(float *statustext_time, u32 *profiler_current_page,
+ u32 profiler_max_page);
+
+ void increaseViewRange(float *statustext_time);
+ void decreaseViewRange(float *statustext_time);
+ void toggleFullViewRange(float *statustext_time);
+
+ void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags);
+ void updatePlayerControl(const CameraOrientation &cam);
+ void step(f32 *dtime);
+ void processClientEvents(CameraOrientation *cam, float *damage_flash);
+ void updateCamera(VolatileRunFlags *flags, u32 busy_time, f32 dtime,
+ float time_from_last_punch);
+ void updateSound(f32 dtime);
+ void processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
+ InteractParams *interactArgs, f32 dtime, bool show_hud,
+ bool show_debug);
+ void handlePointingAtNode(InteractParams *interactArgs,
+ const PointedThing &pointed, const ItemDefinition &playeritem_def,
+ const ToolCapabilities &playeritem_toolcap, f32 dtime);
+ void handlePointingAtObject(InteractParams *interactArgs,
+ const PointedThing &pointed, const ItemStack &playeritem,
+ const v3f &player_position, bool show_debug);
+ void handleDigging(InteractParams *interactArgs, const PointedThing &pointed,
+ const v3s16 &nodepos, const ToolCapabilities &playeritem_toolcap,
+ f32 dtime);
+ void updateFrame(std::vector<aabb3f> &highlight_boxes, ProfilerGraph *graph,
+ RunStats *stats, InteractParams *interactArgs,
+ f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam);
+ void updateGui(float *statustext_time, const RunStats &stats, f32 dtime,
+ const VolatileRunFlags &flags, const CameraOrientation &cam);
+ void updateProfilerGraphs(ProfilerGraph *graph);
+
+ // Misc
+ void limitFps(FpsControl *params, f32 *dtime);
+
+ void showOverlayMessage(const char *msg, float dtime, int percent,
+ bool draw_clouds = true);
+
+ inline const char *boolToCStr(bool v);
- if(bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
- error_message = L"Unable to listen on " +
- narrow_to_wide(bind_addr.serializeString()) +
- L" because IPv6 is disabled";
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- // Break out of client scope
- return;
- }
+private:
+ InputHandler *input;
- server = new Server(map_dir, gamespec,
- simple_singleplayer_mode,
- bind_addr.isIPv6());
+ Client *client;
+ Server *server;
- server->start(bind_addr);
- }
+ gui::IGUIFont *font;
- do{ // Client scope (breakable do-while(0))
+ IWritableTextureSource *texture_src;
+ IWritableShaderSource *shader_src;
- /*
- Create client
- */
+ // When created, these will be filled with data received from the server
+ IWritableItemDefManager *itemdef_manager;
+ IWritableNodeDefManager *nodedef_manager;
- {
- wchar_t* text = wgettext("Creating client...");
- draw_load_screen(text, device, guienv, font, 0, 50);
- delete[] text;
- }
- infostream<<"Creating client"<<std::endl;
+ GameOnDemandSoundFetcher soundfetcher; // useful when testing
+ ISoundManager *sound;
+ bool sound_is_dummy;
+ SoundMaker *soundmaker;
- MapDrawControl draw_control;
+ ChatBackend *chat_backend;
- {
- wchar_t* text = wgettext("Resolving address...");
- draw_load_screen(text, device, guienv, font, 0, 75);
- delete[] text;
- }
- Address connect_address(0,0,0,0, port);
- try {
- connect_address.Resolve(address.c_str());
- if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
- //connect_address.Resolve("localhost");
- if (connect_address.isIPv6()) {
- IPv6AddressBytes addr_bytes;
- addr_bytes.bytes[15] = 1;
- connect_address.setAddress(&addr_bytes);
- } else {
- connect_address.setAddress(127,0,0,1);
- }
- }
- }
- catch(ResolveError &e) {
- error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- // Break out of client scope
- break;
- }
- if(connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
- error_message = L"Unable to connect to " +
- narrow_to_wide(connect_address.serializeString()) +
- L" because IPv6 is disabled";
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- // Break out of client scope
- break;
- }
+ GUIFormSpecMenu *current_formspec;
- /*
- Create client
- */
- Client client(device, playername.c_str(), password, draw_control,
- tsrc, shsrc, itemdef, nodedef, sound, &eventmgr,
- connect_address.isIPv6());
+ EventManager *eventmgr;
+ QuicktuneShortcutter *quicktune;
- // Client acts as our GameDef
- IGameDef *gamedef = &client;
+ GUIChatConsole *gui_chat_console; // Free using ->Drop()
+ MapDrawControl *draw_control;
+ Camera *camera;
+ Clouds *clouds; // Free using ->Drop()
+ Sky *sky; // Free using ->Drop()
+ Inventory *local_inventory;
+ Hud *hud;
- /*
- Attempt to connect to the server
+ /* 'cache'
+ This class does take ownership/responsibily for cleaning up etc of any of
+ these items (e.g. device)
*/
+ IrrlichtDevice *device;
+ video::IVideoDriver *driver;
+ scene::ISceneManager *smgr;
+ u32 text_height;
+ bool *kill;
+ std::wstring *error_message;
+ IGameDef *gamedef; // Convenience (same as *client)
+ scene::ISceneNode *skybox;
+
+ bool random_input;
+ bool simple_singleplayer_mode;
+ /* End 'cache' */
+
+ /* Pre-calculated values
+ */
+ int crack_animation_length;
+
+ /* GUI stuff
+ */
+ gui::IGUIStaticText *guitext; // First line of debug text
+ gui::IGUIStaticText *guitext2; // Second line of debug text
+ gui::IGUIStaticText *guitext_info; // At the middle of the screen
+ gui::IGUIStaticText *guitext_status;
+ gui::IGUIStaticText *guitext_chat; // Chat text
+ gui::IGUIStaticText *guitext_profiler; // Profiler text
- infostream<<"Connecting to server at ";
- connect_address.print(&infostream);
- infostream<<std::endl;
- client.connect(connect_address);
+ std::wstring infotext;
+ std::wstring statustext;
+};
- /*
- Wait for server to accept connection
- */
- bool could_connect = false;
- bool connect_aborted = false;
- try{
- float time_counter = 0.0;
- input->clear();
- float fps_max = g_settings->getFloat("fps_max");
- bool cloud_menu_background = g_settings->getBool("menu_clouds");
- u32 lasttime = device->getTimer()->getTime();
- while(device->run())
- {
- f32 dtime = 0.033; // in seconds
- if (cloud_menu_background) {
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- dtime = (time - lasttime) / 1000.0;
- else
- dtime = 0;
- lasttime = time;
- }
- // Update client and server
- client.step(dtime);
- if(server != NULL)
- server->step(dtime);
+MinetestApp::MinetestApp() :
+ client(NULL),
+ server(NULL),
+ font(NULL),
+ texture_src(NULL),
+ shader_src(NULL),
+ itemdef_manager(NULL),
+ nodedef_manager(NULL),
+ sound(NULL),
+ sound_is_dummy(false),
+ soundmaker(NULL),
+ chat_backend(NULL),
+ current_formspec(NULL),
+ eventmgr(NULL),
+ quicktune(NULL),
+ gui_chat_console(NULL),
+ draw_control(NULL),
+ camera(NULL),
+ clouds(NULL),
+ sky(NULL),
+ local_inventory(NULL),
+ hud(NULL)
+{
- // End condition
- if(client.getState() == LC_Init) {
- could_connect = true;
- break;
- }
- // Break conditions
- if(client.accessDenied()) {
- error_message = L"Access denied. Reason: "
- +client.accessDeniedReason();
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- break;
- }
- if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
- connect_aborted = true;
- infostream<<"Connect aborted [Escape]"<<std::endl;
- break;
- }
+}
- // Display status
- {
- wchar_t* text = wgettext("Connecting to server...");
- draw_load_screen(text, device, guienv, font, dtime, 100);
- delete[] text;
- }
- // On some computers framerate doesn't seem to be
- // automatically limited
- if (cloud_menu_background) {
- // Time of frame without fps limit
- float busytime;
- u32 busytime_u32;
- // not using getRealTime is necessary for wine
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- busytime_u32 = time - lasttime;
- else
- busytime_u32 = 0;
- busytime = busytime_u32 / 1000.0;
-
- // FPS limiter
- u32 frametime_min = 1000./fps_max;
-
- if(busytime_u32 < frametime_min) {
- u32 sleeptime = frametime_min - busytime_u32;
- device->sleep(sleeptime);
- }
- } else {
- sleep_ms(25);
- }
- time_counter += dtime;
- }
- }
- catch(con::PeerNotFoundException &e)
- {}
- /*
- Handle failure to connect
- */
- if(!could_connect) {
- if(error_message == L"" && !connect_aborted) {
- error_message = L"Connection failed";
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- }
- // Break out of client scope
- break;
- }
+/****************************************************************************
+ MinetestApp Public
+ ****************************************************************************/
- /*
- Wait until content has been received
- */
- bool got_content = false;
- bool content_aborted = false;
- {
- float time_counter = 0.0;
- input->clear();
- float fps_max = g_settings->getFloat("fps_max");
- bool cloud_menu_background = g_settings->getBool("menu_clouds");
- u32 lasttime = device->getTimer()->getTime();
- while (device->run()) {
- f32 dtime = 0.033; // in seconds
- if (cloud_menu_background) {
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- dtime = (time - lasttime) / 1000.0;
- else
- dtime = 0;
- lasttime = time;
- }
- // Update client and server
- client.step(dtime);
- if (server != NULL)
- server->step(dtime);
+MinetestApp::~MinetestApp()
+{
+ delete client;
+ delete soundmaker;
+ if (!sound_is_dummy)
+ delete sound;
- // End condition
- if (client.mediaReceived() &&
- client.itemdefReceived() &&
- client.nodedefReceived()) {
- got_content = true;
- break;
- }
- // Break conditions
- if (client.accessDenied()) {
- error_message = L"Access denied. Reason: "
- +client.accessDeniedReason();
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- break;
- }
- if (client.getState() < LC_Init) {
- error_message = L"Client disconnected";
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- break;
- }
- if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
- content_aborted = true;
- infostream<<"Connect aborted [Escape]"<<std::endl;
- break;
- }
+ delete server; // deleted first to stop all server threads
- // Display status
- int progress=0;
- if (!client.itemdefReceived())
- {
- wchar_t* text = wgettext("Item definitions...");
- progress = 0;
- draw_load_screen(text, device, guienv, font, dtime, progress);
- delete[] text;
- }
- else if (!client.nodedefReceived())
- {
- wchar_t* text = wgettext("Node definitions...");
- progress = 25;
- draw_load_screen(text, device, guienv, font, dtime, progress);
- delete[] text;
- }
- else
- {
+ delete hud;
+ delete local_inventory;
+ delete camera;
+ delete quicktune;
+ delete eventmgr;
+ delete texture_src;
+ delete shader_src;
+ delete nodedef_manager;
+ delete itemdef_manager;
+ delete draw_control;
- std::stringstream message;
- message.precision(3);
- message << gettext("Media...");
+ extendedResourceCleanup();
+}
- if ( ( USE_CURL == 0) ||
- (!g_settings->getBool("enable_remote_media_server"))) {
- float cur = client.getCurRate();
- std::string cur_unit = gettext(" KB/s");
+bool MinetestApp::startup(bool *kill,
+ bool random_input,
+ InputHandler *input,
+ IrrlichtDevice *device,
+ gui::IGUIFont *font,
+ const std::string &map_dir,
+ const std::string &playername,
+ const std::string &password,
+ std::string *address, // can change if simple_singleplayer_mode
+ u16 port,
+ std::wstring *error_message,
+ ChatBackend *chat_backend,
+ const SubgameSpec &gamespec,
+ bool simple_singleplayer_mode)
+{
+ // "cache"
+ this->device = device;
+ this->font = font;
+ this->kill = kill;
+ this->error_message = error_message;
+ this->random_input = random_input;
+ this->input = input;
+ this->chat_backend = chat_backend;
+ this->simple_singleplayer_mode = simple_singleplayer_mode;
+
+ driver = device->getVideoDriver();
+ smgr = device->getSceneManager();
+ text_height = font->getDimension(L"Random test string").Height;
+
+ if (!init(map_dir, address, port, gamespec))
+ return false;
+
+ if (!createClient(playername, password, address, port, error_message))
+ return false;
+
+ return true;
+}
- if (cur > 900) {
- cur /= 1024.0;
- cur_unit = gettext(" MB/s");
- }
- message << " ( " << cur << cur_unit << " )";
- }
- progress = 50+client.mediaReceiveProgress()*50+0.5;
- draw_load_screen(narrow_to_wide(message.str().c_str()), device,
- guienv, font, dtime, progress);
- }
- // On some computers framerate doesn't seem to be
- // automatically limited
- if (cloud_menu_background) {
- // Time of frame without fps limit
- float busytime;
- u32 busytime_u32;
- // not using getRealTime is necessary for wine
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- busytime_u32 = time - lasttime;
- else
- busytime_u32 = 0;
- busytime = busytime_u32 / 1000.0;
-
- // FPS limiter
- u32 frametime_min = 1000./fps_max;
-
- if(busytime_u32 < frametime_min) {
- u32 sleeptime = frametime_min - busytime_u32;
- device->sleep(sleeptime);
- }
- } else {
- sleep_ms(25);
- }
- time_counter += dtime;
- }
- }
+void MinetestApp::run()
+{
+ ProfilerGraph graph;
+ RunStats stats = { 0 };
+ CameraOrientation cam_view = { 0 };
+ InteractParams interactArgs = { 0 };
+ FpsControl draw_times = { 0 };
+ VolatileRunFlags flags = { 0 };
+ f32 dtime; // in seconds
- if(!got_content){
- if(error_message == L"" && !content_aborted){
- error_message = L"Something failed";
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- }
- // Break out of client scope
- break;
- }
+ interactArgs.time_from_last_punch = 10.0;
+ interactArgs.profiler_max_page = 3;
- /*
- After all content has been received:
- Update cached textures, meshes and materials
- */
- client.afterContentReceived(device,font);
+ flags.show_chat = true;
+ flags.show_hud = true;
- /*
- Create the camera node
- */
- Camera camera(smgr, draw_control, gamedef);
- if (!camera.successfullyCreated(error_message))
- return;
+ /* FIXME: This should be updated every iteration, or not stored locally
+ now that key settings can be changed in-game */
+ flags.invert_mouse = g_settings->getBool("invert_mouse");
- f32 camera_yaw = 0; // "right/left"
- f32 camera_pitch = 0; // "up/down"
+ /* Clear the profiler */
+ Profiler::GraphValues dummyvalues;
+ g_profiler->graphGet(dummyvalues);
- /*
- Clouds
- */
+ draw_times.last_time = device->getTimer()->getTime();
- Clouds *clouds = NULL;
- if(g_settings->getBool("enable_clouds"))
- {
- clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
- }
+ shader_src->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
+ sky,
+ &flags.force_fog_off,
+ &interactArgs.fog_range,
+ client));
- /*
- Skybox thingy
- */
+ while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
- Sky *sky = NULL;
- sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
+ /* Must be called immediately after a device->run() call because it
+ * uses device->getTimer()->getTime()
+ */
+ limitFps(&draw_times, &dtime);
- scene::ISceneNode* skybox = NULL;
+ updateStats(&stats, draw_times, dtime);
+ updateInteractTimers(&interactArgs, dtime);
- /*
- A copy of the local inventory
- */
- Inventory local_inventory(itemdef);
+ if (!checkConnection())
+ break;
+ if (!handleCallbacks())
+ break;
- /*
- Find out size of crack animation
- */
- int crack_animation_length = 5;
- {
- video::ITexture *t = tsrc->getTexture("crack_anylength.png");
- v2u32 size = t->getOriginalSize();
- crack_animation_length = size.Y / size.X;
+ processQueues();
+
+ std::vector<aabb3f> highlight_boxes;
+
+ infotext = L"";
+ hud->resizeHotbar();
+ addProfilerGraphs(stats, draw_times, dtime);
+ processUserInput(&flags, &interactArgs, dtime);
+ // Update camera before player movement to avoid camera lag of one frame
+ updateCameraDirection(&cam_view, &flags);
+ updatePlayerControl(cam_view);
+ step(&dtime);
+ processClientEvents(&cam_view, &interactArgs.damage_flash);
+ updateCamera(&flags, draw_times.busy_time, dtime,
+ interactArgs.time_from_last_punch);
+ updateSound(dtime);
+ processPlayerInteraction(highlight_boxes, &interactArgs, dtime,
+ flags.show_hud, flags.show_debug);
+ updateFrame(highlight_boxes, &graph, &stats, &interactArgs, dtime,
+ flags, cam_view);
+ updateProfilerGraphs(&graph);
}
+}
- /*
- Add some gui stuff
- */
- // First line of debug text
- gui::IGUIStaticText *guitext = guienv->addStaticText(
- L"Minetest",
- core::rect<s32>(0, 0, 0, 0),
- false, false, guiroot);
- // Second line of debug text
- gui::IGUIStaticText *guitext2 = guienv->addStaticText(
+void MinetestApp::shutdown()
+{
+ showOverlayMessage("Shutting down...", 0, 0, false);
+
+ if (clouds)
+ clouds->drop();
+
+ if (gui_chat_console)
+ gui_chat_console->drop();
+
+ if (sky)
+ sky->drop();
+
+ clear_particles();
+
+ /* cleanup menus */
+ while (g_menumgr.menuCount() > 0) {
+ g_menumgr.m_stack.front()->setVisible(false);
+ g_menumgr.deletingMenu(g_menumgr.m_stack.front());
+ }
+
+ if (current_formspec) {
+ current_formspec->drop();
+ current_formspec = NULL;
+ }
+
+ chat_backend->addMessage(L"", L"# Disconnected.");
+ chat_backend->addMessage(L"", L"");
+
+ if (client) {
+ client->Stop();
+ while (!client->isShutdown()) {
+ assert(texture_src != NULL);
+ assert(shader_src != NULL);
+ texture_src->processQueue();
+ shader_src->processQueue();
+ sleep_ms(100);
+ }
+ }
+}
+
+
+
+/****************************************************************************
+ Startup
+ ****************************************************************************/
+
+bool MinetestApp::init(
+ const std::string &map_dir,
+ std::string *address,
+ u16 port,
+ const SubgameSpec &gamespec)
+{
+ showOverlayMessage("Loading...", 0, 0);
+
+ texture_src = createTextureSource(device);
+ shader_src = createShaderSource(device);
+
+ itemdef_manager = createItemDefManager();
+ nodedef_manager = createNodeDefManager();
+
+ eventmgr = new EventManager();
+ quicktune = new QuicktuneShortcutter();
+
+ if (!(texture_src && shader_src && itemdef_manager && nodedef_manager
+ && eventmgr && quicktune))
+ return false;
+
+ if (!initSound())
+ return false;
+
+ // Create a server if not connecting to an existing one
+ if (*address == "") {
+ if (!createSingleplayerServer(map_dir, gamespec, port, address))
+ return false;
+ }
+
+ return true;
+}
+
+bool MinetestApp::initSound()
+{
+#if USE_SOUND
+ if (g_settings->getBool("enable_sound")) {
+ infostream << "Attempting to use OpenAL audio" << std::endl;
+ sound = createOpenALSoundManager(&soundfetcher);
+ if (!sound)
+ infostream << "Failed to initialize OpenAL audio" << std::endl;
+ } else
+ infostream << "Sound disabled." << std::endl;
+#endif
+
+ if (!sound) {
+ infostream << "Using dummy audio." << std::endl;
+ sound = &dummySoundManager;
+ sound_is_dummy = true;
+ }
+
+ soundmaker = new SoundMaker(sound, nodedef_manager);
+ if (!soundmaker)
+ return false;
+
+ soundmaker->registerReceiver(eventmgr);
+
+ return true;
+}
+
+bool MinetestApp::createSingleplayerServer(const std::string map_dir,
+ const SubgameSpec &gamespec, u16 port, std::string *address)
+{
+ showOverlayMessage("Creating server...", 0, 25);
+
+ std::string bind_str = g_settings->get("bind_address");
+ Address bind_addr(0, 0, 0, 0, port);
+
+ if (g_settings->getBool("ipv6_server")) {
+ bind_addr.setAddress((IPv6AddressBytes *) NULL);
+ }
+
+ try {
+ bind_addr.Resolve(bind_str.c_str());
+ *address = bind_str;
+ } catch (ResolveError &e) {
+ infostream << "Resolving bind address \"" << bind_str
+ << "\" failed: " << e.what()
+ << " -- Listening on all addresses." << std::endl;
+ }
+
+ if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
+ *error_message = L"Unable to listen on " +
+ narrow_to_wide(bind_addr.serializeString()) +
+ L" because IPv6 is disabled";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+
+ server = new Server(map_dir, gamespec, simple_singleplayer_mode,
+ bind_addr.isIPv6());
+
+ server->start(bind_addr);
+
+ return true;
+}
+
+bool MinetestApp::createClient(const std::string &playername,
+ const std::string &password, std::string *address, u16 port,
+ std::wstring *error_message)
+{
+ showOverlayMessage("Creating client...", 0, 50);
+
+ draw_control = new MapDrawControl;
+ if (!draw_control)
+ return false;
+
+ bool could_connect, connect_aborted;
+
+ if (!connectToServer(playername, password, address, port,
+ &could_connect, &connect_aborted))
+ return false;
+
+ if (!could_connect) {
+ if (*error_message == L"" && !connect_aborted) {
+ // Should not happen if error messages are set properly
+ *error_message = L"Connection failed for unknown reason";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ }
+ return false;
+ }
+
+ if (!getServerContent(&connect_aborted)) {
+ if (*error_message == L"" && !connect_aborted) {
+ // Should not happen if error messages are set properly
+ *error_message = L"Connection failed for unknown reason";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ }
+ return false;
+ }
+
+ // Update cached textures, meshes and materials
+ client->afterContentReceived(device, font);
+
+ /* Camera
+ */
+ camera = new Camera(smgr, *draw_control, gamedef);
+ if (!camera || !camera->successfullyCreated(*error_message))
+ return false;
+
+ /* Clouds
+ */
+ if (g_settings->getBool("enable_clouds")) {
+ clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
+ if (!clouds) {
+ *error_message = L"Memory allocation error";
+ *error_message += narrow_to_wide(" (clouds)");
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+ }
+
+ /* Skybox
+ */
+ sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
+ skybox = NULL; // This is used/set later on in the main run loop
+
+ local_inventory = new Inventory(itemdef_manager);
+
+ if (!(sky && local_inventory)) {
+ *error_message = L"Memory allocation error";
+ *error_message += narrow_to_wide(" (sky or local inventory)");
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+
+ /* Pre-calculated values
+ */
+ video::ITexture *t = texture_src->getTexture("crack_anylength.png");
+ if (t) {
+ v2u32 size = t->getOriginalSize();
+ crack_animation_length = size.Y / size.X;
+ } else {
+ crack_animation_length = 5;
+ }
+
+ if (!initGui(error_message))
+ return false;
+
+ /* Set window caption
+ */
+ core::stringw str = L"Minetest [";
+ str += driver->getName();
+ str += "]";
+ device->setWindowCaption(str.c_str());
+
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ player->hurt_tilt_timer = 0;
+ player->hurt_tilt_strength = 0;
+
+ hud = new Hud(driver, smgr, guienv, font, text_height, gamedef,
+ player, local_inventory);
+
+ if (!hud) {
+ *error_message = L"Memory error: could not create HUD";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool MinetestApp::initGui(std::wstring *error_message)
+{
+ // First line of debug text
+ guitext = guienv->addStaticText(
+ L"Minetest",
+ core::rect<s32>(0, 0, 0, 0),
+ false, false, guiroot);
+
+ // Second line of debug text
+ guitext2 = guienv->addStaticText(
L"",
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
+
// At the middle of the screen
// Object infos are shown in this
- gui::IGUIStaticText *guitext_info = guienv->addStaticText(
+ guitext_info = guienv->addStaticText(
L"",
- core::rect<s32>(0,0,400,text_height*5+5) + v2s32(100,200),
+ core::rect<s32>(0, 0, 400, text_height * 5 + 5) + v2s32(100, 200),
false, true, guiroot);
// Status text (displays info when showing and hiding GUI stuff, etc.)
- gui::IGUIStaticText *guitext_status = guienv->addStaticText(
+ guitext_status = guienv->addStaticText(
L"<Status>",
- core::rect<s32>(0,0,0,0),
+ core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
guitext_status->setVisible(false);
- std::wstring statustext;
- float statustext_time = 0;
-
// Chat text
- gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
+ guitext_chat = guienv->addStaticText(
L"",
- core::rect<s32>(0,0,0,0),
+ core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
false, true, guiroot);
// Remove stale "recent" chat messages from previous connections
- chat_backend.clearRecentChat();
+ chat_backend->clearRecentChat();
+
// Chat backend and console
- GUIChatConsole *gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, &chat_backend, &client);
+ gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
+ -1, chat_backend, client);
+ if (!gui_chat_console) {
+ *error_message = L"Could not allocate memory for chat console";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
// Profiler text (size is updated when text is updated)
- gui::IGUIStaticText *guitext_profiler = guienv->addStaticText(
+ guitext_profiler = guienv->addStaticText(
L"<Profiler>",
- core::rect<s32>(0,0,0,0),
+ core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
- guitext_profiler->setBackgroundColor(video::SColor(120,0,0,0));
+ guitext_profiler->setBackgroundColor(video::SColor(120, 0, 0, 0));
guitext_profiler->setVisible(false);
guitext_profiler->setWordWrap(true);
#ifdef HAVE_TOUCHSCREENGUI
+
if (g_touchscreengui)
- g_touchscreengui->init(tsrc,porting::getDisplayDensity());
+ g_touchscreengui->init(tsrc, porting::getDisplayDensity());
+
#endif
- /*
- Some statistics are collected in these
- */
- u32 drawtime = 0;
- u32 beginscenetime = 0;
- u32 endscenetime = 0;
+ return true;
+}
- float recent_turn_speed = 0.0;
+bool MinetestApp::connectToServer(const std::string &playername,
+ const std::string &password, std::string *address, u16 port,
+ bool *connect_ok, bool *aborted)
+{
+ showOverlayMessage("Resolving address...", 0, 75);
- ProfilerGraph graph;
- // Initially clear the profiler
- Profiler::GraphValues dummyvalues;
- g_profiler->graphGet(dummyvalues);
+ Address connect_address(0, 0, 0, 0, port);
- float nodig_delay_timer = 0.0;
- float dig_time = 0.0;
- u16 dig_index = 0;
- PointedThing pointed_old;
- bool digging = false;
- bool ldown_for_dig = false;
+ try {
+ connect_address.Resolve(address->c_str());
- float damage_flash = 0;
+ if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
+ //connect_address.Resolve("localhost");
+ if (connect_address.isIPv6()) {
+ IPv6AddressBytes addr_bytes;
+ addr_bytes.bytes[15] = 1;
+ connect_address.setAddress(&addr_bytes);
+ } else {
+ connect_address.setAddress(127, 0, 0, 1);
+ }
+ }
+ } catch (ResolveError &e) {
+ *error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
- float jump_timer = 0;
- bool reset_jump_timer = false;
+ if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
+ *error_message = L"Unable to connect to " +
+ narrow_to_wide(connect_address.serializeString()) +
+ L" because IPv6 is disabled";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
- const float object_hit_delay = 0.2;
- float object_hit_delay_timer = 0.0;
- float time_from_last_punch = 10;
+ client = new Client(device, playername.c_str(), password, *draw_control,
+ texture_src, shader_src, itemdef_manager, nodedef_manager, sound,
+ eventmgr, connect_address.isIPv6());
- float update_draw_list_timer = 0.0;
- v3f update_draw_list_last_cam_dir;
+ if (!client)
+ return false;
- bool invert_mouse = g_settings->getBool("invert_mouse");
+ gamedef = client; // Client acts as our GameDef
- bool update_wielded_item_trigger = true;
- bool show_hud = true;
- bool show_chat = true;
- bool force_fog_off = false;
- f32 fog_range = 100*BS;
- bool disable_camera_update = false;
- bool show_debug = g_settings->getBool("show_debug");
- bool show_profiler_graph = false;
- u32 show_profiler = 0;
- u32 show_profiler_max = 3; // Number of pages
+ infostream << "Connecting to server at ";
+ connect_address.print(&infostream);
+ infostream << std::endl;
- float time_of_day = 0;
- float time_of_day_smooth = 0;
+ client->connect(connect_address);
- float repeat_rightclick_timer = 0;
/*
- Shader constants
+ Wait for server to accept connection
*/
- shsrc->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
- sky, &force_fog_off, &fog_range, &client));
- /*
- Main loop
- */
+ try {
+ input->clear();
- bool first_loop_after_window_activation = true;
+ FpsControl fps_control = { 0 };
+ f32 dtime; // in seconds
- // TODO: Convert the static interval timers to these
- // Interval limiter for profiler
- IntervalLimiter m_profiler_interval;
+ while (device->run()) {
- // Time is in milliseconds
- // NOTE: getRealTime() causes strange problems in wine (imprecision?)
- // NOTE: So we have to use getTime() and call run()s between them
- u32 lasttime = device->getTimer()->getTime();
+ limitFps(&fps_control, &dtime);
- LocalPlayer* player = client.getEnv().getLocalPlayer();
- player->hurt_tilt_timer = 0;
- player->hurt_tilt_strength = 0;
+ // Update client and server
+ client->step(dtime);
- /*
- HUD object
- */
- Hud hud(driver, smgr, guienv, font, text_height,
- gamedef, player, &local_inventory);
+ if (server != NULL)
+ server->step(dtime);
- core::stringw str = L"Minetest [";
- str += driver->getName();
- str += "]";
- device->setWindowCaption(str.c_str());
+ // End condition
+ if (client->getState() == LC_Init) {
+ *connect_ok = true;
+ break;
+ }
- // Info text
- std::wstring infotext;
+ // Break conditions
+ if (client->accessDenied()) {
+ *error_message = L"Access denied. Reason: "
+ + client->accessDeniedReason();
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ break;
+ }
+
+ if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
+ *aborted = true;
+ infostream << "Connect aborted [Escape]" << std::endl;
+ break;
+ }
+
+ // Update status
+ showOverlayMessage("Connecting to server...", dtime, 100);
+ }
+ } catch (con::PeerNotFoundException &e) {
+ // TODO: Should something be done here? At least an info/error
+ // message?
+ return false;
+ }
+
+ return true;
+}
+
+bool MinetestApp::getServerContent(bool *aborted)
+{
+ input->clear();
+
+ FpsControl fps_control = { 0 };
+ f32 dtime; // in seconds
+
+ while (device->run()) {
+
+ limitFps(&fps_control, &dtime);
+
+ // Update client and server
+ client->step(dtime);
+
+ if (server != NULL)
+ server->step(dtime);
+
+ // End condition
+ if (client->mediaReceived() && client->itemdefReceived() &&
+ client->nodedefReceived()) {
+ break;
+ }
+
+ // Error conditions
+ if (client->accessDenied()) {
+ *error_message = L"Access denied. Reason: "
+ + client->accessDeniedReason();
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+
+ if (client->getState() < LC_Init) {
+ *error_message = L"Client disconnected";
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+
+ if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
+ *aborted = true;
+ infostream << "Connect aborted [Escape]" << std::endl;
+ return false;
+ }
+
+ // Display status
+ int progress = 0;
+
+ if (!client->itemdefReceived()) {
+ wchar_t *text = wgettext("Item definitions...");
+ progress = 0;
+ draw_load_screen(text, device, guienv, font, dtime, progress);
+ delete[] text;
+ } else if (!client->nodedefReceived()) {
+ wchar_t *text = wgettext("Node definitions...");
+ progress = 25;
+ draw_load_screen(text, device, guienv, font, dtime, progress);
+ delete[] text;
+ } else {
+ std::stringstream message;
+ message.precision(3);
+ message << gettext("Media...");
+
+ if ((USE_CURL == 0) ||
+ (!g_settings->getBool("enable_remote_media_server"))) {
+ float cur = client->getCurRate();
+ std::string cur_unit = gettext(" KB/s");
+
+ if (cur > 900) {
+ cur /= 1024.0;
+ cur_unit = gettext(" MB/s");
+ }
+
+ message << " ( " << cur << cur_unit << " )";
+ }
+
+ progress = 50 + client->mediaReceiveProgress() * 50 + 0.5;
+ draw_load_screen(narrow_to_wide(message.str().c_str()), device,
+ guienv, font, dtime, progress);
+ }
+ }
+
+ return true;
+}
+
+
+
+/****************************************************************************
+ Run
+ ****************************************************************************/
+
+inline void MinetestApp::updateInteractTimers(InteractParams *args, f32 dtime)
+{
+ if (args->nodig_delay_timer >= 0)
+ args->nodig_delay_timer -= dtime;
+
+ if (args->object_hit_delay_timer >= 0)
+ args->object_hit_delay_timer -= dtime;
+
+ args->time_from_last_punch += dtime;
+}
+
+
+/* returns false if app should exit, otherwise true
+ */
+inline bool MinetestApp::checkConnection()
+{
+ if (client->accessDenied()) {
+ *error_message = L"Access denied. Reason: "
+ + client->accessDeniedReason();
+ errorstream << wide_to_narrow(*error_message) << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+/* returns false if app should exit, otherwise true
+ */
+inline bool MinetestApp::handleCallbacks()
+{
+ if (g_gamecallback->disconnect_requested) {
+ g_gamecallback->disconnect_requested = false;
+ return false;
+ }
+
+ if (g_gamecallback->changepassword_requested) {
+ (new GUIPasswordChange(guienv, guiroot, -1,
+ &g_menumgr, client))->drop();
+ g_gamecallback->changepassword_requested = false;
+ }
+
+ if (g_gamecallback->changevolume_requested) {
+ (new GUIVolumeChange(guienv, guiroot, -1,
+ &g_menumgr, client))->drop();
+ g_gamecallback->changevolume_requested = false;
+ }
+
+ if (g_gamecallback->keyconfig_requested) {
+ (new GUIKeyChangeMenu(guienv, guiroot, -1,
+ &g_menumgr))->drop();
+ g_gamecallback->keyconfig_requested = false;
+ }
+
+ return true;
+}
+
+
+void MinetestApp::processQueues()
+{
+ texture_src->processQueue();
+ itemdef_manager->processQueue(gamedef);
+ shader_src->processQueue();
+}
+
+
+void MinetestApp::addProfilerGraphs(const RunStats &stats,
+ const FpsControl &draw_times, f32 dtime)
+{
+ g_profiler->graphAdd("mainloop_other",
+ draw_times.busy_time / 1000.0f - stats.drawtime / 1000.0f);
+
+ if (draw_times.sleep_time != 0)
+ g_profiler->graphAdd("mainloop_sleep", draw_times.sleep_time / 1000.0f);
+ g_profiler->graphAdd("mainloop_dtime", dtime);
+
+ g_profiler->add("Elapsed time", dtime);
+ g_profiler->avg("FPS", 1. / dtime);
+}
+
+
+void MinetestApp::updateStats(RunStats *stats, const FpsControl &draw_times,
+ f32 dtime)
+{
+
+ f32 jitter;
+ Jitter *jp;
+
+ /* Time average and jitter calculation
+ */
+ jp = &stats->dtime_jitter;
+ jp->avg = jp->avg * 0.96 + dtime * 0.04;
+
+ jitter = dtime - jp->avg;
+
+ if (jitter > jp->max)
+ jp->max = jitter;
+
+ jp->counter += dtime;
+
+ if (jp->counter > 0.0) {
+ jp->counter -= 3.0;
+ jp->max_sample = jp->max;
+ jp->max_fraction = jp->max_sample / (jp->avg + 0.001);
+ jp->max = 0.0;
+ }
+
+ /* Busytime average and jitter calculation
+ */
+ jp = &stats->busy_time_jitter;
+ jp->avg = jp->avg + draw_times.busy_time * 0.02;
+
+ jitter = draw_times.busy_time - jp->avg;
+
+ if (jitter > jp->max)
+ jp->max = jitter;
+ if (jitter < jp->min)
+ jp->min = jitter;
+
+ jp->counter += dtime;
+
+ if (jp->counter > 0.0) {
+ jp->counter -= 3.0;
+ jp->max_sample = jp->max;
+ jp->min_sample = jp->min;
+ jp->max = 0.0;
+ jp->min = 0.0;
+ }
+}
+
+
+
+/****************************************************************************
+ Input handling
+ ****************************************************************************/
+
+void MinetestApp::processUserInput(VolatileRunFlags *flags,
+ InteractParams *interact_args, f32 dtime)
+{
+ // Reset input if window not active or some menu is active
+ if (device->isWindowActive() == false
+ || noMenuActive() == false
+ || guienv->hasFocus(gui_chat_console)) {
+ input->clear();
+ }
+
+ if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
+ gui_chat_console->closeConsoleAtOnce();
+ }
+
+ // Input handler step() (used by the random input generator)
+ input->step(dtime);
+
+#ifdef HAVE_TOUCHSCREENGUI
+
+ if (g_touchscreengui) {
+ g_touchscreengui->step(dtime);
+ }
+
+#endif
+#ifdef __ANDROID__
+
+ if (current_formspec != 0)
+ current_formspec->getAndroidUIInput();
+
+#endif
+
+ // Increase timer for double tap of "keymap_jump"
+ if (g_settings->getBool("doubletap_jump") && interact_args->jump_timer <= 0.2)
+ interact_args->jump_timer += dtime;
+
+ processKeyboardInput(
+ flags,
+ &interact_args->statustext_time,
+ &interact_args->jump_timer,
+ &interact_args->profiler_current_page,
+ interact_args->profiler_max_page);
+
+ processItemSelection(&interact_args->new_playeritem);
+}
+
+
+void MinetestApp::processKeyboardInput(VolatileRunFlags *flags,
+ float *statustext_time,
+ float *jump_timer,
+ u32 *profiler_current_page,
+ u32 profiler_max_page)
+{
+ if (input->wasKeyDown(getKeySetting("keymap_drop"))) {
+ dropSelectedItem();
+ } else if (input->wasKeyDown(getKeySetting("keymap_inventory"))) {
+ openInventory();
+ } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
+ show_pause_menu(¤t_formspec, client, gamedef, texture_src, device,
+ simple_singleplayer_mode);
+ } else if (input->wasKeyDown(getKeySetting("keymap_chat"))) {
+ show_chat_menu(¤t_formspec, client, gamedef, texture_src, device,
+ client, "");
+ } else if (input->wasKeyDown(getKeySetting("keymap_cmd"))) {
+ show_chat_menu(¤t_formspec, client, gamedef, texture_src, device,
+ client, "/");
+ } else if (input->wasKeyDown(getKeySetting("keymap_console"))) {
+ openConsole();
+ } else if (input->wasKeyDown(getKeySetting("keymap_freemove"))) {
+ toggleFreeMove(statustext_time);
+ } else if (input->wasKeyDown(getKeySetting("keymap_jump"))) {
+ toggleFreeMoveAlt(statustext_time, jump_timer);
+ } else if (input->wasKeyDown(getKeySetting("keymap_fastmove"))) {
+ toggleFast(statustext_time);
+ } else if (input->wasKeyDown(getKeySetting("keymap_noclip"))) {
+ toggleNoClip(statustext_time);
+ } else if (input->wasKeyDown(getKeySetting("keymap_screenshot"))) {
+ client->makeScreenshot(device);
+ } else if (input->wasKeyDown(getKeySetting("keymap_toggle_hud"))) {
+ toggleHud(statustext_time, &flags->show_hud);
+ } else if (input->wasKeyDown(getKeySetting("keymap_toggle_chat"))) {
+ toggleChat(statustext_time, &flags->show_chat);
+ } else if (input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off"))) {
+ toggleFog(statustext_time, &flags->force_fog_off);
+ } else if (input->wasKeyDown(getKeySetting("keymap_toggle_update_camera"))) {
+ toggleUpdateCamera(statustext_time, &flags->disable_camera_update);
+ } else if (input->wasKeyDown(getKeySetting("keymap_toggle_debug"))) {
+ toggleDebug(statustext_time, &flags->show_debug, &flags->show_profiler_graph);
+ } else if (input->wasKeyDown(getKeySetting("keymap_toggle_profiler"))) {
+ toggleProfiler(statustext_time, profiler_current_page, profiler_max_page);
+ } else if (input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min"))) {
+ increaseViewRange(statustext_time);
+ } else if (input->wasKeyDown(getKeySetting("keymap_decrease_viewing_range_min"))) {
+ decreaseViewRange(statustext_time);
+ } else if (input->wasKeyDown(getKeySetting("keymap_rangeselect"))) {
+ toggleFullViewRange(statustext_time);
+ }
+
+ // Handle QuicktuneShortcutter
+ if (input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
+ quicktune->next();
+ else if (input->wasKeyDown(getKeySetting("keymap_quicktune_prev")))
+ quicktune->prev();
+ else if (input->wasKeyDown(getKeySetting("keymap_quicktune_inc")))
+ quicktune->inc();
+ else if (input->wasKeyDown(getKeySetting("keymap_quicktune_dec")))
+ quicktune->dec();
+
+ std::string msg = quicktune->getMessage();
+ if (msg != "") {
+ statustext = narrow_to_wide(msg);
+ *statustext_time = 0;
+ }
+
+ // Print debug stacks
+ if (input->wasKeyDown(getKeySetting("keymap_print_debug_stacks"))) {
+ dstream << "-----------------------------------------"
+ << std::endl;
+ dstream << DTIME << "Printing debug stacks:" << std::endl;
+ dstream << "-----------------------------------------"
+ << std::endl;
+ debug_stacks_print();
+ }
+}
+
+
+void MinetestApp::processItemSelection(u16 *new_playeritem)
+{
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+
+ /* Item selection using mouse wheel
+ */
+ *new_playeritem = client->getPlayerItem();
+
+ s32 wheel = input->getMouseWheel();
+ u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
+ player->hud_hotbar_itemcount - 1);
+
+ if (wheel < 0)
+ *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : max_item;
+ else if (wheel > 0)
+ *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
+ // else wheel == 0
+
+
+ /* Item selection using keyboard
+ */
+ for (u16 i = 0; i < 10; i++) {
+ static const KeyPress *item_keys[10] = {
+ NumberKey + 1, NumberKey + 2, NumberKey + 3, NumberKey + 4,
+ NumberKey + 5, NumberKey + 6, NumberKey + 7, NumberKey + 8,
+ NumberKey + 9, NumberKey + 0,
+ };
+
+ if (input->wasKeyDown(*item_keys[i])) {
+ if (i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount) {
+ *new_playeritem = i;
+ infostream << "Selected item: " << new_playeritem << std::endl;
+ }
+ break;
+ }
+ }
+}
+
+
+void MinetestApp::dropSelectedItem()
+{
+ IDropAction *a = new IDropAction();
+ a->count = 0;
+ a->from_inv.setCurrentPlayer();
+ a->from_list = "main";
+ a->from_i = client->getPlayerItem();
+ client->inventoryAction(a);
+}
+
+
+void MinetestApp::openInventory()
+{
+ infostream << "the_game: " << "Launching inventory" << std::endl;
+
+ PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
+ TextDest *txt_dst = new TextDestPlayerInventory(client);
+
+ create_formspec_menu(¤t_formspec, client, gamedef, texture_src,
+ device, fs_src, txt_dst, client);
+
+ InventoryLocation inventoryloc;
+ inventoryloc.setCurrentPlayer();
+ current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
+}
+
+
+void MinetestApp::openConsole()
+{
+ if (!gui_chat_console->isOpenInhibited()) {
+ // Open up to over half of the screen
+ gui_chat_console->openConsole(0.6);
+ guienv->setFocus(gui_chat_console);
+ }
+}
+
+
+void MinetestApp::toggleFreeMove(float *statustext_time)
+{
+ static const wchar_t *msg[] = { L"free_move disabled", L"free_move enabled" };
+
+ bool free_move = !g_settings->getBool("free_move");
+ g_settings->set("free_move", boolToCStr(free_move));
+
+ *statustext_time = 0;
+ statustext = msg[free_move];
+ if (free_move && !client->checkPrivilege("fly"))
+ statustext += L" (note: no 'fly' privilege)";
+}
+
+
+void MinetestApp::toggleFreeMoveAlt(float *statustext_time, float *jump_timer)
+{
+ if (g_settings->getBool("doubletap_jump") && *jump_timer < 0.2f) {
+ toggleFreeMove(statustext_time);
+ *jump_timer = 0;
+ }
+}
+
+
+void MinetestApp::toggleFast(float *statustext_time)
+{
+ static const wchar_t *msg[] = { L"fast_move disabled", L"fast_move enabled" };
+ bool fast_move = !g_settings->getBool("fast_move");
+ g_settings->set("fast_move", boolToCStr(fast_move));
+
+ *statustext_time = 0;
+ statustext = msg[fast_move];
+
+ if (fast_move && !client->checkPrivilege("fast"))
+ statustext += L" (note: no 'fast' privilege)";
+}
+
+
+void MinetestApp::toggleNoClip(float *statustext_time)
+{
+ static const wchar_t *msg[] = { L"noclip disabled", L"noclip enabled" };
+ bool noclip = !g_settings->getBool("noclip");
+ g_settings->set("noclip", boolToCStr(noclip));
+
+ *statustext_time = 0;
+ statustext = msg[noclip];
+
+ if (noclip && !client->checkPrivilege("noclip"))
+ statustext += L" (note: no 'noclip' privilege)";
+}
+
+
+void MinetestApp::toggleChat(float *statustext_time, bool *flag)
+{
+ static const wchar_t *msg[] = { L"Chat hidden", L"Chat shown" };
+
+ *flag = !*flag;
+ *statustext_time = 0;
+ statustext = msg[*flag];
+}
+
+
+void MinetestApp::toggleHud(float *statustext_time, bool *flag)
+{
+ static const wchar_t *msg[] = { L"HUD hidden", L"HUD shown" };
+
+ *flag = !*flag;
+ *statustext_time = 0;
+ statustext = msg[*flag];
+ client->setHighlighted(client->getHighlighted(), *flag);
+}
+
+
+void MinetestApp::toggleFog(float *statustext_time, bool *flag)
+{
+ static const wchar_t *msg[] = { L"Fog enabled", L"Fog disabled" };
+
+ *flag = !*flag;
+ *statustext_time = 0;
+ statustext = msg[*flag];
+}
+
+
+void MinetestApp::toggleDebug(float *statustext_time, bool *show_debug,
+ bool *show_profiler_graph)
+{
+ // Initial / 3x toggle: Chat only
+ // 1x toggle: Debug text with chat
+ // 2x toggle: Debug text with profiler graph
+ if (!*show_debug) {
+ *show_debug = true;
+ *show_profiler_graph = false;
+ statustext = L"Debug info shown";
+ } else if (*show_profiler_graph) {
+ *show_debug = false;
+ *show_profiler_graph = false;
+ statustext = L"Debug info and profiler graph hidden";
+ } else {
+ *show_profiler_graph = true;
+ statustext = L"Profiler graph shown";
+ }
+ *statustext_time = 0;
+}
+
+
+void MinetestApp::toggleUpdateCamera(float *statustext_time, bool *flag)
+{
+ static const wchar_t *msg[] = {
+ L"Camera update enabled",
+ L"Camera update disabled"
+ };
+
+ *flag = !*flag;
+ *statustext_time = 0;
+ statustext = msg[*flag];
+}
+
+
+void MinetestApp::toggleProfiler(float *statustext_time, u32 *profiler_current_page,
+ u32 profiler_max_page)
+{
+ *profiler_current_page = (*profiler_current_page + 1) % (profiler_max_page + 1);
+
+ // FIXME: This updates the profiler with incomplete values
+ update_profiler_gui(guitext_profiler, font, text_height,
+ *profiler_current_page, profiler_max_page);
+
+ if (*profiler_current_page != 0) {
+ std::wstringstream sstr;
+ sstr << "Profiler shown (page " << *profiler_current_page
+ << " of " << profiler_max_page << ")";
+ statustext = sstr.str();
+ } else {
+ statustext = L"Profiler hidden";
+ }
+ *statustext_time = 0;
+}
+
+
+void MinetestApp::increaseViewRange(float *statustext_time)
+{
+ s16 range = g_settings->getS16("viewing_range_nodes_min");
+ s16 range_new = range + 10;
+ g_settings->set("viewing_range_nodes_min", itos(range_new));
+ statustext = narrow_to_wide("Minimum viewing range changed to "
+ + itos(range_new));
+ *statustext_time = 0;
+}
+
+
+void MinetestApp::decreaseViewRange(float *statustext_time)
+{
+ s16 range = g_settings->getS16("viewing_range_nodes_min");
+ s16 range_new = range - 10;
+
+ if (range_new < 0)
+ range_new = range;
+
+ g_settings->set("viewing_range_nodes_min", itos(range_new));
+ statustext = narrow_to_wide("Minimum viewing range changed to "
+ + itos(range_new));
+ *statustext_time = 0;
+}
+
+
+void MinetestApp::toggleFullViewRange(float *statustext_time)
+{
+ static const wchar_t *msg[] = {
+ L"Disabled full viewing range",
+ L"Enabled full viewing range"
+ };
+
+ draw_control->range_all = !draw_control->range_all;
+ infostream << msg[draw_control->range_all] << std::endl;
+ statustext = msg[draw_control->range_all];
+ *statustext_time = 0;
+}
+
+
+void MinetestApp::updateCameraDirection(CameraOrientation *cam,
+ VolatileRunFlags *flags)
+{
+ // float turn_amount = 0; // Deprecated?
+
+ if (!(device->isWindowActive() && noMenuActive()) || random_input) {
+
+ // FIXME: Clean this up
+
+#ifndef ANDROID
+ // Mac OSX gets upset if this is set every frame
+ if (device->getCursorControl()->isVisible() == false)
+ device->getCursorControl()->setVisible(true);
+#endif
+
+ //infostream<<"window inactive"<<std::endl;
+ flags->first_loop_after_window_activation = true;
+ return;
+ }
+
+#ifndef __ANDROID__
+ if (!random_input) {
+ // Mac OSX gets upset if this is set every frame
+ if (device->getCursorControl()->isVisible())
+ device->getCursorControl()->setVisible(false);
+ }
+#endif
+
+ if (flags->first_loop_after_window_activation) {
+ //infostream<<"window active, first loop"<<std::endl;
+ flags->first_loop_after_window_activation = false;
+ } else {
+
+#ifdef HAVE_TOUCHSCREENGUI
+
+ if (g_touchscreengui) {
+ camera_yaw = g_touchscreengui->getYaw();
+ camera_pitch = g_touchscreengui->getPitch();
+ } else {
+#endif
+ s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
+ s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
+
+ if (flags->invert_mouse
+ || (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT)) {
+ dy = -dy;
+ }
+
+ //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
+
+ float d = g_settings->getFloat("mouse_sensitivity");
+ d = rangelim(d, 0.01, 100.0);
+ cam->camera_yaw -= dx * d;
+ cam->camera_pitch += dy * d;
+ // turn_amount = v2f(dx, dy).getLength() * d; // deprecated?
+
+#ifdef HAVE_TOUCHSCREENGUI
+ }
+#endif
+
+ if (cam->camera_pitch < -89.5)
+ cam->camera_pitch = -89.5;
+ else if (cam->camera_pitch > 89.5)
+ cam->camera_pitch = 89.5;
+ }
+
+ input->setMousePos(driver->getScreenSize().Width / 2,
+ driver->getScreenSize().Height / 2);
+
+ // Deprecated? Not used anywhere else
+ // recent_turn_speed = recent_turn_speed * 0.9 + turn_amount * 0.1;
+ // std::cerr<<"recent_turn_speed = "<<recent_turn_speed<<std::endl;
+}
+
+
+void MinetestApp::updatePlayerControl(const CameraOrientation &cam)
+{
+ PlayerControl control(
+ input->isKeyDown(getKeySetting("keymap_forward")),
+ input->isKeyDown(getKeySetting("keymap_backward")),
+ input->isKeyDown(getKeySetting("keymap_left")),
+ input->isKeyDown(getKeySetting("keymap_right")),
+ input->isKeyDown(getKeySetting("keymap_jump")),
+ input->isKeyDown(getKeySetting("keymap_special1")),
+ input->isKeyDown(getKeySetting("keymap_sneak")),
+ input->getLeftState(),
+ input->getRightState(),
+ cam.camera_pitch,
+ cam.camera_yaw
+ );
+ client->setPlayerControl(control);
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ player->keyPressed =
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_forward")) & 0x1) << 0) |
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_backward")) & 0x1) << 1) |
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_left")) & 0x1) << 2) |
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_right")) & 0x1) << 3) |
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_jump")) & 0x1) << 4) |
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_special1")) & 0x1) << 5) |
+ ( (u32)(input->isKeyDown(getKeySetting("keymap_sneak")) & 0x1) << 6) |
+ ( (u32)(input->getLeftState() & 0x1) << 7) |
+ ( (u32)(input->getRightState() & 0x1) << 8
+ );
+
+}
+
+
+inline void MinetestApp::step(f32 *dtime)
+{
+ bool can_be_and_is_paused =
+ (simple_singleplayer_mode && g_menumgr.pausesGame());
+
+ if (can_be_and_is_paused) { // This is for a singleplayer server
+ *dtime = 0; // No time passes
+ } else {
+ if (server != NULL) {
+ //TimeTaker timer("server->step(dtime)");
+ server->step(*dtime);
+ }
+
+ //TimeTaker timer("client.step(dtime)");
+ client->step(*dtime);
+ }
+}
+
+
+void MinetestApp::processClientEvents(CameraOrientation *cam, float *damage_flash)
+{
+ ClientEvent event = client->getClientEvent();
+
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+
+ for ( ; event.type != CE_NONE; event = client->getClientEvent()) {
+
+ if (event.type == CE_PLAYER_DAMAGE &&
+ client->getHP() != 0) {
+ //u16 damage = event.player_damage.amount;
+ //infostream<<"Player damage: "<<damage<<std::endl;
+
+ *damage_flash += 100.0;
+ *damage_flash += 8.0 * event.player_damage.amount;
+
+ player->hurt_tilt_timer = 1.5;
+ player->hurt_tilt_strength = event.player_damage.amount / 4;
+ player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0);
+
+ MtEvent *e = new SimpleTriggerEvent("PlayerDamage");
+ gamedef->event()->put(e);
+ } else if (event.type == CE_PLAYER_FORCE_MOVE) {
+ cam->camera_yaw = event.player_force_move.yaw;
+ cam->camera_pitch = event.player_force_move.pitch;
+ } else if (event.type == CE_DEATHSCREEN) {
+ show_deathscreen(¤t_formspec, client, gamedef, texture_src,
+ device, client);
+
+ chat_backend->addMessage(L"", L"You died.");
+
+ /* Handle visualization */
+ *damage_flash = 0;
+ player->hurt_tilt_timer = 0;
+ player->hurt_tilt_strength = 0;
+
+ } else if (event.type == CE_SHOW_FORMSPEC) {
+ FormspecFormSource *fs_src =
+ new FormspecFormSource(*(event.show_formspec.formspec));
+ TextDestPlayerInventory *txt_dst =
+ new TextDestPlayerInventory(client, *(event.show_formspec.formname));
+
+ create_formspec_menu(¤t_formspec, client, gamedef,
+ texture_src, device, fs_src, txt_dst, client);
+
+ delete(event.show_formspec.formspec);
+ delete(event.show_formspec.formname);
+ } else if (event.type == CE_SPAWN_PARTICLE) {
+ video::ITexture *texture =
+ gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
+
+ new Particle(gamedef, smgr, player, client->getEnv(),
+ *event.spawn_particle.pos,
+ *event.spawn_particle.vel,
+ *event.spawn_particle.acc,
+ event.spawn_particle.expirationtime,
+ event.spawn_particle.size,
+ event.spawn_particle.collisiondetection,
+ event.spawn_particle.vertical,
+ texture,
+ v2f(0.0, 0.0),
+ v2f(1.0, 1.0));
+ } else if (event.type == CE_ADD_PARTICLESPAWNER) {
+ video::ITexture *texture =
+ gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
+
+ new ParticleSpawner(gamedef, smgr, player,
+ event.add_particlespawner.amount,
+ event.add_particlespawner.spawntime,
+ *event.add_particlespawner.minpos,
+ *event.add_particlespawner.maxpos,
+ *event.add_particlespawner.minvel,
+ *event.add_particlespawner.maxvel,
+ *event.add_particlespawner.minacc,
+ *event.add_particlespawner.maxacc,
+ event.add_particlespawner.minexptime,
+ event.add_particlespawner.maxexptime,
+ event.add_particlespawner.minsize,
+ event.add_particlespawner.maxsize,
+ event.add_particlespawner.collisiondetection,
+ event.add_particlespawner.vertical,
+ texture,
+ event.add_particlespawner.id);
+ } else if (event.type == CE_DELETE_PARTICLESPAWNER) {
+ delete_particlespawner(event.delete_particlespawner.id);
+ } else if (event.type == CE_HUDADD) {
+ u32 id = event.hudadd.id;
+
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ HudElement *e = player->getHud(id);
+
+ if (e != NULL) {
+ delete event.hudadd.pos;
+ delete event.hudadd.name;
+ delete event.hudadd.scale;
+ delete event.hudadd.text;
+ delete event.hudadd.align;
+ delete event.hudadd.offset;
+ delete event.hudadd.world_pos;
+ delete event.hudadd.size;
+ continue;
+ }
+
+ e = new HudElement;
+ e->type = (HudElementType)event.hudadd.type;
+ e->pos = *event.hudadd.pos;
+ e->name = *event.hudadd.name;
+ e->scale = *event.hudadd.scale;
+ e->text = *event.hudadd.text;
+ e->number = event.hudadd.number;
+ e->item = event.hudadd.item;
+ e->dir = event.hudadd.dir;
+ e->align = *event.hudadd.align;
+ e->offset = *event.hudadd.offset;
+ e->world_pos = *event.hudadd.world_pos;
+ e->size = *event.hudadd.size;
+
+ u32 new_id = player->addHud(e);
+ //if this isn't true our huds aren't consistent
+ assert(new_id == id);
+
+ delete event.hudadd.pos;
+ delete event.hudadd.name;
+ delete event.hudadd.scale;
+ delete event.hudadd.text;
+ delete event.hudadd.align;
+ delete event.hudadd.offset;
+ delete event.hudadd.world_pos;
+ delete event.hudadd.size;
+ } else if (event.type == CE_HUDRM) {
+ HudElement *e = player->removeHud(event.hudrm.id);
+
+ if (e != NULL)
+ delete(e);
+ } else if (event.type == CE_HUDCHANGE) {
+ u32 id = event.hudchange.id;
+ HudElement *e = player->getHud(id);
+
+ if (e == NULL) {
+ delete event.hudchange.v3fdata;
+ delete event.hudchange.v2fdata;
+ delete event.hudchange.sdata;
+ delete event.hudchange.v2s32data;
+ continue;
+ }
+
+ switch (event.hudchange.stat) {
+ case HUD_STAT_POS:
+ e->pos = *event.hudchange.v2fdata;
+ break;
+
+ case HUD_STAT_NAME:
+ e->name = *event.hudchange.sdata;
+ break;
+
+ case HUD_STAT_SCALE:
+ e->scale = *event.hudchange.v2fdata;
+ break;
+
+ case HUD_STAT_TEXT:
+ e->text = *event.hudchange.sdata;
+ break;
- for(;;)
- {
- if(device->run() == false || kill == true ||
- g_gamecallback->shutdown_requested)
- break;
+ case HUD_STAT_NUMBER:
+ e->number = event.hudchange.data;
+ break;
- v2u32 screensize = driver->getScreenSize();
+ case HUD_STAT_ITEM:
+ e->item = event.hudchange.data;
+ break;
- // Time of frame without fps limit
- float busytime;
- u32 busytime_u32;
- {
- // not using getRealTime is necessary for wine
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- busytime_u32 = time - lasttime;
- else
- busytime_u32 = 0;
- busytime = busytime_u32 / 1000.0;
- }
+ case HUD_STAT_DIR:
+ e->dir = event.hudchange.data;
+ break;
- g_profiler->graphAdd("mainloop_other", busytime - (float)drawtime/1000.0f);
+ case HUD_STAT_ALIGN:
+ e->align = *event.hudchange.v2fdata;
+ break;
- // Necessary for device->getTimer()->getTime()
- device->run();
+ case HUD_STAT_OFFSET:
+ e->offset = *event.hudchange.v2fdata;
+ break;
- /*
- FPS limiter
- */
+ case HUD_STAT_WORLD_POS:
+ e->world_pos = *event.hudchange.v3fdata;
+ break;
- {
- float fps_max = g_menumgr.pausesGame() ?
- g_settings->getFloat("pause_fps_max") :
- g_settings->getFloat("fps_max");
- u32 frametime_min = 1000./fps_max;
-
- if(busytime_u32 < frametime_min)
- {
- u32 sleeptime = frametime_min - busytime_u32;
- device->sleep(sleeptime);
- g_profiler->graphAdd("mainloop_sleep", (float)sleeptime/1000.0f);
+ case HUD_STAT_SIZE:
+ e->size = *event.hudchange.v2s32data;
+ break;
}
- }
- // Necessary for device->getTimer()->getTime()
- device->run();
+ delete event.hudchange.v3fdata;
+ delete event.hudchange.v2fdata;
+ delete event.hudchange.sdata;
+ delete event.hudchange.v2s32data;
+ } else if (event.type == CE_SET_SKY) {
+ sky->setVisible(false);
+
+ if (skybox) {
+ skybox->remove();
+ skybox = NULL;
+ }
+
+ // Handle according to type
+ if (*event.set_sky.type == "regular") {
+ sky->setVisible(true);
+ } else if (*event.set_sky.type == "skybox" &&
+ event.set_sky.params->size() == 6) {
+ sky->setFallbackBgColor(*event.set_sky.bgcolor);
+ skybox = smgr->addSkyBoxSceneNode(
+ texture_src->getTexture((*event.set_sky.params)[0]),
+ texture_src->getTexture((*event.set_sky.params)[1]),
+ texture_src->getTexture((*event.set_sky.params)[2]),
+ texture_src->getTexture((*event.set_sky.params)[3]),
+ texture_src->getTexture((*event.set_sky.params)[4]),
+ texture_src->getTexture((*event.set_sky.params)[5]));
+ }
+ // Handle everything else as plain color
+ else {
+ if (*event.set_sky.type != "plain")
+ infostream << "Unknown sky type: "
+ << (*event.set_sky.type) << std::endl;
- /*
- Time difference calculation
- */
- f32 dtime; // in seconds
+ sky->setFallbackBgColor(*event.set_sky.bgcolor);
+ }
- u32 time = device->getTimer()->getTime();
- if(time > lasttime)
- dtime = (time - lasttime) / 1000.0;
- else
- dtime = 0;
- lasttime = time;
+ delete event.set_sky.bgcolor;
+ delete event.set_sky.type;
+ delete event.set_sky.params;
+ } else if (event.type == CE_OVERRIDE_DAY_NIGHT_RATIO) {
+ bool enable = event.override_day_night_ratio.do_override;
+ u32 value = event.override_day_night_ratio.ratio_f * 1000;
+ client->getEnv().setDayNightRatioOverride(enable, value);
+ }
+ }
+}
- g_profiler->graphAdd("mainloop_dtime", dtime);
- /* Run timers */
+void MinetestApp::updateCamera(VolatileRunFlags *flags, u32 busy_time,
+ f32 dtime, float time_from_last_punch)
+{
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
- if(nodig_delay_timer >= 0)
- nodig_delay_timer -= dtime;
- if(object_hit_delay_timer >= 0)
- object_hit_delay_timer -= dtime;
- time_from_last_punch += dtime;
+ /*
+ For interaction purposes, get info about the held item
+ - What item is it?
+ - Is it a usable item?
+ - Can it point to liquids?
+ */
+ ItemStack playeritem;
+ {
+ InventoryList *mlist = local_inventory->getList("main");
- g_profiler->add("Elapsed time", dtime);
- g_profiler->avg("FPS", 1./dtime);
+ if (mlist && client->getPlayerItem() < mlist->getSize())
+ playeritem = mlist->getItem(client->getPlayerItem());
+ }
- /*
- Time average and jitter calculation
- */
+ ToolCapabilities playeritem_toolcap =
+ playeritem.getToolCapabilities(itemdef_manager);
- static f32 dtime_avg1 = 0.0;
- dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
- f32 dtime_jitter1 = dtime - dtime_avg1;
-
- static f32 dtime_jitter1_max_sample = 0.0;
- static f32 dtime_jitter1_max_fraction = 0.0;
- {
- static f32 jitter1_max = 0.0;
- static f32 counter = 0.0;
- if(dtime_jitter1 > jitter1_max)
- jitter1_max = dtime_jitter1;
- counter += dtime;
- if(counter > 0.0)
- {
- counter -= 3.0;
- dtime_jitter1_max_sample = jitter1_max;
- dtime_jitter1_max_fraction
- = dtime_jitter1_max_sample / (dtime_avg1+0.001);
- jitter1_max = 0.0;
- }
- }
+ v3s16 old_camera_offset = camera->getOffset();
- /*
- Busytime average and jitter calculation
- */
+ if (input->wasKeyDown(getKeySetting("keymap_camera_mode"))) {
+ camera->toggleCameraMode();
+ GenericCAO *playercao = player->getCAO();
- static f32 busytime_avg1 = 0.0;
- busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
- f32 busytime_jitter1 = busytime - busytime_avg1;
-
- static f32 busytime_jitter1_max_sample = 0.0;
- static f32 busytime_jitter1_min_sample = 0.0;
- {
- static f32 jitter1_max = 0.0;
- static f32 jitter1_min = 0.0;
- static f32 counter = 0.0;
- if(busytime_jitter1 > jitter1_max)
- jitter1_max = busytime_jitter1;
- if(busytime_jitter1 < jitter1_min)
- jitter1_min = busytime_jitter1;
- counter += dtime;
- if(counter > 0.0){
- counter -= 3.0;
- busytime_jitter1_max_sample = jitter1_max;
- busytime_jitter1_min_sample = jitter1_min;
- jitter1_max = 0.0;
- jitter1_min = 0.0;
- }
- }
+ assert(playercao != NULL);
- /*
- Handle miscellaneous stuff
- */
+ playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
+ }
- if(client.accessDenied())
- {
- error_message = L"Access denied. Reason: "
- +client.accessDeniedReason();
- errorstream<<wide_to_narrow(error_message)<<std::endl;
- break;
- }
+ float full_punch_interval = playeritem_toolcap.full_punch_interval;
+ float tool_reload_ratio = time_from_last_punch / full_punch_interval;
- if(g_gamecallback->disconnect_requested)
- {
- g_gamecallback->disconnect_requested = false;
- break;
- }
+ tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
+ camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio,
+ client->getEnv());
+ camera->step(dtime);
- if(g_gamecallback->changepassword_requested)
- {
- (new GUIPasswordChange(guienv, guiroot, -1,
- &g_menumgr, &client))->drop();
- g_gamecallback->changepassword_requested = false;
- }
+ v3f camera_position = camera->getPosition();
+ v3f camera_direction = camera->getDirection();
+ f32 camera_fov = camera->getFovMax();
+ v3s16 camera_offset = camera->getOffset();
- if(g_gamecallback->changevolume_requested)
- {
- (new GUIVolumeChange(guienv, guiroot, -1,
- &g_menumgr, &client))->drop();
- g_gamecallback->changevolume_requested = false;
- }
+ flags->camera_offset_changed = (camera_offset != old_camera_offset);
- if(g_gamecallback->keyconfig_requested)
- {
- (new GUIKeyChangeMenu(guienv, guiroot, -1,
- &g_menumgr))->drop();
- g_gamecallback->keyconfig_requested = false;
- }
+ if (!flags->disable_camera_update) {
+ client->getEnv().getClientMap().updateCamera(camera_position,
+ camera_direction, camera_fov, camera_offset);
+ if (flags->camera_offset_changed) {
+ client->updateCameraOffset(camera_offset);
+ client->getEnv().updateCameraOffset(camera_offset);
- /* Process TextureSource's queue */
- tsrc->processQueue();
+ if (clouds)
+ clouds->updateCameraOffset(camera_offset);
+ }
+ }
+}
- /* Process ItemDefManager's queue */
- itemdef->processQueue(gamedef);
- /*
- Process ShaderSource's queue
- */
- shsrc->processQueue();
+void MinetestApp::updateSound(f32 dtime)
+{
+ // Update sound listener
+ v3s16 camera_offset = camera->getOffset();
+ sound->updateListener(camera->getCameraNode()->getPosition() + intToFloat(camera_offset, BS),
+ v3f(0, 0, 0), // velocity
+ camera->getDirection(),
+ camera->getCameraNode()->getUpVector());
+ sound->setListenerGain(g_settings->getFloat("sound_volume"));
- /*
- Random calculations
- */
- hud.resizeHotbar();
- // Hilight boxes collected during the loop and displayed
- std::vector<aabb3f> hilightboxes;
+ // Update sound maker
+ soundmaker->step(dtime);
- /* reset infotext */
- infotext = L"";
- /*
- Profiler
- */
- float profiler_print_interval =
- g_settings->getFloat("profiler_print_interval");
- bool print_to_log = true;
- if(profiler_print_interval == 0){
- print_to_log = false;
- profiler_print_interval = 5;
- }
- if(m_profiler_interval.step(dtime, profiler_print_interval))
- {
- if(print_to_log){
- infostream<<"Profiler:"<<std::endl;
- g_profiler->print(infostream);
- }
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
- update_profiler_gui(guitext_profiler, font, text_height,
- show_profiler, show_profiler_max);
+ ClientMap &map = client->getEnv().getClientMap();
+ MapNode n = map.getNodeNoEx(player->getStandingNodePos());
+ soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
+}
- g_profiler->clear();
- }
- /*
- Direct handling of user input
- */
+void MinetestApp::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
+ InteractParams *interactArgs, f32 dtime, bool show_hud, bool show_debug)
+{
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
- // Reset input if window not active or some menu is active
- if(device->isWindowActive() == false
- || noMenuActive() == false
- || guienv->hasFocus(gui_chat_console))
- {
- input->clear();
- }
- if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen())
- {
- gui_chat_console->closeConsoleAtOnce();
- }
+ ItemStack playeritem;
+ {
+ InventoryList *mlist = local_inventory->getList("main");
- // Input handler step() (used by the random input generator)
- input->step(dtime);
-#ifdef HAVE_TOUCHSCREENGUI
- if (g_touchscreengui) {
- g_touchscreengui->step(dtime);
- }
-#endif
-#ifdef __ANDROID__
- if (current_formspec != 0)
- current_formspec->getAndroidUIInput();
-#endif
+ if (mlist && client->getPlayerItem() < mlist->getSize())
+ playeritem = mlist->getItem(client->getPlayerItem());
+ }
- // Increase timer for doubleclick of "jump"
- if(g_settings->getBool("doubletap_jump") && jump_timer <= 0.2)
- jump_timer += dtime;
+ const ItemDefinition &playeritem_def =
+ playeritem.getDefinition(itemdef_manager);
- /*
- Launch menus and trigger stuff according to keys
- */
- if(input->wasKeyDown(getKeySetting("keymap_drop")))
- {
- // drop selected item
- IDropAction *a = new IDropAction();
- a->count = 0;
- a->from_inv.setCurrentPlayer();
- a->from_list = "main";
- a->from_i = client.getPlayerItem();
- client.inventoryAction(a);
- }
- else if(input->wasKeyDown(getKeySetting("keymap_inventory")))
- {
- infostream<<"the_game: "
- <<"Launching inventory"<<std::endl;
+ v3f player_position = player->getPosition();
+ v3f camera_position = camera->getPosition();
+ v3f camera_direction = camera->getDirection();
+ v3s16 camera_offset = camera->getOffset();
- PlayerInventoryFormSource* fs_src = new PlayerInventoryFormSource(&client);
- TextDest* txt_dst = new TextDestPlayerInventory(&client);
- create_formspec_menu(¤t_formspec, &client, gamedef, tsrc, device, fs_src, txt_dst, &client);
+ /*
+ Calculate what block is the crosshair pointing to
+ */
- InventoryLocation inventoryloc;
- inventoryloc.setCurrentPlayer();
- current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
- }
- else if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey))
- {
- show_pause_menu(¤t_formspec, &client, gamedef, tsrc, device,
- simple_singleplayer_mode);
- }
- else if(input->wasKeyDown(getKeySetting("keymap_chat")))
- {
- show_chat_menu(¤t_formspec, &client, gamedef, tsrc, device,
- &client,"");
- }
- else if(input->wasKeyDown(getKeySetting("keymap_cmd")))
- {
- show_chat_menu(¤t_formspec, &client, gamedef, tsrc, device,
- &client,"/");
- }
- else if(input->wasKeyDown(getKeySetting("keymap_console")))
- {
- if (!gui_chat_console->isOpenInhibited())
- {
- // Open up to over half of the screen
- gui_chat_console->openConsole(0.6);
- guienv->setFocus(gui_chat_console);
- }
- }
- else if(input->wasKeyDown(getKeySetting("keymap_freemove")))
- {
- if(g_settings->getBool("free_move"))
- {
- g_settings->set("free_move","false");
- statustext = L"free_move disabled";
- statustext_time = 0;
- }
- else
- {
- g_settings->set("free_move","true");
- statustext = L"free_move enabled";
- statustext_time = 0;
- if(!client.checkPrivilege("fly"))
- statustext += L" (note: no 'fly' privilege)";
- }
- }
- else if(input->wasKeyDown(getKeySetting("keymap_jump")))
- {
- if(g_settings->getBool("doubletap_jump") && jump_timer < 0.2)
- {
- if(g_settings->getBool("free_move"))
- {
- g_settings->set("free_move","false");
- statustext = L"free_move disabled";
- statustext_time = 0;
- }
- else
- {
- g_settings->set("free_move","true");
- statustext = L"free_move enabled";
- statustext_time = 0;
- if(!client.checkPrivilege("fly"))
- statustext += L" (note: no 'fly' privilege)";
- }
- }
- reset_jump_timer = true;
- }
- else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
- {
- if(g_settings->getBool("fast_move"))
- {
- g_settings->set("fast_move","false");
- statustext = L"fast_move disabled";
- statustext_time = 0;
- }
- else
- {
- g_settings->set("fast_move","true");
- statustext = L"fast_move enabled";
- statustext_time = 0;
- if(!client.checkPrivilege("fast"))
- statustext += L" (note: no 'fast' privilege)";
- }
- }
- else if(input->wasKeyDown(getKeySetting("keymap_noclip")))
- {
- if(g_settings->getBool("noclip"))
- {
- g_settings->set("noclip","false");
- statustext = L"noclip disabled";
- statustext_time = 0;
- }
- else
- {
- g_settings->set("noclip","true");
- statustext = L"noclip enabled";
- statustext_time = 0;
- if(!client.checkPrivilege("noclip"))
- statustext += L" (note: no 'noclip' privilege)";
- }
- }
- else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
- {
- client.makeScreenshot(device);
- }
- else if(input->wasKeyDown(getKeySetting("keymap_toggle_hud")))
- {
- show_hud = !show_hud;
- if(show_hud) {
- statustext = L"HUD shown";
- client.setHighlighted(client.getHighlighted(), true);
- } else {
- statustext = L"HUD hidden";
- client.setHighlighted(client.getHighlighted(), false);
- }
- statustext_time = 0;
- }
- else if(input->wasKeyDown(getKeySetting("keymap_toggle_chat")))
- {
- show_chat = !show_chat;
- if(show_chat)
- statustext = L"Chat shown";
- else
- statustext = L"Chat hidden";
- statustext_time = 0;
- }
- else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off")))
- {
- force_fog_off = !force_fog_off;
- if(force_fog_off)
- statustext = L"Fog disabled";
- else
- statustext = L"Fog enabled";
- statustext_time = 0;
- }
- else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera")))
- {
- disable_camera_update = !disable_camera_update;
- if(disable_camera_update)
- statustext = L"Camera update disabled";
- else
- statustext = L"Camera update enabled";
- statustext_time = 0;
- }
- else if(input->wasKeyDown(getKeySetting("keymap_toggle_debug")))
- {
- // Initial / 3x toggle: Chat only
- // 1x toggle: Debug text with chat
- // 2x toggle: Debug text with profiler graph
- if(!show_debug)
- {
- show_debug = true;
- show_profiler_graph = false;
- statustext = L"Debug info shown";
- statustext_time = 0;
- }
- else if(show_profiler_graph)
- {
- show_debug = false;
- show_profiler_graph = false;
- statustext = L"Debug info and profiler graph hidden";
- statustext_time = 0;
- }
- else
- {
- show_profiler_graph = true;
- statustext = L"Profiler graph shown";
- statustext_time = 0;
- }
- }
- else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler")))
- {
- show_profiler = (show_profiler + 1) % (show_profiler_max + 1);
-
- // FIXME: This updates the profiler with incomplete values
- update_profiler_gui(guitext_profiler, font, text_height,
- show_profiler, show_profiler_max);
-
- if(show_profiler != 0)
- {
- std::wstringstream sstr;
- sstr<<"Profiler shown (page "<<show_profiler
- <<" of "<<show_profiler_max<<")";
- statustext = sstr.str();
- statustext_time = 0;
- }
- else
- {
- statustext = L"Profiler hidden";
- statustext_time = 0;
- }
- }
- else if(input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min")))
- {
- s16 range = g_settings->getS16("viewing_range_nodes_min");
- s16 range_new = range + 10;
- g_settings->set("viewing_range_nodes_min", itos(range_new));
- statustext = narrow_to_wide(
- "Minimum viewing range changed to "
- + itos(range_new));
- statustext_time = 0;
- }
- else if(input->wasKeyDown(getKeySetting("keymap_decrease_viewing_range_min")))
- {
- s16 range = g_settings->getS16("viewing_range_nodes_min");
- s16 range_new = range - 10;
- if(range_new < 0)
- range_new = range;
- g_settings->set("viewing_range_nodes_min",
- itos(range_new));
- statustext = narrow_to_wide(
- "Minimum viewing range changed to "
- + itos(range_new));
- statustext_time = 0;
- }
+ f32 d = playeritem_def.range; // max. distance
+ f32 d_hand = itemdef_manager->get("").range;
- // Reset jump_timer
- if(!input->isKeyDown(getKeySetting("keymap_jump")) && reset_jump_timer)
- {
- reset_jump_timer = false;
- jump_timer = 0.0;
- }
+ if (d < 0 && d_hand >= 0)
+ d = d_hand;
+ else if (d < 0)
+ d = 4.0;
- // Handle QuicktuneShortcutter
- if(input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
- quicktune.next();
- if(input->wasKeyDown(getKeySetting("keymap_quicktune_prev")))
- quicktune.prev();
- if(input->wasKeyDown(getKeySetting("keymap_quicktune_inc")))
- quicktune.inc();
- if(input->wasKeyDown(getKeySetting("keymap_quicktune_dec")))
- quicktune.dec();
- {
- std::string msg = quicktune.getMessage();
- if(msg != ""){
- statustext = narrow_to_wide(msg);
- statustext_time = 0;
- }
- }
+ core::line3d<f32> shootline;
- // Item selection with mouse wheel
- u16 new_playeritem = client.getPlayerItem();
- {
- s32 wheel = input->getMouseWheel();
- u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
- player->hud_hotbar_itemcount-1);
-
- if(wheel < 0)
- {
- if(new_playeritem < max_item)
- new_playeritem++;
- else
- new_playeritem = 0;
- }
- else if(wheel > 0)
- {
- if(new_playeritem > 0)
- new_playeritem--;
- else
- new_playeritem = max_item;
- }
- }
+ if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) {
- // Item selection
- for(u16 i=0; i<10; i++)
- {
- const KeyPress *kp = NumberKey + (i + 1) % 10;
- if(input->wasKeyDown(*kp))
- {
- if(i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount)
- {
- new_playeritem = i;
-
- infostream<<"Selected item: "
- <<new_playeritem<<std::endl;
- }
- }
- }
+ shootline = core::line3d<f32>(camera_position,
+ camera_position + camera_direction * BS * (d + 1));
- // Viewing range selection
- if(input->wasKeyDown(getKeySetting("keymap_rangeselect")))
- {
- draw_control.range_all = !draw_control.range_all;
- if(draw_control.range_all)
- {
- infostream<<"Enabled full viewing range"<<std::endl;
- statustext = L"Enabled full viewing range";
- statustext_time = 0;
- }
- else
- {
- infostream<<"Disabled full viewing range"<<std::endl;
- statustext = L"Disabled full viewing range";
- statustext_time = 0;
- }
- }
+ } else {
+ // prevent player pointing anything in front-view
+ if (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT)
+ shootline = core::line3d<f32>(0, 0, 0, 0, 0, 0);
+ }
- // Print debug stacks
- if(input->wasKeyDown(getKeySetting("keymap_print_debug_stacks")))
- {
- dstream<<"-----------------------------------------"
- <<std::endl;
- dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
- dstream<<"-----------------------------------------"
- <<std::endl;
- debug_stacks_print();
- }
+#ifdef HAVE_TOUCHSCREENGUI
- /*
- Mouse and camera control
- NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame
- */
+ if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
+ shootline = g_touchscreengui->getShootline();
+ shootline.start += intToFloat(camera_offset, BS);
+ shootline.end += intToFloat(camera_offset, BS);
+ }
- float turn_amount = 0;
- if((device->isWindowActive() && noMenuActive()) || random_input)
- {
-#ifndef __ANDROID__
- if(!random_input)
- {
- // Mac OSX gets upset if this is set every frame
- if(device->getCursorControl()->isVisible())
- device->getCursorControl()->setVisible(false);
- }
#endif
- if(first_loop_after_window_activation){
- //infostream<<"window active, first loop"<<std::endl;
- first_loop_after_window_activation = false;
+ PointedThing pointed = getPointedThing(
+ // input
+ client, player_position, camera_direction,
+ camera_position, shootline, d,
+ playeritem_def.liquids_pointable,
+ !interactArgs->ldown_for_dig,
+ camera_offset,
+ // output
+ highlight_boxes,
+ interactArgs->selected_object);
+
+ if (pointed != interactArgs->pointed_old) {
+ infostream << "Pointing at " << pointed.dump() << std::endl;
+
+ if (g_settings->getBool("enable_node_highlighting")) {
+ if (pointed.type == POINTEDTHING_NODE) {
+ client->setHighlighted(pointed.node_undersurface, show_hud);
} else {
-#ifdef HAVE_TOUCHSCREENGUI
- if (g_touchscreengui) {
- camera_yaw = g_touchscreengui->getYaw();
- camera_pitch = g_touchscreengui->getPitch();
- } else {
-#endif
- s32 dx = input->getMousePos().X - (driver->getScreenSize().Width/2);
- s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2);
- if ((invert_mouse)
- || (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)) {
- dy = -dy;
- }
- //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
-
- /*const float keyspeed = 500;
- if(input->isKeyDown(irr::KEY_UP))
- dy -= dtime * keyspeed;
- if(input->isKeyDown(irr::KEY_DOWN))
- dy += dtime * keyspeed;
- if(input->isKeyDown(irr::KEY_LEFT))
- dx -= dtime * keyspeed;
- if(input->isKeyDown(irr::KEY_RIGHT))
- dx += dtime * keyspeed;*/
-
- float d = g_settings->getFloat("mouse_sensitivity");
- d = rangelim(d, 0.01, 100.0);
- camera_yaw -= dx*d;
- camera_pitch += dy*d;
- turn_amount = v2f(dx, dy).getLength() * d;
-
-#ifdef HAVE_TOUCHSCREENGUI
- }
-#endif
- if(camera_pitch < -89.5) camera_pitch = -89.5;
- if(camera_pitch > 89.5) camera_pitch = 89.5;
+ client->setHighlighted(pointed.node_undersurface, false);
}
- input->setMousePos((driver->getScreenSize().Width/2),
- (driver->getScreenSize().Height/2));
- }
- else{
-#ifndef ANDROID
- // Mac OSX gets upset if this is set every frame
- if(device->getCursorControl()->isVisible() == false)
- device->getCursorControl()->setVisible(true);
-#endif
-
- //infostream<<"window inactive"<<std::endl;
- first_loop_after_window_activation = true;
}
- recent_turn_speed = recent_turn_speed * 0.9 + turn_amount * 0.1;
- //std::cerr<<"recent_turn_speed = "<<recent_turn_speed<<std::endl;
+ }
- /*
- Player speed control
- */
- {
- /*bool a_up,
- bool a_down,
- bool a_left,
- bool a_right,
- bool a_jump,
- bool a_superspeed,
- bool a_sneak,
- bool a_LMB,
- bool a_RMB,
- float a_pitch,
- float a_yaw*/
- PlayerControl control(
- input->isKeyDown(getKeySetting("keymap_forward")),
- input->isKeyDown(getKeySetting("keymap_backward")),
- input->isKeyDown(getKeySetting("keymap_left")),
- input->isKeyDown(getKeySetting("keymap_right")),
- input->isKeyDown(getKeySetting("keymap_jump")),
- input->isKeyDown(getKeySetting("keymap_special1")),
- input->isKeyDown(getKeySetting("keymap_sneak")),
- input->getLeftState(),
- input->getRightState(),
- camera_pitch,
- camera_yaw
- );
- client.setPlayerControl(control);
- LocalPlayer* player = client.getEnv().getLocalPlayer();
- player->keyPressed=
- (((int)input->isKeyDown(getKeySetting("keymap_forward")) & 0x1) << 0) |
- (((int)input->isKeyDown(getKeySetting("keymap_backward")) & 0x1) << 1) |
- (((int)input->isKeyDown(getKeySetting("keymap_left")) & 0x1) << 2) |
- (((int)input->isKeyDown(getKeySetting("keymap_right")) & 0x1) << 3) |
- (((int)input->isKeyDown(getKeySetting("keymap_jump")) & 0x1) << 4) |
- (((int)input->isKeyDown(getKeySetting("keymap_special1")) & 0x1) << 5) |
- (((int)input->isKeyDown(getKeySetting("keymap_sneak")) & 0x1) << 6) |
- (((int)input->getLeftState() & 0x1) << 7) |
- (((int)input->getRightState() & 0x1) << 8);
+ /*
+ Stop digging when
+ - releasing left mouse button
+ - pointing away from node
+ */
+ if (interactArgs->digging) {
+ if (input->getLeftReleased()) {
+ infostream << "Left button released"
+ << " (stopped digging)" << std::endl;
+ interactArgs->digging = false;
+ } else if (pointed != interactArgs->pointed_old) {
+ if (pointed.type == POINTEDTHING_NODE
+ && interactArgs->pointed_old.type == POINTEDTHING_NODE
+ && pointed.node_undersurface
+ == interactArgs->pointed_old.node_undersurface) {
+ // Still pointing to the same node, but a different face.
+ // Don't reset.
+ } else {
+ infostream << "Pointing away from node"
+ << " (stopped digging)" << std::endl;
+ interactArgs->digging = false;
+ }
}
- /*
- Run server, client (and process environments)
- */
- bool can_be_and_is_paused =
- (simple_singleplayer_mode && g_menumgr.pausesGame());
- if(can_be_and_is_paused)
- {
- // No time passes
- dtime = 0;
- }
- else
- {
- if(server != NULL)
- {
- //TimeTaker timer("server->step(dtime)");
- server->step(dtime);
- }
- {
- //TimeTaker timer("client.step(dtime)");
- client.step(dtime);
- }
+ if (!interactArgs->digging) {
+ client->interact(1, interactArgs->pointed_old);
+ client->setCrack(-1, v3s16(0, 0, 0));
+ interactArgs->dig_time = 0.0;
}
+ }
- {
- // Read client events
- for(;;) {
- ClientEvent event = client.getClientEvent();
- if(event.type == CE_NONE) {
- break;
- }
- else if(event.type == CE_PLAYER_DAMAGE &&
- client.getHP() != 0) {
- //u16 damage = event.player_damage.amount;
- //infostream<<"Player damage: "<<damage<<std::endl;
+ if (!interactArgs->digging && interactArgs->ldown_for_dig && !input->getLeftState()) {
+ interactArgs->ldown_for_dig = false;
+ }
- damage_flash += 100.0;
- damage_flash += 8.0 * event.player_damage.amount;
+ interactArgs->left_punch = false;
- player->hurt_tilt_timer = 1.5;
- player->hurt_tilt_strength = event.player_damage.amount/4;
- player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0);
+ soundmaker->m_player_leftpunch_sound.name = "";
- MtEvent *e = new SimpleTriggerEvent("PlayerDamage");
- gamedef->event()->put(e);
- }
- else if(event.type == CE_PLAYER_FORCE_MOVE) {
- camera_yaw = event.player_force_move.yaw;
- camera_pitch = event.player_force_move.pitch;
- }
- else if(event.type == CE_DEATHSCREEN) {
- show_deathscreen(¤t_formspec, &client, gamedef, tsrc,
- device, &client);
+ if (input->getRightState())
+ interactArgs->repeat_rightclick_timer += dtime;
+ else
+ interactArgs->repeat_rightclick_timer = 0;
- chat_backend.addMessage(L"", L"You died.");
+ if (playeritem_def.usable && input->getLeftState()) {
+ if (input->getLeftClicked())
+ client->interact(4, pointed);
+ } else if (pointed.type == POINTEDTHING_NODE) {
+ ToolCapabilities playeritem_toolcap =
+ playeritem.getToolCapabilities(itemdef_manager);
+ handlePointingAtNode(interactArgs, pointed, playeritem_def,
+ playeritem_toolcap, dtime);
+ } else if (pointed.type == POINTEDTHING_OBJECT) {
+ handlePointingAtObject(interactArgs, pointed, playeritem,
+ player_position, show_debug);
+ } else if (input->getLeftState()) {
+ // When button is held down in air, show continuous animation
+ interactArgs->left_punch = true;
+ }
- /* Handle visualization */
- damage_flash = 0;
+ interactArgs->pointed_old = pointed;
- LocalPlayer* player = client.getEnv().getLocalPlayer();
- player->hurt_tilt_timer = 0;
- player->hurt_tilt_strength = 0;
+ if (interactArgs->left_punch || input->getLeftClicked())
+ camera->setDigging(0); // left click animation
- }
- else if (event.type == CE_SHOW_FORMSPEC) {
- FormspecFormSource* fs_src =
- new FormspecFormSource(*(event.show_formspec.formspec));
- TextDestPlayerInventory* txt_dst =
- new TextDestPlayerInventory(&client,*(event.show_formspec.formname));
+ input->resetLeftClicked();
+ input->resetRightClicked();
- create_formspec_menu(¤t_formspec, &client, gamedef,
- tsrc, device, fs_src, txt_dst, &client);
+ input->resetLeftReleased();
+ input->resetRightReleased();
+}
- delete(event.show_formspec.formspec);
- delete(event.show_formspec.formname);
- }
- else if(event.type == CE_SPAWN_PARTICLE) {
- LocalPlayer* player = client.getEnv().getLocalPlayer();
- video::ITexture *texture =
- gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
-
- new Particle(gamedef, smgr, player, client.getEnv(),
- *event.spawn_particle.pos,
- *event.spawn_particle.vel,
- *event.spawn_particle.acc,
- event.spawn_particle.expirationtime,
- event.spawn_particle.size,
- event.spawn_particle.collisiondetection,
- event.spawn_particle.vertical,
- texture,
- v2f(0.0, 0.0),
- v2f(1.0, 1.0));
- }
- else if(event.type == CE_ADD_PARTICLESPAWNER) {
- LocalPlayer* player = client.getEnv().getLocalPlayer();
- video::ITexture *texture =
- gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
-
- new ParticleSpawner(gamedef, smgr, player,
- event.add_particlespawner.amount,
- event.add_particlespawner.spawntime,
- *event.add_particlespawner.minpos,
- *event.add_particlespawner.maxpos,
- *event.add_particlespawner.minvel,
- *event.add_particlespawner.maxvel,
- *event.add_particlespawner.minacc,
- *event.add_particlespawner.maxacc,
- event.add_particlespawner.minexptime,
- event.add_particlespawner.maxexptime,
- event.add_particlespawner.minsize,
- event.add_particlespawner.maxsize,
- event.add_particlespawner.collisiondetection,
- event.add_particlespawner.vertical,
- texture,
- event.add_particlespawner.id);
- }
- else if(event.type == CE_DELETE_PARTICLESPAWNER) {
- delete_particlespawner (event.delete_particlespawner.id);
- }
- else if (event.type == CE_HUDADD) {
- u32 id = event.hudadd.id;
-
- HudElement *e = player->getHud(id);
-
- if (e != NULL) {
- delete event.hudadd.pos;
- delete event.hudadd.name;
- delete event.hudadd.scale;
- delete event.hudadd.text;
- delete event.hudadd.align;
- delete event.hudadd.offset;
- delete event.hudadd.world_pos;
- delete event.hudadd.size;
- continue;
- }
- e = new HudElement;
- e->type = (HudElementType)event.hudadd.type;
- e->pos = *event.hudadd.pos;
- e->name = *event.hudadd.name;
- e->scale = *event.hudadd.scale;
- e->text = *event.hudadd.text;
- e->number = event.hudadd.number;
- e->item = event.hudadd.item;
- e->dir = event.hudadd.dir;
- e->align = *event.hudadd.align;
- e->offset = *event.hudadd.offset;
- e->world_pos = *event.hudadd.world_pos;
- e->size = *event.hudadd.size;
-
- u32 new_id = player->addHud(e);
- //if this isn't true our huds aren't consistent
- assert(new_id == id);
-
- delete event.hudadd.pos;
- delete event.hudadd.name;
- delete event.hudadd.scale;
- delete event.hudadd.text;
- delete event.hudadd.align;
- delete event.hudadd.offset;
- delete event.hudadd.world_pos;
- delete event.hudadd.size;
- }
- else if (event.type == CE_HUDRM) {
- HudElement* e = player->removeHud(event.hudrm.id);
+void MinetestApp::handlePointingAtNode(InteractParams *interactArgs,
+ const PointedThing &pointed, const ItemDefinition &playeritem_def,
+ const ToolCapabilities &playeritem_toolcap, f32 dtime)
+{
+ v3s16 nodepos = pointed.node_undersurface;
+ v3s16 neighbourpos = pointed.node_abovesurface;
- if (e != NULL)
- delete (e);
- }
- else if (event.type == CE_HUDCHANGE) {
- u32 id = event.hudchange.id;
- HudElement* e = player->getHud(id);
- if (e == NULL)
- {
- delete event.hudchange.v3fdata;
- delete event.hudchange.v2fdata;
- delete event.hudchange.sdata;
- delete event.hudchange.v2s32data;
- continue;
- }
+ /*
+ Check information text of node
+ */
- switch (event.hudchange.stat) {
- case HUD_STAT_POS:
- e->pos = *event.hudchange.v2fdata;
- break;
- case HUD_STAT_NAME:
- e->name = *event.hudchange.sdata;
- break;
- case HUD_STAT_SCALE:
- e->scale = *event.hudchange.v2fdata;
- break;
- case HUD_STAT_TEXT:
- e->text = *event.hudchange.sdata;
- break;
- case HUD_STAT_NUMBER:
- e->number = event.hudchange.data;
- break;
- case HUD_STAT_ITEM:
- e->item = event.hudchange.data;
- break;
- case HUD_STAT_DIR:
- e->dir = event.hudchange.data;
- break;
- case HUD_STAT_ALIGN:
- e->align = *event.hudchange.v2fdata;
- break;
- case HUD_STAT_OFFSET:
- e->offset = *event.hudchange.v2fdata;
- break;
- case HUD_STAT_WORLD_POS:
- e->world_pos = *event.hudchange.v3fdata;
- break;
- case HUD_STAT_SIZE:
- e->size = *event.hudchange.v2s32data;
- break;
- }
+ ClientMap &map = client->getEnv().getClientMap();
+ NodeMetadata *meta = map.getNodeMetadata(nodepos);
- delete event.hudchange.v3fdata;
- delete event.hudchange.v2fdata;
- delete event.hudchange.sdata;
- delete event.hudchange.v2s32data;
- }
- else if (event.type == CE_SET_SKY) {
- sky->setVisible(false);
- if(skybox){
- skybox->remove();
- skybox = NULL;
- }
- // Handle according to type
- if(*event.set_sky.type == "regular") {
- sky->setVisible(true);
- }
- else if(*event.set_sky.type == "skybox" &&
- event.set_sky.params->size() == 6) {
- sky->setFallbackBgColor(*event.set_sky.bgcolor);
- skybox = smgr->addSkyBoxSceneNode(
- tsrc->getTexture((*event.set_sky.params)[0]),
- tsrc->getTexture((*event.set_sky.params)[1]),
- tsrc->getTexture((*event.set_sky.params)[2]),
- tsrc->getTexture((*event.set_sky.params)[3]),
- tsrc->getTexture((*event.set_sky.params)[4]),
- tsrc->getTexture((*event.set_sky.params)[5]));
- }
- // Handle everything else as plain color
- else {
- if(*event.set_sky.type != "plain")
- infostream<<"Unknown sky type: "
- <<(*event.set_sky.type)<<std::endl;
- sky->setFallbackBgColor(*event.set_sky.bgcolor);
- }
+ if (meta) {
+ infotext = narrow_to_wide(meta->getString("infotext"));
+ } else {
+ MapNode n = map.getNode(nodepos);
- delete event.set_sky.bgcolor;
- delete event.set_sky.type;
- delete event.set_sky.params;
- }
- else if (event.type == CE_OVERRIDE_DAY_NIGHT_RATIO) {
- bool enable = event.override_day_night_ratio.do_override;
- u32 value = event.override_day_night_ratio.ratio_f * 1000;
- client.getEnv().setDayNightRatioOverride(enable, value);
- }
- }
+ if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
+ infotext = L"Unknown node: ";
+ infotext += narrow_to_wide(nodedef_manager->get(n).name);
}
+ }
- //TimeTaker //timer2("//timer2");
+ if (interactArgs->nodig_delay_timer <= 0.0 && input->getLeftState()
+ && client->checkPrivilege("interact")) {
+ handleDigging(interactArgs, pointed, nodepos, playeritem_toolcap, dtime);
+ }
- /*
- For interaction purposes, get info about the held item
- - What item is it?
- - Is it a usable item?
- - Can it point to liquids?
- */
- ItemStack playeritem;
- {
- InventoryList *mlist = local_inventory.getList("main");
- if((mlist != NULL) && (client.getPlayerItem() < mlist->getSize()))
- playeritem = mlist->getItem(client.getPlayerItem());
- }
- const ItemDefinition &playeritem_def =
- playeritem.getDefinition(itemdef);
- ToolCapabilities playeritem_toolcap =
- playeritem.getToolCapabilities(itemdef);
+ if ((input->getRightClicked() ||
+ interactArgs->repeat_rightclick_timer >=
+ g_settings->getFloat("repeat_rightclick_time")) &&
+ client->checkPrivilege("interact")) {
+ interactArgs->repeat_rightclick_timer = 0;
+ infostream << "Ground right-clicked" << std::endl;
- /*
- Update camera
- */
+ if (meta && meta->getString("formspec") != "" && !random_input
+ && !input->isKeyDown(getKeySetting("keymap_sneak"))) {
+ infostream << "Launching custom inventory view" << std::endl;
- v3s16 old_camera_offset = camera.getOffset();
+ InventoryLocation inventoryloc;
+ inventoryloc.setNodeMeta(nodepos);
- LocalPlayer* player = client.getEnv().getLocalPlayer();
- float full_punch_interval = playeritem_toolcap.full_punch_interval;
- float tool_reload_ratio = time_from_last_punch / full_punch_interval;
+ NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
+ &client->getEnv().getClientMap(), nodepos);
+ TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
- if(input->wasKeyDown(getKeySetting("keymap_camera_mode"))) {
- camera.toggleCameraMode();
- GenericCAO* playercao = player->getCAO();
+ create_formspec_menu(¤t_formspec, client, gamedef,
+ texture_src, device, fs_src, txt_dst, client);
- assert( playercao != NULL );
- if (camera.getCameraMode() > CAMERA_MODE_FIRST) {
- playercao->setVisible(true);
- }
- else {
- playercao->setVisible(false);
+ current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
+ } else {
+ // Report right click to server
+
+ camera->setDigging(1); // right click animation (always shown for feedback)
+
+ // If the wielded item has node placement prediction,
+ // make that happen
+ bool placed = nodePlacementPrediction(*client,
+ playeritem_def,
+ nodepos, neighbourpos);
+
+ if (placed) {
+ // Report to server
+ client->interact(3, pointed);
+ // Read the sound
+ soundmaker->m_player_rightpunch_sound =
+ playeritem_def.sound_place;
+ } else {
+ soundmaker->m_player_rightpunch_sound =
+ SimpleSoundSpec();
}
+
+ if (playeritem_def.node_placement_prediction == "" ||
+ nodedef_manager->get(map.getNode(nodepos)).rightclickable)
+ client->interact(3, pointed); // Report to server
}
- tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
- camera.update(player, dtime, busytime, tool_reload_ratio,
- client.getEnv());
- camera.step(dtime);
+ }
+}
- v3f player_position = player->getPosition();
- v3f camera_position = camera.getPosition();
- v3f camera_direction = camera.getDirection();
- f32 camera_fov = camera.getFovMax();
- v3s16 camera_offset = camera.getOffset();
- bool camera_offset_changed = (camera_offset != old_camera_offset);
+void MinetestApp::handlePointingAtObject(InteractParams *interactArgs,
+ const PointedThing &pointed,
+ const ItemStack &playeritem,
+ const v3f &player_position,
+ bool show_debug)
+{
+ infotext = narrow_to_wide(interactArgs->selected_object->infoText());
- if(!disable_camera_update){
- client.getEnv().getClientMap().updateCamera(camera_position,
- camera_direction, camera_fov, camera_offset);
- if (camera_offset_changed){
- client.updateCameraOffset(camera_offset);
- client.getEnv().updateCameraOffset(camera_offset);
- if (clouds)
- clouds->updateCameraOffset(camera_offset);
- }
- }
+ if (infotext == L"" && show_debug) {
+ infotext = narrow_to_wide(interactArgs->selected_object->debugInfoText());
+ }
- // Update sound listener
- sound->updateListener(camera.getCameraNode()->getPosition()+intToFloat(camera_offset, BS),
- v3f(0,0,0), // velocity
- camera.getDirection(),
- camera.getCameraNode()->getUpVector());
- sound->setListenerGain(g_settings->getFloat("sound_volume"));
+ if (input->getLeftState()) {
+ bool do_punch = false;
+ bool do_punch_damage = false;
- /*
- Update sound maker
- */
- {
- soundmaker.step(dtime);
+ if (interactArgs->object_hit_delay_timer <= 0.0) {
+ do_punch = true;
+ do_punch_damage = true;
+ interactArgs->object_hit_delay_timer = object_hit_delay;
+ }
+
+ if (input->getLeftClicked())
+ do_punch = true;
- ClientMap &map = client.getEnv().getClientMap();
- MapNode n = map.getNodeNoEx(player->getStandingNodePos());
- soundmaker.m_player_step_sound = nodedef->get(n).sound_footstep;
+ if (do_punch) {
+ infostream << "Left-clicked object" << std::endl;
+ interactArgs->left_punch = true;
}
- /*
- Calculate what block is the crosshair pointing to
- */
+ if (do_punch_damage) {
+ // Report direct punch
+ v3f objpos = interactArgs->selected_object->getPosition();
+ v3f dir = (objpos - player_position).normalize();
- //u32 t1 = device->getTimer()->getRealTime();
+ bool disable_send = interactArgs->selected_object->directReportPunch(
+ dir, &playeritem, interactArgs->time_from_last_punch);
+ interactArgs->time_from_last_punch = 0;
- f32 d = playeritem_def.range; // max. distance
- f32 d_hand = itemdef->get("").range;
- if(d < 0 && d_hand >= 0)
- d = d_hand;
- else if(d < 0)
- d = 4.0;
- core::line3d<f32> shootline(camera_position,
- camera_position + camera_direction * BS * (d+1));
+ if (!disable_send)
+ client->interact(0, pointed);
+ }
+ } else if (input->getRightClicked()) {
+ infostream << "Right-clicked object" << std::endl;
+ client->interact(3, pointed); // place
+ }
+}
- // prevent player pointing anything in front-view
- if (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)
- shootline = core::line3d<f32>(0,0,0,0,0,0);
+void MinetestApp::handleDigging(InteractParams *interactArgs,
+ const PointedThing &pointed, const v3s16 &nodepos,
+ const ToolCapabilities &playeritem_toolcap, f32 dtime)
+{
+ if (!interactArgs->digging) {
+ infostream << "Started digging" << std::endl;
+ client->interact(0, pointed);
+ interactArgs->digging = true;
+ interactArgs->ldown_for_dig = true;
+ }
-#ifdef HAVE_TOUCHSCREENGUI
- if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
- shootline = g_touchscreengui->getShootline();
- shootline.start += intToFloat(camera_offset,BS);
- shootline.end += intToFloat(camera_offset,BS);
- }
-#endif
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ ClientMap &map = client->getEnv().getClientMap();
+ MapNode n = client->getEnv().getClientMap().getNode(nodepos);
- ClientActiveObject *selected_object = NULL;
-
- PointedThing pointed = getPointedThing(
- // input
- &client, player_position, camera_direction,
- camera_position, shootline, d,
- playeritem_def.liquids_pointable, !ldown_for_dig,
- camera_offset,
- // output
- hilightboxes,
- selected_object);
-
- if(pointed != pointed_old)
- {
- infostream<<"Pointing at "<<pointed.dump()<<std::endl;
- if (g_settings->getBool("enable_node_highlighting")) {
- if (pointed.type == POINTEDTHING_NODE) {
- client.setHighlighted(pointed.node_undersurface, show_hud);
- } else {
- client.setHighlighted(pointed.node_undersurface, false);
- }
- }
- }
+ // NOTE: Similar piece of code exists on the server side for
+ // cheat detection.
+ // Get digging parameters
+ DigParams params = getDigParams(nodedef_manager->get(n).groups,
+ &playeritem_toolcap);
- /*
- Stop digging when
- - releasing left mouse button
- - pointing away from node
- */
- if(digging)
- {
- if(input->getLeftReleased())
- {
- infostream<<"Left button released"
- <<" (stopped digging)"<<std::endl;
- digging = false;
- }
- else if(pointed != pointed_old)
- {
- if (pointed.type == POINTEDTHING_NODE
- && pointed_old.type == POINTEDTHING_NODE
- && pointed.node_undersurface == pointed_old.node_undersurface)
- {
- // Still pointing to the same node,
- // but a different face. Don't reset.
- }
- else
- {
- infostream<<"Pointing away from node"
- <<" (stopped digging)"<<std::endl;
- digging = false;
- }
- }
- if(!digging)
- {
- client.interact(1, pointed_old);
- client.setCrack(-1, v3s16(0,0,0));
- dig_time = 0.0;
- }
- }
- if(!digging && ldown_for_dig && !input->getLeftState())
- {
- ldown_for_dig = false;
- }
+ // If can't dig, try hand
+ if (!params.diggable) {
+ const ItemDefinition &hand = itemdef_manager->get("");
+ const ToolCapabilities *tp = hand.tool_capabilities;
- bool left_punch = false;
- soundmaker.m_player_leftpunch_sound.name = "";
+ if (tp)
+ params = getDigParams(nodedef_manager->get(n).groups, tp);
+ }
- if(input->getRightState())
- repeat_rightclick_timer += dtime;
- else
- repeat_rightclick_timer = 0;
+ if (params.diggable == false) {
+ // I guess nobody will wait for this long
+ interactArgs->dig_time_complete = 10000000.0;
+ } else {
+ interactArgs->dig_time_complete = params.time;
- if(playeritem_def.usable && input->getLeftState())
- {
- if(input->getLeftClicked())
- client.interact(4, pointed);
+ if (g_settings->getBool("enable_particles")) {
+ const ContentFeatures &features =
+ client->getNodeDefManager()->get(n);
+ addPunchingParticles(gamedef, smgr, player,
+ client->getEnv(), nodepos, features.tiles);
}
- else if(pointed.type == POINTEDTHING_NODE)
- {
- v3s16 nodepos = pointed.node_undersurface;
- v3s16 neighbourpos = pointed.node_abovesurface;
-
- /*
- Check information text of node
- */
-
- ClientMap &map = client.getEnv().getClientMap();
- NodeMetadata *meta = map.getNodeMetadata(nodepos);
- if(meta){
- infotext = narrow_to_wide(meta->getString("infotext"));
- } else {
- MapNode n = map.getNode(nodepos);
- if(nodedef->get(n).tiledef[0].name == "unknown_node.png"){
- infotext = L"Unknown node: ";
- infotext += narrow_to_wide(nodedef->get(n).name);
- }
- }
+ }
- /*
- Handle digging
- */
-
- if(nodig_delay_timer <= 0.0 && input->getLeftState()
- && client.checkPrivilege("interact"))
- {
- if(!digging)
- {
- infostream<<"Started digging"<<std::endl;
- client.interact(0, pointed);
- digging = true;
- ldown_for_dig = true;
- }
- MapNode n = client.getEnv().getClientMap().getNode(nodepos);
-
- // NOTE: Similar piece of code exists on the server side for
- // cheat detection.
- // Get digging parameters
- DigParams params = getDigParams(nodedef->get(n).groups,
- &playeritem_toolcap);
- // If can't dig, try hand
- if(!params.diggable){
- const ItemDefinition &hand = itemdef->get("");
- const ToolCapabilities *tp = hand.tool_capabilities;
- if(tp)
- params = getDigParams(nodedef->get(n).groups, tp);
- }
+ if (interactArgs->dig_time_complete >= 0.001) {
+ interactArgs->dig_index = (float)crack_animation_length
+ * interactArgs->dig_time
+ / interactArgs->dig_time_complete;
+ } else {
+ // This is for torches
+ interactArgs->dig_index = crack_animation_length;
+ }
- float dig_time_complete = 0.0;
+ SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig;
- if(params.diggable == false)
- {
- // I guess nobody will wait for this long
- dig_time_complete = 10000000.0;
- }
- else
- {
- dig_time_complete = params.time;
- if (g_settings->getBool("enable_particles"))
- {
- const ContentFeatures &features =
- client.getNodeDefManager()->get(n);
- addPunchingParticles
- (gamedef, smgr, player, client.getEnv(),
- nodepos, features.tiles);
- }
- }
+ if (sound_dig.exists() && params.diggable) {
+ if (sound_dig.name == "__group") {
+ if (params.main_group != "") {
+ soundmaker->m_player_leftpunch_sound.gain = 0.5;
+ soundmaker->m_player_leftpunch_sound.name =
+ std::string("default_dig_") +
+ params.main_group;
+ }
+ } else {
+ soundmaker->m_player_leftpunch_sound = sound_dig;
+ }
+ }
- if(dig_time_complete >= 0.001)
- {
- dig_index = (u16)((float)crack_animation_length
- * dig_time/dig_time_complete);
- }
- // This is for torches
- else
- {
- dig_index = crack_animation_length;
- }
+ // Don't show cracks if not diggable
+ if (interactArgs->dig_time_complete >= 100000.0) {
+ } else if (interactArgs->dig_index < crack_animation_length) {
+ //TimeTaker timer("client.setTempMod");
+ //infostream<<"dig_index="<<dig_index<<std::endl;
+ client->setCrack(interactArgs->dig_index, nodepos);
+ } else {
+ infostream << "Digging completed" << std::endl;
+ client->interact(2, pointed);
+ client->setCrack(-1, v3s16(0, 0, 0));
+ MapNode wasnode = map.getNode(nodepos);
+ client->removeNode(nodepos);
- SimpleSoundSpec sound_dig = nodedef->get(n).sound_dig;
- if(sound_dig.exists() && params.diggable){
- if(sound_dig.name == "__group"){
- if(params.main_group != ""){
- soundmaker.m_player_leftpunch_sound.gain = 0.5;
- soundmaker.m_player_leftpunch_sound.name =
- std::string("default_dig_") +
- params.main_group;
- }
- } else{
- soundmaker.m_player_leftpunch_sound = sound_dig;
- }
- }
+ if (g_settings->getBool("enable_particles")) {
+ const ContentFeatures &features =
+ client->getNodeDefManager()->get(wasnode);
+ addDiggingParticles
+ (gamedef, smgr, player, client->getEnv(),
+ nodepos, features.tiles);
+ }
- // Don't show cracks if not diggable
- if(dig_time_complete >= 100000.0)
- {
- }
- else if(dig_index < crack_animation_length)
- {
- //TimeTaker timer("client.setTempMod");
- //infostream<<"dig_index="<<dig_index<<std::endl;
- client.setCrack(dig_index, nodepos);
- }
- else
- {
- infostream<<"Digging completed"<<std::endl;
- client.interact(2, pointed);
- client.setCrack(-1, v3s16(0,0,0));
- MapNode wasnode = map.getNode(nodepos);
- client.removeNode(nodepos);
-
- if (g_settings->getBool("enable_particles"))
- {
- const ContentFeatures &features =
- client.getNodeDefManager()->get(wasnode);
- addDiggingParticles
- (gamedef, smgr, player, client.getEnv(),
- nodepos, features.tiles);
- }
+ interactArgs->dig_time = 0;
+ interactArgs->digging = false;
- dig_time = 0;
- digging = false;
-
- nodig_delay_timer = dig_time_complete
- / (float)crack_animation_length;
-
- // We don't want a corresponding delay to
- // very time consuming nodes
- if(nodig_delay_timer > 0.3)
- nodig_delay_timer = 0.3;
- // We want a slight delay to very little
- // time consuming nodes
- float mindelay = 0.15;
- if(nodig_delay_timer < mindelay)
- nodig_delay_timer = mindelay;
-
- // Send event to trigger sound
- MtEvent *e = new NodeDugEvent(nodepos, wasnode);
- gamedef->event()->put(e);
- }
+ interactArgs->nodig_delay_timer =
+ interactArgs->dig_time_complete / (float)crack_animation_length;
- if(dig_time_complete < 100000.0)
- dig_time += dtime;
- else {
- dig_time = 0;
- client.setCrack(-1, nodepos);
- }
+ // We don't want a corresponding delay to
+ // very time consuming nodes
+ if (interactArgs->nodig_delay_timer > 0.3)
+ interactArgs->nodig_delay_timer = 0.3;
- camera.setDigging(0); // left click animation
- }
+ // We want a slight delay to very little
+ // time consuming nodes
+ const float mindelay = 0.15;
- if((input->getRightClicked() ||
- repeat_rightclick_timer >=
- g_settings->getFloat("repeat_rightclick_time")) &&
- client.checkPrivilege("interact"))
- {
- repeat_rightclick_timer = 0;
- infostream<<"Ground right-clicked"<<std::endl;
+ if (interactArgs->nodig_delay_timer < mindelay)
+ interactArgs->nodig_delay_timer = mindelay;
- if(meta && meta->getString("formspec") != "" && !random_input
- && !input->isKeyDown(getKeySetting("keymap_sneak")))
- {
- infostream<<"Launching custom inventory view"<<std::endl;
+ // Send event to trigger sound
+ MtEvent *e = new NodeDugEvent(nodepos, wasnode);
+ gamedef->event()->put(e);
+ }
- InventoryLocation inventoryloc;
- inventoryloc.setNodeMeta(nodepos);
+ if (interactArgs->dig_time_complete < 100000.0) {
+ interactArgs->dig_time += dtime;
+ } else {
+ interactArgs->dig_time = 0;
+ client->setCrack(-1, nodepos);
+ }
- NodeMetadataFormSource* fs_src = new NodeMetadataFormSource(
- &client.getEnv().getClientMap(), nodepos);
- TextDest* txt_dst = new TextDestNodeMetadata(nodepos, &client);
+ camera->setDigging(0); // left click animation
+}
- create_formspec_menu(¤t_formspec, &client, gamedef,
- tsrc, device, fs_src, txt_dst, &client);
- current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
- }
- // Otherwise report right click to server
- else
- {
- camera.setDigging(1); // right click animation (always shown for feedback)
-
- // If the wielded item has node placement prediction,
- // make that happen
- bool placed = nodePlacementPrediction(client,
- playeritem_def,
- nodepos, neighbourpos);
-
- if(placed) {
- // Report to server
- client.interact(3, pointed);
- // Read the sound
- soundmaker.m_player_rightpunch_sound =
- playeritem_def.sound_place;
- } else {
- soundmaker.m_player_rightpunch_sound =
- SimpleSoundSpec();
- }
+void MinetestApp::updateFrame(std::vector<aabb3f> &highlight_boxes,
+ ProfilerGraph *graph, RunStats *stats, InteractParams *interactArgs,
+ f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam)
+{
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
- if (playeritem_def.node_placement_prediction == "" ||
- nodedef->get(map.getNode(nodepos)).rightclickable)
- client.interact(3, pointed); // Report to server
- }
- }
- }
- else if(pointed.type == POINTEDTHING_OBJECT)
- {
- infotext = narrow_to_wide(selected_object->infoText());
+ /*
+ Fog range
+ */
- if(infotext == L"" && show_debug){
- infotext = narrow_to_wide(selected_object->debugInfoText());
- }
+ if (draw_control->range_all) {
+ interactArgs->fog_range = 100000 * BS;
+ } else {
+ interactArgs->fog_range = draw_control->wanted_range * BS
+ + 0.0 * MAP_BLOCKSIZE * BS;
+ interactArgs->fog_range = MYMIN(
+ interactArgs->fog_range,
+ (draw_control->farthest_drawn + 20) * BS);
+ interactArgs->fog_range *= 0.9;
+ }
- //if(input->getLeftClicked())
- if(input->getLeftState())
- {
- bool do_punch = false;
- bool do_punch_damage = false;
- if(object_hit_delay_timer <= 0.0){
- do_punch = true;
- do_punch_damage = true;
- object_hit_delay_timer = object_hit_delay;
- }
- if(input->getLeftClicked()){
- do_punch = true;
- }
- if(do_punch){
- infostream<<"Left-clicked object"<<std::endl;
- left_punch = true;
- }
- if(do_punch_damage){
- // Report direct punch
- v3f objpos = selected_object->getPosition();
- v3f dir = (objpos - player_position).normalize();
-
- bool disable_send = selected_object->directReportPunch(
- dir, &playeritem, time_from_last_punch);
- time_from_last_punch = 0;
- if(!disable_send)
- client.interact(0, pointed);
- }
- }
- else if(input->getRightClicked())
- {
- infostream<<"Right-clicked object"<<std::endl;
- client.interact(3, pointed); // place
- }
- }
- else if(input->getLeftState())
- {
- // When button is held down in air, show continuous animation
- left_punch = true;
- }
+ /*
+ Calculate general brightness
+ */
+ u32 daynight_ratio = client->getEnv().getDayNightRatio();
+ float time_brightness = decode_light_f((float)daynight_ratio / 1000.0);
+ float direct_brightness = 0;
+ bool sunlight_seen = false;
+
+ if (g_settings->getBool("free_move")) {
+ direct_brightness = time_brightness;
+ sunlight_seen = true;
+ } else {
+ ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
+ float old_brightness = sky->getBrightness();
+ direct_brightness = client->getEnv().getClientMap()
+ .getBackgroundBrightness(MYMIN(interactArgs->fog_range * 1.2, 60 * BS),
+ daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
+ / 255.0;
+ }
- pointed_old = pointed;
+ float time_of_day = 0;
+ float time_of_day_smooth = 0;
- if(left_punch || input->getLeftClicked())
- {
- camera.setDigging(0); // left click animation
- }
+ time_of_day = client->getEnv().getTimeOfDayF();
- input->resetLeftClicked();
- input->resetRightClicked();
+ const float maxsm = 0.05;
- input->resetLeftReleased();
- input->resetRightReleased();
+ if (fabs(time_of_day - time_of_day_smooth) > maxsm &&
+ fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
+ fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
+ time_of_day_smooth = time_of_day;
- /*
- Calculate stuff for drawing
- */
+ const float todsm = 0.05;
- /*
- Fog range
- */
+ if (time_of_day_smooth > 0.8 && time_of_day < 0.2)
+ time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
+ + (time_of_day + 1.0) * todsm;
+ else
+ time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
+ + time_of_day * todsm;
- if(draw_control.range_all)
- fog_range = 100000*BS;
- else {
- fog_range = draw_control.wanted_range*BS + 0.0*MAP_BLOCKSIZE*BS;
- fog_range = MYMIN(fog_range, (draw_control.farthest_drawn+20)*BS);
- fog_range *= 0.9;
- }
+ sky->update(time_of_day_smooth, time_brightness, direct_brightness,
+ sunlight_seen, camera->getCameraMode(), player->getYaw(),
+ player->getPitch());
- /*
- Calculate general brightness
- */
- u32 daynight_ratio = client.getEnv().getDayNightRatio();
- float time_brightness = decode_light_f((float)daynight_ratio/1000.0);
- float direct_brightness = 0;
- bool sunlight_seen = false;
- if(g_settings->getBool("free_move")){
- direct_brightness = time_brightness;
- sunlight_seen = true;
+ /*
+ Update clouds
+ */
+ if (clouds) {
+ v3f player_position = player->getPosition();
+ if (sky->getCloudsVisible()) {
+ clouds->setVisible(true);
+ clouds->step(dtime);
+ clouds->update(v2f(player_position.X, player_position.Z),
+ sky->getCloudColor());
} else {
- ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
- float old_brightness = sky->getBrightness();
- direct_brightness = (float)client.getEnv().getClientMap()
- .getBackgroundBrightness(MYMIN(fog_range*1.2, 60*BS),
- daynight_ratio, (int)(old_brightness*255.5), &sunlight_seen)
- / 255.0;
- }
-
- time_of_day = client.getEnv().getTimeOfDayF();
- float maxsm = 0.05;
- if(fabs(time_of_day - time_of_day_smooth) > maxsm &&
- fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
- fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
- time_of_day_smooth = time_of_day;
- float todsm = 0.05;
- if(time_of_day_smooth > 0.8 && time_of_day < 0.2)
- time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
- + (time_of_day+1.0) * todsm;
- else
- time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
- + time_of_day * todsm;
-
- sky->update(time_of_day_smooth, time_brightness, direct_brightness,
- sunlight_seen,camera.getCameraMode(), player->getYaw(),
- player->getPitch());
-
- video::SColor bgcolor = sky->getBgColor();
- video::SColor skycolor = sky->getSkyColor();
-
- /*
- Update clouds
- */
- if(clouds){
- if(sky->getCloudsVisible()){
- clouds->setVisible(true);
- clouds->step(dtime);
- clouds->update(v2f(player_position.X, player_position.Z),
- sky->getCloudColor());
- } else{
- clouds->setVisible(false);
- }
+ clouds->setVisible(false);
}
+ }
- /*
- Update particles
- */
+ /*
+ Update particles
+ */
- allparticles_step(dtime);
- allparticlespawners_step(dtime, client.getEnv());
+ allparticles_step(dtime);
+ allparticlespawners_step(dtime, client->getEnv());
- /*
- Fog
- */
+ /*
+ Fog
+ */
- if(g_settings->getBool("enable_fog") && !force_fog_off)
- {
- driver->setFog(
- bgcolor,
+ if (g_settings->getBool("enable_fog") && !flags.force_fog_off) {
+ driver->setFog(
+ sky->getBgColor(),
video::EFT_FOG_LINEAR,
- fog_range*0.4,
- fog_range*1.0,
+ interactArgs->fog_range * 0.4,
+ interactArgs->fog_range * 1.0,
0.01,
false, // pixel fog
false // range fog
- );
- }
- else
- {
- driver->setFog(
- bgcolor,
+ );
+ } else {
+ driver->setFog(
+ sky->getBgColor(),
video::EFT_FOG_LINEAR,
- 100000*BS,
- 110000*BS,
+ 100000 * BS,
+ 110000 * BS,
0.01,
false, // pixel fog
false // range fog
- );
- }
-
- /*
- Update gui stuff (0ms)
- */
-
- //TimeTaker guiupdatetimer("Gui updating");
-
- if(show_debug)
- {
- static float drawtime_avg = 0;
- drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
- /*static float beginscenetime_avg = 0;
- beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
- static float scenetime_avg = 0;
- scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
- static float endscenetime_avg = 0;
- endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/
-
- u16 fps = (1.0/dtime_avg1);
-
- std::ostringstream os(std::ios_base::binary);
- os<<std::fixed
- <<"Minetest "<<minetest_version_hash
- <<" FPS = "<<fps
- <<" (R: range_all="<<draw_control.range_all<<")"
- <<std::setprecision(0)
- <<" drawtime = "<<drawtime_avg
- <<std::setprecision(1)
- <<", dtime_jitter = "
- <<(dtime_jitter1_max_fraction * 100.0)<<" %"
- <<std::setprecision(1)
- <<", v_range = "<<draw_control.wanted_range
- <<std::setprecision(3)
- <<", RTT = "<<client.getRTT();
- guitext->setText(narrow_to_wide(os.str()).c_str());
- guitext->setVisible(true);
- }
- else if(show_hud || show_chat)
- {
- std::ostringstream os(std::ios_base::binary);
- os<<"Minetest "<<minetest_version_hash;
- guitext->setText(narrow_to_wide(os.str()).c_str());
- guitext->setVisible(true);
- }
- else
- {
- guitext->setVisible(false);
- }
+ );
+ }
- if (guitext->isVisible())
- {
- core::rect<s32> rect(
- 5,
- 5,
- screensize.X,
- 5 + text_height
- );
- guitext->setRelativePosition(rect);
- }
+ /*
+ Get chat messages from client
+ */
- if(show_debug)
- {
- std::ostringstream os(std::ios_base::binary);
- os<<std::setprecision(1)<<std::fixed
- <<"(" <<(player_position.X/BS)
- <<", "<<(player_position.Y/BS)
- <<", "<<(player_position.Z/BS)
- <<") (yaw="<<(wrapDegrees_0_360(camera_yaw))
- <<") (seed = "<<((u64)client.getMapSeed())
- <<")";
- guitext2->setText(narrow_to_wide(os.str()).c_str());
- guitext2->setVisible(true);
-
- core::rect<s32> rect(
- 5,
- 5 + text_height,
- screensize.X,
- 5 + (text_height * 2)
- );
- guitext2->setRelativePosition(rect);
- }
- else
- {
- guitext2->setVisible(false);
- }
+ v2u32 screensize = driver->getScreenSize();
- {
- guitext_info->setText(infotext.c_str());
- guitext_info->setVisible(show_hud && g_menumgr.menuCount() == 0);
- }
+ updateChat(*client, dtime, flags.show_debug, screensize,
+ flags.show_chat, interactArgs->profiler_current_page,
+ *chat_backend, guitext_chat, font);
- {
- float statustext_time_max = 1.5;
- if(!statustext.empty())
- {
- statustext_time += dtime;
- if(statustext_time >= statustext_time_max)
- {
- statustext = L"";
- statustext_time = 0;
- }
- }
- guitext_status->setText(statustext.c_str());
- guitext_status->setVisible(!statustext.empty());
-
- if(!statustext.empty())
- {
- s32 status_y = screensize.Y - 130;
- core::rect<s32> rect(
- 10,
- status_y - guitext_status->getTextHeight(),
- 10 + guitext_status->getTextWidth(),
- status_y
- );
- guitext_status->setRelativePosition(rect);
-
- // Fade out
- video::SColor initial_color(255,0,0,0);
- if(guienv->getSkin())
- initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
- video::SColor final_color = initial_color;
- final_color.setAlpha(0);
- video::SColor fade_color =
- initial_color.getInterpolated_quadratic(
- initial_color,
- final_color,
- pow(statustext_time / (float)statustext_time_max, 2.0f));
- guitext_status->setOverrideColor(fade_color);
- guitext_status->enableOverrideColor(true);
- }
- }
+ /*
+ Inventory
+ */
- /*
- Get chat messages from client
- */
- updateChat(client, dtime, show_debug, screensize, show_chat,
- show_profiler, chat_backend, guitext_chat, font);
+ bool update_wielded_item_trigger = true;
- /*
- Inventory
- */
+ if (client->getPlayerItem() != interactArgs->new_playeritem) {
+ client->selectPlayerItem(interactArgs->new_playeritem);
+ }
- if(client.getPlayerItem() != new_playeritem)
- {
- client.selectPlayerItem(new_playeritem);
- }
- if(client.getLocalInventoryUpdated())
- {
- //infostream<<"Updating local inventory"<<std::endl;
- client.getLocalInventory(local_inventory);
+ if (client->getLocalInventoryUpdated()) {
+ //infostream<<"Updating local inventory"<<std::endl;
+ client->getLocalInventory(*local_inventory);
- update_wielded_item_trigger = true;
- }
- if(update_wielded_item_trigger)
- {
- update_wielded_item_trigger = false;
- // Update wielded tool
- InventoryList *mlist = local_inventory.getList("main");
- ItemStack item;
- if((mlist != NULL) && (client.getPlayerItem() < mlist->getSize()))
- item = mlist->getItem(client.getPlayerItem());
- camera.wield(item, client.getPlayerItem());
- }
+ update_wielded_item_trigger = true;
+ }
- /*
- Update block draw list every 200ms or when camera direction has
- changed much
- */
- update_draw_list_timer += dtime;
- if(update_draw_list_timer >= 0.2 ||
- update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2 ||
- camera_offset_changed){
- update_draw_list_timer = 0;
- client.getEnv().getClientMap().updateDrawList(driver);
- update_draw_list_last_cam_dir = camera_direction;
- }
+ if (update_wielded_item_trigger) {
+ update_wielded_item_trigger = false;
+ // Update wielded tool
+ InventoryList *mlist = local_inventory->getList("main");
+ ItemStack item;
- /*
- 1. Delete formspec menu reference if menu was removed
- 2. Else, make sure formspec menu is on top
- */
- if (current_formspec) {
- if (current_formspec->getReferenceCount() == 1) {
- current_formspec->drop();
- current_formspec = NULL;
- } else if (!noMenuActive()) {
- guiroot->bringToFront(current_formspec);
- }
- }
+ if (mlist && (client->getPlayerItem() < mlist->getSize()))
+ item = mlist->getItem(client->getPlayerItem());
- /*
- Drawing begins
- */
- TimeTaker tt_draw("mainloop: draw");
- {
- TimeTaker timer("beginScene");
- driver->beginScene(true, true, skycolor);
- beginscenetime = timer.stop(true);
- }
+ camera->wield(item, client->getPlayerItem());
+ }
+ /*
+ Update block draw list every 200ms or when camera direction has
+ changed much
+ */
+ interactArgs->update_draw_list_timer += dtime;
+
+ v3f camera_direction = camera->getDirection();
+ if (interactArgs->update_draw_list_timer >= 0.2
+ || interactArgs->update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
+ || flags.camera_offset_changed) {
+ interactArgs->update_draw_list_timer = 0;
+ client->getEnv().getClientMap().updateDrawList(driver);
+ interactArgs->update_draw_list_last_cam_dir = camera_direction;
+ }
- draw_scene(driver, smgr, camera, client, player, hud, guienv,
- hilightboxes, screensize, skycolor, show_hud);
+ updateGui(&interactArgs->statustext_time, *stats, dtime, flags, cam);
- /*
- Profiler graph
- */
- if(show_profiler_graph)
- {
- graph.draw(10, screensize.Y - 10, driver, font);
+ /*
+ make sure menu is on top
+ 1. Delete formspec menu reference if menu was removed
+ 2. Else, make sure formspec menu is on top
+ */
+ if (current_formspec) {
+ if (current_formspec->getReferenceCount() == 1) {
+ current_formspec->drop();
+ current_formspec = NULL;
+ } else if (!noMenuActive()) {
+ guiroot->bringToFront(current_formspec);
}
+ }
- /*
- Damage flash
- */
- if(damage_flash > 0.0)
- {
- video::SColor color(std::min(damage_flash, 180.0f),180,0,0);
- driver->draw2DRectangle(color,
- core::rect<s32>(0,0,screensize.X,screensize.Y),
- NULL);
+ /*
+ Drawing begins
+ */
- damage_flash -= 100.0*dtime;
- }
+ video::SColor skycolor = sky->getSkyColor();
- /*
- Damage camera tilt
- */
- if(player->hurt_tilt_timer > 0.0)
- {
- player->hurt_tilt_timer -= dtime*5;
- if(player->hurt_tilt_timer < 0)
- player->hurt_tilt_strength = 0;
- }
+ TimeTaker tt_draw("mainloop: draw");
+ {
+ TimeTaker timer("beginScene");
+ driver->beginScene(true, true, skycolor);
+ stats->beginscenetime = timer.stop(true);
+ }
- /*
- End scene
- */
- {
- TimeTaker timer("endScene");
- driver->endScene();
- endscenetime = timer.stop(true);
- }
+ draw_scene(driver, smgr, *camera, *client, player, *hud, guienv,
+ highlight_boxes, screensize, skycolor, flags.show_hud);
- drawtime = tt_draw.stop(true);
- g_profiler->graphAdd("mainloop_draw", (float)drawtime/1000.0f);
+ /*
+ Profiler graph
+ */
+ if (flags.show_profiler_graph)
+ graph->draw(10, screensize.Y - 10, driver, font);
- /*
- End of drawing
- */
+ /*
+ Damage flash
+ */
+ if (interactArgs->damage_flash > 0.0) {
+ video::SColor color(std::min(interactArgs->damage_flash, 180.0f),
+ 180,
+ 0,
+ 0);
+ driver->draw2DRectangle(color,
+ core::rect<s32>(0, 0, screensize.X, screensize.Y),
+ NULL);
- /*
- Log times and stuff for visualization
- */
- Profiler::GraphValues values;
- g_profiler->graphGet(values);
- graph.put(values);
+ interactArgs->damage_flash -= 100.0 * dtime;
}
/*
- Drop stuff
+ Damage camera tilt
*/
- if (clouds)
- clouds->drop();
- if (gui_chat_console)
- gui_chat_console->drop();
- if (sky)
- sky->drop();
- clear_particles();
+ if (player->hurt_tilt_timer > 0.0) {
+ player->hurt_tilt_timer -= dtime * 5;
- /* cleanup menus */
- while (g_menumgr.menuCount() > 0)
- {
- g_menumgr.m_stack.front()->setVisible(false);
- g_menumgr.deletingMenu(g_menumgr.m_stack.front());
- }
- if (current_formspec) {
- current_formspec->drop();
- current_formspec = NULL;
+ if (player->hurt_tilt_timer < 0)
+ player->hurt_tilt_strength = 0;
}
/*
- Draw a "shutting down" screen, which will be shown while the map
- generator and other stuff quits
+ End scene
*/
{
- wchar_t* text = wgettext("Shutting down stuff...");
- draw_load_screen(text, device, guienv, font, 0, -1, false);
- delete[] text;
+ TimeTaker timer("endScene");
+ driver->endScene();
+ stats->endscenetime = timer.stop(true);
}
- chat_backend.addMessage(L"", L"# Disconnected.");
- chat_backend.addMessage(L"", L"");
+ stats->drawtime = tt_draw.stop(true);
+ g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f);
+}
+
+
+void MinetestApp::updateGui(float *statustext_time, const RunStats& stats,
+ f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam)
+{
+ v2u32 screensize = driver->getScreenSize();
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ v3f player_position = player->getPosition();
- client.Stop();
+ if (flags.show_debug) {
+ static float drawtime_avg = 0;
+ drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05;
- //force answer all texture and shader jobs (TODO return empty values)
+ u16 fps = 1.0 / stats.dtime_jitter.avg;
- while(!client.isShutdown()) {
- tsrc->processQueue();
- shsrc->processQueue();
- sleep_ms(100);
+ std::ostringstream os(std::ios_base::binary);
+ os << std::fixed
+ << "Minetest " << minetest_version_hash
+ << " FPS = " << fps
+ << " (R: range_all=" << draw_control->range_all << ")"
+ << std::setprecision(0)
+ << " drawtime = " << drawtime_avg
+ << std::setprecision(1)
+ << ", dtime_jitter = "
+ << (stats.dtime_jitter.max_fraction * 100.0) << " %"
+ << std::setprecision(1)
+ << ", v_range = " << draw_control->wanted_range
+ << std::setprecision(3)
+ << ", RTT = " << client->getRTT();
+ guitext->setText(narrow_to_wide(os.str()).c_str());
+ guitext->setVisible(true);
+ } else if (flags.show_hud || flags.show_chat) {
+ std::ostringstream os(std::ios_base::binary);
+ os << "Minetest " << minetest_version_hash;
+ guitext->setText(narrow_to_wide(os.str()).c_str());
+ guitext->setVisible(true);
+ } else {
+ guitext->setVisible(false);
}
- // Client scope (client is destructed before destructing *def and tsrc)
- }while(0);
- } // try-catch
- catch(SerializationError &e)
- {
- error_message = L"A serialization error occurred:\n"
- + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
- L" running a different version of Minetest.";
- errorstream<<wide_to_narrow(error_message)<<std::endl;
+ if (guitext->isVisible()) {
+ core::rect<s32> rect(
+ 5, 5,
+ screensize.X, 5 + text_height
+ );
+ guitext->setRelativePosition(rect);
}
- catch(ServerError &e) {
- error_message = narrow_to_wide(e.what());
- errorstream << "ServerError: " << e.what() << std::endl;
+
+ if (flags.show_debug) {
+ std::ostringstream os(std::ios_base::binary);
+ os << std::setprecision(1) << std::fixed
+ << "(" << (player_position.X / BS)
+ << ", " << (player_position.Y / BS)
+ << ", " << (player_position.Z / BS)
+ << ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
+ << ") (seed = " << ((u64)client->getMapSeed())
+ << ")";
+ guitext2->setText(narrow_to_wide(os.str()).c_str());
+ guitext2->setVisible(true);
+
+ core::rect<s32> rect(
+ 5, 5 + text_height,
+ screensize.X, 5 + text_height * 2
+ );
+ guitext2->setRelativePosition(rect);
+ } else {
+ guitext2->setVisible(false);
}
- catch(ModError &e) {
- errorstream << "ModError: " << e.what() << std::endl;
- error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
+
+ guitext_info->setText(infotext.c_str());
+ guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0);
+
+ float statustext_time_max = 1.5;
+
+ if (!statustext.empty()) {
+ *statustext_time += dtime;
+
+ if (*statustext_time >= statustext_time_max) {
+ statustext = L"";
+ *statustext_time = 0;
+ }
+ }
+
+ guitext_status->setText(statustext.c_str());
+ guitext_status->setVisible(!statustext.empty());
+
+ if (!statustext.empty()) {
+ s32 status_y = screensize.Y - 130;
+ core::rect<s32> rect(
+ 10, status_y - guitext_status->getTextHeight(),
+ 10 + guitext_status->getTextWidth(), status_y
+ );
+ guitext_status->setRelativePosition(rect);
+
+ // Fade out
+ video::SColor initial_color(255, 0, 0, 0);
+
+ if (guienv->getSkin())
+ initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
+
+ video::SColor final_color = initial_color;
+ final_color.setAlpha(0);
+ video::SColor fade_color = initial_color.getInterpolated_quadratic(
+ initial_color, final_color,
+ pow(*statustext_time / statustext_time_max, 2.0f));
+ guitext_status->setOverrideColor(fade_color);
+ guitext_status->enableOverrideColor(true);
}
+}
+
+
+/* Log times and stuff for visualization */
+inline void MinetestApp::updateProfilerGraphs(ProfilerGraph *graph)
+{
+ Profiler::GraphValues values;
+ g_profiler->graphGet(values);
+ graph->put(values);
+}
- if(!sound_is_dummy)
- delete sound;
+/****************************************************************************
+ Misc
+ ****************************************************************************/
+
+/* On some computers framerate doesn't seem to be automatically limited
+ *
+ * *Must* be called after device->run() so that device->getTimer()->getTime();
+ * is correct
+ */
+inline void MinetestApp::limitFps(FpsControl *params, f32 *dtime)
+{
+ // not using getRealTime is necessary for wine
+ u32 time = device->getTimer()->getTime();
+
+ u32 last_time = params->last_time;
+
+ if (time > last_time) // Make sure last_time hasn't overflowed
+ params->busy_time = time - last_time;
+ else
+ params->busy_time = 0;
+
+ u32 frametime_min = 1000 / (g_menumgr.pausesGame()
+ ? g_settings->getFloat("pause_fps_max")
+ : g_settings->getFloat("fps_max"));
+
+ if (params->busy_time < frametime_min) {
+ params->sleep_time = frametime_min - params->busy_time;
+ device->sleep(params->sleep_time);
+ } else {
+ params->sleep_time = 0;
+ }
+
+ // Necessary for device->getTimer()->getTime()
+ device->run();
+ time = device->getTimer()->getTime();
+
+ if (time > last_time) // Make sure last_time hasn't overflowed
+ *dtime = (time - last_time) / 1000.0;
+ else
+ *dtime = 0;
+
+ params->last_time = time;
+}
+
+
+void MinetestApp::showOverlayMessage(const char *msg, float dtime,
+ int percent, bool draw_clouds)
+{
+ wchar_t *text = wgettext(msg);
+ draw_load_screen(text, device, guienv, font, dtime, percent, draw_clouds);
+ delete[] text;
+}
+
+
+inline const char *MinetestApp::boolToCStr(bool v)
+{
+ static const char *str[] = { "false", "true" };
+ return str[v];
+}
+
- //has to be deleted first to stop all server threads
- delete server;
- delete tsrc;
- delete shsrc;
- delete nodedef;
- delete itemdef;
+/****************************************************************************
+ Shutdown / cleanup
+ ****************************************************************************/
- //extended resource accounting
+void MinetestApp::extendedResourceCleanup()
+{
+ // Extended resource accounting
infostream << "Irrlicht resources after cleanup:" << std::endl;
infostream << "\tRemaining meshes : "
- << device->getSceneManager()->getMeshCache()->getMeshCount() << std::endl;
+ << device->getSceneManager()->getMeshCache()->getMeshCount() << std::endl;
infostream << "\tRemaining textures : "
- << driver->getTextureCount() << std::endl;
- for (unsigned int i = 0; i < driver->getTextureCount(); i++ ) {
- irr::video::ITexture* texture = driver->getTextureByIndex(i);
+ << driver->getTextureCount() << std::endl;
+
+ for (unsigned int i = 0; i < driver->getTextureCount(); i++) {
+ irr::video::ITexture *texture = driver->getTextureByIndex(i);
infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str()
- << std::endl;
+ << std::endl;
}
+
clearTextureNameCache();
infostream << "\tRemaining materials: "
- << driver-> getMaterialRendererCount ()
- << " (note: irrlicht doesn't support removing renderers)"<< std::endl;
+ << driver-> getMaterialRendererCount()
+ << " (note: irrlicht doesn't support removing renderers)" << std::endl;
+}
+
+
+
+/****************************************************************************
+ extern function for launching the game
+ ****************************************************************************/
+
+void the_game(bool *kill,
+ bool random_input,
+ InputHandler *input,
+ IrrlichtDevice *device,
+ gui::IGUIFont *font,
+
+ const std::string &map_dir,
+ const std::string &playername,
+ const std::string &password,
+ const std::string &address, // If empty local server is created
+ u16 port,
+
+ std::wstring &error_message,
+ ChatBackend &chat_backend,
+ const SubgameSpec &gamespec, // Used for local game
+ bool simple_singleplayer_mode)
+{
+ MinetestApp app;
+
+ /* Make a copy of the server address because if a local singleplayer server
+ * is created then this is updated and we don't want to change the value
+ * passed to us by the calling function
+ */
+ std::string server_address = address;
+
+ try {
+
+ if (app.startup(kill, random_input, input, device, font, map_dir,
+ playername, password, &server_address, port,
+ &error_message, &chat_backend, gamespec,
+ simple_singleplayer_mode)) {
+
+ //std::cout << "App started" << std::endl;
+ app.run();
+ app.shutdown();
+ }
+
+ } catch (SerializationError &e) {
+ error_message = L"A serialization error occurred:\n"
+ + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
+ L" running a different version of Minetest.";
+ errorstream << wide_to_narrow(error_message) << std::endl;
+ } catch (ServerError &e) {
+ error_message = narrow_to_wide(e.what());
+ errorstream << "ServerError: " << e.what() << std::endl;
+ } catch (ModError &e) {
+ errorstream << "ModError: " << e.what() << std::endl;
+ error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
+ }
}