summaryrefslogblamecommitdiff
path: root/src/game.cpp
blob: bbec5eae73306ca9cafb22253238f70b2580d658 (plain) (tree)



















                                                                             
        
   
 
                 
 



                                   
                   




                       
                
                 
                




                        
                             
                     

                                
                    
                     
                                
                        
                    
                         
                          
                          
                             

                      
                      



                       
                          
                        

                         
 

                          
                     
                             
 


                            
                                  
                
                                        
                                
                                      

                         
                                                        
 
                             
                                  
 



















                                 
                            
 

                                                                 
 


                                            


                                                         


                                 



                               
   











                                                 


                                             


                                             
                    

 



                                                   
                                               
 
                       
                
              
                    
 
 
                                    
 
                                  
                                             

          
                                                          
     

 

































































































                                                                          

              
                                
 

                        

                                                                            
 
                        
                                                                      

                            
                             

                                                       
 


                                  
     
 
                                                
 
                          



























                                                                       


                                                                
                       


                          
                             






























                                                                        
                        
                    

 

              



                                  

 

               


                                         
 








                                                

                                                                              
                                                                 

                                                       


                                  
                                                        


                                 
                                                        


                               
                                                       










                                                      
 



                                 
                          
 
                                                   
                                      
         
                                         
             






                                                      
                                









                                                
                                          

                                         
                                        




















                                                                          



                                                  


                                             

                                           




                                                      
                                                                            

                                                      
                                                      
















                                                              

                                                                                        

                                                           








                                                                   

                                                              



                               


                                                 















                                                                         
             

                                                  
                                                     


                                                
                 

















































                                                                                 
                 
 
                           
                                                         
                 
                                              















                                                         
                     


                                              


                                            
                 

             
 
                       
                                                   
         


                                                    


                                                       



                                                                           
                                                             


                                


                                                                          
 

                           
                                              
                     

                                                



                                                                 





                                                



                                                              









                                                                               

                                                                        




                                                                   
                                                    





                                   












                                                                       

                                                                                
                     


                                                  


                     

                                                                          

                                                   

                                                                           
                                                          







                                                                           
                     

                                                     



                                      
 







                                                                  
 

                                                             
             












                                                                        
             
         
 
                     



                                        
 



                                          

                  
 
                           
                                                                     
                                     


                               

                           
                                                     
 
                                                           
                                                           
         
                            
                                                   
                                         
         
                                                               

                           
                                                   
                                         
         
                                                               
         
                            
                                                   
                                        
         
                                                                 

                           
                                                   
                                        
         



                                          
                                                   
                                      




                                           
                                                   
                                      




                                       
                                                   
                                      




                                        
                                                   
                                      
         
 

                                                
                                                               
                                                                                       
                                              
 
                                               
         
                                                            
                                                                               
                               
                                                                               
                               
 
                                                                          
                                                         
                                                                       
                               
 
                                                       
                                                           


                                                                      
                                                                            
             
                                                  




                                                                        
         
 
                             
                                                
         
                                                                      
             
                                                                      





                                                          

                                         

             
 

                          


                                                  
 

                        


                                     

             

                                              
                                                    
                             
                                                       


                                
     
 
 

               
                
                                            
                                                    

                                                    
















                                                                        
 
                                             





                                                            
                     
                                                                            
 
                                                                
 

                                                                     
                     

                      
 





                                                            
                     


                                                                            
                         


                                                                      

                                                         
                                                          
                             
                                                                         
                         
                            
                         
                                                                 
                         
                     



















                                                                           
 
                                                


                                      




                                                                          
                         

                                                               
                     
                                                   
                     
                                           

                                                     
                                                     
                     
 





















































                                                                       
                                      
                     


                                                
                                                     
                             
                                                    
                                                                        




                                                                 
                                                                
                                          
                             
                         

                            
                                               

                         

                                                
                                              
                         
                     













                                                                            
 
                                                
 
                                      
                     
                                                               
                     
 





                                                                        
 
                                                        
                     
                                                      
                     
 











                                                         
                     









                                                                       
                     
 

                                                
 
                                                            
                     







                                                             
                     
 

                                                
 






























                                                                               
                          
                 
 











                                                                              
                              








































                                                                             
                     

                      
 






                                                                      
                     
                               







                                                                               

                                  



                                                                             

                                  

                                                                               

                                  























                                                                            
                                            

                                                            
 
                                                             
                     









                                                                         
                                                                            
                                                            
                         
                                                                          
                         
                     

                      
 



                                                            
 
                                                             
                     












                                                                   
                         
                                         
                                             
                                                        
                             
                                           

                                           
                                                                   

                                                                        
                         
                     








                                                   
                                                                              

                                                                       
                     





                                       

                                                                                  
 


                                            
                                                                                  
 

                                                       
 
                                        
 

                                                              
 






                                                                    
 

                                      
 

                                                   
 




                                                       
 



                                         








































































                                                                             
                                                                         














                                                                             



                                                      
                                                   
                                            
                                               
                                            


                                 
                                         

                                                        
                                                                     







                                                                 

                                  




                                                          
                                  




                                                            
                                  
                     
























                                                                            
                                            






























                                                                     
                     
































                                                      
                                        










                                                               

                         









































                                                                          
                     







































                                                                         
                     




                                                                      
                         
                     

                      
 





































                                                                               
                                   
                                                                    

                                      

                                                                 
                                   



                                                                    
                                      
                         
                     

                      
 




                                                       
 










                                                                              
                                                      

                                       
                         









                                                                           
                     










                                                            

                                      
                                                      
                     







                                                       
 
                                    
                                                                              

                              
 


                                                                
                     


                                     
                                                  

                                   
                     










                                                           
                                      








                                                                              
                                      

                                                                         
                                      
                         

                                                                           
                     

























                                                                      
                                  

                                                                              

                                  









                                                 
                     







                                                                         
                     






















































                                                                            
                                         
                      
 



                                                                 
         

                              
     
 
/*
 *  The Mana World
 *  Copyright 2004 The Mana World Development Team
 *
 *  This file is part of The Mana World.
 *
 *  The Mana World is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  The Mana World 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with The Mana World; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  $Id$
 */

#include "game.h"

#include <guichan/sdl/sdlinput.hpp>

#include "being.h"
#include "configuration.h"
#include "engine.h"
#include "equipment.h"
#include "floor_item.h"
#include "graphics.h"
#include "inventory.h"
#include "item.h"
#include "log.h"
#include "main.h"
#include "map.h"
#include "playerinfo.h"
#include "sound.h"

#include "gui/buy.h"
#include "gui/buysell.h"
#include "gui/chargedialog.h"
#include "gui/chat.h"
#include "gui/confirm_dialog.h"
#include "gui/equipmentwindow.h"
#include "gui/gui.h"
#include "gui/help.h"
#include "gui/inventorywindow.h"
#include "gui/minimap.h"
#include "gui/npc.h"
#include "gui/npc_text.h"
#include "gui/ok_dialog.h"
#include "gui/popupmenu.h"
#include "gui/requesttrade.h"
#include "gui/sell.h"
#include "gui/setup.h"
#include "gui/skill.h"
#include "gui/stats.h"
#include "gui/status.h"
#include "gui/trade.h"

#include "net/messagein.h"
#include "net/network.h"
#include "net/protocol.h"


extern Graphics *graphics;

std::string map_path;
std::string tradePartnerName;

bool refresh_beings = false;
unsigned char keyb_state;
volatile int tick_time;
volatile bool action_time = false;
int server_tick;
int fps = 0, frame = 0, current_npc = 0;
bool displayPathToMouse = false;
unsigned short startX = 0, startY = 0;
Being *autoTarget = NULL;
Engine *engine = NULL;
SDL_Joystick *joypad = NULL;       /**< Joypad object */

OkDialog *deathNotice = NULL;
ConfirmDialog *exitConfirm = NULL;

ChatWindow *chatWindow;
StatusWindow *statusWindow;
BuyDialog *buyDialog;
SellDialog *sellDialog;
BuySellDialog *buySellDialog;
InventoryWindow *inventoryWindow;
NpcListDialog *npcListDialog;
NpcTextDialog *npcTextDialog;
SkillDialog *skillDialog;
//NewSkillDialog *newSkillWindow;
StatsWindow *statsWindow;
Setup* setupWindow;
Minimap *minimap;
EquipmentWindow *equipmentWindow;
ChargeDialog *chargeDialog;
TradeWindow *tradeWindow;
//BuddyWindow *buddyWindow;
HelpWindow *helpWindow;
PopupMenu *popupMenu;

Inventory *inventory = NULL;

const int EMOTION_TIME = 150;    /**< Duration of emotion icon */
const int MAX_TIME = 10000;

/**
 * Listener used for handling death message.
 */
class DeatchNoticeListener : public gcn::ActionListener {
    public:
        void action(const std::string &eventId) {
            writeWord(0, 0x00b2);
            writeByte(2, 0);
            writeSet(3);
            deathNotice = NULL;
        }
} deathNoticeListener;

/**
 * Listener used for exitting handling.
 */
class ExitListener : public gcn::ActionListener {
    void action(const std::string &eventId) {
        if (eventId == "yes") {
            state = EXIT;
        }
        exitConfirm = NULL;
    }
} exitListener;

/**
 * Advances game logic counter.
 */
Uint32 nextTick(Uint32 interval, void *param)
{
    tick_time++;
    if (tick_time == MAX_TIME) tick_time = 0;
    return interval;
}

/**
 * Lets u only trigger an action every other second
 * tmp. counts fps
 */
Uint32 nextSecond(Uint32 interval, void *param)
{
    action_time = true;
    fps = frame;
    frame = 0;
    return interval;
}

int get_elapsed_time(int start_time)
{
    if (start_time <= tick_time) {
        return (tick_time - start_time) * 10;
    }
    else {
        return (tick_time + (MAX_TIME - start_time)) * 10;
    }
}

/**
 * Create all the various globally accessible gui windows
 */
void createGuiWindows()
{
    // Create dialogs
    chatWindow = new ChatWindow(
            config.getValue("homeDir", "") + std::string("/chatlog.txt"));
    statusWindow = new StatusWindow();
    buyDialog = new BuyDialog();
    sellDialog = new SellDialog();
    buySellDialog = new BuySellDialog();
    inventoryWindow = new InventoryWindow();
    npcTextDialog = new NpcTextDialog();
    npcListDialog = new NpcListDialog();
    skillDialog = new SkillDialog();
    //newSkillWindow = new NewSkillDialog();
    statsWindow = new StatsWindow();
    setupWindow = new Setup();
    minimap = new Minimap();
    equipmentWindow = new EquipmentWindow();
    chargeDialog = new ChargeDialog();
    tradeWindow = new TradeWindow();
    //buddyWindow = new BuddyWindow();
    helpWindow = new HelpWindow();
    popupMenu = new PopupMenu();

    // Initialize window posisitons
    int screenW = graphics->getWidth();
    int screenH = graphics->getHeight();

    chatWindow->setPosition(0, screenH - chatWindow->getHeight());
    statusWindow->setPosition(screenW - statusWindow->getWidth() - 5, 5);
    inventoryWindow->setPosition(screenW - statusWindow->getWidth() -
            inventoryWindow->getWidth() - 10, 5);
    statsWindow->setPosition(
            screenW - 5 - statsWindow->getWidth(),
            statusWindow->getHeight() + 20);
    chargeDialog->setPosition(
            screenW - 5 - chargeDialog->getWidth(),
            screenH - chargeDialog->getHeight() - 15);
    tradeWindow->setPosition(screenW - statusWindow->getWidth() -
            tradeWindow->getWidth() - 10,
            inventoryWindow->getY() + inventoryWindow->getHeight());
    /*buddyWindow->setPosition(10,
      minimap->getHeight() + 30);*/
    equipmentWindow->setPosition(5,140);

    // Set initial window visibility
    chatWindow->setVisible(true);
    statusWindow->setVisible(true);
    buyDialog->setVisible(false);
    sellDialog->setVisible(false);
    buySellDialog->setVisible(false);
    inventoryWindow->setVisible(false);
    npcTextDialog->setVisible(false);
    npcListDialog->setVisible(false);
    skillDialog->setVisible(false);
    //newSkillWindow->setVisible(false);
    statsWindow->setVisible(false);
    setupWindow->setVisible(false);
    equipmentWindow->setVisible(false);
    chargeDialog->setVisible(false);
    tradeWindow->setVisible(false);
    //buddyWindow->setVisible(false);
    helpWindow->setVisible(false);
    popupMenu->setVisible(false);

    // Do not focus any text field
    gui->focusNone();
}

/**
 * Destroy all the globally accessible gui windows
 */
void destroyGuiWindows()
{
    delete chatWindow;
    delete statusWindow;
    delete buyDialog;
    delete sellDialog;
    delete buySellDialog;
    delete inventoryWindow;
    delete npcListDialog;
    delete npcTextDialog;
    delete skillDialog;
    delete statsWindow;
    delete setupWindow;
    delete minimap;
    delete equipmentWindow;
    delete chargeDialog;
    //delete newSkillWindow;
    delete tradeWindow;
    //delete buddyWindow;
    delete helpWindow;
    delete popupMenu;
}

void do_init()
{
    engine->changeMap(map_path);

    // Initialize timers
    tick_time = 0;
    SDL_AddTimer(10, nextTick, NULL);                     // Logic counter
    SDL_AddTimer(1000, nextSecond, NULL);                 // Seconds counter

    // Initialize beings
    player_node = createBeing(account_ID, 0, engine->getCurrentMap());
    player_node->x = startX;
    player_node->y = startY;
    player_node->speed = 150;
    player_node->setHairColor(player_info->hair_color);
    player_node->setHairStyle(player_info->hair_style);

    if (player_info->weapon == 11)
    {
        player_info->weapon = 2;
    }

    player_node->setWeapon(player_info->weapon);

    remove("packet.list");

    // Initialize joypad
    SDL_InitSubSystem(SDL_INIT_JOYSTICK);
    //SDL_JoystickEventState(SDL_ENABLE);
    int num_joy = SDL_NumJoysticks();
    logger->log("%i joysticks/gamepads found", num_joy);
    for (int i = 0; i < num_joy; i++)
        logger->log("- %s", SDL_JoystickName(i));
    // TODO: The user should be able to choose which one to use
    // Open the first device
    if (num_joy > 0)
    {
        joypad = SDL_JoystickOpen(0);
        if (joypad == NULL)
        {
            logger->log("Couldn't open joystick: %s", SDL_GetError());
        }
        else {
            logger->log("Axes: %i ", SDL_JoystickNumAxes(joypad));
            logger->log("Balls: %i", SDL_JoystickNumBalls(joypad));
            logger->log("Hats: %i", SDL_JoystickNumHats(joypad));
            logger->log("Buttons: %i", SDL_JoystickNumButtons(joypad));
        }
    }
}

void game()
{
    // Needs to be initialised _before_ the engine is created...
    inventory = new Inventory();

    createGuiWindows();
    engine = new Engine();
    do_init();

    int gameTime = tick_time;

    while (state != EXIT)
    {
        // Handle all necessary game logic
        while (get_elapsed_time(gameTime) > 0)
        {
            do_input();
            engine->logic();
            gui->logic();
            gameTime++;
        }

        gameTime = tick_time;

        // Update the screen when application is active, delay otherwise
        if (SDL_GetAppState() & SDL_APPACTIVE)
        {
            engine->draw();
            graphics->updateScreen();
        }
        else
        {
            SDL_Delay(10);
        }

        // Handle network stuff and flush it
        do_parse();
        flush();
    }

    delete engine;
    destroyGuiWindows();
    close_session();
}

void do_exit()
{
    if (joypad != NULL)
    {
        SDL_JoystickClose(joypad);
    }
}

void do_input()
{
    // Get the state of the keyboard keys
    Uint8* keys;
    keys = SDL_GetKeyState(NULL);

    // Get the state of the joypad buttons
    // TODO: Only 6- buttons joypads are allowed
    bool joy[10];
    for (int i=0; i<10; i++)
    {
        joy[i] = false;
    }
    if (joypad != NULL)
    {
        // TODO: one different value of tolerance is needed for each direction
        // This probably means the need for a tuning utility/window
        int tolerance = (int)config.getValue("joytolerance", 10);
        SDL_JoystickUpdate();
        if (SDL_JoystickGetAxis(joypad, 0) > tolerance)
        {
            joy[JOY_RIGHT] = true;
        }
        if (SDL_JoystickGetAxis(joypad, 0) < -tolerance)
        {
            joy[JOY_LEFT] = true;
        }
        if (SDL_JoystickGetAxis(joypad, 1) < -tolerance)
        {
            joy[JOY_UP] = true;
        }
        if (SDL_JoystickGetAxis(joypad, 1) > tolerance)
        {
            joy[JOY_DOWN] = true;
        }
        for (int i=0; i<6; i++)
        {
            if (SDL_JoystickGetButton(joypad, i) == 1)
            {
                joy[JOY_BTN0 + i] = true;
            }
        }
    }

    // Events
    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        bool used = false;

        // Keyboard events (for discontinuous keys)
        if (event.type == SDL_KEYDOWN)
        {
            switch (event.key.keysym.sym)
            {
                    // In-game Help
                case SDLK_F1:
                    if (helpWindow->isVisible()) {
                        helpWindow->setVisible(false);
                    } else {
                        helpWindow->loadHelp("index");
                    }
                    used = true;
                    break;

                    // Player sit action
                case SDLK_F5:
                    if (!action_time) {
                        break;
                    }

                    switch (player_node->action)
                    {
                        case Being::STAND:
                            action(2, 0);
                            break;
                        case Being::SIT:
                            action(3, 0);
                            break;
                    }
                    break;

                    // Display path to mouse (debug purpose)
                case SDLK_F6:
                    displayPathToMouse = !displayPathToMouse;
                    break;

                    // Input chat window
                case SDLK_RETURN:
                    if (chatWindow->isFocused()) {
                        break;
                    }

                    // Quit by pressing Enter if the exit confirm is there
                    if (exitConfirm)
                    {
                        state = EXIT;
                    }
                    // Accept the Death Notice...
                    else if (deathNotice)
                    {
                        deathNotice->action("ok");
                        writeWord(0, 0x00b2);
                        writeByte(2, 0);
                        writeSet(3);
                        deathNotice = NULL;
                    }
                    // Close the Browser if opened
                    else if (helpWindow->isVisible())
                    {
                        helpWindow->setVisible(false);
                    }
                    // Close the config window, cancelling changes if opened
                    else if (setupWindow->isVisible())
                    {
                        setupWindow->action("cancel");
                    }
                    // Else, open the chat edit box
                    else
                    {
                        chatWindow->requestChatFocus();
                        used = true;
                    }
                    break;

                    // Picking up items on the floor
                case SDLK_g:
                case SDLK_z:
                    if (!chatWindow->isFocused()) {
                        unsigned short x = player_node->x;
                        unsigned short y = player_node->y;
                        int id = find_floor_item_by_cor(x, y);

                        // If none below the player, try the tile in front of the player
                        if (!id) {
                            switch (player_node->direction)
                            {
                                case Being::NORTH: y--; break;
                                case Being::SOUTH: y++; break;
                                case Being::WEST:  x--; break;
                                case Being::EAST:  x++; break;
                                case Being::NW:    x--; y--; break;
                                case Being::NE:    x++; y--; break;
                                case Being::SW:    x--; y++; break;
                                case Being::SE:    x++; y++; break;
                                default: break;
                            }
                            id = find_floor_item_by_cor(x, y);
                        }

                        if (id)
                        {
                            writeWord(0, 0x009f);
                            writeLong(2, id);
                            writeSet(6);
                        }
                        used = true;
                    }
                    break;

                    // Quitting confirmation dialog
                case SDLK_ESCAPE:
                    if (!exitConfirm) {
                        exitConfirm = new ConfirmDialog(
                                "Quit", "Are you sure you want to quit?",
                                (gcn::ActionListener*)&exitListener);
                    }
                    break;

                default:
                    break;
            }

            // Keys pressed together with Alt/Meta
            // Emotions and some internal gui windows
            if (event.key.keysym.mod & KMOD_ALT)
            {
                switch (event.key.keysym.sym)
                {
                        // Inventory window
                    case SDLK_i:
                        inventoryWindow->setVisible(
                                !inventoryWindow->isVisible());
                        used = true;
                        break;

                        // Statistics window
                    case SDLK_s:
                        statsWindow->setVisible(!statsWindow->isVisible());
                        used = true;
                        break;

                    /*
                        // New skills window
                    case SDLK_n:
                        newSkillWindow->setVisible(!newSkillWindow->isVisible());
                        used = true;
                        break;
                    */

                        // Skill window
                    case SDLK_k:
                        skillDialog->setVisible(!skillDialog->isVisible());
                        used = true;
                        break;

                        // Setup window
                    case SDLK_c:
                        setupWindow->setVisible(true);
                        used = true;
                        break;

                        // Equipment window
                    case SDLK_e:
                        equipmentWindow->setVisible(
                                !equipmentWindow->isVisible());
                        used = true;
                        break;

                    /*
                        // Buddy window
                    case SDLK_b:
                        buddyWindow->setVisible(!buddyWindow->isVisible());
                        used = true;
                        break;
                    */

                    default:
                        break;
                }

                // Emotions
                if (action_time && !player_node->emotion)
                {
                    unsigned char emotion = 0;
                    switch (event.key.keysym.sym)
                    {
                        case SDLK_1: emotion = 1; break;
                        case SDLK_2: emotion = 2; break;
                        case SDLK_3: emotion = 3; break;
                        case SDLK_4: emotion = 4; break;
                        case SDLK_5: emotion = 5; break;
                        case SDLK_6: emotion = 6; break;
                        case SDLK_7: emotion = 7; break;
                        case SDLK_8: emotion = 8; break;
                        case SDLK_9: emotion = 9; break;
                        case SDLK_0: emotion = 10; break;
                        default: break;
                    }

                    if (emotion)
                    {
                        writeWord(0, 0x00bf);
                        writeByte(2, emotion);
                        writeSet(3);
                        action_time = false;
                        used = true;
                    }
                }
            }
        }

        // Mouse events
        else if (event.type == SDL_MOUSEBUTTONDOWN)
        {
            int mx = event.button.x / 32 + camera_x;
            int my = event.button.y / 32 + camera_y;

            // Mouse button left
            if (event.button.button == SDL_BUTTON_LEFT)
            {
                // Don't move when shift is pressed
                // XXX Is this too hackish? I'm not sure if making the Gui
                // class a KeyListener is a good idea and works as expected
                // at all...
                if (keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT]) {
                    used = true;
                }

                // Check for default actions for NPC/Monster/Players
                Being *target = findNode(mx, my);
                unsigned int floorItemId = find_floor_item_by_cor(mx, my);

                if (target)
                {
                    switch (target->getType())
                    {
                        // Player default: trade
                        case Being::PLAYER:
                            writeWord(0, 0x00e4);
                            writeLong(2, target->getId());
                            writeSet(6);
                            tradePartnerName = target->getName();
                            break;

                            // NPC default: talk
                        case Being::NPC:
                            if (!current_npc)
                            {
                                writeWord(0, 0x0090);
                                writeLong(2, target->getId());
                                writeByte(6, 0);
                                writeSet(7);
                                current_npc = target->getId();
                            }
                            break;

                            // Monster default: attack
                        case Being::MONSTER:
                            /**
                             * TODO: Move player to mouse click position before
                             * attack the monster (maybe using follow mode).
                             */
                            if (target->action != Being::MONSTER_DEAD &&
                                    player_node->action == Being::STAND)
                            {
                                attack(target);
                                // Autotarget by default with mouse
                                //if (keys[SDLK_LSHIFT])
                                //{
                                autoTarget = target;
                                //}
                            }
                            break;

                        default:
                            break;
                    }
                }
                // Check for default action to items on the floor
                else if (floorItemId != 0)
                {
                    /**
                     * TODO: Move player to mouse click position before
                     * pick up the items on the floor.
                     *
                     * Provisory: pick up only items near of player.
                     */
                    int dx = mx - player_node->x;
                    int dy = my - player_node->y;
                    // "sqrt(dx*dx + dy*dy) < 2" is equal to "dx*dx + dy*dy < 4"
                    if ((dx*dx + dy*dy) < 4)
                    {
                        writeWord(0, 0x009f);
                        writeLong(2, floorItemId);
                        writeSet(6);
                    }
                }

                // Just cancel the popup menu if shown, and don't make the
                // character walk
                if (popupMenu->isVisible() == true)
                {
                    // If we click elsewhere than in the window, do not use
                    // the event
                    // The user wanted to close the popup.
                    // Still buggy : Wonder if the x, y, width, and height
                    // aren't reported partially with these functions.
                    if (event.button.x >=
                            (popupMenu->getX() + popupMenu->getWidth()) ||
                        event.button.x < popupMenu->getX() ||
                        event.button.y >=
                            (popupMenu->getY() + popupMenu->getHeight()) ||
                        event.button.y < popupMenu->getY())
                    {
                        used = true;
                        popupMenu->setVisible(false);
                    }
                }

            } // End Mouse left button

            // Mouse button middle
            else if (event.button.button == SDL_BUTTON_MIDDLE)
            {
                /**
                 * Some people haven't a mouse with three buttons,
                 * right Usiu??? ;-)
                 */
            }

            // Mouse button right
            else if (event.button.button == SDL_BUTTON_RIGHT)
            {
                Being *being;
                FloorItem *floorItem;

                if ((being = findNode(mx, my))) {
                    popupMenu->showPopup(event.button.x, event.button.y,
                            being);
                } else if ((floorItem = find_floor_item_by_id(
                            find_floor_item_by_cor(mx, my)))) {
                    popupMenu->showPopup(event.button.x, event.button.y,
                            floorItem);
                } else {
                    popupMenu->setVisible(false);
                }
            }
        }

        // Quit event
        else if (event.type == SDL_QUIT)
        {
            state = EXIT;
        }

        // Push input to GUI when not used
        if (!used) {
            guiInput->pushInput(event);
        }

    } // End while

    // Moving player around
    if ((player_node->action != Being::DEAD) && (current_npc == 0) &&
            !chatWindow->isFocused())
    {
        int x = player_node->x;
        int y = player_node->y;
        int xDirection = 0;
        int yDirection = 0;
        Being::Direction Direction = Being::DIR_NONE;

        // Translate pressed keys to movement and direction
        if (keys[SDLK_UP] || keys[SDLK_KP8] || joy[JOY_UP])
        {
            yDirection = -1;
            if (player_node->action != Being::WALK)
                Direction = Being::NORTH;
        }
        if (keys[SDLK_DOWN] || keys[SDLK_KP2] || joy[JOY_DOWN])
        {
            yDirection = 1;
            if (player_node->action != Being::WALK)
                Direction = Being::SOUTH;
        }
        if (keys[SDLK_LEFT] || keys[SDLK_KP4] || joy[JOY_LEFT])
        {
            xDirection = -1;
            if (player_node->action != Being::WALK)
                Direction = Being::WEST;
        }
        if (keys[SDLK_RIGHT] || keys[SDLK_KP6] || joy[JOY_RIGHT])
        {
            xDirection = 1;
            if (player_node->action != Being::WALK)
                Direction = Being::EAST;
        }
        if (keys[SDLK_KP1]) // Bottom Left
        {
            xDirection = -1;
            yDirection = 1;
            if (player_node->action != Being::WALK)
                Direction = Being::SW;
        }
        if (keys[SDLK_KP3]) // Bottom Right
        {
            xDirection = 1;
            yDirection = 1;
            if (player_node->action != Being::WALK)
                Direction = Being::SE;
        }
        if (keys[SDLK_KP7]) // Top Left
        {
            xDirection = -1;
            yDirection = -1;
            if (player_node->action != Being::WALK)
                Direction = Being::NW;
        }
        if (keys[SDLK_KP9]) // Top Right
        {
            xDirection = 1;
            yDirection = -1;
            if (player_node->action != Being::WALK)
                Direction = Being::NE;
        }

        Map *tiledMap = engine->getCurrentMap();

        // Allow keyboard control to interrupt an existing path
        if ((xDirection != 0 || yDirection != 0) && player_node->action == Being::WALK)
            player_node->setDestination(x, y);

        if (player_node->action != Being::WALK)
        {
            // Prevent skipping corners over colliding tiles
            if ((xDirection != 0) && tiledMap->tileCollides(x + xDirection, y))
                xDirection = 0;
            if ((yDirection != 0) && tiledMap->tileCollides(x, y + yDirection))
                yDirection = 0;

            // Choose a straight direction when diagonal target is blocked
            if ((yDirection != 0) && (xDirection != 0) &&
                    !tiledMap->getWalk(x + xDirection, y + yDirection))
                xDirection = 0;

            // Walk to where the player can actually go
            if (((xDirection != 0) || (yDirection != 0)) &&
                    tiledMap->getWalk(x + xDirection, y + yDirection))
            {
                walk(x + xDirection, y + yDirection, Direction);
                player_node->setDestination(x + xDirection, y + yDirection);
            }
            else if (Direction != Being::DIR_NONE)
            {
                // Update the player direction to where he wants to walk
                // Warning: Not communicated to the server yet
                player_node->direction = Direction;
            }
        }

        // Attacking monsters
        if (player_node->action == Being::STAND)
        {
            if (keys[SDLK_LCTRL] || keys[SDLK_RCTRL] || joy[JOY_BTN0])
            {
                Being *monster = attack(x, y, player_node->direction);
                if (monster == NULL && autoTarget != NULL)
                {
                    attack(autoTarget);
                }
                else if (keys[SDLK_LSHIFT])
                {
                    autoTarget = monster;
                }
            }
        }

        if (joy[JOY_BTN1])
        {
            unsigned short x = player_node->x;
            unsigned short y = player_node->y;
            int id = find_floor_item_by_cor(x, y);

            if (id != 0)
            {
                writeWord(0, 0x009f);
                writeLong(2, id);
                writeSet(6);
            }
        }
        else if (joy[JOY_BTN2] && action_time)
        {
            if (player_node->action == Being::STAND)
                action(2, 0);
            else if (player_node->action == Being::SIT)
                action(3, 0);
            action_time = false;
        }
    }
}

void do_parse()
{
    int n_items;
    Map *tiledMap = engine->getCurrentMap();
    Equipment *equipment = Equipment::getInstance();

    // We need at least 2 bytes to identify a packet
    while (in_size >= 2)
    {
        MessageIn msg = get_next_message();

        // Parse packet based on their id
        switch (msg.getId())
        {
            case SMSG_LOGIN_SUCCESS:
                // Connected to game server succesfully, set spawn point
                {
                    msg.readLong(); // server tick
                    msg.readCoordinates(player_node->x,
                            player_node->y,
                            player_node->direction);
                    msg.skip(2);    // unknown
                }
                break;

                // Received speech from being
            case SMSG_BEING_CHAT:
                {
                    int chatMsgLength = msg.readShort() - 8;
                    Being *being = findNode(msg.readLong());

                    if (being != NULL && chatMsgLength > 0)
                    {
                        std::string chatMsg = msg.readString(chatMsgLength);

                        chatWindow->chat_log(chatMsg, BY_OTHER);

                        chatMsg.erase(0, chatMsg.find(" : ", 0) + 3);
                        being->setSpeech(chatMsg, SPEECH_TIME);
                    }
                }
                break;

            case SMSG_PLAYER_CHAT:
            case SMSG_GM_CHAT:
                {
                    int chatMsgLength = msg.readShort() - 4;

                    if (chatMsgLength > 0)
                    {
                        std::string chatMsg = msg.readString(chatMsgLength);

                        if (msg.getId() == SMSG_PLAYER_CHAT)
                        {
                            chatWindow->chat_log(chatMsg, BY_PLAYER);

                            unsigned int pos = chatMsg.find(" : ", 0);
                            if (pos != std::string::npos)
                            {
                                chatMsg.erase(0, pos + 3);
                            }
                            player_node->setSpeech(chatMsg, SPEECH_TIME);
                        }
                        else
                        {
                            chatWindow->chat_log(chatMsg, BY_GM);
                        }
                    }
                }
                break;

            case SMSG_WALK_RESPONSE:
                // It is assumed by the client any request to walk actually
                // succeeds on the server. The plan is to have a correction
                // message when the server senses the client has the wrong
                // idea.
                break;

            case SMSG_BEING_VISIBLE:
            case SMSG_BEING_MOVE:
                // Information about a being in range
                {
                    int id = msg.readLong();
                    unsigned short speed = msg.readShort();
                    msg.readShort();  // unknown
                    msg.readShort();  // unknown
                    msg.readShort();  // option
                    unsigned short job = msg.readShort();  // class

                    Being *being = findNode(id);

                    if (being == NULL)
                    {
                        // Being with id >= 110000000 and job 0 are better
                        // known as ghosts, so don't create those.
                        if (job == 0 && id >= 110000000)
                        {
                            break;
                        }

                        being = createBeing(id, job, tiledMap);
                    }
                    else if (msg.getId() == 0x0078)
                    {
                        being->clearPath();
                        being->frame = 0;
                        being->walk_time = tick_time;
                        being->action = Being::STAND;
                    }

                    // Prevent division by 0 when calculating frame
                    if (speed == 0) { speed = 150; }

                    being->speed = speed;
                    being->job = job;
                    being->setHairStyle(msg.readShort());
                    being->setWeapon(msg.readShort());
                    msg.readShort();  // head option bottom

                    if (msg.getId() == SMSG_BEING_MOVE)
                    {
                        msg.readLong(); // server tick
                    }

                    msg.readShort();  // shield
                    msg.readShort();  // head option top
                    msg.readShort();  // head option mid
                    being->setHairColor(msg.readShort());
                    msg.readShort();  // unknown
                    msg.readShort();  // head dir
                    msg.readShort();  // guild
                    msg.readShort();  // unknown
                    msg.readShort();  // unknown
                    msg.readShort();  // manner
                    msg.readShort();  // karma
                    msg.readByte();   // unknown
                    msg.readByte();   // sex

                    if (msg.getId() == SMSG_BEING_MOVE)
                    {
                        unsigned short srcX, srcY, dstX, dstY;
                        msg.readCoordinatePair(srcX, srcY, dstX, dstY);
                        being->action = Being::STAND;
                        being->x = srcX;
                        being->y = srcY;
                        being->setDestination(dstX, dstY);
                    }
                    else
                    {
                        msg.readCoordinates(being->x, being->y,
                                            being->direction);
                    }

                    msg.readByte();   // unknown
                    msg.readByte();   // unknown
                    msg.readByte();   // unknown / sit
                }
                break;

            case SMSG_BEING_REMOVE:
                // A being should be removed or has died
                {
                    Being *being = findNode(msg.readLong());

                    if (being != NULL)
                    {
                        if (msg.readByte() == 1)
                        {
                            // Death
                            switch (being->getType())
                            {
                                case Being::MONSTER:
                                    being->action = Being::MONSTER_DEAD;
                                    being->frame = 0;
                                    being->walk_time = tick_time;
                                    break;

                                default:
                                    being->action = Being::DEAD;
                                    break;
                            }
                        }
                        else
                        {
                            remove_node(being);
                        }

                        if (being == autoTarget)
                        {
                            autoTarget = NULL;
                        }
                    }
                }
                break;

            case SMSG_PLAYER_UPDATE_1:
            case SMSG_PLAYER_UPDATE_2:
            case SMSG_PLAYER_MOVE:
                // An update about a player, potentially including movement.
                {
                    int id = msg.readLong();
                    unsigned short speed = msg.readShort();
                    msg.readShort();  // option 1
                    msg.readShort();  // option 2
                    msg.readShort();  // option
                    unsigned short job = msg.readShort();

                    Being *being = findNode(id);

                    if (being == NULL)
                    {
                        being = createBeing(id, job, tiledMap);
                    }

                    being->speed = speed;
                    being->job = job;
                    being->setHairStyle(msg.readShort());
                    being->setWeaponById(msg.readShort());  // item id 1
                    msg.readShort();  // item id 2
                    msg.readShort();  // head option bottom

                    if (msg.getId() == SMSG_PLAYER_MOVE)
                    {
                        msg.readLong(); // server tick
                    }

                    msg.readShort();  // head option top
                    msg.readShort();  // head option mid
                    being->setHairColor(msg.readShort());
                    msg.readShort();  // unknown
                    msg.readShort();  // head dir
                    msg.readLong();   // guild
                    msg.readLong();   // emblem
                    msg.readShort();  // manner
                    msg.readByte();   // karma
                    msg.readByte();   // sex

                    if (msg.getId() == SMSG_PLAYER_MOVE)
                    {
                        unsigned short srcX, srcY, dstX, dstY;
                        msg.readCoordinatePair(srcX, srcY, dstX, dstY);
                        being->x = srcX;
                        being->y = srcY;
                        being->setDestination(dstX, dstY);
                    }
                    else
                    {
                        msg.readCoordinates(being->x, being->y,
                                            being->direction);
                    }

                    msg.readByte();   // unknown
                    msg.readByte();   // unknown

                    if (msg.getId() == SMSG_PLAYER_UPDATE_1)
                    {
                        if (msg.readByte() == 2)
                        {
                            being->action = Being::SIT;
                        }
                    }
                    else if (msg.getId() == SMSG_PLAYER_MOVE)
                    {
                        msg.readByte(); // unknown
                    }

                    msg.readByte();   // Lv
                    msg.readByte();   // unknown

                    being->walk_time = tick_time;
                    being->frame = 0;
                }
                break;

            case SMSG_NPC_MESSAGE:
                msg.readShort();  // length
                current_npc = msg.readLong();
                npcTextDialog->addText(msg.readString(msg.getLength() - 8));
                npcListDialog->setVisible(false);
                npcTextDialog->setVisible(true);
                break;

            case SMSG_NPC_NEXT:
            case SMSG_NPC_CLOSE:
                // Next/Close button in NPC dialog, currently unused
                break;

            case SMSG_TRADE_REQUEST:
                // If a trade window or request window is already open, send a
                // trade cancel to any other trade request.
                //
                // Note that it would be nice if the server would prevent this
                // situation, and that the requesting player would get a
                // special message about the player being occupied.

                if (tradeWindow->isVisible() == true || requestTradeDialogOpen)
                {
                    writeWord(0, CMSG_TRADE_RESPONSE);
                    writeByte(2, 0x04);
                    writeSet(3);
                    break;
                }

                requestTradeDialogOpen = true;
                tradePartnerName = msg.readString(24);
                new RequestTradeDialog(tradePartnerName);
                break;

            case SMSG_TRADE_RESPONSE:
                switch (msg.readByte())
                {
                    case 0: // Too far away
                        chatWindow->chat_log("Trading isn't possible. "
                                             "Trade partner is too far away.",
                                             BY_SERVER);
                        break;
                    case 1: // Character doesn't exist
                        chatWindow->chat_log("Trading isn't possible. "
                                             "Character doesn't exist.",
                                             BY_SERVER);
                        break;
                    case 2: // Invite request check failed...
                        chatWindow->chat_log("Trade cancelled due to an "
                                             "unknown reason.", BY_SERVER);
                        break;
                    case 3: // Trade accepted
                        tradeWindow->reset();
                        tradeWindow->setCaption(
                                "Trade: You and " + tradePartnerName);
                        tradeWindow->setVisible(true);
                        requestTradeDialogOpen = false;
                        break;
                    case 4: // Trade cancelled
                        chatWindow->chat_log("Trade cancelled.", BY_SERVER);
                        tradeWindow->setVisible(false);
                        break;
                    default: // Shouldn't happen as well, but to be sure
                        chatWindow->chat_log("Unhandled trade cancel packet",
                                             BY_SERVER);
                        break;
                }
                break;

            case SMSG_TRADE_ITEM_ADD:
                {
                    long amount = msg.readLong();
                    short type = msg.readShort();
                    msg.readByte();  // identified flag
                    msg.readByte();  // attribute
                    msg.readByte();  // refine
                    msg.skip(8);     // card (4 shorts)

                    // TODO: handle also identified, etc
                    if (type == 0) {
                        tradeWindow->addMoney(amount);
                    } else {
                        tradeWindow->addItem(type, false, amount, false);
                    }
                }
                break;

            case SMSG_TRADE_ITEM_ADD_RESPONSE:
                // Trade: New Item add response (was 0x00ea, now 01b1)
                {
                    Item *item = inventory->getItem(msg.readShort());
                    short quantity = msg.readShort();

                    switch (msg.readByte())
                    {
                        case 0:
                            // Successfully added item
                            if (item->isEquipment() && item->isEquipped())
                            {
                                inventory->unequipItem(item);
                            }
                            tradeWindow->addItem(item->getId(), true, quantity,
                                                 item->isEquipment());
                            item->increaseQuantity(-quantity);
                            break;
                        case 1:
                            // Add item failed - player overweighted
                            chatWindow->chat_log("Failed adding item. Trade "
                                                 "partner is over weighted.",
                                                 BY_SERVER);
                            break;
                        default:
                            chatWindow->chat_log("Failed adding item for "
                                                 "unknown reason.", BY_SERVER);
                            break;
                    }
                }
                break;

            case SMSG_TRADE_OK:
                // 0 means ok from myself, 1 means ok from other;
                tradeWindow->receivedOk(msg.readByte() == 0);
                break;

            case SMSG_TRADE_CANCEL:
                chatWindow->chat_log("Trade canceled.", BY_SERVER);
                tradeWindow->setVisible(false);
                tradeWindow->reset();
                break;

            case SMSG_TRADE_COMPLETE:
                chatWindow->chat_log("Trade completed.", BY_SERVER);
                tradeWindow->setVisible(false);
                tradeWindow->reset();
                break;

            case SMSG_PLAYER_INVENTORY:
                {
                    // Only called on map load / warp. First reset all items
                    // to not load them twice on map change.
                    inventory->resetItems();
                    msg.readShort();  // length
                    int number = (msg.getLength() - 4) / 18;

                    for (int loop = 0; loop < number; loop++)
                    {
                        short index = msg.readShort();
                        short itemId = msg.readShort();
                        msg.readByte(); // type
                        msg.readByte(); // identify flag
                        short amount = msg.readShort();
                        msg.skip(2);    // unknown
                        msg.skip(8);    // card (4 shorts)

                        inventory->addItem(index, itemId, amount, false);

                        // Trick because arrows are not considered equipment
                        if (itemId == 1199 || itemId == 529)
                        {
                            inventory->getItem(index)->setEquipment(true);
                        }
                    }
                }
                break;

            case SMSG_PLAYER_EQUIPMENT:
                {
                    msg.readShort(); // length
                    int number = (msg.getLength() - 4) / 20;

                    for (int loop = 0; loop < number; loop++)
                    {
                        short index = msg.readShort();
                        short itemId = msg.readShort();
                        msg.readByte();  // type
                        msg.readByte();  // identify flag
                        msg.readShort(); // equip type
                        short equipPoint = msg.readShort();
                        msg.readByte();  // attribute
                        msg.readByte();  // refine
                        msg.skip(8);     // card

                        inventory->addItem(index, itemId, 1, true);

                        if (equipPoint)
                        {
                            int mask = 1;
                            int position = 0;
                            while (!(equipPoint & mask))
                            {
                                mask <<= 1;
                                position++;
                            }
                            Item *item = inventory->getItem(index);
                            item->setEquipped(true);
                            equipment->setEquipment(position - 1, item);
                        }
                    }
                }
                break;

            case SMSG_ITEM_USE_RESPONSE:
                {
                    short index = msg.readShort();
                    short amount = msg.readShort();

                    if (msg.readByte() == 0) {
                        chatWindow->chat_log("Failed to use item", BY_SERVER);
                    } else {
                        inventory->getItem(index)->setQuantity(amount);
                    }
                }
                break;

            case SMSG_PLAYER_WARP:
                {
                    // Set new map path
                    map_path = "maps/" + msg.readString(16);
                    map_path= map_path.substr(0, map_path.rfind(".")) + ".tmx.gz";

                    int x = msg.readShort();
                    int y = msg.readShort();

                    logger->log("Warping to %s (%d, %d)", map_path.c_str(), x, y);

                    engine->changeMap(map_path);
                    tiledMap = engine->getCurrentMap();

                    empty_floor_items();

                    // Remove the player, so it is not deleted
                    beings.remove(player_node);

                    // Delete all beings except the local player
                    std::list<Being *>::iterator i;
                    for (i = beings.begin(); i != beings.end(); i++)
                    {
                        delete (*i);
                    }
                    beings.clear();

                    autoTarget = NULL;
                    current_npc = 0;

                    // Re-add the local player node
                    beings.push_back(player_node);

                    player_node->action = Being::STAND;
                    player_node->frame = 0;
                    player_node->x = x;
                    player_node->y = y;
                    player_node->setMap(tiledMap);

                    // Send "map loaded"
                    writeWord(0, 0x007d);
                    writeSet(2);
                    flush();
                }
                break;

            case SMSG_SKILL_FAILED:
                // Action failed (ex. sit because you have not reached the
                // right level)
                CHATSKILL action;
                action.skill   = msg.readShort();
                action.bskill  = msg.readShort();
                action.unused  = msg.readShort(); // unknown
                action.success = msg.readByte();
                action.reason  = msg.readByte();
                if (action.success != SKILL_FAILED &&
                    action.bskill == BSKILL_EMOTE)
                {
                    printf("Action: %d/%d", action.bskill, action.success);
                }
                chatWindow->chat_log(action);
                break;

            case SMSG_PLAYER_STAT_UPDATE_1:
                switch (msg.readShort())
                {
                    //case 0x0000:
                    //    player_node->speed = msg.readLong();
                    //    break;
                    case 0x0005:
                        player_info->hp = msg.readLong();
                        break;
                    case 0x0006:
                        player_info->max_hp = msg.readLong();
                        break;
                    case 0x0007:
                        player_info->sp = msg.readLong();
                        break;
                    case 0x0008:
                        player_info->max_sp = msg.readLong();
                        break;
                    case 0x000b:
                        player_info->lv = msg.readLong();
                        break;
                    case 0x000c:
                        player_info->skill_point = msg.readLong();
                        skillDialog->setPoints(player_info->skill_point);
                        break;
                    case 0x0018:
                        player_info->totalWeight = msg.readLong();
                        break;
                    case 0x0019:
                        player_info->maxWeight = msg.readLong();
                        break;
                    case 0x0037:
                        player_info->job_lv = msg.readLong();
                        break;
                    case 0x0009:
                        player_info->statsPointsToAttribute = msg.readLong();
                        break;
                    case 0x0035:
                        player_node->aspd = msg.readLong();
                        break;
                }

                if (player_info->hp == 0 && deathNotice == NULL)
                {
                    deathNotice = new OkDialog("Message",
                            "You're now dead, press ok to restart",
                            &deathNoticeListener);
                    deathNotice->releaseModalFocus();
                    player_node->action = Being::DEAD;
                }
                break;

                // Stop walking
                // case 0x0088:  // Disabled because giving some problems
                //if (being = findNode(readLong(2))) {
                //    if (being->getId() != player_node->getId()) {
                //        being->action = STAND;
                //        being->frame = 0;
                //        set_coordinates(being->coordinates,
                //                        readWord(6), readWord(8),
                //                        get_direction(being->coordinates));
                //    }
                //}
                //break;

            case SMSG_BEING_ACTION:
                {
                    Being *srcBeing = findNode(msg.readLong());
                    Being *dstBeing = findNode(msg.readLong());
//                    msg.readLong();   // server tick
//                    msg.readLong();   // src speed
//                    msg.readLong();   // dst speed
                    msg.skip(12);
                    short param1 = msg.readShort();
                    msg.skip(2);  // param 2
                    char type = msg.readByte();
                    msg.skip(2);  // param 3

                    switch (type)
                    {
                        case 0: // Damage
                            if (dstBeing == NULL) break;

                            dstBeing->setDamage(param1, SPEECH_TIME);

                            if (srcBeing != NULL &&
                                srcBeing != player_node)
                            {
                                // buggy
                                srcBeing->action = Being::ATTACK;
                                srcBeing->frame = 0;
                                srcBeing->walk_time = tick_time;
                            }
                            break;

                        case 2: // Sit
                            if (srcBeing == NULL) break;
                            srcBeing->frame = 0;
                            srcBeing->action = Being::SIT;
                            break;

                        case 3: // Stand up
                            if (srcBeing == NULL) break;
                            srcBeing->frame = 0;
                            srcBeing->action = Being::STAND;
                            break;
                    }
                }
                break;

            case SMSG_PLAYER_STAT_UPDATE_2:
                switch (msg.readShort()) {
                    case 0x0001:
                        player_info->xp = msg.readLong();
                        break;
                    case 0x0002:
                        player_info->job_xp = msg.readLong();
                        break;
                    case 0x0014:
                        player_info->gp = msg.readLong();
                        break;
                    case 0x0016:
                        player_info->xpForNextLevel = msg.readLong();
                        break;
                    case 0x0017:
                        player_info->jobXpForNextLevel = msg.readLong();
                        break;
                }
                break;

            case SMSG_BEING_LEVELUP:
                if ((unsigned long)msg.readLong() == player_node->getId()) {
                    logger->log("Level up");
                    sound.playSfx("sfx/levelup.ogg");
                } else {
                    logger->log("Someone else went level up");
                }
                msg.readLong();  // type
                break;

            case SMSG_BEING_EMOTION:
                {
                    Being *being = findNode(msg.readLong());
                    if (being == NULL) break;

                    being->emotion = msg.readByte();
                    being->emotion_time = EMOTION_TIME;
                }
                break;

            case SMSG_PLAYER_STAT_UPDATE_3:
                {
                    long type = msg.readLong();
                    long base = msg.readLong();
                    long bonus = msg.readLong();
                    long total = base + bonus;

                    switch (type) {
                        case 0x000d: player_info->STR = total; break;
                        case 0x000e: player_info->AGI = total; break;
                        case 0x000f: player_info->VIT = total; break;
                        case 0x0010: player_info->INT = total; break;
                        case 0x0011: player_info->DEX = total; break;
                        case 0x0012: player_info->LUK = total; break;
                    }
                }
                break;

            case SMSG_NPC_BUY_SELL_CHOICE:
                buyDialog->setVisible(false);
                buyDialog->reset();
                sellDialog->setVisible(false);
                sellDialog->reset();
                buySellDialog->setVisible(true);
                current_npc = msg.readLong();
                break;

            case SMSG_NPC_BUY:
                msg.readShort();  // length
                n_items = (msg.getLength() - 4) / 11;
                buyDialog->reset();
                buyDialog->setMoney(player_info->gp);
                buyDialog->setVisible(true);

                for (int k = 0; k < n_items; k++)
                {
                    long value = msg.readLong();
                    msg.readLong();  // DCvalue
                    msg.readByte();  // type
                    short itemId = msg.readShort();
                    buyDialog->addItem(itemId, value);
                }
                break;

            case SMSG_NPC_SELL:
                msg.readShort();  // length
                n_items = (msg.getLength() - 4) / 10;
                if (n_items > 0) {
                    sellDialog->reset();
                    sellDialog->setVisible(true);

                    for (int k = 0; k < n_items; k++)
                    {
                        short index = msg.readShort();
                        long value = msg.readLong();
                        msg.readLong();  // OCvalue

                        Item *item = inventory->getItem(index);
                        if (item && !(item->isEquipped())) {
                            sellDialog->addItem(item, value);
                        }
                    }
                }
                else {
                    chatWindow->chat_log("Nothing to sell", BY_SERVER);
                    current_npc = 0;
                }
                break;

            case SMSG_NPC_BUY_RESPONSE:
                if (msg.readByte() == 0) {
                    chatWindow->chat_log("Thanks for buying", BY_SERVER);
                } else {
                    chatWindow->chat_log("Unable to buy", BY_SERVER);
                }
                break;

            case SMSG_NPC_SELL_RESPONSE:
                if (msg.readByte() == 0) {
                    chatWindow->chat_log("Thanks for selling", BY_SERVER);
                } else {
                    chatWindow->chat_log("Unable to sell", BY_SERVER);
                }
                break;

            case SMSG_PLAYER_INVENTORY_ADD:
                {
                    short index = msg.readShort();
                    short amount = msg.readShort();
                    short itemId = msg.readShort();
                    msg.readByte();  // identify flag
                    msg.readByte();  // attribute
                    msg.readByte();  // refine
                    msg.skip(8);     // card
                    short equipType = msg.readShort();
                    msg.readByte();  // type
                    char fail = msg.readByte();

                    if (fail > 0) {
                        chatWindow->chat_log("Unable to pick up item",
                                             BY_SERVER);
                    } else {
                        inventory->addItem(index, itemId, amount,
                                           equipType != 0);
                    }
                }
                break;

            case SMSG_PLAYER_INVENTORY_REMOVE:
                {
                    short index = msg.readShort();
                    short amount = msg.readShort();
                    inventory->getItem(index)->increaseQuantity(-amount);
                }
                break;

            case SMSG_PLAYER_INVENTORY_USE:
                {
                    short index = msg.readShort();
                    msg.readShort(); // item id
                    msg.readLong();  // id
                    short amountLeft = msg.readShort();
                    msg.readByte();  // type

                    inventory->getItem(index)->setQuantity(amountLeft);
                }
                break;

            case SMSG_PLAYER_SKILLS:
                msg.readShort();  // length
                n_items = (msg.getLength() - 4) / 37;
                skillDialog->cleanList();

                for (int k = 0; k < n_items; k++)
                {
                    short skillId = msg.readShort();
                    msg.readShort();  // target type
                    msg.readShort();  // unknown
                    short level = msg.readShort();
                    short sp = msg.readShort();
                    msg.readShort();  // range
                    std::string skillName = msg.readString(24);
                    char up = msg.readByte();

                    if (level != 0 || up != 0)
                    {
                        if (skillDialog->hasSkill(skillId)) {
                            skillDialog->setSkill(skillId, level, sp);
                        }
                        else {
                            skillDialog->addSkill(skillId, level, sp);
                        }
                    }
                }
                break;

            case 0x010c:
                // Display MVP player
                msg.readLong(); // id
                chatWindow->chat_log("MVP player", BY_SERVER);
                break;

            case SMSG_ITEM_VISIBLE:
            case SMSG_ITEM_DROPPED:
                {
                    long id = msg.readLong();
                    short itemId = msg.readShort();
                    msg.readByte();  // identify flag
                    short x = msg.readShort();
                    short y = msg.readShort();
                    msg.skip(4);     // amount,subX,subY / subX,subY,amount

                    add_floor_item(new FloorItem(id, itemId, x, y));
                }
                break;

            case SMSG_ITEM_REMOVE:
                remove_floor_item(msg.readLong());
                break;

            case SMSG_NPC_CHOICE:
                msg.readShort();  // length
                current_npc = msg.readLong();
                npcListDialog->parseItems(msg.readString(msg.getLength() - 8));
                npcListDialog->setVisible(true);
                break;

            case SMSG_BEING_CHANGE_LOOKS:
                {
                    Being *being = findNode(msg.readLong());

                    if (being)
                    {
                        switch (msg.readByte()) {
                            case 1:
                                being->setHairStyle(msg.readByte());
                                break;
                            case 2:
                                being->setWeapon(msg.readByte());
                                break;
                            case 6:
                                being->setHairColor(msg.readByte());
                                break;
                            default:
                                msg.readByte(); // unsupported
                                break;
                        }
                    }
                }
                break;

            case SMSG_PLAYER_EQUIP:
                {
                    short index = msg.readShort();
                    short equipPoint = msg.readShort();
                    char type = msg.readByte();

                    logger->log("Equipping: %i %i %i",
                                index, equipPoint, type);

                    if (type == 0) {
                        chatWindow->chat_log("Unable to equip.", BY_SERVER);
                    }
                    else if (equipPoint)
                    {
                        // Unequip any existing equipped item in this position
                        int mask = 1;
                        int position = 0;
                        while (!(equipPoint & mask)) {
                            mask <<= 1;
                            position++;
                        }
                        logger->log("Position %i", position - 1);
                        Item *item = equipment->getEquipment(position - 1);
                        if (item) {
                            item->setEquipped(false);
                        }

                        item = inventory->getItem(index);
                        item->setEquipped(true);
                        equipment->setEquipment(position - 1, item);
                        player_node->setWeaponById(item->getId());
                    }
                }
                break;

            case 0x01d7:
                // Equipment related
                {
                    Being *being = findNode(msg.readLong());
                    msg.readByte();  // equip point
                    short itemId1 = msg.readShort();
                    msg.readShort(); // item id 2

                    if (being != NULL)
                    {
                        being->setWeaponById(itemId1);
                    }
                }
                break;

            case SMSG_PLAYER_UNEQUIP:
                {
                    short index = msg.readShort();
                    short equipPoint = msg.readShort();
                    char type = msg.readByte();

                    if (type == 0) {
                        chatWindow->chat_log("Unable to unequip.", BY_SERVER);
                        break;
                    }

                    if (equipPoint == 0) {
                        // No point given, no point in searching
                        break;
                    }

                    int mask = 1;
                    int position = 0;
                    while (!(equipPoint & mask)) {
                        mask <<= 1;
                        position++;
                    }

                    Item *item = inventory->getItem(index);

                    if (item != NULL)
                    {
                        item->setEquipped(false);

                        switch (item->getId()) {
                            case 529:
                            case 1199:
                                equipment->setArrows(NULL);
                                break;
                            case 521:
                            case 522:
                            case 530:
                            case 536:
                            case 1200:
                            case 1201:
                                player_node->setWeapon(0);
                                // TODO: Why this break? Shouldn't a weapon be
                                //       unequipped in inventory too?
                                break;
                            default:
                                equipment->removeEquipment(position - 1);
                                break;
                        }
                        logger->log("Unequipping: %i %i(%i) %i",
                                    index, equipPoint, type, position - 1);
                    }
                }
                break;

            case SMSG_PLAYER_ARROW_EQUIP:
                {
                    short id = msg.readShort();

                    if (id > 1) {
                        Item *item = inventory->getItem(id);
                        if (item) {
                            item->setEquipped(true);
                            equipment->setArrows(item);
                            logger->log("Arrows equipped: %i", id);
                        }
                    }
                }
                break;

            case SMSG_PLAYER_ARROW_MESSAGE:
                {
                    short type = msg.readShort();

                    switch (type) {
                        case 0:
                            chatWindow->chat_log("Equip arrows first",
                                                 BY_SERVER);
                            break;
                        default:
                            logger->log("0x013b: Unhandled message %i", type);
                            break;
                    }
                }
                break;

            case SMSG_PLAYER_STAT_UPDATE_4:
                {
                    short type = msg.readShort();
                    char fail = msg.readByte();
                    char value = msg.readByte();

                    if (fail == 1)
                    {
                        switch (type) {
                            case 0x000d: player_info->STR = value; break;
                            case 0x000e: player_info->AGI = value; break;
                            case 0x000f: player_info->VIT = value; break;
                            case 0x0010: player_info->INT = value; break;
                            case 0x0011: player_info->DEX = value; break;
                            case 0x0012: player_info->LUK = value; break;
                        }
                    }
                }
                break;

                // Updates stats and status points
            case SMSG_PLAYER_STAT_UPDATE_5:
                player_info->statsPointsToAttribute = msg.readShort();
                player_info->STR   = msg.readByte();
                player_info->STRUp = msg.readByte();
                player_info->AGI   = msg.readByte();
                player_info->AGIUp = msg.readByte();
                player_info->VIT   = msg.readByte();
                player_info->VITUp = msg.readByte();
                player_info->INT   = msg.readByte();
                player_info->INTUp = msg.readByte();
                player_info->DEX   = msg.readByte();
                player_info->DEXUp = msg.readByte();
                player_info->LUK   = msg.readByte();
                player_info->LUKUp = msg.readByte();
                msg.readShort();  // ATK
                msg.readShort();  // ATK bonus
                msg.readShort();  // MATK max
                msg.readShort();  // MATK min
                msg.readShort();  // DEF
                msg.readShort();  // DEF bonus
                msg.readShort();  // MDEF
                msg.readShort();  // MDEF bonus
                msg.readShort();  // HIT
                msg.readShort();  // FLEE
                msg.readShort();  // FLEE bonus
                msg.readShort();  // critical
                msg.readShort();  // unknown
                break;

            case SMSG_PLAYER_STAT_UPDATE_6:
                switch (msg.readShort()) {
                    case 0x0020: player_info->STRUp = msg.readByte(); break;
                    case 0x0021: player_info->AGIUp = msg.readByte(); break;
                    case 0x0022: player_info->VITUp = msg.readByte(); break;
                    case 0x0023: player_info->INTUp = msg.readByte(); break;
                    case 0x0024: player_info->DEXUp = msg.readByte(); break;
                    case 0x0025: player_info->LUKUp = msg.readByte(); break;
                }
                break;

            case SMSG_BEING_NAME_RESPONSE:
                {
                    Being *being = findNode(msg.readLong());

                    if (being) {
                        being->setName(msg.readString(24));
                    }
                }
                break;

            case 0x0119:
                // Change in players look
                break;

            default:
                // Manage non implemented packets
                logger->log("Unhandled packet: %x", msg.getId());
                break;
        }

        skip(msg.getLength());
    }
}