922 lines
25 KiB
C++
922 lines
25 KiB
C++
/*
|
|
Minetest
|
|
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <iomanip>
|
|
#include <cmath>
|
|
#include "client/renderingengine.h"
|
|
#include "camera.h"
|
|
#include "client.h"
|
|
#include "client/clientevent.h"
|
|
//#include "client/gameui.h"
|
|
#include "client/inputhandler.h"
|
|
#include "client/sound.h"
|
|
#include "client/tile.h" // For TextureSource
|
|
#include "client/keys.h"
|
|
#include "client/joystick_controller.h"
|
|
#include "clientmap.h"
|
|
#include "clouds.h"
|
|
#include "config.h"
|
|
#include "content_cao.h"
|
|
#include "client/event_manager.h"
|
|
#include "fontengine.h"
|
|
#include "itemdef.h"
|
|
#include "log.h"
|
|
#include "filesys.h"
|
|
#include "gettext.h"
|
|
#include "gui/cheatMenu.h"
|
|
#include "gui/guiChatConsole.h"
|
|
#include "gui/guiConfirmRegistration.h"
|
|
#include "gui/guiFormSpecMenu.h"
|
|
#include "gui/guiKeyChangeMenu.h"
|
|
#include "gui/guiPasswordChange.h"
|
|
#include "gui/guiVolumeChange.h"
|
|
#include "gui/mainmenumanager.h"
|
|
#include "gui/profilergraph.h"
|
|
#include "mapblock.h"
|
|
#include "minimap.h"
|
|
#include "nodedef.h" // Needed for determining pointing to nodes
|
|
#include "nodemetadata.h"
|
|
#include "particles.h"
|
|
#include "porting.h"
|
|
#include "profiler.h"
|
|
#include "raycast.h"
|
|
#include "server.h"
|
|
#include "settings.h"
|
|
#include "shader.h"
|
|
#include "sky.h"
|
|
#include "translation.h"
|
|
#include "util/basic_macros.h"
|
|
#include "util/directiontables.h"
|
|
#include "util/pointedthing.h"
|
|
#include "util/quicktune_shortcutter.h"
|
|
#include "irrlicht_changes/static_text.h"
|
|
#include "irr_ptr.h"
|
|
#include "version.h"
|
|
#include "script/scripting_client.h"
|
|
#include "hud.h"
|
|
#include "irrlichttypes.h"
|
|
#include <string>
|
|
|
|
class InputHandler;
|
|
class ChatBackend;
|
|
class RenderingEngine;
|
|
struct SubgameSpec;
|
|
struct GameStartData;
|
|
|
|
struct Jitter {
|
|
f32 max, min, avg, counter, max_sample, min_sample, max_fraction;
|
|
};
|
|
|
|
struct RunStats {
|
|
u32 drawtime;
|
|
|
|
Jitter dtime_jitter, busy_time_jitter;
|
|
};
|
|
|
|
struct CameraOrientation {
|
|
f32 camera_yaw; // "right/left"
|
|
f32 camera_pitch; // "up/down"
|
|
};
|
|
|
|
/*
|
|
Text input system
|
|
*/
|
|
|
|
struct TextDestNodeMetadata : public TextDest
|
|
{
|
|
TextDestNodeMetadata(v3s16 p, Client *client)
|
|
{
|
|
m_p = p;
|
|
m_client = client;
|
|
}
|
|
// This is deprecated I guess? -celeron55
|
|
void gotText(const std::wstring &text)
|
|
{
|
|
std::string ntext = wide_to_utf8(text);
|
|
infostream << "Submitting 'text' field of node at (" << m_p.X << ","
|
|
<< m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
|
|
StringMap fields;
|
|
fields["text"] = ntext;
|
|
m_client->sendNodemetaFields(m_p, "", fields);
|
|
}
|
|
void gotText(const StringMap &fields)
|
|
{
|
|
m_client->sendNodemetaFields(m_p, "", fields);
|
|
}
|
|
|
|
v3s16 m_p;
|
|
Client *m_client;
|
|
};
|
|
|
|
struct TextDestPlayerInventory : public TextDest
|
|
{
|
|
TextDestPlayerInventory(Client *client)
|
|
{
|
|
m_client = client;
|
|
m_formname = "";
|
|
}
|
|
TextDestPlayerInventory(Client *client, const std::string &formname)
|
|
{
|
|
m_client = client;
|
|
m_formname = formname;
|
|
}
|
|
void gotText(const StringMap &fields)
|
|
{
|
|
m_client->sendInventoryFields(m_formname, fields);
|
|
}
|
|
|
|
Client *m_client;
|
|
};
|
|
|
|
struct LocalFormspecHandler : public TextDest
|
|
{
|
|
LocalFormspecHandler(const std::string &formname)
|
|
{
|
|
m_formname = formname;
|
|
}
|
|
|
|
LocalFormspecHandler(const std::string &formname, Client *client):
|
|
m_client(client)
|
|
{
|
|
m_formname = formname;
|
|
}
|
|
|
|
void gotText(const StringMap &fields)
|
|
{
|
|
if (m_formname == "MT_PAUSE_MENU") {
|
|
if (fields.find("btn_sound") != fields.end()) {
|
|
g_gamecallback->changeVolume();
|
|
return;
|
|
}
|
|
|
|
if (fields.find("btn_key_config") != fields.end()) {
|
|
g_gamecallback->keyConfig();
|
|
return;
|
|
}
|
|
|
|
if (fields.find("btn_exit_menu") != fields.end()) {
|
|
g_gamecallback->disconnect();
|
|
return;
|
|
}
|
|
|
|
if (fields.find("btn_exit_os") != fields.end()) {
|
|
g_gamecallback->exitToOS();
|
|
#ifndef __ANDROID__
|
|
RenderingEngine::get_raw_device()->closeDevice();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (fields.find("btn_change_password") != fields.end()) {
|
|
g_gamecallback->changePassword();
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (m_formname == "MT_DEATH_SCREEN") {
|
|
assert(m_client != 0);
|
|
m_client->sendRespawn();
|
|
return;
|
|
}
|
|
|
|
if (m_client->modsLoaded())
|
|
m_client->getScript()->on_formspec_input(m_formname, fields);
|
|
}
|
|
|
|
Client *m_client = nullptr;
|
|
};
|
|
|
|
/* Form update callback */
|
|
|
|
class NodeMetadataFormSource: public IFormSource
|
|
{
|
|
public:
|
|
NodeMetadataFormSource(ClientMap *map, v3s16 p):
|
|
m_map(map),
|
|
m_p(p)
|
|
{
|
|
}
|
|
const std::string &getForm() const
|
|
{
|
|
static const std::string empty_string = "";
|
|
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
|
|
|
|
if (!meta)
|
|
return empty_string;
|
|
|
|
return meta->getString("formspec");
|
|
}
|
|
|
|
virtual std::string resolveText(const std::string &str)
|
|
{
|
|
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
|
|
|
|
if (!meta)
|
|
return str;
|
|
|
|
return meta->resolveString(str);
|
|
}
|
|
|
|
ClientMap *m_map;
|
|
v3s16 m_p;
|
|
};
|
|
|
|
class PlayerInventoryFormSource: public IFormSource
|
|
{
|
|
public:
|
|
PlayerInventoryFormSource(Client *client):
|
|
m_client(client)
|
|
{
|
|
}
|
|
|
|
const std::string &getForm() const
|
|
{
|
|
LocalPlayer *player = m_client->getEnv().getLocalPlayer();
|
|
return player->inventory_formspec;
|
|
}
|
|
|
|
Client *m_client;
|
|
};
|
|
|
|
class NodeDugEvent: public MtEvent
|
|
{
|
|
public:
|
|
v3s16 p;
|
|
MapNode n;
|
|
|
|
NodeDugEvent(v3s16 p, MapNode n):
|
|
p(p),
|
|
n(n)
|
|
{}
|
|
MtEvent::Type getType() const
|
|
{
|
|
return MtEvent::NODE_DUG;
|
|
}
|
|
};
|
|
|
|
class SoundMaker
|
|
{
|
|
ISoundManager *m_sound;
|
|
const NodeDefManager *m_ndef;
|
|
public:
|
|
bool makes_footstep_sound;
|
|
float m_player_step_timer;
|
|
float m_player_jump_timer;
|
|
|
|
SimpleSoundSpec m_player_step_sound;
|
|
SimpleSoundSpec m_player_leftpunch_sound;
|
|
SimpleSoundSpec m_player_rightpunch_sound;
|
|
|
|
SoundMaker(ISoundManager *sound, const NodeDefManager *ndef):
|
|
m_sound(sound),
|
|
m_ndef(ndef),
|
|
makes_footstep_sound(true),
|
|
m_player_step_timer(0.0f),
|
|
m_player_jump_timer(0.0f)
|
|
{
|
|
}
|
|
|
|
void playPlayerStep()
|
|
{
|
|
if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
|
|
m_player_step_timer = 0.03;
|
|
if (makes_footstep_sound)
|
|
m_sound->playSound(m_player_step_sound, false);
|
|
}
|
|
}
|
|
|
|
void playPlayerJump()
|
|
{
|
|
if (m_player_jump_timer <= 0.0f) {
|
|
m_player_jump_timer = 0.2f;
|
|
m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false);
|
|
}
|
|
}
|
|
|
|
static void viewBobbingStep(MtEvent *e, void *data)
|
|
{
|
|
SoundMaker *sm = (SoundMaker *)data;
|
|
sm->playPlayerStep();
|
|
}
|
|
|
|
static void playerRegainGround(MtEvent *e, void *data)
|
|
{
|
|
SoundMaker *sm = (SoundMaker *)data;
|
|
sm->playPlayerStep();
|
|
}
|
|
|
|
static void playerJump(MtEvent *e, void *data)
|
|
{
|
|
SoundMaker *sm = (SoundMaker *)data;
|
|
sm->playPlayerJump();
|
|
}
|
|
|
|
static void cameraPunchLeft(MtEvent *e, void *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;
|
|
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;
|
|
sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
|
|
}
|
|
|
|
static void playerDamage(MtEvent *e, void *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;
|
|
sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
|
|
}
|
|
|
|
void registerReceiver(MtEventManager *mgr)
|
|
{
|
|
mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this);
|
|
mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this);
|
|
mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this);
|
|
mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this);
|
|
mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this);
|
|
mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this);
|
|
mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this);
|
|
mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this);
|
|
}
|
|
|
|
void step(float dtime)
|
|
{
|
|
m_player_step_timer -= dtime;
|
|
m_player_jump_timer -= dtime;
|
|
}
|
|
};
|
|
|
|
// Locally stored sounds don't need to be preloaded because of this
|
|
class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
|
|
{
|
|
std::set<std::string> m_fetched;
|
|
private:
|
|
void paths_insert(std::set<std::string> &dst_paths,
|
|
const std::string &base,
|
|
const std::string &name)
|
|
{
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg");
|
|
dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg");
|
|
}
|
|
public:
|
|
void fetchSounds(const std::string &name,
|
|
std::set<std::string> &dst_paths,
|
|
std::set<std::string> &dst_datas)
|
|
{
|
|
if (m_fetched.count(name))
|
|
return;
|
|
|
|
m_fetched.insert(name);
|
|
|
|
paths_insert(dst_paths, porting::path_share, name);
|
|
paths_insert(dst_paths, porting::path_user, name);
|
|
}
|
|
};
|
|
|
|
|
|
typedef s32 SamplerLayer_t;
|
|
|
|
|
|
class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
|
{
|
|
Sky *m_sky;
|
|
bool *m_force_fog_off;
|
|
f32 *m_fog_range;
|
|
bool m_fog_enabled;
|
|
CachedPixelShaderSetting<float, 4> m_sky_bg_color;
|
|
CachedPixelShaderSetting<float> m_fog_distance;
|
|
CachedVertexShaderSetting<float> m_animation_timer_vertex;
|
|
CachedPixelShaderSetting<float> m_animation_timer_pixel;
|
|
CachedPixelShaderSetting<float, 3> m_day_light;
|
|
CachedPixelShaderSetting<float, 4> m_star_color;
|
|
CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
|
|
CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
|
|
CachedPixelShaderSetting<float, 3> m_minimap_yaw;
|
|
CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
|
|
CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
|
|
CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
|
|
CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture;
|
|
Client *m_client;
|
|
|
|
public:
|
|
void onSettingsChange(const std::string &name)
|
|
{
|
|
if (name == "enable_fog")
|
|
m_fog_enabled = g_settings->getBool("enable_fog");
|
|
}
|
|
|
|
static void settingsCallback(const std::string &name, void *userdata)
|
|
{
|
|
reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
|
|
}
|
|
|
|
void setSky(Sky *sky) { m_sky = sky; }
|
|
|
|
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_sky_bg_color("skyBgColor"),
|
|
m_fog_distance("fogDistance"),
|
|
m_animation_timer_vertex("animationTimer"),
|
|
m_animation_timer_pixel("animationTimer"),
|
|
m_day_light("dayLight"),
|
|
m_star_color("starColor"),
|
|
m_eye_position_pixel("eyePosition"),
|
|
m_eye_position_vertex("eyePosition"),
|
|
m_minimap_yaw("yawVec"),
|
|
m_camera_offset_pixel("cameraOffset"),
|
|
m_camera_offset_vertex("cameraOffset"),
|
|
m_base_texture("baseTexture"),
|
|
m_normal_texture("normalTexture"),
|
|
m_client(client)
|
|
{
|
|
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
|
|
m_fog_enabled = g_settings->getBool("enable_fog");
|
|
}
|
|
|
|
~GameGlobalShaderConstantSetter()
|
|
{
|
|
g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this);
|
|
}
|
|
|
|
void onSetConstants(video::IMaterialRendererServices *services) override
|
|
{
|
|
// Background color
|
|
video::SColor bgcolor = m_sky->getBgColor();
|
|
video::SColorf bgcolorf(bgcolor);
|
|
float bgcolorfa[4] = {
|
|
bgcolorf.r,
|
|
bgcolorf.g,
|
|
bgcolorf.b,
|
|
bgcolorf.a,
|
|
};
|
|
m_sky_bg_color.set(bgcolorfa, services);
|
|
|
|
// Fog distance
|
|
float fog_distance = 10000 * BS;
|
|
|
|
if (m_fog_enabled && !*m_force_fog_off)
|
|
fog_distance = *m_fog_range;
|
|
|
|
m_fog_distance.set(&fog_distance, services);
|
|
|
|
u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
|
|
video::SColorf sunlight;
|
|
get_sunlight_color(&sunlight, daynight_ratio);
|
|
float dnc[3] = {
|
|
sunlight.r,
|
|
sunlight.g,
|
|
sunlight.b };
|
|
m_day_light.set(dnc, services);
|
|
|
|
video::SColorf star_color = m_sky->getCurrentStarColor();
|
|
float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
|
|
m_star_color.set(clr, services);
|
|
|
|
u32 animation_timer = porting::getTimeMs() % 1000000;
|
|
float animation_timer_f = (float)animation_timer / 100000.f;
|
|
m_animation_timer_vertex.set(&animation_timer_f, services);
|
|
m_animation_timer_pixel.set(&animation_timer_f, services);
|
|
|
|
float eye_position_array[3];
|
|
v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
|
|
epos.getAs3Values(eye_position_array);
|
|
m_eye_position_pixel.set(eye_position_array, services);
|
|
m_eye_position_vertex.set(eye_position_array, services);
|
|
|
|
if (m_client->getMinimap()) {
|
|
float minimap_yaw_array[3];
|
|
v3f minimap_yaw = m_client->getMinimap()->getYawVec();
|
|
minimap_yaw.getAs3Values(minimap_yaw_array);
|
|
m_minimap_yaw.set(minimap_yaw_array, services);
|
|
}
|
|
|
|
float camera_offset_array[3];
|
|
v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS);
|
|
offset.getAs3Values(camera_offset_array);
|
|
m_camera_offset_pixel.set(camera_offset_array, services);
|
|
m_camera_offset_vertex.set(camera_offset_array, services);
|
|
|
|
SamplerLayer_t base_tex = 0, normal_tex = 1;
|
|
m_base_texture.set(&base_tex, services);
|
|
m_normal_texture.set(&normal_tex, services);
|
|
}
|
|
};
|
|
|
|
|
|
class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory
|
|
{
|
|
Sky *m_sky;
|
|
bool *m_force_fog_off;
|
|
f32 *m_fog_range;
|
|
Client *m_client;
|
|
std::vector<GameGlobalShaderConstantSetter *> created_nosky;
|
|
public:
|
|
GameGlobalShaderConstantSetterFactory(bool *force_fog_off,
|
|
f32 *fog_range, Client *client) :
|
|
m_sky(NULL),
|
|
m_force_fog_off(force_fog_off),
|
|
m_fog_range(fog_range),
|
|
m_client(client)
|
|
{}
|
|
|
|
void setSky(Sky *sky) {
|
|
m_sky = sky;
|
|
for (GameGlobalShaderConstantSetter *ggscs : created_nosky) {
|
|
ggscs->setSky(m_sky);
|
|
}
|
|
created_nosky.clear();
|
|
}
|
|
|
|
virtual IShaderConstantSetter* create()
|
|
{
|
|
auto *scs = new GameGlobalShaderConstantSetter(
|
|
m_sky, m_force_fog_off, m_fog_range, m_client);
|
|
if (!m_sky)
|
|
created_nosky.push_back(scs);
|
|
return scs;
|
|
}
|
|
};
|
|
|
|
#ifdef __ANDROID__
|
|
#define SIZE_TAG "size[11,5.5]"
|
|
#else
|
|
#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
****************************************************************************/
|
|
|
|
const float object_hit_delay = 0.2;
|
|
|
|
struct FpsControl {
|
|
u32 last_time, busy_time, sleep_time;
|
|
};
|
|
|
|
|
|
/* 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)
|
|
*/
|
|
|
|
struct GameRunData {
|
|
u16 dig_index;
|
|
u16 new_playeritem;
|
|
PointedThing pointed_old;
|
|
bool digging;
|
|
bool punching;
|
|
bool btn_down_for_dig;
|
|
bool dig_instantly;
|
|
bool digging_blocked;
|
|
bool reset_jump_timer;
|
|
float nodig_delay_timer;
|
|
float dig_time;
|
|
float dig_time_complete;
|
|
float repeat_place_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;
|
|
|
|
f32 fog_range;
|
|
|
|
v3f update_draw_list_last_cam_dir;
|
|
|
|
float time_of_day_smooth;
|
|
};
|
|
|
|
class Game;
|
|
|
|
struct ClientEventHandler
|
|
{
|
|
void (Game::*handler)(ClientEvent *, CameraOrientation *);
|
|
};
|
|
|
|
using PausedNodesList = std::vector<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, float>>;
|
|
|
|
class Game {
|
|
public:
|
|
Game();
|
|
~Game();
|
|
|
|
bool startup(bool *kill,
|
|
InputHandler *input,
|
|
RenderingEngine *rendering_engine,
|
|
const GameStartData &game_params,
|
|
std::string &error_message,
|
|
bool *reconnect,
|
|
ChatBackend *chat_backend);
|
|
|
|
|
|
void run();
|
|
void shutdown();
|
|
|
|
// Basic initialisation
|
|
bool init(const std::string &map_dir, const std::string &address,
|
|
u16 port, const SubgameSpec &gamespec);
|
|
bool initSound();
|
|
bool createSingleplayerServer(const std::string &map_dir,
|
|
const SubgameSpec &gamespec, u16 port);
|
|
|
|
// Client creation
|
|
bool createClient(const GameStartData &start_data);
|
|
bool initGui();
|
|
|
|
// Client connection
|
|
bool connectToServer(const GameStartData &start_data,
|
|
bool *connect_ok, bool *aborted);
|
|
bool getServerContent(bool *aborted);
|
|
|
|
// Main loop
|
|
|
|
void updateInteractTimers(f32 dtime);
|
|
bool checkConnection();
|
|
bool handleCallbacks();
|
|
void processQueues();
|
|
void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime);
|
|
void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
|
|
void updateProfilerGraphs(ProfilerGraph *graph);
|
|
|
|
// Input related
|
|
void processUserInput(f32 dtime);
|
|
void processKeyInput();
|
|
void processItemSelection(u16 *new_playeritem);
|
|
|
|
void dropSelectedItem(bool single_item = false);
|
|
void openInventory();
|
|
void openEnderchest();
|
|
void openConsole(float scale, const wchar_t *line=NULL);
|
|
void toggleFreeMove();
|
|
void toggleFreeMoveAlt();
|
|
void togglePitchMove();
|
|
void toggleFast();
|
|
void toggleNoClip();
|
|
void toggleKillaura();
|
|
void toggleFreecam();
|
|
void toggleScaffold();
|
|
void toggleNextItem();
|
|
void toggleCinematic();
|
|
void toggleAutoforward();
|
|
|
|
void toggleMinimap(bool shift_pressed);
|
|
void toggleFog();
|
|
void toggleDebug();
|
|
void toggleUpdateCamera();
|
|
void updatePlayerCAOVisibility();
|
|
|
|
void increaseViewRange();
|
|
void decreaseViewRange();
|
|
void toggleFullViewRange();
|
|
void checkZoomEnabled();
|
|
|
|
void updateCameraDirection(CameraOrientation *cam, float dtime);
|
|
void updateCameraOrientation(CameraOrientation *cam, float dtime);
|
|
void updatePlayerControl(const CameraOrientation &cam);
|
|
void step(f32 *dtime);
|
|
void processClientEvents(CameraOrientation *cam);
|
|
void updateCamera(u32 busy_time, f32 dtime);
|
|
void updateSound(f32 dtime);
|
|
void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug);
|
|
/*!
|
|
* Returns the object or node the player is pointing at.
|
|
* Also updates the selected thing in the Hud.
|
|
*
|
|
* @param[in] shootline the shootline, starting from
|
|
* the camera position. This also gives the maximal distance
|
|
* of the search.
|
|
* @param[in] liquids_pointable if false, liquids are ignored
|
|
* @param[in] look_for_object if false, objects are ignored
|
|
* @param[in] camera_offset offset of the camera
|
|
* @param[out] selected_object the selected object or
|
|
* NULL if not found
|
|
*/
|
|
PointedThing updatePointedThing(
|
|
const core::line3d<f32> &shootline, bool liquids_pointable,
|
|
bool look_for_object, const v3s16 &camera_offset);
|
|
void handlePointingAtNothing(const ItemStack &playerItem);
|
|
void handlePointingAtNode(const PointedThing &pointed,
|
|
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
|
|
void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
|
|
const v3f &player_position, bool show_debug);
|
|
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
|
|
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
|
|
void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
|
|
const CameraOrientation &cam);
|
|
|
|
// Misc
|
|
void limitFps(FpsControl *fps_timings, f32 *dtime);
|
|
|
|
void showOverlayMessage(const char *msg, float dtime, int percent,
|
|
bool draw_clouds = true);
|
|
|
|
static void freecamChangedCallback(const std::string &setting_name, void *data);
|
|
static void settingChangedCallback(const std::string &setting_name, void *data);
|
|
static void updateAllMapBlocksCallback(const std::string &setting_name, void *data);
|
|
void readSettings();
|
|
|
|
bool isKeyDown(GameKeyType k);
|
|
bool wasKeyDown(GameKeyType k);
|
|
bool wasKeyPressed(GameKeyType k);
|
|
bool wasKeyReleased(GameKeyType k);
|
|
|
|
#ifdef __ANDROID__
|
|
void handleAndroidChatInput();
|
|
#endif
|
|
|
|
struct Flags {
|
|
bool force_fog_off = false;
|
|
bool disable_camera_update = false;
|
|
};
|
|
|
|
void showDeathFormspec();
|
|
void showPauseMenu();
|
|
|
|
void pauseAnimation();
|
|
void resumeAnimation();
|
|
|
|
// ClientEvent handlers
|
|
void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_HandleParticleEvent(ClientEvent *event,
|
|
CameraOrientation *cam);
|
|
void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam);
|
|
void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
|
|
CameraOrientation *cam);
|
|
void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
|
|
|
|
void updateChat(f32 dtime, const v2u32 &screensize);
|
|
|
|
bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item,
|
|
const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed,
|
|
const NodeMetadata *meta, bool force = false);
|
|
static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
|
|
|
|
f32 getSensitivityScaleFactor() const;
|
|
|
|
InputHandler *input = nullptr;
|
|
|
|
Client *client = nullptr;
|
|
Server *server = nullptr;
|
|
|
|
IWritableTextureSource *texture_src = nullptr;
|
|
IWritableShaderSource *shader_src = nullptr;
|
|
|
|
// When created, these will be filled with data received from the server
|
|
IWritableItemDefManager *itemdef_manager = nullptr;
|
|
NodeDefManager *nodedef_manager = nullptr;
|
|
|
|
GameOnDemandSoundFetcher soundfetcher; // useful when testing
|
|
ISoundManager *sound = nullptr;
|
|
bool sound_is_dummy = false;
|
|
SoundMaker *soundmaker = nullptr;
|
|
|
|
ChatBackend *chat_backend = nullptr;
|
|
LogOutputBuffer m_chat_log_buf;
|
|
|
|
EventManager *eventmgr = nullptr;
|
|
QuicktuneShortcutter *quicktune = nullptr;
|
|
bool registration_confirmation_shown = false;
|
|
|
|
std::unique_ptr<GameUI> m_game_ui;
|
|
GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
|
|
CheatMenu *m_cheat_menu = nullptr;
|
|
MapDrawControl *draw_control = nullptr;
|
|
Camera *camera = nullptr;
|
|
Clouds *clouds = nullptr; // Free using ->Drop()
|
|
Sky *sky = nullptr; // Free using ->Drop()
|
|
Hud *hud = nullptr;
|
|
Minimap *mapper = nullptr;
|
|
|
|
// Map server hud ids to client hud ids
|
|
std::unordered_map<u32, u32> m_hud_server_to_client;
|
|
|
|
GameRunData runData;
|
|
Flags m_flags;
|
|
|
|
/* 'cache'
|
|
This class does take ownership/responsibily for cleaning up etc of any of
|
|
these items (e.g. device)
|
|
*/
|
|
IrrlichtDevice *device;
|
|
RenderingEngine *m_rendering_engine;
|
|
video::IVideoDriver *driver;
|
|
scene::ISceneManager *smgr;
|
|
bool *kill;
|
|
std::string *error_message;
|
|
bool *reconnect_requested;
|
|
scene::ISceneNode *skybox;
|
|
PausedNodesList paused_animated_nodes;
|
|
|
|
bool simple_singleplayer_mode;
|
|
/* End 'cache' */
|
|
|
|
/* Pre-calculated values
|
|
*/
|
|
int crack_animation_length;
|
|
|
|
IntervalLimiter profiler_interval;
|
|
|
|
/*
|
|
* TODO: Local caching of settings is not optimal and should at some stage
|
|
* be updated to use a global settings object for getting thse values
|
|
* (as opposed to the this local caching). This can be addressed in
|
|
* a later release.
|
|
*/
|
|
bool m_cache_doubletap_jump;
|
|
bool m_cache_enable_clouds;
|
|
bool m_cache_enable_joysticks;
|
|
bool m_cache_enable_particles;
|
|
bool m_cache_enable_fog;
|
|
bool m_cache_enable_noclip;
|
|
bool m_cache_enable_free_move;
|
|
f32 m_cache_mouse_sensitivity;
|
|
f32 m_cache_joystick_frustum_sensitivity;
|
|
f32 m_repeat_place_time;
|
|
f32 m_cache_cam_smoothing;
|
|
f32 m_cache_fog_start;
|
|
|
|
bool m_invert_mouse = false;
|
|
bool m_first_loop_after_window_activation = false;
|
|
bool m_camera_offset_changed = false;
|
|
|
|
bool m_does_lost_focus_pause_game = false;
|
|
|
|
CameraOrientation cam_view_target = { 0 };
|
|
CameraOrientation cam_view = { 0 };
|
|
|
|
int m_reset_HW_buffer_counter = 0;
|
|
#ifdef __ANDROID__
|
|
bool m_cache_hold_aux1;
|
|
bool m_android_chat_open;
|
|
#endif
|
|
};
|
|
extern Game *g_game;
|
|
|
|
void the_game(bool *kill,
|
|
InputHandler *input,
|
|
RenderingEngine *rendering_engine,
|
|
const GameStartData &start_data,
|
|
std::string &error_message,
|
|
ChatBackend &chat_backend,
|
|
bool *reconnect_requested);
|