summaryrefslogblamecommitdiff
path: root/src/net/beinghandler.cpp
blob: e88914ac7ed2c633010b6c90640fc39954be2184 (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 "beinghandler.h"

#include <SDL_types.h>

#include "messagein.h"
#include "protocol.h"

#include "../being.h"
#include "../beingmanager.h"
#include "../game.h"
#include "../localplayer.h"
#include "../log.h"
#include "../main.h"
#include "../particle.h"
#include "../sound.h"

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

BeingHandler::BeingHandler()
{
    static const Uint16 _messages[] = {
        //SMSG_BEING_VISIBLE,
        //SMSG_BEING_MOVE,
        //SMSG_BEING_REMOVE,
        //SMSG_BEING_ACTION,
        //SMSG_BEING_LEVELUP,
        //SMSG_BEING_EMOTION,
        //SMSG_BEING_CHANGE_LOOKS,
        //SMSG_BEING_NAME_RESPONSE,
        //SMSG_PLAYER_UPDATE_1,
        //SMSG_PLAYER_UPDATE_2,
        //SMSG_PLAYER_MOVE,
        //0x0119,
        GPMSG_BEING_ATTACK,
        GPMSG_BEING_ENTER,
        GPMSG_BEING_LEAVE,
        GPMSG_BEINGS_MOVE,
        GPMSG_BEINGS_DAMAGE,
        GPMSG_BEING_ACTION_CHANGE,
        0
    };
    handledMessages = _messages;
}

void BeingHandler::handleMessage(MessageIn &msg)
{
    /*
    Uint32 id;
    Uint16 job, speed;
    Uint16 headBottom, headTop, headMid;
    Sint16 param1;
    Sint8 type;
    Being *srcBeing, *dstBeing;
    */

    switch (msg.getId())
    {
        case GPMSG_BEING_ENTER:
            handleBeingEnterMessage(msg);
            break;
        case GPMSG_BEING_LEAVE:
            handleBeingLeaveMessage(msg);
            break;
        case GPMSG_BEINGS_MOVE:
            handleBeingsMoveMessage(msg);
            break;
        case GPMSG_BEING_ATTACK:
            handleBeingAttackMessage(msg);
            break;
        case GPMSG_BEINGS_DAMAGE:
            handleBeingsDamageMessage(msg);
            break;
        case GPMSG_BEING_ACTION_CHANGE:
            handleBeingActionChangeMessage(msg);
            break;

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

            dstBeing = beingManager->findBeing(id);

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

                dstBeing = beingManager->createBeing(id, job);
            }
            else if (msg.getId() == 0x0078)
            {
                dstBeing->clearPath();
                dstBeing->mFrame = 0;
                dstBeing->mWalkTime = tick_time;
                dstBeing->setAction(Being::STAND);
            }

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

            dstBeing->setWalkSpeed(speed);
            dstBeing->mJob = job;
            dstBeing->setHairStyle(msg.readShort());
            dstBeing->setWeapon(msg.readShort());
            dstBeing->setVisibleEquipment(
                    Being::BOTTOMCLOTHES_SPRITE, msg.readShort());

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

            msg.readShort();  // shield
            dstBeing->setVisibleEquipment(Being::HAIT_SPRITE, msg.readShort());
            dstBeing->setVisibleEquipment(
                    Being::TOPCLOTHES_SPRITE, msg.readShort());
            dstBeing->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
            dstBeing->setSex(1 - msg.readByte());   // sex

            if (msg.getId() == SMSG_BEING_MOVE)
            {
                //Uint16 srcX, srcY, dstX, dstY;
                //msg.readCoordinatePair(srcX, srcY, dstX, dstY);
                //dstBeing->setAction(Being::STAND);
                //dstBeing->mX = srcX;
                //dstBeing->mY = srcY;
                //dstBeing->setDestination(dstX, dstY);
            }
            else
            {
                //Uint8 dir;
                //msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir);
                //dstBeing->setDirection(dir);
            }

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

        case SMSG_BEING_REMOVE:
            // A being should be removed or has died
            dstBeing = beingManager->findBeing(msg.readLong());

            if (!dstBeing)
                break;

            if (msg.readByte() == 1)
            {
                dstBeing->setAction(Being::DEAD);
            }
            else
            {
                beingManager->destroyBeing(dstBeing);
            }

            if (dstBeing == player_node->getTarget())
            {
                player_node->stopAttack();
            }
            break;

        case SMSG_BEING_ACTION:
            srcBeing = beingManager->findBeing(msg.readLong());
            dstBeing = beingManager->findBeing(msg.readLong());
            msg.readLong();   // server tick
            msg.readLong();   // src speed
            msg.readLong();   // dst speed
            param1 = msg.readShort();
            msg.readShort();  // param 2
            type = msg.readByte();
            msg.readShort();  // param 3

            switch (type)
            {
                case 0: // Damage
                    if (dstBeing) {
                        dstBeing->takeDamage(param1);
                    }
                    if (srcBeing) {
                        srcBeing->handleAttack(dstBeing, param1);
                    }
                    break;

                case 2: // Sit
                    if (srcBeing) {
                        srcBeing->mFrame = 0;
                        srcBeing->setAction(Being::SIT);
                    }
                    break;

                case 3: // Stand up
                    if (srcBeing) {
                        srcBeing->mFrame = 0;
                        srcBeing->setAction(Being::STAND);
                    }
                    break;
            }
            break;

        case SMSG_BEING_LEVELUP:
            id = (Uint32) msg->readLong();

            if (id == player_node->getId()) {
                logger->log("Level up");
                sound.playSfx("sfx/levelup.ogg");
            }
            else {
                logger->log("Someone else went level up");
            }
            Particle *levelupFX;
            if (msg->readLong() == 0) { // type
                levelupFX = particleEngine->addEffect("graphics/particles/levelup.particle.xml", 0, 0);
            }
            else {
                levelupFX = particleEngine->addEffect("graphics/particles/skillup.particle.xml", 0, 0);
            }
            beingManager->findBeing(id)->controlParticle(levelupFX);
            break;

        case SMSG_BEING_EMOTION:
            if (!(dstBeing = beingManager->findBeing(msg.readLong())))
            {
                break;
            }

            dstBeing->mEmotion = msg.readByte();
            dstBeing->mEmotionTime = EMOTION_TIME;
            break;

        case SMSG_BEING_CHANGE_LOOKS:
        {
            if (!(dstBeing = beingManager->findBeing(msg.readLong())))
            {
                break;
            }

            int type = msg.readByte();
            int id = msg.readByte();

            switch (type) {
                case 1:
                    dstBeing->setHairStyle(id);
                    break;
                case 2:
                    dstBeing->setWeapon(id);
                    break;
                case 3:     // Change lower headgear for eAthena, pants for us
                    dstBeing->setVisibleEquipment(
                            Being::BOTTOMCLOTHES_SPRITE,
                            id);
                    break;
                case 4:     // Change upper headgear for eAthena, hat for us
                    dstBeing->setVisibleEquipment(
                            Being::HAT_SPRITE,
                            id);
                    break;
                case 5:     // Change middle headgear for eathena, armor for us
                     dstBeing->setVisibleEquipment(
                            Being::TOPCLOTHES_SPRITE,
                            id);
                    break;
                case 6:
                    dstBeing->setHairColor(id);
                    break;
                default:
                    logger->log("c3: %i\n", id); // unsupported
                    break;
            }
        }
            break;

        case SMSG_BEING_NAME_RESPONSE:
            if ((dstBeing = beingManager->findBeing(msg.readLong())))
            {
                dstBeing->setName(msg.readString(24));
            }
            break;

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

            dstBeing = beingManager->findBeing(id);

            if (dstBeing == NULL)
            {
                dstBeing = beingManager->createBeing(id, job);
            }

            dstBeing->setWalkSpeed(speed);
            dstBeing->mJob = job;
            dstBeing->setHairStyle(msg.readShort());
            dstBeing->setWeaponById(msg.readShort());  // item id 1
            msg.readShort();  // item id 2
            headBottom = msg.readShort();

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

            headTop = msg.readShort();
            headMid = msg.readShort();
            dstBeing->setHairColor(msg.readShort());
            msg.readShort();  // unknown
            msg.readShort();  // head dir
            msg.readLong();  // guild
            msg.readLong();  // emblem
            msg.readShort();  // manner
            msg.readByte();   // karma
            dstBeing->setSex(1 - msg.readByte());   // sex
            dstBeing->setVisibleEquipment(
                    Being::BOTTOMCLOTHES_SPRITE, headBottom);
            dstBeing->setVisibleEquipment(Being::HAT_SPRITE, headTop);
            dstBeing->setVisibleEquipment(Being::TOPCLOTHES_SPRITE, headMid);

            if (msg.getId() == SMSG_PLAYER_MOVE)
            {
                //Uint16 srcX, srcY, dstX, dstY;
                //msg.readCoordinatePair(srcX, srcY, dstX, dstY);
                //dstBeing->mX = srcX;
                //dstBeing->mY = srcY;
                //dstBeing->setDestination(dstX, dstY);
            }
            else
            {
                //Uint8 dir;
                //msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir);
                //dstBeing->setDirection(dir);
            }

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

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

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

            dstBeing->mWalkTime = tick_time;
            dstBeing->mFrame = 0;
            break;

        case 0x0119:
            // Change in players look
            logger->log("0x0119 %li %i %i %x %i\n", msg.readLong(),
                   msg.readShort(), msg.readShort(), msg.readShort(),
                   msg.readByte());
            break;
        */
    }
}

void
BeingHandler::handleBeingEnterMessage(MessageIn &msg)
{
    int type = msg.readByte(); // type
    int id = msg.readShort();
    Being::Action action = (Being::Action)msg.readByte();
    Uint16 px = msg.readShort();
    Uint16 py = msg.readShort();

    switch (type)
    {
        case OBJECT_PLAYER:
        {
            std::string name = msg.readString();
            Being *being;
            if (player_node->getName() == name)
            {
                being = player_node;
                being->setId(id);
            }
            else
            {
                being = beingManager->createBeing(id, 0);
                being->setName(name);
            }
            being->setHairStyle(msg.readByte());
            being->setHairColor(msg.readByte());
            being->setSex(msg.readByte());
            being->mX = px;
            being->mY = py;
            being->setDestination(px, py);
            being->setAction(action);
        } break;
        case OBJECT_MONSTER:
        {
            int monsterId = msg.readShort();
            Being *being;
            being = beingManager->createBeing(id, 1002 + monsterId);
            being->setWalkSpeed(150); // TODO
            being->mX = px;
            being->mY = py;
            being->setDestination(px, py);
            being->setAction(action);
        } break;
    }
}

void BeingHandler::handleBeingLeaveMessage(MessageIn &msg)
{
    Being *being = beingManager->findBeing(msg.readShort());
    if (!being) return;

    beingManager->destroyBeing(being);
}

void BeingHandler::handleBeingsMoveMessage(MessageIn &msg)
{
    while (msg.getUnreadLength())
    {
        Uint16 id = msg.readShort();
        Uint8 flags = msg.readByte();
        Being *being = beingManager->findBeing(id);
        int sx = 0, sy = 0, dx = 0, dy = 0;
        if (flags & MOVING_POSITION)
        {
            Uint16 sx2, sy2;
            msg.readCoordinates(sx2, sy2);
            sx = sx2 * 32 + 16;
            sy = sy2 * 32 + 16;
        }
        if (flags & MOVING_DESTINATION)
        {
            dx = msg.readShort();
            dy = msg.readShort();
            if (!(flags & MOVING_POSITION))
            {
                sx = dx;
                sy = dy;
            }
        }
        if (!being || !(flags & (MOVING_POSITION | MOVING_DESTINATION)))
        {
            continue;
        }
        if (abs(being->mX - sx) + abs(being->mY - sy) > 4 * 32)
        {
            // Too large a desynchronization.
            being->mX = sx;
            being->mY = sy;
            being->setDestination(dx, dy);
        }
        else if (!(flags & MOVING_POSITION))
        {
            being->setDestination(dx, dy);
        }
        else if (!(flags & MOVING_DESTINATION))
        {
            being->adjustCourse(sx, sy);
        }
        else
        {
            being->adjustCourse(sx, sy, dx, dy);
        }
    }
}

void BeingHandler::handleBeingAttackMessage(MessageIn &msg)
{
    Being *being = beingManager->findBeing(msg.readShort());
    if (!being) return;

    being->setAction(Being::ATTACK);
    being->setDirection(msg.readByte());
}

void BeingHandler::handleBeingsDamageMessage(MessageIn &msg)
{
    while (msg.getUnreadLength())
    {
        Being *being = beingManager->findBeing(msg.readShort());
        int damage = msg.readShort();
        if (being)
        {
            being->takeDamage(damage);
        }
    }
}

void BeingHandler::handleBeingActionChangeMessage(MessageIn &msg)
{
    Being* being = beingManager->findBeing(msg.readShort());
    if (!being) return;

    being->setAction((Being::Action) msg.readByte());
}