*/
#include "game.h"
-#include "irrlichttypes_extrabloated.h"
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IMaterialRendererServices.h>
-#include "IMeshCache.h"
+
+#include <iomanip>
+#include "camera.h"
#include "client.h"
-#include "server.h"
-#include "guiPasswordChange.h"
-#include "guiVolumeChange.h"
-#include "guiKeyChangeMenu.h"
-#include "guiFormSpecMenu.h"
-#include "tool.h"
-#include "guiChatConsole.h"
-#include "config.h"
-#include "version.h"
+#include "client/tile.h" // For TextureSource
+#include "clientmap.h"
#include "clouds.h"
-#include "particles.h"
-#include "camera.h"
-#include "mapblock.h"
-#include "settings.h"
-#include "profiler.h"
-#include "mainmenumanager.h"
-#include "gettext.h"
+#include "config.h"
+#include "content_cao.h"
+#include "drawscene.h"
+#include "event_manager.h"
+#include "fontengine.h"
+#include "itemdef.h"
#include "log.h"
#include "filesys.h"
-// Needed for determining pointing to nodes
-#include "nodedef.h"
-#include "nodemetadata.h"
-#include "main.h" // For g_settings
-#include "itemdef.h"
-#include "tile.h" // For TextureSource
-#include "shader.h" // For ShaderSource
+#include "gettext.h"
+#include "guiChatConsole.h"
+#include "guiFormSpecMenu.h"
+#include "guiKeyChangeMenu.h"
+#include "guiPasswordChange.h"
+#include "guiVolumeChange.h"
+#include "hud.h"
#include "logoutputbuffer.h"
-#include "subgame.h"
+#include "mainmenumanager.h"
+#include "mapblock.h"
+#include "nodedef.h" // Needed for determining pointing to nodes
+#include "nodemetadata.h"
+#include "particles.h"
+#include "profiler.h"
#include "quicktune_shortcutter.h"
-#include "clientmap.h"
-#include "hud.h"
+#include "server.h"
+#include "settings.h"
+#include "shader.h" // For ShaderSource
#include "sky.h"
+#include "subgame.h"
+#include "tool.h"
+#include "util/directiontables.h"
+#include "util/pointedthing.h"
+#include "version.h"
+
#include "sound.h"
+
#if USE_SOUND
-#include "sound_openal.h"
+ #include "sound_openal.h"
#endif
-#include "event_manager.h"
-#include <iomanip>
-#include <list>
-#include "util/directiontables.h"
-#include "util/pointedthing.h"
-#include "drawscene.h"
-#include "content_cao.h"
-#include "fontengine.h"
#ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
+ #include "touchscreengui.h"
#endif
+extern Settings *g_settings;
+extern Profiler *g_profiler;
+
/*
Text input system
*/
color(color)
{}
};
- std::list<Piece> m_log;
+ std::vector<Piece> m_log;
public:
u32 m_log_max_size;
{
std::map<std::string, Meta> m_meta;
- for (std::list<Piece>::const_iterator k = m_log.begin();
+ for (std::vector<Piece>::const_iterator k = m_log.begin();
k != m_log.end(); k++) {
const Piece &piece = *k;
s32 graphh = 50;
s32 textx = x_left + m_log_max_size + 15;
s32 textx2 = textx + 200 - 15;
-
- // Draw background
- /*{
- u32 num_graphs = m_meta.size();
- core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
- textx2, y_bottom);
- video::SColor bgcolor(120,0,0,0);
- driver->draw2DRectangle(bgcolor, rect, NULL);
- }*/
-
s32 meta_i = 0;
for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
float lastscaledvalue = 0.0;
bool lastscaledvalue_exists = false;
- for (std::list<Piece>::const_iterator j = m_log.begin();
+ for (std::vector<Piece>::const_iterator j = m_log.begin();
j != m_log.end(); j++) {
const Piece &piece = *j;
float value = 0;
bool *m_force_fog_off;
f32 *m_fog_range;
Client *m_client;
+ bool m_fogEnabled;
public:
+ void onSettingsChange(const std::string &name)
+ {
+ if (name == "enable_fog")
+ m_fogEnabled = g_settings->getBool("enable_fog");
+ }
+
+ static void SettingsCallback(const std::string name, void *userdata)
+ {
+ reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
+ }
+
GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
f32 *fog_range, Client *client) :
m_sky(sky),
m_force_fog_off(force_fog_off),
m_fog_range(fog_range),
m_client(client)
- {}
- ~GameGlobalShaderConstantSetter() {}
+ {
+ g_settings->registerChangedCallback("enable_fog", SettingsCallback, this);
+ m_fogEnabled = g_settings->getBool("enable_fog");
+ }
+
+ ~GameGlobalShaderConstantSetter()
+ {
+ g_settings->deregisterChangedCallback("enable_fog", SettingsCallback, this);
+ }
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel)
// Fog distance
float fog_distance = 10000 * BS;
- if (g_settings->getBool("enable_fog") && !*m_force_fog_off)
+ if (m_fogEnabled && !*m_force_fog_off)
fog_distance = *m_fog_range;
services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
#ifdef __ANDROID__
#define SIZE_TAG "size[11,5.5]"
#else
-#define SIZE_TAG "size[11,5.5,true]"
+#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
#endif
static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
std::string(FORMSPEC_VERSION_STRING) +
SIZE_TAG
"bgcolor[#320000b4;true]"
- "label[4.85,1.35;You died.]"
+ "label[4.85,1.35;" + gettext("You died.") + "]"
"button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
;
<< wide_to_narrow(wstrgettext("Change Password")) << "]";
}
+#ifndef __ANDROID__
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
<< wide_to_narrow(wstrgettext("Sound Volume")) << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
<< wide_to_narrow(wstrgettext("Change Keys")) << "]";
+#endif
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_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")) << "]"
<< "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"
+ << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME "\n"
+ << g_build_info << "\n"
<< "path_user = " << wrap_rows(porting::path_user, 20)
<< "\n;]";
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
-
+ (*cur_formspec)->setFocus(L"btn_continue");
(*cur_formspec)->doPause = true;
}
KEYMAP_ID_FREEMOVE,
KEYMAP_ID_FASTMOVE,
KEYMAP_ID_NOCLIP,
+ KEYMAP_ID_CINEMATIC,
KEYMAP_ID_SCREENSHOT,
KEYMAP_ID_TOGGLE_HUD,
KEYMAP_ID_TOGGLE_CHAT,
key[KEYMAP_ID_FREEMOVE] = getKeySetting("keymap_freemove");
key[KEYMAP_ID_FASTMOVE] = getKeySetting("keymap_fastmove");
key[KEYMAP_ID_NOCLIP] = getKeySetting("keymap_noclip");
+ key[KEYMAP_ID_CINEMATIC] = getKeySetting("keymap_cinematic");
key[KEYMAP_ID_SCREENSHOT] = getKeySetting("keymap_screenshot");
key[KEYMAP_ID_TOGGLE_HUD] = getKeySetting("keymap_toggle_hud");
key[KEYMAP_ID_TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat");
* many functions that do require objects of thse types do not modify them
* (so they can be passed as a const qualified parameter)
*/
-
struct CameraOrientation {
f32 camera_yaw; // "right/left"
f32 camera_pitch; // "up/down"
// If address is "", local server is used and address is updated
std::string *address,
u16 port,
- std::wstring *error_message,
+ std::string &error_message,
ChatBackend *chat_backend,
const SubgameSpec &gamespec, // Used for local game
bool simple_singleplayer_mode);
// 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);
+ const std::string &password, std::string *address, u16 port);
+ bool initGui();
// Client connection
bool connectToServer(const std::string &playername,
// Main loop
- void updateInteractTimers(GameRunData *args, f32 dtime);
+ void updateInteractTimers(GameRunData *runData, f32 dtime);
bool checkConnection();
bool handleCallbacks();
void processQueues();
- void updateProfilers(const GameRunData &run_data, const RunStats &stats,
+ void updateProfilers(const GameRunData &runData, const RunStats &stats,
const FpsControl &draw_times, f32 dtime);
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, GameRunData *interact_args,
+ void processUserInput(VolatileRunFlags *flags, GameRunData *runData,
f32 dtime);
void processKeyboardInput(VolatileRunFlags *flags,
float *statustext_time,
void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
void toggleFast(float *statustext_time);
void toggleNoClip(float *statustext_time);
+ void toggleCinematic(float *statustext_time);
void toggleChat(float *statustext_time, bool *flag);
void toggleHud(float *statustext_time, bool *flag);
void toggleFullViewRange(float *statustext_time);
void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags);
+ void updateCameraOrientation(CameraOrientation *cam,
+ const VolatileRunFlags &flags);
void updatePlayerControl(const CameraOrientation &cam);
void step(f32 *dtime);
void processClientEvents(CameraOrientation *cam, float *damage_flash);
// Misc
void limitFps(FpsControl *fps_timings, f32 *dtime);
- void showOverlayMessage(const char *msg, float dtime, int percent,
+ void showOverlayMessage(const wchar_t *msg, float dtime, int percent,
bool draw_clouds = true);
private:
video::IVideoDriver *driver;
scene::ISceneManager *smgr;
bool *kill;
- std::wstring *error_message;
+ std::string *error_message;
IGameDef *gamedef; // Convenience (same as *client)
scene::ISceneNode *skybox;
m_cache_enable_fog = g_settings->getBool("enable_fog");
m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
+
+ m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
}
const std::string &password,
std::string *address, // can change if simple_singleplayer_mode
u16 port,
- std::wstring *error_message,
+ std::string &error_message,
ChatBackend *chat_backend,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode)
// "cache"
this->device = device;
this->kill = kill;
- this->error_message = error_message;
+ this->error_message = &error_message;
this->random_input = random_input;
this->input = input;
this->chat_backend = chat_backend;
driver = device->getVideoDriver();
smgr = device->getSceneManager();
+ smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
+
if (!init(map_dir, address, port, gamespec))
return false;
- if (!createClient(playername, password, address, port, error_message))
+ if (!createClient(playername, password, address, port))
return false;
return true;
{
ProfilerGraph graph;
RunStats stats = { 0 };
+ CameraOrientation cam_view_target = { 0 };
CameraOrientation cam_view = { 0 };
GameRunData runData = { 0 };
FpsControl draw_times = { 0 };
flags.show_hud = true;
flags.show_debug = g_settings->getBool("show_debug");
flags.invert_mouse = g_settings->getBool("invert_mouse");
+ flags.first_loop_after_window_activation = true;
/* Clear the profiler */
Profiler::GraphValues dummyvalues;
std::vector<aabb3f> highlight_boxes;
+ set_light_table(g_settings->getFloat("display_gamma"));
+
while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
/* Must be called immediately after a device->run() call because it
updateProfilers(runData, stats, draw_times, dtime);
processUserInput(&flags, &runData, dtime);
// Update camera before player movement to avoid camera lag of one frame
- updateCameraDirection(&cam_view, &flags);
+ updateCameraDirection(&cam_view_target, &flags);
+ float cam_smoothing = 0;
+ if (g_settings->getBool("cinematic"))
+ cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
+ else
+ cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
+ cam_smoothing = rangelim(cam_smoothing, 0.01f, 1.0f);
+ cam_view.camera_yaw += (cam_view_target.camera_yaw -
+ cam_view.camera_yaw) * cam_smoothing;
+ cam_view.camera_pitch += (cam_view_target.camera_pitch -
+ cam_view.camera_pitch) * cam_smoothing;
updatePlayerControl(cam_view);
step(&dtime);
processClientEvents(&cam_view, &runData.damage_flash);
void Game::shutdown()
{
- showOverlayMessage("Shutting down...", 0, 0, false);
+ showOverlayMessage(wgettext("Shutting down..."), 0, 0, false);
if (clouds)
clouds->drop();
if (sky)
sky->drop();
- clear_particles();
-
/* cleanup menus */
while (g_menumgr.menuCount() > 0) {
g_menumgr.m_stack.front()->setVisible(false);
}
-
+/****************************************************************************/
/****************************************************************************
Startup
****************************************************************************/
+/****************************************************************************/
bool Game::init(
const std::string &map_dir,
u16 port,
const SubgameSpec &gamespec)
{
- showOverlayMessage("Loading...", 0, 0);
+ showOverlayMessage(wgettext("Loading..."), 0, 0);
texture_src = createTextureSource(device);
shader_src = createShaderSource(device);
bool Game::createSingleplayerServer(const std::string map_dir,
const SubgameSpec &gamespec, u16 port, std::string *address)
{
- showOverlayMessage("Creating server...", 0, 25);
+ showOverlayMessage(wgettext("Creating server..."), 0, 5);
std::string bind_str = g_settings->get("bind_address");
Address bind_addr(0, 0, 0, 0, port);
try {
bind_addr.Resolve(bind_str.c_str());
- *address = bind_str;
} catch (ResolveError &e) {
infostream << "Resolving bind address \"" << bind_str
<< "\" failed: " << e.what()
}
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;
+ *error_message = "Unable to listen on " +
+ bind_addr.serializeString() +
+ " because IPv6 is disabled";
+ errorstream << *error_message << std::endl;
return false;
}
}
bool Game::createClient(const std::string &playername,
- const std::string &password, std::string *address, u16 port,
- std::wstring *error_message)
+ const std::string &password, std::string *address, u16 port)
{
- showOverlayMessage("Creating client...", 0, 50);
+ showOverlayMessage(wgettext("Creating client..."), 0, 10);
draw_control = new MapDrawControl;
if (!draw_control)
return false;
if (!could_connect) {
- if (*error_message == L"" && !connect_aborted) {
+ if (error_message->empty() && !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;
+ *error_message = "Connection failed for unknown reason";
+ errorstream << *error_message << std::endl;
}
return false;
}
if (!getServerContent(&connect_aborted)) {
- if (*error_message == L"" && !connect_aborted) {
+ if (error_message->empty() && !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;
+ *error_message = "Connection failed for unknown reason";
+ errorstream << *error_message << std::endl;
}
return false;
}
// Update cached textures, meshes and materials
- client->afterContentReceived(device, g_fontengine->getFont());
+ client->afterContentReceived(device);
/* Camera
*/
if (m_cache_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;
+ *error_message = "Memory allocation error (clouds)";
+ errorstream << *error_message << std::endl;
return false;
}
}
/* Skybox
*/
- sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
+ sky = new Sky(smgr->getRootSceneNode(), smgr, -1, texture_src);
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;
+ *error_message = "Memory allocation error (sky or local inventory)";
+ errorstream << *error_message << std::endl;
return false;
}
crack_animation_length = 5;
}
- if (!initGui(error_message))
+ if (!initGui())
return false;
/* Set window caption
*/
- core::stringw str = L"Minetest [";
+ std::wstring str = narrow_to_wide(PROJECT_NAME);
+ str += L" [";
str += driver->getName();
- str += "]";
+ str += L"]";
device->setWindowCaption(str.c_str());
LocalPlayer *player = client->getEnv().getLocalPlayer();
hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory);
if (!hud) {
- *error_message = L"Memory error: could not create HUD";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Memory error: could not create HUD";
+ errorstream << *error_message << std::endl;
return false;
}
return true;
}
-bool Game::initGui(std::wstring *error_message)
+bool Game::initGui()
{
// First line of debug text
guitext = guienv->addStaticText(
- L"Minetest",
+ narrow_to_wide(PROJECT_NAME).c_str(),
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
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;
+ *error_message = "Could not allocate memory for chat console";
+ errorstream << *error_message << std::endl;
return false;
}
const std::string &password, std::string *address, u16 port,
bool *connect_ok, bool *aborted)
{
- showOverlayMessage("Resolving address...", 0, 75);
+ *connect_ok = false; // Let's not be overly optimistic
+ *aborted = false;
+ bool local_server_mode = false;
+
+ showOverlayMessage(wgettext("Resolving address..."), 0, 15);
Address connect_address(0, 0, 0, 0, port);
} else {
connect_address.setAddress(127, 0, 0, 1);
}
+ local_server_mode = true;
}
} catch (ResolveError &e) {
- *error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = std::string("Couldn't resolve address: ") + e.what();
+ errorstream << *error_message << std::endl;
return 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;
+ *error_message = "Unable to connect to " +
+ connect_address.serializeString() +
+ " because IPv6 is disabled";
+ errorstream << *error_message << std::endl;
return false;
}
client = new Client(device,
- playername.c_str(), password, simple_singleplayer_mode,
+ playername.c_str(), password,
*draw_control, texture_src, shader_src,
itemdef_manager, nodedef_manager, sound, eventmgr,
connect_address.isIPv6());
gamedef = client; // Client acts as our GameDef
-
infostream << "Connecting to server at ";
connect_address.print(&infostream);
infostream << std::endl;
- client->connect(connect_address);
-
+ client->connect(connect_address, *address,
+ simple_singleplayer_mode || local_server_mode);
/*
Wait for server to accept connection
input->clear();
FpsControl fps_control = { 0 };
- f32 dtime; // in seconds
+ f32 dtime;
+ f32 wait_time = 0; // in seconds
+
+ fps_control.last_time = device->getTimer()->getTime();
while (device->run()) {
// Break conditions
if (client->accessDenied()) {
- *error_message = L"Access denied. Reason: "
+ *error_message = "Access denied. Reason: "
+ client->accessDeniedReason();
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ errorstream << *error_message << std::endl;
break;
}
break;
}
+ wait_time += dtime;
+ // Only time out if we aren't waiting for the server we started
+ if ((*address != "") && (wait_time > 10)) {
+ *error_message = "Connection timed out.";
+ errorstream << *error_message << std::endl;
+ break;
+ }
+
// Update status
- showOverlayMessage("Connecting to server...", dtime, 100);
+ showOverlayMessage(wgettext("Connecting to server..."), dtime, 20);
}
} catch (con::PeerNotFoundException &e) {
// TODO: Should something be done here? At least an info/error
FpsControl fps_control = { 0 };
f32 dtime; // in seconds
+ fps_control.last_time = device->getTimer()->getTime();
+
while (device->run()) {
limitFps(&fps_control, &dtime);
}
// Error conditions
- if (client->accessDenied()) {
- *error_message = L"Access denied. Reason: "
- + client->accessDeniedReason();
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ if (!checkConnection())
return false;
- }
if (client->getState() < LC_Init) {
- *error_message = L"Client disconnected";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Client disconnected";
+ errorstream << *error_message << std::endl;
return false;
}
}
// Display status
- int progress = 0;
+ int progress = 25;
if (!client->itemdefReceived()) {
- wchar_t *text = wgettext("Item definitions...");
- progress = 0;
+ const wchar_t *text = wgettext("Item definitions...");
+ progress = 25;
draw_load_screen(text, device, guienv, dtime, progress);
delete[] text;
} else if (!client->nodedefReceived()) {
- wchar_t *text = wgettext("Node definitions...");
- progress = 25;
+ const wchar_t *text = wgettext("Node definitions...");
+ progress = 30;
draw_load_screen(text, device, guienv, dtime, progress);
delete[] text;
} else {
message << " ( " << cur << cur_unit << " )";
}
- progress = 50 + client->mediaReceiveProgress() * 50 + 0.5;
- draw_load_screen(narrow_to_wide(message.str().c_str()), device,
+ progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
+ draw_load_screen(narrow_to_wide(message.str()), device,
guienv, dtime, progress);
}
}
}
-
+/****************************************************************************/
/****************************************************************************
Run
****************************************************************************/
+/****************************************************************************/
-inline void Game::updateInteractTimers(GameRunData *args, f32 dtime)
+inline void Game::updateInteractTimers(GameRunData *runData, f32 dtime)
{
- if (args->nodig_delay_timer >= 0)
- args->nodig_delay_timer -= dtime;
+ if (runData->nodig_delay_timer >= 0)
+ runData->nodig_delay_timer -= dtime;
- if (args->object_hit_delay_timer >= 0)
- args->object_hit_delay_timer -= dtime;
+ if (runData->object_hit_delay_timer >= 0)
+ runData->object_hit_delay_timer -= dtime;
- args->time_from_last_punch += dtime;
+ runData->time_from_last_punch += dtime;
}
inline bool Game::checkConnection()
{
if (client->accessDenied()) {
- *error_message = L"Access denied. Reason: "
+ *error_message = "Access denied. Reason: "
+ client->accessDeniedReason();
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ errorstream << *error_message << std::endl;
return false;
}
}
-void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
+void Game::updateProfilers(const GameRunData &runData, const RunStats &stats,
const FpsControl &draw_times, f32 dtime)
{
float profiler_print_interval =
}
update_profiler_gui(guitext_profiler, g_fontengine,
- run_data.profiler_current_page, run_data.profiler_max_page,
+ runData.profiler_current_page, runData.profiler_max_page,
driver->getScreenSize().Height);
g_profiler->clear();
****************************************************************************/
void Game::processUserInput(VolatileRunFlags *flags,
- GameRunData *interact_args, f32 dtime)
+ GameRunData *runData, f32 dtime)
{
// Reset input if window not active or some menu is active
if (device->isWindowActive() == false
#endif
// Increase timer for double tap of "keymap_jump"
- if (m_cache_doubletap_jump && interact_args->jump_timer <= 0.2)
- interact_args->jump_timer += dtime;
+ if (m_cache_doubletap_jump && runData->jump_timer <= 0.2)
+ runData->jump_timer += dtime;
processKeyboardInput(
flags,
- &interact_args->statustext_time,
- &interact_args->jump_timer,
- &interact_args->reset_jump_timer,
- &interact_args->profiler_current_page,
- interact_args->profiler_max_page);
+ &runData->statustext_time,
+ &runData->jump_timer,
+ &runData->reset_jump_timer,
+ &runData->profiler_current_page,
+ runData->profiler_max_page);
- processItemSelection(&interact_args->new_playeritem);
+ processItemSelection(&runData->new_playeritem);
}
toggleFast(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) {
toggleNoClip(statustext_time);
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CINEMATIC])) {
+ toggleCinematic(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) {
client->makeScreenshot(device);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) {
void Game::openInventory()
{
+ /*
+ * Don't permit to open inventory is CAO or player doesn't exists.
+ * This prevent showing an empty inventory at player load
+ */
+
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ if (player == NULL || player->getCAO() == NULL)
+ return;
+
infostream << "the_game: " << "Launching inventory" << std::endl;
PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
statustext += L" (note: no 'noclip' privilege)";
}
+void Game::toggleCinematic(float *statustext_time)
+{
+ static const wchar_t *msg[] = { L"cinematic disabled", L"cinematic enabled" };
+ bool cinematic = !g_settings->getBool("cinematic");
+ g_settings->set("cinematic", bool_to_cstr(cinematic));
+
+ *statustext_time = 0;
+ statustext = msg[cinematic];
+}
+
void Game::toggleChat(float *statustext_time, bool *flag)
{
void Game::updateCameraDirection(CameraOrientation *cam,
VolatileRunFlags *flags)
{
- // float turn_amount = 0; // Deprecated?
+ 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 (!(device->isWindowActive() && noMenuActive()) || random_input) {
+ if (flags->first_loop_after_window_activation)
+ flags->first_loop_after_window_activation = false;
+ else
+ updateCameraOrientation(cam, *flags);
- // FIXME: Clean this up
+ input->setMousePos((driver->getScreenSize().Width / 2),
+ (driver->getScreenSize().Height / 2));
+ } else {
#ifndef ANDROID
// Mac OSX gets upset if this is set every frame
device->getCursorControl()->setVisible(true);
#endif
- //infostream<<"window inactive"<<std::endl;
- flags->first_loop_after_window_activation = true;
- return;
- }
+ if (!flags->first_loop_after_window_activation)
+ flags->first_loop_after_window_activation = true;
-#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 {
+void Game::updateCameraOrientation(CameraOrientation *cam,
+ const VolatileRunFlags &flags)
+{
#ifdef HAVE_TOUCHSCREENGUI
-
- if (g_touchscreengui) {
- cam->camera_yaw = g_touchscreengui->getYaw();
- cam->camera_pitch = g_touchscreengui->getPitch();
- } else {
+ if (g_touchscreengui) {
+ cam->camera_yaw = g_touchscreengui->getYaw();
+ cam->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;
- }
+ s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
+ s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
- //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
+ if (flags.invert_mouse
+ || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
+ dy = -dy;
+ }
- float d = m_cache_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?
+ cam->camera_yaw -= dx * m_cache_mouse_sensitivity;
+ cam->camera_pitch += dy * m_cache_mouse_sensitivity;
#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;
}
+#endif
- 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;
+ cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
}
cam.camera_pitch,
cam.camera_yaw
);
+
+ u32 keypress_bits =
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) |
+ ( (u32)(input->getLeftState() & 0x1) << 7) |
+ ( (u32)(input->getRightState() & 0x1) << 8
+ );
+
+#ifdef ANDROID
+ /* For Android, invert the meaning of holding down the fast button (i.e.
+ * holding down the fast button -- if there is one -- means walk), unless
+ * performing an action, sneaking or jumping.
+ */
+ const u32 autofast_exludebits =
+ (1U << 4) | (1U << 6) // jump, sneak
+ | (1U << 7) | (1U << 8); // left state, right state
+
+ if ((keypress_bits & autofast_exludebits) == 0) {
+ control.aux1 = control.aux1 ^ true;
+ keypress_bits ^= ((u32)(1U << 5));
+ }
+#endif
+
client->setPlayerControl(control);
LocalPlayer *player = client->getEnv().getLocalPlayer();
- player->keyPressed =
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) |
- ( (u32)(input->getLeftState() & 0x1) << 7) |
- ( (u32)(input->getRightState() & 0x1) << 8
- );
+ player->keyPressed = keypress_bits;
//tt.stop();
}
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_SPAWN_PARTICLE) ||
+ (event.type == CE_ADD_PARTICLESPAWNER) ||
+ (event.type == CE_DELETE_PARTICLESPAWNER)) {
+ client->getParticleManager()->handleParticleEvent(&event, gamedef,
+ smgr, player);
} else if (event.type == CE_HUDADD) {
u32 id = event.hudadd.id;
u32 new_id = player->addHud(e);
//if this isn't true our huds aren't consistent
- assert(new_id == id);
+ sanity_check(new_id == id);
delete event.hudadd.pos;
delete event.hudadd.name;
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]));
+ texture_src->getTextureForMesh((*event.set_sky.params)[0]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[1]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[2]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[3]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[4]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[5]));
}
// Handle everything else as plain color
else {
v3s16 old_camera_offset = camera->getOffset();
if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) {
- camera->toggleCameraMode();
GenericCAO *playercao = player->getCAO();
- assert(playercao != NULL);
+ // If playercao not loaded, don't change camera
+ if (playercao == NULL)
+ return;
+
+ camera->toggleCameraMode();
playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
}
if (m_cache_enable_particles) {
const ContentFeatures &features =
client->getNodeDefManager()->get(n);
- addPunchingParticles(gamedef, smgr, player,
- client->getEnv(), nodepos, features.tiles);
+ client->getParticleManager()->addPunchingParticles(gamedef, smgr,
+ player, nodepos, features.tiles);
}
}
if (m_cache_enable_particles) {
const ContentFeatures &features =
client->getNodeDefManager()->get(wasnode);
- addDiggingParticles
- (gamedef, smgr, player, client->getEnv(),
- nodepos, features.tiles);
+ client->getParticleManager()->addDiggingParticles(gamedef, smgr,
+ player, nodepos, features.tiles);
}
runData->dig_time = 0;
/*
Update particles
*/
-
- allparticles_step(dtime);
- allparticlespawners_step(dtime, client->getEnv());
+ client->getParticleManager()->step(dtime);
/*
Fog
}
+inline static const char *yawToDirectionString(int yaw)
+{
+ // NOTE: TODO: This can be done mathematically without the else/else-if
+ // cascade.
+
+ const char *player_direction;
+
+ yaw = wrapDegrees_0_360(yaw);
+
+ if (yaw >= 45 && yaw < 135)
+ player_direction = "West [-X]";
+ else if (yaw >= 135 && yaw < 225)
+ player_direction = "South [-Z]";
+ else if (yaw >= 225 && yaw < 315)
+ player_direction = "East [+X]";
+ else
+ player_direction = "North [+Z]";
+
+ return player_direction;
+}
+
+
void Game::updateGui(float *statustext_time, const RunStats &stats,
const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
const CameraOrientation &cam)
std::ostringstream os(std::ios_base::binary);
os << std::fixed
- << "Minetest " << minetest_version_hash
+ << PROJECT_NAME " " << g_version_hash
<< " FPS = " << fps
<< " (R: range_all=" << draw_control->range_all << ")"
<< std::setprecision(0)
guitext->setVisible(true);
} else if (flags.show_hud || flags.show_chat) {
std::ostringstream os(std::ios_base::binary);
- os << "Minetest " << minetest_version_hash;
+ os << PROJECT_NAME " " << g_version_hash;
guitext->setText(narrow_to_wide(os.str()).c_str());
guitext->setVisible(true);
} else {
<< ", " << (player_position.Y / BS)
<< ", " << (player_position.Z / BS)
<< ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
+ << " " << yawToDirectionString(cam.camera_yaw)
<< ") (seed = " << ((u64)client->getMapSeed())
<< ")";
guitext_status->setVisible(!statustext.empty());
if (!statustext.empty()) {
- s32 status_y = screensize.Y - 130;
+ s32 status_width = guitext_status->getTextWidth();
+ s32 status_height = guitext_status->getTextHeight();
+ s32 status_y = screensize.Y - 150;
+ s32 status_x = (screensize.X - status_width) / 2;
core::rect<s32> rect(
- 10, status_y - guitext_status->getTextHeight(),
- 10 + guitext_status->getTextWidth(), status_y
+ status_x , status_y - status_height,
+ status_x + status_width, status_y
);
guitext_status->setRelativePosition(rect);
// not using getRealTime is necessary for wine
device->getTimer()->tick(); // Maker sure device time is up-to-date
u32 time = device->getTimer()->getTime();
-
u32 last_time = fps_timings->last_time;
if (time > last_time) // Make sure time hasn't overflowed
fps_timings->last_time = time;
}
-
-void Game::showOverlayMessage(const char *msg, float dtime,
+// Note: This will free (using delete[])! \p msg. If you want to use it later,
+// pass a copy of it to this function
+// Note: \p msg must be allocated using new (not malloc())
+void Game::showOverlayMessage(const wchar_t *msg, float dtime,
int percent, bool draw_clouds)
{
- wchar_t *text = wgettext(msg);
- draw_load_screen(text, device, guienv, dtime, percent, draw_clouds);
- delete[] text;
+ draw_load_screen(msg, device, guienv, dtime, percent, draw_clouds);
+ delete[] msg;
}
+/****************************************************************************/
/****************************************************************************
Shutdown / cleanup
****************************************************************************/
+/****************************************************************************/
void Game::extendedResourceCleanup()
{
}
-
+/****************************************************************************/
/****************************************************************************
extern function for launching the game
****************************************************************************/
+/****************************************************************************/
void the_game(bool *kill,
bool random_input,
const std::string &address, // If empty local server is created
u16 port,
- std::wstring &error_message,
+ std::string &error_message,
ChatBackend &chat_backend,
const SubgameSpec &gamespec, // Used for local game
bool simple_singleplayer_mode)
try {
if (game.startup(kill, random_input, input, device, map_dir,
- playername, password, &server_address, port,
- &error_message, &chat_backend, gamespec,
- simple_singleplayer_mode)) {
-
+ playername, password, &server_address, port,
+ error_message, &chat_backend, gamespec,
+ simple_singleplayer_mode)) {
game.run();
game.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;
+ error_message = std::string("A serialization error occurred:\n")
+ + e.what() + "\n\nThe server is probably "
+ " running a different version of " PROJECT_NAME ".";
+ errorstream << error_message << std::endl;
} catch (ServerError &e) {
- error_message = narrow_to_wide(e.what());
- errorstream << "ServerError: " << e.what() << std::endl;
+ error_message = e.what();
+ errorstream << "ServerError: " << error_message << std::endl;
} catch (ModError &e) {
- errorstream << "ModError: " << e.what() << std::endl;
- error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
+ error_message = e.what() + strgettext("\nCheck debug.txt for details.");
+ errorstream << "ModError: " << error_message << std::endl;
}
}
+