#include "particles.h"
#include "porting.h"
#include "profiler.h"
-#include "quicktune_shortcutter.h"
#include "raycast.h"
#include "server.h"
#include "settings.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 "version.h"
#include "script/scripting_client.h"
#else
#include "client/sound.h"
#endif
-/*
- 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;
- }
-
- if (fields.find("quit") != fields.end()) {
- return;
- }
-
- if (fields.find("btn_continue") != fields.end()) {
- return;
- }
- }
-
- if (m_formname == "MT_DEATH_SCREEN") {
- assert(m_client != 0);
- m_client->sendRespawn();
- return;
- }
-
- if (m_client && m_client->moddingEnabled())
- 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;
-
- 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)
- {
- }
-
- 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);
- }
- }
-
- 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;
- }
-
- 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;
- }
-};
-
-// 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);
- }
-};
-
-
-// before 1.8 there isn't a "integer interface", only float
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
-typedef f32 SamplerLayer_t;
-#else
-typedef s32 SamplerLayer_t;
-#endif
-
-
-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, 3> m_eye_position_pixel;
- CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
- CachedPixelShaderSetting<float, 3> m_minimap_yaw;
- CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
- CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture;
- CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags;
- 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_eye_position_pixel("eyePosition"),
- m_eye_position_vertex("eyePosition"),
- m_minimap_yaw("yawVec"),
- m_base_texture("baseTexture"),
- m_normal_texture("normalTexture"),
- m_texture_flags("textureFlags"),
- 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);
- }
-
- virtual void onSetConstants(video::IMaterialRendererServices *services,
- bool is_highlevel)
- {
- if (!is_highlevel)
- return;
-
- // 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);
-
- u32 animation_timer = porting::getTimeMs() % 100000;
- 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();
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
- eye_position_array[0] = epos.X;
- eye_position_array[1] = epos.Y;
- eye_position_array[2] = epos.Z;
-#else
- epos.getAs3Values(eye_position_array);
-#endif
- 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();
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
- minimap_yaw_array[0] = minimap_yaw.X;
- minimap_yaw_array[1] = minimap_yaw.Y;
- minimap_yaw_array[2] = minimap_yaw.Z;
-#else
- minimap_yaw.getAs3Values(minimap_yaw_array);
-#endif
- m_minimap_yaw.set(minimap_yaw_array, services);
- }
-
- SamplerLayer_t base_tex = 0,
- normal_tex = 1,
- flags_tex = 2;
- m_base_texture.set(&base_tex, services);
- m_normal_texture.set(&normal_tex, services);
- m_texture_flags.set(&flags_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()
- {
- GameGlobalShaderConstantSetter *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 ldown_for_dig;
- bool dig_instantly;
- bool digging_blocked;
- bool left_punch;
- bool update_wielded_item_trigger;
- bool reset_jump_timer;
- 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;
-
- f32 fog_range;
-
- v3f update_draw_list_last_cam_dir;
-
- float time_of_day_smooth;
-};
-
-class Game;
-
-struct ClientEventHandler
-{
- void (Game::*handler)(ClientEvent *, CameraOrientation *);
-};
-
-/****************************************************************************
- THE GAME
- ****************************************************************************/
-
-/* 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 Game {
-public:
- Game();
- ~Game();
-
- bool startup(bool *kill,
- bool random_input,
- InputHandler *input,
- 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::string &error_message,
- bool *reconnect,
- 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);
- bool initGui();
-
- // 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(f32 dtime);
- bool checkConnection();
- bool handleCallbacks();
- void processQueues();
- void updateProfilers(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);
-
- // Input related
- void processUserInput(f32 dtime);
- void processKeyInput();
- void processItemSelection(u16 *new_playeritem);
-
- void dropSelectedItem(bool single_item = false);
- void openInventory();
- void openConsole(float scale, const wchar_t *line=NULL);
- void toggleFreeMove();
- void toggleFreeMoveAlt();
- void togglePitchMove();
- void toggleFast();
- void toggleNoClip();
- void toggleCinematic();
- void toggleAutoforward();
-
- void toggleMinimap(bool shift_pressed);
- void toggleFog();
- void toggleDebug();
- void toggleUpdateCamera();
-
- 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);
- void updateProfilerGraphs(ProfilerGraph *graph);
-
- // Misc
- void limitFps(FpsControl *fps_timings, f32 *dtime);
-
- void showOverlayMessage(const char *msg, float dtime, int percent,
- bool draw_clouds = true);
-
- static void settingChangedCallback(const std::string &setting_name, void *data);
- void readSettings();
-
- inline bool isKeyDown(GameKeyType k)
- {
- return input->isKeyDown(k);
- }
- inline bool wasKeyDown(GameKeyType k)
- {
- return input->wasKeyDown(k);
- }
-
-#ifdef __ANDROID__
- void handleAndroidChatInput();
-#endif
-
-private:
- struct Flags {
- bool force_fog_off = false;
- bool disable_camera_update = false;
- };
-
- void showDeathFormspec();
- void showPauseMenu();
-
- // 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_OverrideDayNigthRatio(ClientEvent *event,
- CameraOrientation *cam);
- void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
-
- void updateChat(f32 dtime, const v2u32 &screensize);
-
- bool nodePlacementPrediction(const ItemDefinition &selected_def,
- const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos);
- static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
-
- 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;
-
- 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()
- 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;
-
- 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;
- video::IVideoDriver *driver;
- scene::ISceneManager *smgr;
- bool *kill;
- std::string *error_message;
- bool *reconnect_requested;
- scene::ISceneNode *skybox;
-
- bool random_input;
- 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_right_click_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;
-
-#ifdef __ANDROID__
- bool m_cache_hold_aux1;
- bool m_android_chat_open;
-#endif
-};
Game::Game() :
m_game_ui(new GameUI())
// Reinit runData
runData = GameRunData();
runData.time_from_last_punch = 10.0;
- runData.update_wielded_item_trigger = true;
m_game_ui->initFlags();
previous_screen_size = current_screen_size;
}
- /* Must be called immediately after a device->run() call because it
- * uses device->getTimer()->getTime()
- */
+ // Calculate dtime =
+ // RenderingEngine::run() from this iteration
+ // + Sleep time until the wanted FPS are reached
limitFps(&draw_times, &dtime);
+ // Prepare render data for next iteration
+
updateStats(&stats, draw_times, dtime);
updateInteractTimers(dtime);
}
-void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime)
+void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
+ f32 dtime)
{
float profiler_print_interval =
g_settings->getFloat("profiler_print_interval");
if (profiler_print_interval == 0) {
print_to_log = false;
- profiler_print_interval = 5;
+ profiler_print_interval = 3;
}
if (profiler_interval.step(dtime, profiler_print_interval)) {
g_profiler->clear();
}
- addProfilerGraphs(stats, draw_times, dtime);
-}
-
+ // Update update graphs
+ g_profiler->graphAdd("Time non-rendering [ms]",
+ draw_times.busy_time - stats.drawtime);
-void Game::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);
+ g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time);
+ g_profiler->graphAdd("FPS", 1.0f / dtime);
}
-
void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
f32 dtime)
{
} else if (wasKeyDown(KeyType::CMD)) {
openConsole(0.2, L"/");
} else if (wasKeyDown(KeyType::CMD_LOCAL)) {
- if (client->moddingEnabled())
+ if (client->modsLoaded())
openConsole(0.2, L".");
else
m_game_ui->showStatusText(wgettext("Client side scripting is disabled"));
toggleFast();
} else if (wasKeyDown(KeyType::NOCLIP)) {
toggleNoClip();
+ } else if (wasKeyDown(KeyType::XRAY)) {
+ toggleXray();
+ } else if (wasKeyDown(KeyType::FULLBRIGHT)) {
+ toggleFullbright();
+ } else if (wasKeyDown(KeyType::KILLAURA)) {
+ toggleKillaura();
} else if (wasKeyDown(KeyType::MUTE)) {
bool new_mute_sound = !g_settings->getBool("mute_sound");
g_settings->setBool("mute_sound", new_mute_sound);
InventoryLocation inventoryloc;
inventoryloc.setCurrentPlayer();
- if (!client->moddingEnabled()
+ if (!client->modsLoaded()
|| !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
TextDest *txt_dst = new TextDestPlayerInventory(client);
auto *&formspec = m_game_ui->updateFormspec("");
}
}
+void Game::toggleXray()
+{
+ bool xray = ! g_settings->getBool("xray");
+ g_settings->set("xray", bool_to_cstr(xray));
+
+ if (xray) {
+ m_game_ui->showTranslatedStatusText("Xray enabled");
+ } else {
+ m_game_ui->showTranslatedStatusText("Xray disabled");
+ }
+ client->m_mesh_update_thread.doUpdate();
+}
+
+void Game::toggleFullbright()
+{
+ bool fullbright = ! g_settings->getBool("fullbright");
+ g_settings->set("fullbright", bool_to_cstr(fullbright));
+
+ if (fullbright) {
+ m_game_ui->showTranslatedStatusText("Fullbright enabled");
+ } else {
+ m_game_ui->showTranslatedStatusText("Fullbright disabled");
+ }
+ client->m_mesh_update_thread.doUpdate();
+}
+
+void Game::toggleKillaura()
+{
+ bool killaura = ! g_settings->getBool("killaura");
+ g_settings->set("killaura", bool_to_cstr(killaura));
+
+ if (killaura) {
+ m_game_ui->showTranslatedStatusText("Killaura enabled");
+ } else {
+ m_game_ui->showTranslatedStatusText("Killaura disabled");
+ }
+}
+
void Game::toggleCinematic()
{
bool cinematic = !g_settings->getBool("cinematic");
void Game::checkZoomEnabled()
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
- if (player->getZoomFOV() < 0.001f)
+ if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f)
m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod");
}
}
// autoforward if set: simulate "up" key
- if (player->getPlayerSettings().continuous_forward) {
+ if (player->getPlayerSettings().continuous_forward &&
+ client->activeObjectsReceived() && !player->isDead()) {
control.up = true;
keypress_bits |= 1U << 0;
}
bool can_be_and_is_paused =
(simple_singleplayer_mode && g_menumgr.pausesGame());
- if (can_be_and_is_paused) { // This is for a singleplayer server
+ if (can_be_and_is_paused) { // This is for a singleplayer server
*dtime = 0; // No time passes
} else {
if (server)
{&Game::handleClientEvent_HudRemove},
{&Game::handleClientEvent_HudChange},
{&Game::handleClientEvent_SetSky},
+ {&Game::handleClientEvent_SetSun},
+ {&Game::handleClientEvent_SetMoon},
+ {&Game::handleClientEvent_SetStars},
{&Game::handleClientEvent_OverrideDayNigthRatio},
{&Game::handleClientEvent_CloudParams},
};
void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam)
{
- if (client->moddingEnabled()) {
+ if (client->modsLoaded())
client->getScript()->on_damage_taken(event->player_damage.amount);
- }
// Damage flash and hurt tilt are not used at death
if (client->getHP() > 0) {
{
// If client scripting is enabled, deathscreen is handled by CSM code in
// builtin/client/init.lua
- if (client->moddingEnabled())
+ if (client->modsLoaded())
client->getScript()->on_death();
else
showDeathFormspec();
e->offset = *event->hudadd.offset;
e->world_pos = *event->hudadd.world_pos;
e->size = *event->hudadd.size;
+ e->z_index = event->hudadd.z_index;
hud_server_to_client[server_id] = player->addHud(e);
delete event->hudadd.pos;
case HUD_STAT_SIZE:
e->size = *event->hudchange.v2s32data;
break;
+
+ case HUD_STAT_Z_INDEX:
+ e->z_index = event->hudchange.data;
+ break;
}
delete event->hudchange.v3fdata;
void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
{
sky->setVisible(false);
- // Whether clouds are visible in front of a custom skybox
- sky->setCloudsEnabled(event->set_sky.clouds);
+ // Whether clouds are visible in front of a custom skybox.
+ sky->setCloudsEnabled(event->set_sky->clouds);
if (skybox) {
skybox->remove();
skybox = NULL;
}
-
+ // Clear the old textures out in case we switch rendering type.
+ sky->clearSkyboxTextures();
// Handle according to type
- if (*event->set_sky.type == "regular") {
+ if (event->set_sky->type == "regular") {
+ // Shows the mesh skybox
sky->setVisible(true);
- sky->setCloudsEnabled(true);
- } else if (*event->set_sky.type == "skybox" &&
- event->set_sky.params->size() == 6) {
- sky->setFallbackBgColor(*event->set_sky.bgcolor);
- skybox = RenderingEngine::get_scene_manager()->addSkyBoxSceneNode(
- 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 {
- if (*event->set_sky.type != "plain")
+ // Update mesh based skybox colours if applicable.
+ sky->setSkyColors(*event->set_sky);
+ sky->setHorizonTint(
+ event->set_sky->sun_tint,
+ event->set_sky->moon_tint,
+ event->set_sky->tint_type
+ );
+ } else if (event->set_sky->type == "skybox" &&
+ event->set_sky->textures.size() == 6) {
+ // Disable the dyanmic mesh skybox:
+ sky->setVisible(false);
+ // Set fog colors:
+ sky->setFallbackBgColor(event->set_sky->bgcolor);
+ // Set sunrise and sunset fog tinting:
+ sky->setHorizonTint(
+ event->set_sky->sun_tint,
+ event->set_sky->moon_tint,
+ event->set_sky->tint_type
+ );
+ // Add textures to skybox.
+ for (int i = 0; i < 6; i++)
+ sky->addTextureToSkybox(event->set_sky->textures[i], i, texture_src);
+ } else {
+ // Handle everything else as plain color.
+ if (event->set_sky->type != "plain")
infostream << "Unknown sky type: "
- << (*event->set_sky.type) << std::endl;
-
- sky->setFallbackBgColor(*event->set_sky.bgcolor);
+ << (event->set_sky->type) << std::endl;
+ sky->setVisible(false);
+ sky->setFallbackBgColor(event->set_sky->bgcolor);
+ // Disable directional sun/moon tinting on plain or invalid skyboxes.
+ sky->setHorizonTint(
+ event->set_sky->bgcolor,
+ event->set_sky->bgcolor,
+ "custom"
+ );
}
+ delete event->set_sky;
+}
+
+void Game::handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam)
+{
+ sky->setSunVisible(event->sun_params->visible);
+ sky->setSunTexture(event->sun_params->texture,
+ event->sun_params->tonemap, texture_src);
+ sky->setSunScale(event->sun_params->scale);
+ sky->setSunriseVisible(event->sun_params->sunrise_visible);
+ sky->setSunriseTexture(event->sun_params->sunrise, texture_src);
+ delete event->sun_params;
+}
+
+void Game::handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam)
+{
+ sky->setMoonVisible(event->moon_params->visible);
+ sky->setMoonTexture(event->moon_params->texture,
+ event->moon_params->tonemap, texture_src);
+ sky->setMoonScale(event->moon_params->scale);
+ delete event->moon_params;
+}
- delete event->set_sky.bgcolor;
- delete event->set_sky.type;
- delete event->set_sky.params;
+void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam)
+{
+ sky->setStarsVisible(event->star_params->visible);
+ sky->setStarCount(event->star_params->count, false);
+ sky->setStarColor(event->star_params->starcolor);
+ sky->setStarScale(event->star_params->scale);
+ delete event->star_params;
}
void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
soundmaker->step(dtime);
ClientMap &map = client->getEnv().getClientMap();
- MapNode n = map.getNodeNoEx(player->getFootstepNodePos());
+ MapNode n = map.getNode(player->getFootstepNodePos());
soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
}
const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager);
f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager));
+
+ if(g_settings->getBool("increase_tool_range"))
+ d = 5;
core::line3d<f32> shootline;
else
runData.repeat_rightclick_timer = 0;
-
if (selected_def.usable && input->getLeftState()) {
- if (input->getLeftClicked() && (!client->moddingEnabled()
+ if (input->getLeftClicked() && (!client->modsLoaded()
|| !client->getScript()->on_item_use(selected_item, pointed)))
client->interact(INTERACT_USE, pointed);
} else if (pointed.type == POINTEDTHING_NODE) {
} else if (input->getLeftState()) {
// When button is held down in air, show continuous animation
runData.left_punch = true;
+ // Run callback even though item is not usable
+ if (input->getLeftClicked() && client->modsLoaded())
+ client->getScript()->on_item_use(selected_item, pointed);
} else if (input->getRightClicked()) {
handlePointingAtNothing(selected_item);
}
ClientMap &map = env.getClientMap();
const NodeDefManager *nodedef = map.getNodeDefManager();
+ if (g_settings->getBool("killaura")) {
+ LocalPlayer *player = env.getLocalPlayer();
+ v3f player_pos = player->getPosition();
+ ItemStack selected_item, hand_item;
+ const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
+ std::vector<DistanceSortedActiveObject> allObjects;
+ env.getActiveObjects(shootline.start, shootline.getLength() + 10.0f, allObjects);
+ const v3f line_vector = shootline.getVector();
+ for (const auto &allObject : allObjects) {
+ ClientActiveObject *obj = allObject.obj;
+ s16 id = obj->getId();
+ v3f pos = obj->getPosition();
+ v3f intersection;
+ v3s16 normal;
+ aabb3f selection_box;
+ if (! obj->getSelectionBox(&selection_box))
+ continue;
+ aabb3f offsetted_box(selection_box.MinEdge + pos, selection_box.MaxEdge + pos);
+ boxLineCollision(offsetted_box, shootline.start, line_vector, &intersection, &normal);
+ PointedThing pointed(id, intersection, normal, (intersection - shootline.start).getLengthSQ());
+ if (g_settings->getBool("killaura_fast"))
+ client->interact(INTERACT_START_DIGGING, pointed);
+ else {
+ runData.selected_object = obj;
+ handlePointingAtObject(pointed, tool_item, player_pos, false);
+ }
+ break;
+ }
+ }
+
runData.selected_object = NULL;
-
+
RaycastState s(shootline, look_for_object, liquids_pointable);
PointedThing result;
env.continueRaycast(&s, &result);
}
} else if (result.type == POINTEDTHING_NODE) {
// Update selection boxes
- MapNode n = map.getNodeNoEx(result.node_undersurface);
+ MapNode n = map.getNode(result.node_undersurface);
std::vector<aabb3f> boxes;
n.getSelectionBoxes(nodedef, &boxes,
n.getNeighbors(result.node_undersurface, &map));
v3s16 p = floatToInt(pf, BS);
// Get selection mesh light level
- MapNode n = map.getNodeNoEx(p);
+ MapNode n = map.getNode(p);
u16 node_light = getInteriorLight(n, -1, nodedef);
u16 light_level = node_light;
for (const v3s16 &dir : g_6dirs) {
- n = map.getNodeNoEx(p + dir);
+ n = map.getNode(p + dir);
node_light = getInteriorLight(n, -1, nodedef);
if (node_light > light_level)
light_level = node_light;
m_game_ui->setInfoText(unescape_translate(utf8_to_wide(
meta->getString("infotext"))));
} else {
- MapNode n = map.getNodeNoEx(nodepos);
+ MapNode n = map.getNode(nodepos);
if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
m_game_ui->setInfoText(L"Unknown node: " +
runData.repeat_rightclick_timer = 0;
infostream << "Ground right-clicked" << std::endl;
- if (meta && !meta->getString("formspec").empty() && !random_input
- && !isKeyDown(KeyType::SNEAK)) {
- // Report right click to server
- if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) {
- client->interact(INTERACT_PLACE, pointed);
- }
-
- infostream << "Launching custom inventory view" << std::endl;
-
- InventoryLocation inventoryloc;
- inventoryloc.setNodeMeta(nodepos);
+ camera->setDigging(1); // right click animation (always shown for feedback)
- NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
- &client->getEnv().getClientMap(), nodepos);
- TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
-
- auto *&formspec = m_game_ui->updateFormspec("");
- GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src,
- txt_dst, client->getFormspecPrepend());
-
- formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
- } else {
- // Report right click to server
+ soundmaker->m_player_rightpunch_sound = SimpleSoundSpec();
- camera->setDigging(1); // right click animation (always shown for feedback)
+ // If the wielded item has node placement prediction,
+ // make that happen
+ // And also set the sound and send the interact
+ // But first check for meta formspec and rightclickable
+ auto &def = selected_item.getDefinition(itemdef_manager);
+ bool placed = nodePlacement(def, selected_item, nodepos, neighbourpos,
+ pointed, meta);
- // If the wielded item has node placement prediction,
- // make that happen
- auto &def = selected_item.getDefinition(itemdef_manager);
- bool placed = nodePlacementPrediction(def, selected_item, nodepos,
- neighbourpos);
-
- if (placed) {
- // Report to server
- client->interact(INTERACT_PLACE, pointed);
- // Read the sound
- soundmaker->m_player_rightpunch_sound =
- def.sound_place;
-
- if (client->moddingEnabled())
- client->getScript()->on_placenode(pointed, def);
- } else {
- soundmaker->m_player_rightpunch_sound =
- SimpleSoundSpec();
-
- if (def.node_placement_prediction.empty() ||
- nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) {
- client->interact(INTERACT_PLACE, pointed); // Report to server
- } else {
- soundmaker->m_player_rightpunch_sound =
- def.sound_place_failed;
- }
- }
- }
+ if (placed && client->modsLoaded())
+ client->getScript()->on_placenode(pointed, def);
}
}
-bool Game::nodePlacementPrediction(const ItemDefinition &selected_def,
- const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos)
+bool Game::nodePlacement(const ItemDefinition &selected_def,
+ const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos,
+ const PointedThing &pointed, const NodeMetadata *meta)
{
std::string prediction = selected_def.node_placement_prediction;
const NodeDefManager *nodedef = client->ndef();
MapNode node;
bool is_valid_position;
- node = map.getNodeNoEx(nodepos, &is_valid_position);
- if (!is_valid_position)
+ node = map.getNode(nodepos, &is_valid_position);
+ if (!is_valid_position) {
+ soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
+ return false;
+ }
+
+ // formspec in meta
+ if (meta && !meta->getString("formspec").empty() && !random_input
+ && !isKeyDown(KeyType::SNEAK)) {
+ // on_rightclick callbacks are called anyway
+ if (nodedef_manager->get(map.getNode(nodepos)).rightclickable)
+ client->interact(INTERACT_PLACE, pointed);
+
+ infostream << "Launching custom inventory view" << std::endl;
+
+ InventoryLocation inventoryloc;
+ inventoryloc.setNodeMeta(nodepos);
+
+ NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
+ &client->getEnv().getClientMap(), nodepos);
+ TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
+
+ auto *&formspec = m_game_ui->updateFormspec("");
+ GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src,
+ txt_dst, client->getFormspecPrepend());
+
+ formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
return false;
+ }
- if (!prediction.empty() && !(nodedef->get(node).rightclickable &&
+ // on_rightclick callback
+ if (prediction.empty() || (nodedef->get(node).rightclickable &&
!isKeyDown(KeyType::SNEAK))) {
- verbosestream << "Node placement prediction for "
- << selected_item.name << " is "
- << prediction << std::endl;
- v3s16 p = neighbourpos;
-
- // Place inside node itself if buildable_to
- MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position);
- if (is_valid_position)
- {
- if (nodedef->get(n_under).buildable_to)
- p = nodepos;
- else {
- node = map.getNodeNoEx(p, &is_valid_position);
- if (is_valid_position &&!nodedef->get(node).buildable_to)
- return false;
+ // Report to server
+ client->interact(INTERACT_PLACE, pointed);
+ return false;
+ }
+
+ verbosestream << "Node placement prediction for "
+ << selected_def.name << " is "
+ << prediction << std::endl;
+ v3s16 p = neighbourpos;
+
+ // Place inside node itself if buildable_to
+ MapNode n_under = map.getNode(nodepos, &is_valid_position);
+ if (is_valid_position) {
+ if (nodedef->get(n_under).buildable_to) {
+ p = nodepos;
+ } else {
+ node = map.getNode(p, &is_valid_position);
+ if (is_valid_position && !nodedef->get(node).buildable_to) {
+ // Report to server
+ client->interact(INTERACT_PLACE, pointed);
+ return false;
}
}
+ }
- // Find id of predicted node
- content_t id;
- bool found = nodedef->getId(prediction, id);
+ // Find id of predicted node
+ content_t id;
+ bool found = nodedef->getId(prediction, id);
- if (!found) {
- errorstream << "Node placement prediction failed for "
- << selected_item.name << " (places "
- << prediction
- << ") - Name not known" << std::endl;
- return false;
- }
+ if (!found) {
+ errorstream << "Node placement prediction failed for "
+ << selected_def.name << " (places "
+ << prediction
+ << ") - Name not known" << std::endl;
+ // Handle this as if prediction was empty
+ // Report to server
+ client->interact(INTERACT_PLACE, pointed);
+ return false;
+ }
- const ContentFeatures &predicted_f = nodedef->get(id);
+ const ContentFeatures &predicted_f = nodedef->get(id);
- // Predict param2 for facedir and wallmounted nodes
- u8 param2 = 0;
+ // Predict param2 for facedir and wallmounted nodes
+ u8 param2 = 0;
- if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
+ if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
- v3s16 dir = nodepos - neighbourpos;
+ v3s16 dir = nodepos - neighbourpos;
- 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)) {
- param2 = dir.X < 0 ? 3 : 2;
- } else {
- param2 = dir.Z < 0 ? 5 : 4;
- }
+ 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)) {
+ param2 = dir.X < 0 ? 3 : 2;
+ } else {
+ param2 = dir.Z < 0 ? 5 : 4;
}
+ }
- if (predicted_f.param_type_2 == CPT2_FACEDIR ||
+ if (predicted_f.param_type_2 == CPT2_FACEDIR ||
predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
- v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS);
+ v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS);
- if (abs(dir.X) > abs(dir.Z)) {
- param2 = dir.X < 0 ? 3 : 1;
- } else {
- param2 = dir.Z < 0 ? 2 : 0;
- }
+ if (abs(dir.X) > abs(dir.Z)) {
+ param2 = dir.X < 0 ? 3 : 1;
+ } else {
+ param2 = dir.Z < 0 ? 2 : 0;
}
+ }
+
+ assert(param2 <= 5);
- assert(param2 <= 5);
-
- //Check attachment if node is in group attached_node
- if (((ItemGroupList) predicted_f.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 pp;
-
- if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
+ //Check attachment if node is in group attached_node
+ if (((ItemGroupList) predicted_f.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 pp;
+
+ if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
- pp = p + wallmounted_dirs[param2];
- else
- pp = p + v3s16(0, -1, 0);
+ pp = p + wallmounted_dirs[param2];
+ else
+ pp = p + v3s16(0, -1, 0);
- if (!nodedef->get(map.getNodeNoEx(pp)).walkable)
- return false;
+ if (!nodedef->get(map.getNode(pp)).walkable) {
+ // Report to server
+ client->interact(INTERACT_PLACE, pointed);
+ return false;
}
+ }
- // Apply color
- if ((predicted_f.param_type_2 == CPT2_COLOR
+ // Apply color
+ if ((predicted_f.param_type_2 == CPT2_COLOR
|| predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
|| predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
- const std::string &indexstr = selected_item.metadata.getString(
- "palette_index", 0);
- if (!indexstr.empty()) {
- s32 index = mystoi(indexstr);
- if (predicted_f.param_type_2 == CPT2_COLOR) {
- param2 = index;
- } else if (predicted_f.param_type_2
- == CPT2_COLORED_WALLMOUNTED) {
- // param2 = pure palette index + other
- param2 = (index & 0xf8) | (param2 & 0x07);
- } else if (predicted_f.param_type_2
- == CPT2_COLORED_FACEDIR) {
- // param2 = pure palette index + other
- param2 = (index & 0xe0) | (param2 & 0x1f);
- }
+ const std::string &indexstr = selected_item.metadata.getString(
+ "palette_index", 0);
+ if (!indexstr.empty()) {
+ s32 index = mystoi(indexstr);
+ if (predicted_f.param_type_2 == CPT2_COLOR) {
+ param2 = index;
+ } else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
+ // param2 = pure palette index + other
+ param2 = (index & 0xf8) | (param2 & 0x07);
+ } else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
+ // param2 = pure palette index + other
+ param2 = (index & 0xe0) | (param2 & 0x1f);
}
}
+ }
- // Add node to client map
- MapNode n(id, 0, param2);
+ // 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 ||
+ // 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 ||
g_settings->getBool("enable_build_where_you_stand") ||
(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 "
- << selected_item.name << " (places "
- << prediction
- << ") - Position not loaded" << std::endl;
+ // This triggers the required mesh update too
+ client->addNode(p, n);
+ // Report to server
+ client->interact(INTERACT_PLACE, pointed);
+ // A node is predicted, also play a sound
+ soundmaker->m_player_rightpunch_sound = selected_def.sound_place;
+ return true;
+ } else {
+ soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
+ return false;
}
+ } catch (InvalidPositionException &e) {
+ errorstream << "Node placement prediction failed for "
+ << selected_def.name << " (places "
+ << prediction
+ << ") - Position not loaded" << std::endl;
+ soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
+ return false;
}
-
- return false;
}
void Game::handlePointingAtObject(const PointedThing &pointed,
m_game_ui->setInfoText(infotext);
- if (input->getLeftState()) {
+ if (input->getLeftState() || (g_settings->getBool("killaura") && ! g_settings->getBool("killaura_fast"))) {
bool do_punch = false;
bool do_punch_damage = false;
runData.object_hit_delay_timer = object_hit_delay;
}
- if (input->getLeftClicked())
+ if (input->getLeftClicked() || (g_settings->getBool("killaura") && ! g_settings->getBool("killaura_fast")))
do_punch = true;
if (do_punch) {
dir, &tool_item, runData.time_from_last_punch);
runData.time_from_last_punch = 0;
- if (!disable_send)
+ if (!disable_send) {
client->interact(INTERACT_START_DIGGING, pointed);
+ }
}
} else if (input->getRightClicked()) {
infostream << "Right-clicked object" << std::endl;
// See also: serverpackethandle.cpp, action == 2
LocalPlayer *player = client->getEnv().getLocalPlayer();
ClientMap &map = client->getEnv().getClientMap();
- MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos);
+ MapNode n = client->getEnv().getClientMap().getNode(nodepos);
// NOTE: Similar piece of code exists on the server side for
// cheat detection.
player, nodepos, n, features);
}
}
-
+
+ if(g_settings->getBool("fastdig")) {
+ runData.dig_time_complete = 0;
+ runData.dig_instantly = true;
+ }
if (!runData.digging) {
infostream << "Started digging" << std::endl;
runData.dig_instantly = runData.dig_time_complete == 0;
- if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n))
+ if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n))
return;
client->interact(INTERACT_START_DIGGING, pointed);
runData.digging = true;
runData.nodig_delay_timer = 0.15;
bool is_valid_position;
- MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position);
+ MapNode wasnode = map.getNode(nodepos, &is_valid_position);
if (is_valid_position) {
- if (client->moddingEnabled() &&
+ if (client->modsLoaded() &&
client->getScript()->on_dignode(nodepos, wasnode)) {
return;
}
void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
const CameraOrientation &cam)
{
+ TimeTaker tt_update("Game::updateFrame()");
LocalPlayer *player = client->getEnv().getLocalPlayer();
/*
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(runData.fog_range * 1.2, 60 * BS),
video::SColor clouds_dark = clouds->getColor()
.getInterpolated(video::SColor(255, 0, 0, 0), 0.9);
sky->overrideColors(clouds_dark, clouds->getColor());
- sky->setBodiesVisible(false);
+ sky->setInClouds(true);
runData.fog_range = std::fmin(runData.fog_range * 0.5f, 32.0f * BS);
// do not draw clouds after all
clouds->setVisible(false);
if (player->getWieldIndex() != runData.new_playeritem)
client->setPlayerItem(runData.new_playeritem);
- // Update local inventory if it has changed
- if (client->getLocalInventoryUpdated()) {
- //infostream<<"Updating local inventory"<<std::endl;
- runData.update_wielded_item_trigger = true;
- }
-
- if (runData.update_wielded_item_trigger) {
+ if (client->updateWieldedItem()) {
// Update wielded tool
ItemStack selected_item, hand_item;
ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
camera->wield(tool_item);
-
- runData.update_wielded_item_trigger = false;
}
/*
*/
const video::SColor &skycolor = sky->getSkyColor();
- TimeTaker tt_draw("mainloop: draw");
+ TimeTaker tt_draw("Draw scene");
driver->beginScene(true, true, skycolor);
bool draw_wield_tool = (m_game_ui->m_flags.show_hud &&
/*
Damage flash
*/
- if (runData.damage_flash > 0.0f) {
+ if (runData.damage_flash > 0.0f && ! g_settings->getBool("no_hurt_cam")) {
video::SColor color(runData.damage_flash, 180, 0, 0);
driver->draw2DRectangle(color,
core::rect<s32>(0, 0, screensize.X, screensize.Y),
driver->endScene();
stats->drawtime = tt_draw.stop(true);
- g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f);
+ g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime);
+ g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true));
}
/* Log times and stuff for visualization */
void Game::showDeathFormspec()
{
static std::string formspec_str =
- std::string(FORMSPEC_VERSION_STRING) +
+ std::string("formspec_version[1]") +
SIZE_TAG
"bgcolor[#320000b4;true]"
"label[4.85,1.35;" + gettext("You died") + "]"
float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
std::ostringstream os;
- os << FORMSPEC_VERSION_STRING << SIZE_TAG
+ os << "formspec_version[1]" << SIZE_TAG
<< "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
<< strgettext("Continue") << "]";
<< strgettext("- Creative Mode: ") << creative << "\n";
if (!simple_singleplayer_mode) {
const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
+ //~ PvP = Player versus Player
os << strgettext("- PvP: ") << pvp << "\n"
<< strgettext("- Public: ") << announced << "\n";
std::string server_name = g_settings->get("server_name");
****************************************************************************/
/****************************************************************************/
+Game *g_game;
+
void the_game(bool *kill,
bool random_input,
InputHandler *input,
bool simple_singleplayer_mode)
{
Game game;
+
+ g_game = &game;
/* 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