Tune caves
[oweals/minetest.git] / src / game.cpp
index 49f4b4ad902338cfb748a02a8c9f365af679f8cd..941f286d91abcccf577b32078f0f6cc274b6e86f 100644 (file)
@@ -55,6 +55,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "quicktune_shortcutter.h"
 #include "clientmap.h"
 #include "sky.h"
+#include "sound.h"
+#if USE_SOUND
+       #include "sound_openal.h"
+#endif
+#include "event_manager.h"
 #include <list>
 
 /*
@@ -666,7 +671,9 @@ public:
                static const video::SColor usable_colors[] = {
                        video::SColor(255,255,100,100),
                        video::SColor(255,90,225,90),
-                       video::SColor(255,100,100,255)
+                       video::SColor(255,100,100,255),
+                       video::SColor(255,255,150,50),
+                       video::SColor(255,220,220,100)
                };
                static const u32 usable_colors_count =
                                sizeof(usable_colors) / sizeof(*usable_colors);
@@ -702,7 +709,7 @@ public:
                        s32 y = y_bottom - meta_i * 50;
                        float show_min = meta.min;
                        float show_max = meta.max;
-                       if(show_min >= 0 && show_max >= 0){
+                       if(show_min >= -0.0001 && show_max >= -0.0001){
                                if(show_min <= show_max * 0.5)
                                        show_min = 0;
                        }
@@ -722,6 +729,11 @@ public:
                                        core::rect<s32>(textx, y - graphh/2 - texth/2,
                                        textx2, y - graphh/2 + texth/2),
                                        meta.color);
+                       s32 graph1y = y;
+                       s32 graph1h = graphh;
+                       bool relativegraph = (show_min != 0 && show_min != show_max);
+                       float lastscaledvalue = 0.0;
+                       bool lastscaledvalue_exists = false;
                        for(std::list<Piece>::const_iterator j = m_log.begin();
                                        j != m_log.end(); j++)
                        {
@@ -736,6 +748,7 @@ public:
                                }
                                if(!value_exists){
                                        x++;
+                                       lastscaledvalue_exists = false;
                                        continue;
                                }
                                float scaledvalue = 1.0;
@@ -743,12 +756,23 @@ public:
                                        scaledvalue = (value - show_min) / (show_max - show_min);
                                if(scaledvalue == 1.0 && value == 0){
                                        x++;
+                                       lastscaledvalue_exists = false;
                                        continue;
                                }
-                               s32 ivalue = scaledvalue * graphh;
-                               driver->draw2DLine(v2s32(x, y),
-                                               v2s32(x, y - ivalue),
-                                               meta.color);
+                               if(relativegraph){
+                                       if(lastscaledvalue_exists){
+                                               s32 ivalue1 = lastscaledvalue * graph1h;
+                                               s32 ivalue2 = scaledvalue * graph1h;
+                                               driver->draw2DLine(v2s32(x-1, graph1y - ivalue1),
+                                                               v2s32(x, graph1y - ivalue2), meta.color);
+                                       }
+                                       lastscaledvalue = scaledvalue;
+                                       lastscaledvalue_exists = true;
+                               } else{
+                                       s32 ivalue = scaledvalue * graph1h;
+                                       driver->draw2DLine(v2s32(x, graph1y),
+                                                       v2s32(x, graph1y - ivalue), meta.color);
+                               }
                                x++;
                        }
                        meta_i++;
@@ -756,6 +780,126 @@ public:
        }
 };
 
+class NodeDugEvent: public MtEvent
+{
+public:
+       v3s16 p;
+       MapNode n;
+       
+       NodeDugEvent(v3s16 p, MapNode n):
+               p(p),
+               n(n)
+       {}
+       const char* getType() const
+       {return "NodeDug";}
+};
+
+class SoundMaker
+{
+       ISoundManager *m_sound;
+       INodeDefManager *m_ndef;
+public:
+       float m_player_step_timer;
+
+       SimpleSoundSpec m_player_step_sound;
+       SimpleSoundSpec m_player_leftpunch_sound;
+       SimpleSoundSpec m_player_rightpunch_sound;
+
+       SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
+               m_sound(sound),
+               m_ndef(ndef),
+               m_player_step_timer(0)
+       {
+       }
+
+       void playPlayerStep()
+       {
+               if(m_player_step_timer <= 0 && m_player_step_sound.exists()){
+                       m_player_step_timer = 0.03;
+                       m_sound->playSound(m_player_step_sound, false);
+               }
+       }
+
+       static void viewBobbingStep(MtEvent *e, void *data)
+       {
+               SoundMaker *sm = (SoundMaker*)data;
+               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);
+       }
+
+       void registerReceiver(MtEventManager *mgr)
+       {
+               mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this);
+               mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this);
+               mgr->reg("PlayerJump", SoundMaker::playerJump, this);
+               mgr->reg("CameraPunchLeft", SoundMaker::cameraPunchLeft, this);
+               mgr->reg("CameraPunchRight", SoundMaker::cameraPunchRight, this);
+               mgr->reg("NodeDug", SoundMaker::nodeDug, 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;
+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);
+               std::string base = porting::path_share + DIR_DELIM + "testsounds";
+               dst_paths.insert(base + DIR_DELIM + name + ".ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
+               dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
+       }
+};
+
 void the_game(
        bool &kill,
        bool random_input,
@@ -803,7 +947,32 @@ void the_game(
        IWritableItemDefManager *itemdef = createItemDefManager();
        // Create node definition manager
        IWritableNodeDefManager *nodedef = createNodeDefManager();
+       
+       // Sound fetcher (useful when testing)
+       GameOnDemandSoundFetcher soundfetcher;
+
+       // Sound manager
+       ISoundManager *sound = NULL;
+       bool sound_is_dummy = false;
+#if USE_SOUND
+       infostream<<"Attempting to use OpenAL audio"<<std::endl;
+       sound = createOpenALSoundManager(&soundfetcher);
+       if(!sound)
+               infostream<<"Failed to initialize OpenAL audio"<<std::endl;
+#endif
+       if(!sound){
+               infostream<<"Using dummy audio."<<std::endl;
+               sound = &dummySoundManager;
+               sound_is_dummy = true;
+       }
+
+       // Event manager
+       EventManager eventmgr;
 
+       // Sound maker
+       SoundMaker soundmaker(sound, nodedef);
+       soundmaker.registerReceiver(&eventmgr);
+       
        // Add chat log output for errors to be shown in chat
        LogOutputBuffer chat_log_error_buf(LMT_ERROR);
 
@@ -823,6 +992,7 @@ void the_game(
                server->start(port);
        }
 
+       try{
        do{ // Client scope (breakable do-while(0))
        
        /*
@@ -835,7 +1005,7 @@ void the_game(
        MapDrawControl draw_control;
 
        Client client(device, playername.c_str(), password, draw_control,
-                       tsrc, itemdef, nodedef);
+                       tsrc, itemdef, nodedef, sound, &eventmgr);
        
        // Client acts as our GameDef
        IGameDef *gamedef = &client;
@@ -970,9 +1140,8 @@ void the_game(
                        ss<<L" Item definitions\n";
                        ss<<(client.nodedefReceived()?L"[X]":L"[  ]");
                        ss<<L" Node definitions\n";
-                       //ss<<(client.texturesReceived()?L"[X]":L"[  ]");
-                       ss<<L"["<<(int)(client.textureReceiveProgress()*100+0.5)<<L"%] ";
-                       ss<<L" Textures\n";
+                       ss<<L"["<<(int)(client.mediaReceiveProgress()*100+0.5)<<L"%] ";
+                       ss<<L" Media\n";
 
                        draw_load_screen(ss.str(), driver, font);
                        
@@ -1000,7 +1169,7 @@ void the_game(
        /*
                Create the camera node
        */
-       Camera camera(smgr, draw_control);
+       Camera camera(smgr, draw_control, gamedef);
        if (!camera.successfullyCreated(error_message))
                return;
 
@@ -1039,12 +1208,6 @@ void the_game(
        */
        Inventory local_inventory(itemdef);
 
-       /*
-               Move into game
-       */
-       
-       //gui_loadingtext->remove();
-
        /*
                Add some gui stuff
        */
@@ -1095,26 +1258,6 @@ void the_game(
        guitext_profiler->setBackgroundColor(video::SColor(120,0,0,0));
        guitext_profiler->setVisible(false);
        
-       /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
-                       (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
-       /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
-                       (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
-       
-       // Test the text input system
-       /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
-                       NULL))->drop();*/
-       /*GUIMessageMenu *menu =
-                       new GUIMessageMenu(guienv, guiroot, -1, 
-                               &g_menumgr,
-                               L"Asd");
-       menu->drop();*/
-       
-       // Launch pause menu
-       /*(new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
-                       &g_menumgr))->drop();*/
-       
-       //s32 guitext_chat_pad_bottom = 70;
-
        /*
                Some statistics are collected in these
        */
@@ -1122,14 +1265,13 @@ void the_game(
        u32 beginscenetime = 0;
        u32 scenetime = 0;
        u32 endscenetime = 0;
-       u32 alltime = 0;
        
-       // A test
-       //throw con::PeerNotFoundException("lol");
-
        float recent_turn_speed = 0.0;
        
        ProfilerGraph graph;
+       // Initially clear the profiler
+       Profiler::GraphValues dummyvalues;
+       g_profiler->graphGet(dummyvalues);
 
        float nodig_delay_timer = 0.0;
        float dig_time = 0.0;
@@ -1179,68 +1321,9 @@ void the_game(
 
        for(;;)
        {
-               TimeTaker tt_all("mainloop: all");
-
                if(device->run() == false || kill == true)
                        break;
 
-               if(client.accessDenied())
-               {
-                       error_message = L"Access denied. Reason: "
-                                       +client.accessDeniedReason();
-                       errorstream<<wide_to_narrow(error_message)<<std::endl;
-                       break;
-               }
-
-               if(g_gamecallback->disconnect_requested)
-               {
-                       g_gamecallback->disconnect_requested = false;
-                       break;
-               }
-
-               if(g_gamecallback->changepassword_requested)
-               {
-                       (new GUIPasswordChange(guienv, guiroot, -1,
-                               &g_menumgr, &client))->drop();
-                       g_gamecallback->changepassword_requested = false;
-               }
-
-               /*
-                       Process TextureSource's queue
-               */
-               tsrc->processQueue();
-
-               /*
-                       Random calculations
-               */
-               last_screensize = screensize;
-               screensize = driver->getScreenSize();
-               v2s32 displaycenter(screensize.X/2,screensize.Y/2);
-               //bool screensize_changed = screensize != last_screensize;
-
-               // Resize hotbar
-               if(screensize.Y <= 800)
-                       hotbar_imagesize = 32;
-               else if(screensize.Y <= 1280)
-                       hotbar_imagesize = 48;
-               else
-                       hotbar_imagesize = 64;
-               
-               // Hilight boxes collected during the loop and displayed
-               core::list< core::aabbox3d<f32> > hilightboxes;
-               
-               // Info text
-               std::wstring infotext;
-
-               // When screen size changes, update positions and sizes of stuff
-               /*if(screensize_changed)
-               {
-                       v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
-                       quick_inventory->updatePosition(pos);
-               }*/
-
-               //TimeTaker //timer1("//timer1");
-               
                // Time of frame without fps limit
                float busytime;
                u32 busytime_u32;
@@ -1253,9 +1336,9 @@ void the_game(
                                busytime_u32 = 0;
                        busytime = busytime_u32 / 1000.0;
                }
+               
+               g_profiler->graphAdd("mainloop_other", busytime - (float)drawtime/1000.0f);
 
-               //infostream<<"busytime_u32="<<busytime_u32<<std::endl;
-       
                // Necessary for device->getTimer()->getTime()
                device->run();
 
@@ -1271,6 +1354,7 @@ void the_game(
                        {
                                u32 sleeptime = frametime_min - busytime_u32;
                                device->sleep(sleeptime);
+                               g_profiler->graphAdd("mainloop_sleep", (float)sleeptime/1000.0f);
                        }
                }
 
@@ -1289,6 +1373,8 @@ void the_game(
                        dtime = 0;
                lasttime = time;
 
+               g_profiler->graphAdd("mainloop_dtime", dtime);
+
                /* Run timers */
 
                if(nodig_delay_timer >= 0)
@@ -1296,17 +1382,10 @@ void the_game(
                if(object_hit_delay_timer >= 0)
                        object_hit_delay_timer -= dtime;
                time_from_last_punch += dtime;
-
+               
                g_profiler->add("Elapsed time", dtime);
                g_profiler->avg("FPS", 1./dtime);
 
-               /*
-                       Visualize frametime in terminal
-               */
-               /*for(u32 i=0; i<dtime*400; i++)
-                       infostream<<"X";
-               infostream<<std::endl;*/
-
                /*
                        Time average and jitter calculation
                */
@@ -1360,7 +1439,59 @@ void the_game(
                                jitter1_min = 0.0;
                        }
                }
+
+               /*
+                       Handle miscellaneous stuff
+               */
                
+               if(client.accessDenied())
+               {
+                       error_message = L"Access denied. Reason: "
+                                       +client.accessDeniedReason();
+                       errorstream<<wide_to_narrow(error_message)<<std::endl;
+                       break;
+               }
+
+               if(g_gamecallback->disconnect_requested)
+               {
+                       g_gamecallback->disconnect_requested = false;
+                       break;
+               }
+
+               if(g_gamecallback->changepassword_requested)
+               {
+                       (new GUIPasswordChange(guienv, guiroot, -1,
+                               &g_menumgr, &client))->drop();
+                       g_gamecallback->changepassword_requested = false;
+               }
+
+               /*
+                       Process TextureSource's queue
+               */
+               tsrc->processQueue();
+
+               /*
+                       Random calculations
+               */
+               last_screensize = screensize;
+               screensize = driver->getScreenSize();
+               v2s32 displaycenter(screensize.X/2,screensize.Y/2);
+               //bool screensize_changed = screensize != last_screensize;
+
+               // Resize hotbar
+               if(screensize.Y <= 800)
+                       hotbar_imagesize = 32;
+               else if(screensize.Y <= 1280)
+                       hotbar_imagesize = 48;
+               else
+                       hotbar_imagesize = 64;
+               
+               // Hilight boxes collected during the loop and displayed
+               core::list< core::aabbox3d<f32> > hilightboxes;
+               
+               // Info text
+               std::wstring infotext;
+
                /*
                        Debug info for client
                */
@@ -1941,9 +2072,23 @@ void the_game(
                        client.getEnv().getClientMap().updateCamera(camera_position,
                                camera_direction, camera_fov);
                }
+               
+               // Update sound listener
+               sound->updateListener(camera.getCameraNode()->getPosition(),
+                               v3f(0,0,0), // velocity
+                               camera.getCameraNode()->getTarget(),
+                               camera.getCameraNode()->getUpVector());
 
-               //timer2.stop();
-               //TimeTaker //timer3("//timer3");
+               /*
+                       Update sound maker
+               */
+               {
+                       soundmaker.step(dtime);
+                       
+                       ClientMap &map = client.getEnv().getClientMap();
+                       MapNode n = map.getNodeNoEx(player->getStandingNodePos());
+                       soundmaker.m_player_step_sound = nodedef->get(n).sound_footstep;
+               }
 
                /*
                        Calculate what block is the crosshair pointing to
@@ -2022,6 +2167,7 @@ void the_game(
                }
 
                bool left_punch = false;
+               soundmaker.m_player_leftpunch_sound.name = "";
 
                if(playeritem_usable && input->getLeftState())
                {
@@ -2049,6 +2195,11 @@ void the_game(
                                }
                        }
                        
+                       // We can't actually know, but assume the sound of right-clicking
+                       // to be the sound of placing a node
+                       soundmaker.m_player_rightpunch_sound.gain = 0.5;
+                       soundmaker.m_player_rightpunch_sound.name = "default_place_node";
+                       
                        /*
                                Handle digging
                        */
@@ -2074,6 +2225,20 @@ void the_game(
                                        if(tp)
                                                params = getDigParams(nodedef->get(n).groups, tp);
                                }
+                               
+                               SimpleSoundSpec sound_dig = nodedef->get(n).sound_dig;
+                               if(sound_dig.exists()){
+                                       if(sound_dig.name == "__group"){
+                                               if(params.main_group != ""){
+                                                       soundmaker.m_player_leftpunch_sound.gain = 0.5;
+                                                       soundmaker.m_player_leftpunch_sound.name =
+                                                                       std::string("default_dig_") +
+                                                                                       params.main_group;
+                                               }
+                                       } else{
+                                               soundmaker.m_player_leftpunch_sound = sound_dig;
+                                       }
+                               }
 
                                float dig_time_complete = 0.0;
 
@@ -2113,6 +2278,7 @@ void the_game(
                                        infostream<<"Digging completed"<<std::endl;
                                        client.interact(2, pointed);
                                        client.setCrack(-1, v3s16(0,0,0));
+                                       MapNode wasnode = map.getNode(nodepos);
                                        client.removeNode(nodepos);
 
                                        dig_time = 0;
@@ -2123,17 +2289,17 @@ void the_game(
 
                                        // We don't want a corresponding delay to
                                        // very time consuming nodes
-                                       if(nodig_delay_timer > 0.5)
-                                       {
-                                               nodig_delay_timer = 0.5;
-                                       }
+                                       if(nodig_delay_timer > 0.3)
+                                               nodig_delay_timer = 0.3;
                                        // We want a slight delay to very little
                                        // time consuming nodes
                                        float mindelay = 0.15;
                                        if(nodig_delay_timer < mindelay)
-                                       {
                                                nodig_delay_timer = mindelay;
-                                       }
+                                       
+                                       // Send event to trigger sound
+                                       MtEvent *e = new NodeDugEvent(nodepos, wasnode);
+                                       gamedef->event()->put(e);
                                }
 
                                dig_time += dtime;
@@ -2450,7 +2616,7 @@ void the_game(
                }
 
                {
-                       float statustext_time_max = 3.0;
+                       float statustext_time_max = 1.5;
                        if(!statustext.empty())
                        {
                                statustext_time += dtime;
@@ -2484,7 +2650,7 @@ void the_game(
                                        initial_color.getInterpolated_quadratic(
                                                initial_color,
                                                final_color,
-                                               statustext_time / (float) statustext_time_max);
+                                               pow(statustext_time / (float)statustext_time_max, 2.0f));
                                guitext_status->setOverrideColor(fade_color);
                                guitext_status->enableOverrideColor(true);
                        }
@@ -2554,7 +2720,7 @@ void the_game(
                        ItemStack item;
                        if(mlist != NULL)
                                item = mlist->getItem(client.getPlayerItem());
-                       camera.wield(item, gamedef);
+                       camera.wield(item);
                }
                
                /*
@@ -2682,11 +2848,6 @@ void the_game(
                                        NULL);
                }
 
-               // Clear Z buffer
-               driver->clearZBuffer();
-               // Draw some sky things
-               //draw_horizon(driver, camera.getCameraNode());
-               
                /*
                        End scene
                */
@@ -2718,9 +2879,6 @@ void the_game(
                        lastFPS = fps;
                }
 
-               alltime = tt_all.stop(true);
-               g_profiler->graphAdd("mainloop_other", (float)(alltime-drawtime)/1000.0f);
-
                /*
                        Log times and stuff for visualization
                */
@@ -2755,10 +2913,20 @@ void the_game(
 
        // Client scope (client is destructed before destructing *def and tsrc)
        }while(0);
-
-       delete tsrc;
+       } // try-catch
+       catch(SerializationError &e)
+       {
+               error_message = L"A serialization error occurred:\n"
+                               + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
+                               L" running a different version of Minetest.";
+               errorstream<<wide_to_narrow(error_message)<<std::endl;
+       }
+       
+       if(!sound_is_dummy)
+               delete sound;
        delete nodedef;
        delete itemdef;
+       delete tsrc;
 }