Jump to content

Professional Biolog System


Recommended Posts

  • 2 weeks later...
  • 2 weeks later...

Sistema parece interessante para poupar aquele "sacrificio" de ir ao biologo, sempre que o queiramos entregar algum item.

Acho que só melhorava a interface da janela ao lado do inventário. De resto, está muito bom. Tinha que ser feito pelo Vegas, como de costume.

 

Kind regards

Link to post
Share on other sites
1 hora atrás, charparodar disse:

Não funciona...

Ao compilar falta sempre alguma coisa, uma delas é a função "SendMessagePopUp"

Essa função não existe na source nem no código postado...

Esse sistema está incompleto,  códigos incompletos e falta funções.

Link to post
Share on other sites
  • 11 months later...

Any one has this problem with compile ?? 

I have problem in char.cpp <-- I added all as the tutorial showed but still get the error .

Much appreciate for your help guys 

ERROR _COMPILE_LOG:

Quote

char.cpp:7230: warning: this decimal constant is unsigned only in ISO C90
char.cpp: In member function 'int CHARACTER::GetPolymorphPoint(BYTE) const':
char.cpp:2880: warning: suggest parentheses around && within ||
char.cpp: In member function 'void CHARACTER::PointChange(BYTE, int, bool, bool)':
char.cpp:3088: warning: comparison between signed and unsigned integer expressions
char.cpp: In member function 'void CHARACTER::SetTarget(CHARACTER*)':
char.cpp:5103: warning: suggest parentheses around && within ||
char.cpp: In member function 'bool CHARACTER::CanSummon(int)':
char.cpp:6284: warning: suggest parentheses around && within ||
char.cpp: In member function 'int CHARACTER::GetBiologState(const std::string&) const':
char.cpp:6290: error: 'pPC' was not declared in this scope
char.cpp:6289: warning: unused variable 'q'
../../../Extern/include/cryptopp/cryptlib.h: In member function 'bool CryptoPP::NameValuePairs::GetValue(const char*, T&) const [with T = std::string]':
../../../Extern/include/cryptopp/cryptlib.h:277:   instantiated from here
../../../Extern/include/cryptopp/cryptlib.h:264: warning: dereferencing type-punned pointer will break strict-aliasing rules
../../../Extern/include/cryptopp/cryptlib.h: In member function 'bool CryptoPP::NameValuePairs::GetValue(const char*, T&) const [with T = int]':
../../../Extern/include/cryptopp/cryptlib.h:283:   instantiated from here
../../../Extern/include/cryptopp/cryptlib.h:264: warning: dereferencing type-punned pointer will break strict-aliasing rules
gmake: *** [Makefile:133: OBJDIR/char.o] Error 1
gmake: *** Waiting for unfinished jobs....
 

my char.cpp:

 

Quote

#include "stdafx.h"

#include "../../common/teen_packet.h"
#include "../../common/VnumHelper.h"

#include "char.h"

#include "config.h"
#include "utils.h"
#include "crc32.h"
#include "char_manager.h"
#include "desc_client.h"
#include "desc_manager.h"
#include "buffer_manager.h"
#include "item_manager.h"
#include "motion.h"
#include "vector.h"
#include "packet.h"
#include "cmd.h"
#include "fishing.h"
#include "exchange.h"
#include "battle.h"
#include "affect.h"
#include "shop.h"
#include "shop_manager.h"
#include "safebox.h"
#include "regen.h"
#include "pvp.h"
#include "party.h"
#include "start_position.h"
#include "questmanager.h"
#include "log.h"
#include "p2p.h"
#include "guild.h"
#include "guild_manager.h"
#include "dungeon.h"
#include "messenger_manager.h"
#include "unique_item.h"
#include "priv_manager.h"
#include "war_map.h"
#include "xmas_event.h"
#include "banword.h"
#include "target.h"
#include "wedding.h"
#include "mob_manager.h"
#include "mining.h"
#include "monarch.h"
#include "castle.h"
#include "arena.h"
#include "dev_log.h"
#include "horsename_manager.h"
#include "pcbang.h"
#include "gm.h"
#include "map_location.h"
#include "BlueDragon_Binder.h"
#include "HackShield.h"
#include "skill_power.h"
#include "XTrapManager.h"
#include "buff_on_attributes.h"

#ifdef __PET_SYSTEM__
#include "PetSystem.h"
#endif
#include "DragonSoul.h"

extern const BYTE g_aBuffOnAttrPoints;
extern bool RaceToJob(unsigned race, unsigned *ret_job);

extern int g_nPortalLimitTime;
extern int test_server;

extern bool IS_SUMMONABLE_ZONE(int map_index); // char_item.cpp
bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index);

bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index)
{
    switch (map_index)
    {
    case 301:
    case 302:
    case 303:
    case 304:
        if (ch->GetLevel() < 90)
            return false;
    }
    return true;
}

// <Factor> DynamicCharacterPtr member function definitions

LPCHARACTER DynamicCharacterPtr::Get() const {
    LPCHARACTER p = NULL;
    if (is_pc) {
        p = CHARACTER_MANAGER::instance().FindByPID(id);
    } else {
        p = CHARACTER_MANAGER::instance().Find(id);
    }
    return p;
}

DynamicCharacterPtr& DynamicCharacterPtr::operator=(LPCHARACTER character) {
    if (character == NULL) {
        Reset();
        return *this;
    }
    if (character->IsPC()) {
        is_pc = true;
        id = character->GetPlayerID();
    } else {
        is_pc = false;
        id = character->GetVID();
    }
    return *this;
}

CHARACTER::CHARACTER()
{
    m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty);
    m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
    m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty);

    Initialize();
}

CHARACTER::~CHARACTER()
{
    Destroy();
}

void CHARACTER::Initialize()
{
    CEntity::Initialize(ENTITY_CHARACTER);

    m_bNoOpenedShop = true;

    m_bOpeningSafebox = false;

    m_fSyncTime = get_float_time()-3;
    m_dwPlayerID = 0;
    m_dwKillerPID = 0;

    m_iMoveCount = 0;

    m_pkRegen = NULL;
    regen_id_ = 0;
    m_posRegen.x = m_posRegen.y = m_posRegen.z = 0;
    m_posStart.x = m_posStart.y = 0;
    m_posDest.x = m_posDest.y = 0;
    m_fRegenAngle = 0.0f;

    m_pkMobData        = NULL;
    m_pkMobInst        = NULL;

    m_pkShop        = NULL;
    m_pkChrShopOwner    = NULL;
    m_pkMyShop        = NULL;
    m_pkExchange    = NULL;
    m_pkParty        = NULL;
    m_pkPartyRequestEvent = NULL;

    m_pGuild = NULL;

    m_pkChrTarget = NULL;

    m_pkMuyeongEvent = NULL;

    m_pkWarpNPCEvent = NULL;
    m_pkDeadEvent = NULL;
    m_pkStunEvent = NULL;
    m_pkSaveEvent = NULL;
    m_pkRecoveryEvent = NULL;
    m_pkTimedEvent = NULL;
    m_pkFishingEvent = NULL;
    m_pkWarpEvent = NULL;

    // MINING
    m_pkMiningEvent = NULL;
    // END_OF_MINING

    m_pkPoisonEvent = NULL;
    m_pkFireEvent = NULL;
    m_pkCheckSpeedHackEvent    = NULL;
    m_speed_hack_count    = 0;

    m_pkAffectEvent = NULL;
    m_afAffectFlag = TAffectFlag(0, 0);

    m_pkDestroyWhenIdleEvent = NULL;

    m_pkChrSyncOwner = NULL;

    memset(&m_points, 0, sizeof(m_points));
    memset(&m_pointsInstant, 0, sizeof(m_pointsInstant));
    memset(&m_quickslot, 0, sizeof(m_quickslot));

    m_bCharType = CHAR_TYPE_MONSTER;

    SetPosition(POS_STANDING);

    m_dwPlayStartTime = m_dwLastMoveTime = get_dword_time();

    GotoState(m_stateIdle);
    m_dwStateDuration = 1;

    m_dwLastAttackTime = get_dword_time() - 20000;

    m_bAddChrState = 0;

    m_pkChrStone = NULL;

    m_pkSafebox = NULL;
    m_iSafeboxSize = -1;
    m_iSafeboxLoadTime = 0;

    m_pkMall = NULL;
    m_iMallLoadTime = 0;

    m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
    m_lWarpMapIndex = 0;

    m_posExit.x = m_posExit.y = m_posExit.z = 0;
    m_lExitMapIndex = 0;

    m_pSkillLevels = NULL;

    m_dwMoveStartTime = 0;
    m_dwMoveDuration = 0;

    m_dwFlyTargetID = 0;

    m_dwNextStatePulse = 0;

    m_dwLastDeadTime = get_dword_time()-180000;

    m_bSkipSave = false;

    m_bItemLoaded = false;

    m_bHasPoisoned = false;

    m_pkDungeon = NULL;
    m_iEventAttr = 0;

    m_kAttackLog.dwVID = 0;
    m_kAttackLog.dwTime = 0;

    m_bNowWalking = m_bWalking = false;
    ResetChangeAttackPositionTime();

    m_bDetailLog = false;
    m_bMonsterLog = false;

    m_bDisableCooltime = false;

    m_iAlignment = 0;
    m_iRealAlignment = 0;

    m_iKillerModePulse = 0;
    m_bPKMode = PK_MODE_PEACE;

    m_dwQuestNPCVID = 0;
    m_dwQuestByVnum = 0;
    m_pQuestItem = NULL;

    m_szMobileAuth[0] = '\0';

    m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000;

    m_bUnderRefine = false;

    // REFINE_NPC
    m_dwRefineNPCVID = 0;
    // END_OF_REFINE_NPC

    m_dwPolymorphRace = 0;

    m_bStaminaConsume = false;

    ResetChainLightningIndex();

    m_dwMountVnum = 0;
    m_chHorse = NULL;
    m_chRider = NULL;

    m_pWarMap = NULL;
    m_pWeddingMap = NULL;
    m_bChatCounter = 0;

    ResetStopTime();

    m_dwLastVictimSetTime = get_dword_time() - 3000;
    m_iMaxAggro = -100;

    m_bSendHorseLevel = 0;
    m_bSendHorseHealthGrade = 0;
    m_bSendHorseStaminaGrade = 0;

    m_dwLoginPlayTime = 0;

    m_pkChrMarried = NULL;

    m_posSafeboxOpen.x = -1000;
    m_posSafeboxOpen.y = -1000;

    // EQUIP_LAST_SKILL_DELAY
    m_dwLastSkillTime = get_dword_time();
    // END_OF_EQUIP_LAST_SKILL_DELAY

    // MOB_SKILL_COOLTIME
    memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime));
    // END_OF_MOB_SKILL_COOLTIME

    m_isinPCBang = false;

    // ARENA
    m_pArena = NULL;
    m_nPotionLimit = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count");
    // END_ARENA

    //PREVENT_TRADE_WINDOW
    m_isOpenSafebox = 0;
    //END_PREVENT_TRADE_WINDOW
    
    //PREVENT_REFINE_HACK
    m_iRefineTime = 0;
    //END_PREVENT_REFINE_HACK
    
    //RESTRICT_USE_SEED_OR_MOONBOTTLE
    m_iSeedTime = 0;
    //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
    //PREVENT_PORTAL_AFTER_EXCHANGE
    m_iExchangeTime = 0;
    //END_PREVENT_PORTAL_AFTER_EXCHANGE
    //
    m_iSafeboxLoadTime = 0;

    m_iMyShopTime = 0;

    InitMC();

    m_deposit_pulse = 0;

    SET_OVER_TIME(this, OT_NONE);

    m_strNewName = "";

    m_known_guild.clear();

    m_dwLogOffInterval = 0;

    m_bComboSequence = 0;
    m_dwLastComboTime = 0;
    m_bComboIndex = 0;
    m_iComboHackCount = 0;
    m_dwSkipComboAttackByTime = 0;

    m_dwMountTime = 0;

    m_dwLastGoldDropTime = 0;

    m_HackShieldCheckEvent = NULL;
    m_HackShieldCheckMode = false;

    m_bIsLoadedAffect = false;
    cannot_dead = false;

#ifdef __PET_SYSTEM__
    m_petSystem = 0;
    m_bIsPet = false;
#endif

    m_fAttMul = 1.0f;
    m_fDamMul = 1.0f;

    m_pointsInstant.iDragonSoulActiveDeck = -1;

    memset(&m_tvLastSyncTime, 0, sizeof(m_tvLastSyncTime));
    m_iSyncHackCount = 0;
}

void CHARACTER::Create(const char * c_pszName, DWORD vid, bool isPC)
{
    static int s_crc = 172814;

    char crc_string[128+1];
    snprintf(crc_string, sizeof(crc_string), "%s%p%d", c_pszName, this, ++s_crc);
    m_vid = VID(vid, GetCRC32(crc_string, strlen(crc_string)));

    if (isPC)
        m_stName = c_pszName;
}

void CHARACTER::Destroy()
{
    CloseMyShop();

    if (m_pkRegen)
    {
        if (m_pkDungeon) {
            // Dungeon regen may not be valid at this point
            if (m_pkDungeon->IsValidRegen(m_pkRegen, regen_id_)) {
                --m_pkRegen->count;
            }
        } else {
            // Is this really safe?
            --m_pkRegen->count;
        }
        m_pkRegen = NULL;
    }

    if (m_pkDungeon)
    {
        SetDungeon(NULL);
    }

#ifdef __PET_SYSTEM__
    if (m_petSystem)
    {
        m_petSystem->Destroy();
        delete m_petSystem;

        m_petSystem = 0;
    }
#endif

    HorseSummon(false);

    if (GetRider())
        GetRider()->ClearHorseInfo();

    if( IsPC() )
    {
        if (isHackShieldEnable)
        {
            CHackShieldManager::instance().DeleteClientHandle(GetPlayerID());
        }
    }

    if (GetDesc())
    {
        GetDesc()->BindCharacter(NULL);
//        BindDesc(NULL);
    }

    if (m_pkExchange)
        m_pkExchange->Cancel();

    SetVictim(NULL);

    if (GetShop())
    {
        GetShop()->RemoveGuest(this);
        SetShop(NULL);
    }

    ClearStone();
    ClearSync();
    ClearTarget();

    if (NULL == m_pkMobData)
    {
        DragonSoul_CleanUp();
        ClearItem();
    }

    // <Factor> m_pkParty becomes NULL after CParty destructor call!
    LPPARTY party = m_pkParty;
    if (party)
    {
        if (party->GetLeaderPID() == GetVID() && !IsPC())
        {
            M2_DELETE(party);
        }
        else
        {
            party->Unlink(this); 

            if (!IsPC())
                party->Quit(GetVID());
        }

        SetParty(NULL); // ľČÇصµ µÇÁö¸¸ ľČŔüÇĎ°Ô.
    }

    if (m_pkMobInst)
    {
        M2_DELETE(m_pkMobInst);
        m_pkMobInst = NULL;
    }

    m_pkMobData = NULL;

    if (m_pkSafebox)
    {
        M2_DELETE(m_pkSafebox);
        m_pkSafebox = NULL;
    }

    if (m_pkMall)
    {
        M2_DELETE(m_pkMall);
        m_pkMall = NULL;
    }

    m_set_pkChrSpawnedBy.clear();

    StopMuyeongEvent();
    event_cancel(&m_pkWarpNPCEvent);
    event_cancel(&m_pkRecoveryEvent);
    event_cancel(&m_pkDeadEvent);
    event_cancel(&m_pkSaveEvent);
    event_cancel(&m_pkTimedEvent);
    event_cancel(&m_pkStunEvent);
    event_cancel(&m_pkFishingEvent);
    event_cancel(&m_pkPoisonEvent);
    event_cancel(&m_pkFireEvent);
    event_cancel(&m_pkPartyRequestEvent);
    //DELAYED_WARP
    event_cancel(&m_pkWarpEvent);
    event_cancel(&m_pkCheckSpeedHackEvent);
    //END_DELAYED_WARP

    // RECALL_DELAY
    //event_cancel(&m_pkRecallEvent);
    // END_OF_RECALL_DELAY

    // MINING
    event_cancel(&m_pkMiningEvent);
    // END_OF_MINING

    StopHackShieldCheckCycle();

    for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it)
    {
        LPEVENT pkEvent = it->second;
        event_cancel(&pkEvent);
    }
    m_mapMobSkillEvent.clear();

    //event_cancel(&m_pkAffectEvent);
    ClearAffect();

    for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin();  it != m_map_buff_on_attrs.end(); it++)
    {
        if (NULL != it->second)
        {
            M2_DELETE(it->second);
        }
    }
    m_map_buff_on_attrs.clear();

    event_cancel(&m_pkDestroyWhenIdleEvent);

    if (m_pSkillLevels)
    {
        M2_DELETE_ARRAY(m_pSkillLevels);
        m_pSkillLevels = NULL;
    }

    CEntity::Destroy();

    if (GetSectree())
        GetSectree()->RemoveEntity(this);

    if (m_bMonsterLog)
        CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
}

const char * CHARACTER::GetName() const
{
    return m_stName.empty() ? (m_pkMobData ? m_pkMobData->m_table.szLocaleName : "") : m_stName.c_str();
}

void CHARACTER::OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount)
{
    if (GetPart(PART_MAIN) > 2)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°©żĘŔ» ąţľîľß °łŔÎ »óÁˇŔ» ż­ Ľö ŔÖ˝Ŕ´Ď´Ů."));
        return;
    }

    if (GetMyShop())    // ŔĚąĚ ĽĄŔĚ ż­·Á ŔÖŔ¸¸é ´Ý´Â´Ů.
    {
        CloseMyShop();
        return;
    }

    // ÁřÇŕÁßŔÎ Äů˝şĆ®°ˇ ŔÖŔ¸¸é »óÁˇŔ» ż­ Ľö ľř´Ů.
    quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());

    // GetPCForce´Â NULLŔĎ Ľö ľřŔ¸ąÇ·Î µű·Î Č®ŔÎÇĎÁö ľĘŔ˝
    if (pPC->IsRunning())
        return;

    if (bItemCount == 0)
        return;

    int64_t nTotalMoney = 0;

    for (int n = 0; n < bItemCount; ++n)
    {
        nTotalMoney += static_cast<int64_t>((pTable+n)->price);
    }

    nTotalMoney += static_cast<int64_t>(GetGold());

    if (GOLD_MAX <= nTotalMoney)
    {
        sys_err("[OVERFLOW_GOLD] Overflow (GOLD_MAX) id %u name %s", GetPlayerID(), GetName());
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20ľď łÉŔ» ĂĘ°úÇĎż© »óÁˇŔ» ż­Ľö°ˇ ľř˝Ŕ´Ď´Ů"));
        return;
    }

    char szSign[SHOP_SIGN_MAX_LEN+1];
    strlcpy(szSign, c_pszSign, sizeof(szSign));

    m_stShopSign = szSign;

    if (m_stShopSign.length() == 0)
        return;

    if (LC_IsCanada() == false)
    {
        if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length()))
        {
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("şńĽÓľîłŞ Ŕşľî°ˇ Ć÷ÇÔµČ »óÁˇ Ŕ̸§Ŕ¸·Î »óÁˇŔ» ż­ Ľö ľř˝Ŕ´Ď´Ů."));    
            return;
        }
    }

    // MYSHOP_PRICE_LIST
    std::map<DWORD, DWORD> itemkind;  // ľĆŔĚĹŰ Áľ·ůş° °ˇ°Ý, first: vnum, second: ´ÜŔĎ Ľö·® °ˇ°Ý
    // END_OF_MYSHOP_PRICE_LIST    

    std::set<TItemPos> cont;
    for (BYTE i = 0; i < bItemCount; ++i)
    {
        if (cont.find((pTable + i)->pos) != cont.end())
        {
            sys_err("MYSHOP: duplicate shop item detected! (name: %s)", GetName());
            return;
        }

        // ANTI_GIVE, ANTI_MYSHOP check
        LPITEM pkItem = GetItem((pTable + i)->pos);

        if (pkItem)
        {
            const TItemTable * item_table = pkItem->GetProto();

            if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP)))
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŔŻ·áČ­ ľĆŔĚĹŰŔş °łŔλóÁˇżˇĽ­ ĆǸĹÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                return;
            }

            if (pkItem->IsEquipped() == true)
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ŔĺşńÁßŔÎ ľĆŔĚĹŰŔş °łŔλóÁˇżˇĽ­ ĆǸĹÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                return;
            }

            if (true == pkItem->isLocked())
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»çżëÁßŔÎ ľĆŔĚĹŰŔş °łŔλóÁˇżˇĽ­ ĆǸĹÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                return;
            }

            // MYSHOP_PRICE_LIST
            itemkind[pkItem->GetVnum()] = (pTable + i)->price / pkItem->GetCount();
            // END_OF_MYSHOP_PRICE_LIST
        }

        cont.insert((pTable + i)->pos);
    }

    // MYSHOP_PRICE_LIST
    // ş¸µű¸® °łĽö¸¦ °¨ĽŇ˝ĂŲ´Ů. 
    if (CountSpecifyItem(71049)) { // şń´Ü ş¸µű¸®´Â ľřľÖÁö ľĘ°í °ˇ°ÝÁ¤ş¸¸¦ ŔúŔĺÇŃ´Ů.

        //
        // ľĆŔĚĹŰ °ˇ°ÝÁ¤ş¸¸¦ ŔúŔĺÇϱâ Ŕ§ÇŘ ľĆŔĚĹŰ °ˇ°ÝÁ¤ş¸ ĆĐŶŔ» ¸¸µéľî DB Äł˝Ăżˇ ş¸ł˝´Ů.
        //
        TPacketMyshopPricelistHeader header;
        TItemPriceInfo info;
        
        header.dwOwnerID = GetPlayerID();
        header.byCount = itemkind.size();

        TEMP_BUFFER buf;
        buf.write(&header, sizeof(header));

        for (itertype(itemkind) it = itemkind.begin(); it != itemkind.end(); ++it)
        {
            info.dwVnum = it->first;
            info.dwPrice = it->second;

            buf.write(&info, sizeof(info));
        }

        db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_UPDATE, 0, buf.read_peek(), buf.size());
    } 
    // END_OF_MYSHOP_PRICE_LIST
    else if (CountSpecifyItem(50200))
        RemoveSpecifyItem(50200, 1);
    else
        return; // ş¸µű¸®°ˇ ľřŔ¸¸é Áß´Ü.

    if (m_pkExchange)
        m_pkExchange->Cancel();

    TPacketGCShopSign p;

    p.bHeader = HEADER_GC_SHOP_SIGN;
    p.dwVID = GetVID();
    strlcpy(p.szSign, c_pszSign, sizeof(p.szSign));

    PacketAround(&p, sizeof(TPacketGCShopSign));

    m_pkMyShop = CShopManager::instance().CreatePCShop(this, pTable, bItemCount);

    if (IsPolymorphed() == true)
    {
        RemoveAffect(AFFECT_POLYMORPH);
    }

    if (GetHorse())
    {
        HorseSummon( false, true );
    }
    // new mount ŔĚżë Áßżˇ, °łŔÎ »óÁˇ ż­¸é ŔÚµż unmount
    // StopRidingŔ¸·Î ´ş¸¶żîĆ®±îÁö Ăł¸®Çϸé ÁÁŔşµĄ żÖ ±×·¸°Ô ľČÇŘłů´ÂÁö ľË Ľö ľř´Ů.
    else if (GetMountVnum())
    {
        RemoveAffect(AFFECT_MOUNT);
        RemoveAffect(AFFECT_MOUNT_BONUS);
    }
    //if (!LC_IsNewCIBN())
        SetPolymorph(30000, true);

}

void CHARACTER::CloseMyShop()
{
    if (GetMyShop())
    {
        m_stShopSign.clear();
        CShopManager::instance().DestroyPCShop(this);
        m_pkMyShop = NULL;

        TPacketGCShopSign p;

        p.bHeader = HEADER_GC_SHOP_SIGN;
        p.dwVID = GetVID();
        p.szSign[0] = '\0';

        PacketAround(&p, sizeof(p));
        
        //if (!LC_IsNewCIBN())
            SetPolymorph(GetJob(), true);
    }
}

void EncodeMovePacket(TPacketGCMove & pack, DWORD dwVID, BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, BYTE bRot)
{
    pack.bHeader = HEADER_GC_MOVE;
    pack.bFunc   = bFunc;
    pack.bArg    = bArg;
    pack.dwVID   = dwVID;
    pack.dwTime  = dwTime ? dwTime : get_dword_time();
    pack.bRot    = bRot;
    pack.lX        = x;
    pack.lY        = y;
    pack.dwDuration    = dwDuration;
}

void CHARACTER::RestartAtSamePos()
{
    if (m_bIsObserver)
        return;

    EncodeRemovePacket(this);
    EncodeInsertPacket(this);

    ENTITY_MAP::iterator it = m_map_view.begin();

    while (it != m_map_view.end())
    {
        LPENTITY entity = (it++)->first;

        EncodeRemovePacket(entity);
        if (!m_bIsObserver)
            EncodeInsertPacket(entity);

        if( entity->IsType(ENTITY_CHARACTER) )
        {
            LPCHARACTER lpChar = (LPCHARACTER)entity;
            if( lpChar->IsPC() || lpChar->IsNPC() || lpChar->IsMonster() )
            {
                if (!entity->IsObserverMode())
                    entity->EncodeInsertPacket(this);
            }
        }
        else
        {
            if( !entity->IsObserverMode())
            {
                entity->EncodeInsertPacket(this);
            }
        }
    }
}


// Entityżˇ ł»°ˇ łŞĹ¸łµ´Ů°í ĆĐŶŔ» ş¸ł˝´Ů.
void CHARACTER::EncodeInsertPacket(LPENTITY entity)
{

    LPDESC d;

    if (!(d = entity->GetDesc()))
        return;

    // ±ćµĺŔ̸§ ąö±× ĽöÁ¤ ÄÚµĺ
    LPCHARACTER ch = (LPCHARACTER) entity;
    ch->SendGuildName(GetGuild());
    // ±ćµĺŔ̸§ ąö±× ĽöÁ¤ ÄÚµĺ

    TPacketGCCharacterAdd pack;

    pack.header        = HEADER_GC_CHARACTER_ADD;
    pack.dwVID        = m_vid;
    pack.bType        = GetCharType();
    pack.angle        = GetRotation();
    pack.x        = GetX();
    pack.y        = GetY();
    pack.z        = GetZ();
    pack.wRaceNum    = GetRaceNum();
    if (IsPet())
    {
        pack.bMovingSpeed    = 150;
    }
    else
    {
        pack.bMovingSpeed    = GetLimitPoint(POINT_MOV_SPEED);
    }
    pack.bAttackSpeed    = GetLimitPoint(POINT_ATT_SPEED);
    pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
    pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];

    pack.bStateFlag = m_bAddChrState;

    int iDur = 0;

    if (m_posDest.x != pack.x || m_posDest.y != pack.y)
    {
        iDur = (m_dwMoveStartTime + m_dwMoveDuration) - get_dword_time();

        if (iDur <= 0)
        {
            pack.x = m_posDest.x;
            pack.y = m_posDest.y;
        }
    }

    d->Packet(&pack, sizeof(pack));

    if (IsPC() == true || m_bCharType == CHAR_TYPE_NPC)
    {
        TPacketGCCharacterAdditionalInfo addPacket;
        memset(&addPacket, 0, sizeof(TPacketGCCharacterAdditionalInfo));

        addPacket.header = HEADER_GC_CHAR_ADDITIONAL_INFO;
        addPacket.dwVID = m_vid;

        addPacket.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
        addPacket.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
        addPacket.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
        addPacket.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);

        addPacket.bPKMode = m_bPKMode;
        addPacket.dwMountVnum = GetMountVnum();
        addPacket.bEmpire = m_bEmpire;

        if (IsPC() == true && (LC_IsEurope() == true || LC_IsCanada() == true || LC_IsSingapore() == true))
        {
            addPacket.dwLevel = GetLevel();
        }
        else
        {
            addPacket.dwLevel = 0;
        }

        if (false)
        {
            LPCHARACTER ch = (LPCHARACTER) entity;

            if (GetEmpire() == ch->GetEmpire() || ch->GetGMLevel() > GM_PLAYER || m_bCharType == CHAR_TYPE_NPC)
            {
                goto show_all_info;
            }
            else
            {
                memset(addPacket.name, 0, CHARACTER_NAME_MAX_LEN);
                addPacket.dwGuildID = 0;
                addPacket.sAlignment = 0;
            }
        }
        else
        {
        show_all_info:
            strlcpy(addPacket.name, GetName(), sizeof(addPacket.name));

            if (GetGuild() != NULL)
            {    
                addPacket.dwGuildID = GetGuild()->GetID();
            }
            else
            {
                addPacket.dwGuildID = 0;
            }

            addPacket.sAlignment = m_iAlignment / 10;
        }

        d->Packet(&addPacket, sizeof(TPacketGCCharacterAdditionalInfo));
    }

    if (iDur)
    {
        TPacketGCMove pack;
        EncodeMovePacket(pack, GetVID(), FUNC_MOVE, 0, m_posDest.x, m_posDest.y, iDur, 0, (BYTE) (GetRotation() / 5));
        d->Packet(&pack, sizeof(pack));

        TPacketGCWalkMode p;
        p.vid = GetVID();
        p.header = HEADER_GC_WALK_MODE;
        p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;

        d->Packet(&p, sizeof(p));
    }

    if (entity->IsType(ENTITY_CHARACTER) && GetDesc())
    {
        LPCHARACTER ch = (LPCHARACTER) entity;
        if (ch->IsWalking())
        {
            TPacketGCWalkMode p;
            p.vid = ch->GetVID();
            p.header = HEADER_GC_WALK_MODE;
            p.mode = ch->m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
            GetDesc()->Packet(&p, sizeof(p));
        }
    }

    if (GetMyShop())
    {
        TPacketGCShopSign p;

        p.bHeader = HEADER_GC_SHOP_SIGN;
        p.dwVID = GetVID();
        strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));

        d->Packet(&p, sizeof(TPacketGCShopSign));
    }

    if (entity->IsType(ENTITY_CHARACTER))
    {
        sys_log(3, "EntityInsert %s (RaceNum %d) (%d %d) TO %s",
                GetName(), GetRaceNum(), GetX() / SECTREE_SIZE, GetY() / SECTREE_SIZE, ((LPCHARACTER)entity)->GetName());
    }
}

void CHARACTER::EncodeRemovePacket(LPENTITY entity)
{
    if (entity->GetType() != ENTITY_CHARACTER)
        return;

    LPDESC d;

    if (!(d = entity->GetDesc()))
        return;

    TPacketGCCharacterDelete pack;

    pack.header    = HEADER_GC_CHARACTER_DEL;
    pack.id    = m_vid;

    d->Packet(&pack, sizeof(TPacketGCCharacterDelete));

    if (entity->IsType(ENTITY_CHARACTER))
        sys_log(3, "EntityRemove %s(%d) FROM %s", GetName(), (DWORD) m_vid, ((LPCHARACTER) entity)->GetName());
}

void CHARACTER::UpdatePacket()
{
    if (GetSectree() == NULL) return;

    TPacketGCCharacterUpdate pack;
    TPacketGCCharacterUpdate pack2;

    pack.header = HEADER_GC_CHARACTER_UPDATE;
    pack.dwVID = m_vid;

    pack.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
    pack.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
    pack.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
    pack.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);

    pack.bMovingSpeed    = GetLimitPoint(POINT_MOV_SPEED);
    pack.bAttackSpeed    = GetLimitPoint(POINT_ATT_SPEED);
    pack.bStateFlag    = m_bAddChrState;
    pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
    pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
    pack.dwGuildID    = 0;
    pack.sAlignment    = m_iAlignment / 10;
    pack.bPKMode    = m_bPKMode;

    if (GetGuild())
        pack.dwGuildID = GetGuild()->GetID();

    pack.dwMountVnum    = GetMountVnum();

    pack2 = pack;
    pack2.dwGuildID = 0;
    pack2.sAlignment = 0;

    if (false)
    {
        if (m_bIsObserver != true)
        {
            for (ENTITY_MAP::iterator iter = m_map_view.begin(); iter != m_map_view.end(); iter++)
            {
                LPENTITY pEntity = iter->first;

                if (pEntity != NULL)
                {
                    if (pEntity->IsType(ENTITY_CHARACTER) == true)
                    {
                        if (pEntity->GetDesc() != NULL)
                        {
                            LPCHARACTER pChar = (LPCHARACTER)pEntity;

                            if (GetEmpire() == pChar->GetEmpire() || pChar->GetGMLevel() > GM_PLAYER)
                            {
                                pEntity->GetDesc()->Packet(&pack, sizeof(pack));
                            }
                            else
                            {
                                pEntity->GetDesc()->Packet(&pack2, sizeof(pack2));
                            }
                        }
                    }
                    else
                    {
                        if (pEntity->GetDesc() != NULL)
                        {
                            pEntity->GetDesc()->Packet(&pack, sizeof(pack));
                        }
                    }
                }
            }
        }

        if (GetDesc() != NULL)
        {
            GetDesc()->Packet(&pack, sizeof(pack));
        }
    }
    else
    {
        PacketAround(&pack, sizeof(pack));
    }
}

LPCHARACTER CHARACTER::FindCharacterInView(const char * c_pszName, bool bFindPCOnly)
{
    ENTITY_MAP::iterator it = m_map_view.begin();

    for (; it != m_map_view.end(); ++it)
    {
        if (!it->first->IsType(ENTITY_CHARACTER))
            continue;

        LPCHARACTER tch = (LPCHARACTER) it->first;

        if (bFindPCOnly && tch->IsNPC())
            continue;

        if (!strcasecmp(tch->GetName(), c_pszName))
            return (tch);
    }

    return NULL;
}

void CHARACTER::SetPosition(int pos)
{
    if (pos == POS_STANDING)
    {
        REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
        REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);

        event_cancel(&m_pkDeadEvent);
        event_cancel(&m_pkStunEvent);
    }
    else if (pos == POS_DEAD)
        SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);

    if (!IsStone())
    {
        switch (pos)
        {
            case POS_FIGHTING:
                if (!IsState(m_stateBattle))
                    MonsterLog("[BATTLE] ˝Îżě´Â »óĹÂ");

                GotoState(m_stateBattle);
                break;

            default:
                if (!IsState(m_stateIdle))
                    MonsterLog("[IDLE] ˝¬´Â »óĹÂ");

                GotoState(m_stateIdle);
                break;
        }
    }

    m_pointsInstant.position = pos;
}

void CHARACTER::Save()
{
    if (!m_bSkipSave)
        CHARACTER_MANAGER::instance().DelayedSave(this);
}

void CHARACTER::CreatePlayerProto(TPlayerTable & tab)
{
    memset(&tab, 0, sizeof(TPlayerTable));

    if (GetNewName().empty())
    {
        strlcpy(tab.name, GetName(), sizeof(tab.name));
    }
    else
    {
        strlcpy(tab.name, GetNewName().c_str(), sizeof(tab.name));
    }

    strlcpy(tab.ip, GetDesc()->GetHostName(), sizeof(tab.ip));

    tab.id            = m_dwPlayerID;
    tab.voice        = GetPoint(POINT_VOICE);
    tab.level        = GetLevel();
    tab.level_step    = GetPoint(POINT_LEVEL_STEP);
    tab.exp            = GetExp();
    tab.gold        = GetGold();
    tab.job            = m_points.job;
    tab.part_base    = m_pointsInstant.bBasePart;
    tab.skill_group    = m_points.skill_group;

    DWORD dwPlayedTime = (get_dword_time() - m_dwPlayStartTime);

    if (dwPlayedTime > 60000)
    {
        if (GetSectree() && !GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
        {
            if (GetRealAlignment() < 0)
            {
                if (IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_TIME))
                    UpdateAlignment(120 * (dwPlayedTime / 60000));
                else
                    UpdateAlignment(60 * (dwPlayedTime / 60000));
            }
            else
                UpdateAlignment(5 * (dwPlayedTime / 60000));
        }

        SetRealPoint(POINT_PLAYTIME, GetRealPoint(POINT_PLAYTIME) + dwPlayedTime / 60000);
        ResetPlayTime(dwPlayedTime % 60000);
    }

    tab.playtime = GetRealPoint(POINT_PLAYTIME);
    tab.lAlignment = m_iRealAlignment;

    if (m_posWarp.x != 0 || m_posWarp.y != 0)
    {
        tab.x = m_posWarp.x;
        tab.y = m_posWarp.y;
        tab.z = 0;
        tab.lMapIndex = m_lWarpMapIndex;
    }
    else
    {
        tab.x = GetX();
        tab.y = GetY();
        tab.z = GetZ();
        tab.lMapIndex    = GetMapIndex();
    }

    if (m_lExitMapIndex == 0)
    {
        tab.lExitMapIndex    = tab.lMapIndex;
        tab.lExitX        = tab.x;
        tab.lExitY        = tab.y;
    }
    else
    {
        tab.lExitMapIndex    = m_lExitMapIndex;
        tab.lExitX        = m_posExit.x;
        tab.lExitY        = m_posExit.y;
    }

    sys_log(0, "SAVE: %s %dx%d", GetName(), tab.x, tab.y);

    tab.st = GetRealPoint(POINT_ST);
    tab.ht = GetRealPoint(POINT_HT);
    tab.dx = GetRealPoint(POINT_DX);
    tab.iq = GetRealPoint(POINT_IQ);

    tab.stat_point = GetPoint(POINT_STAT);
    tab.skill_point = GetPoint(POINT_SKILL);
    tab.sub_skill_point = GetPoint(POINT_SUB_SKILL);
    tab.horse_skill_point = GetPoint(POINT_HORSE_SKILL);

    tab.stat_reset_count = GetPoint(POINT_STAT_RESET_COUNT);

    tab.hp = GetHP();
    tab.sp = GetSP();

    tab.stamina = GetStamina();

    tab.sRandomHP = m_points.iRandomHP;
    tab.sRandomSP = m_points.iRandomSP;

    for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i)
        tab.quickslot = m_quickslot;

    if (m_stMobile.length() && !*m_szMobileAuth)
        strlcpy(tab.szMobile, m_stMobile.c_str(), sizeof(tab.szMobile));

    thecore_memcpy(tab.parts, m_pointsInstant.parts, sizeof(tab.parts));

    // REMOVE_REAL_SKILL_LEVLES
    thecore_memcpy(tab.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
    // END_OF_REMOVE_REAL_SKILL_LEVLES

    tab.horse = GetHorseData();
}


void CHARACTER::SaveReal()
{
    if (m_bSkipSave)
        return;

    if (!GetDesc())
    {
        sys_err("Character::Save : no descriptor when saving (name: %s)", GetName());
        return;
    }

    TPlayerTable table;
    CreatePlayerProto(table);

    db_clientdesc->DBPacket(HEADER_GD_PLAYER_SAVE, GetDesc()->GetHandle(), &table, sizeof(TPlayerTable));

    quest::PC * pkQuestPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());

    if (!pkQuestPC)
        sys_err("CHARACTER::Save : null quest::PC pointer! (name %s)", GetName());
    else
    {
        pkQuestPC->Save();
    }

    marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
    if (pMarriage)
        pMarriage->Save();
}

void CHARACTER::FlushDelayedSaveItem()
{
    // ŔúŔĺ ľČµČ ĽŇÁöÇ°Ŕ» ŔüşÎ ŔúŔĺ˝ĂŲ´Ů.
    LPITEM item;

    for (int i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i)
        if ((item = GetInventoryItem(i)))
            ITEM_MANAGER::instance().FlushDelayedSave(item);
}

void CHARACTER::Disconnect(const char * c_pszReason)
{
    assert(GetDesc() != NULL);

    sys_log(0, "DISCONNECT: %s (%s)", GetName(), c_pszReason ? c_pszReason : "unset" );

    if (GetShop())
    {
        GetShop()->RemoveGuest(this);
        SetShop(NULL);
    }

    if (GetArena() != NULL)
    {
        GetArena()->OnDisconnect(GetPlayerID());
    }

    if (GetParty() != NULL)
    {
        GetParty()->UpdateOfflineState(GetPlayerID());
    }

    marriage::CManager::instance().Logout(this);

    // P2P Logout
    TPacketGGLogout p;
    p.bHeader = HEADER_GG_LOGOUT;
    strlcpy(p.szName, GetName(), sizeof(p.szName));
    P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogout));
    char buf[51];
    snprintf(buf, sizeof(buf), "%s %d %d %ld %d", 
        inet_ntoa(GetDesc()->GetAddr().sin_addr), GetGold(), g_bChannel, GetMapIndex(), GetAlignment());

    LogManager::instance().CharLog(this, 0, "LOGOUT", buf);

    if (LC_IsYMIR() || LC_IsKorea() || LC_IsBrazil())
    {
        long playTime = GetRealPoint(POINT_PLAYTIME) - m_dwLoginPlayTime;
        LogManager::instance().LoginLog(false, GetDesc()->GetAccountTable().id, GetPlayerID(), GetLevel(), GetJob(), playTime);

        if (LC_IsBrazil() != true)
            CPCBangManager::instance().Log(GetDesc()->GetHostName(), GetPlayerID(), playTime);
    }

    if (m_pWarMap)
        SetWarMap(NULL);

    if (m_pWeddingMap)
    {
        SetWeddingMap(NULL);
    }

    if (GetGuild())
        GetGuild()->LogoutMember(this);

    quest::CQuestManager::instance().LogoutPC(this);

    if (GetParty())
        GetParty()->Unlink(this);

    // Á׾úŔ» ¶§ Á˘ĽÓ˛÷Ŕ¸¸é °ćÇčġ ÁŮ°Ô Çϱâ
    if (IsStun() || IsDead())
    {
        DeathPenalty(0);
        PointChange(POINT_HP, 50 - GetHP());
    }


    if (!CHARACTER_MANAGER::instance().FlushDelayedSave(this))
    {
        SaveReal();
    }

    FlushDelayedSaveItem();

    SaveAffect();
    m_bIsLoadedAffect = false;

    m_bSkipSave = true; // ŔĚ ŔĚČÄżˇ´Â ´őŔĚ»ó ŔúŔĺÇĎ¸é ľČµČ´Ů.

    quest::CQuestManager::instance().DisconnectPC(this);

    CloseSafebox();

    CloseMall();

    CPVPManager::instance().Disconnect(this);

    CTargetManager::instance().Logout(GetPlayerID());

    MessengerManager::instance().Logout(GetName());

    if (g_TeenDesc)
    {
        int        offset = 0;
        char    buf[245] = {0};

        buf[0] = HEADER_GT_LOGOUT;
        offset += 1;

        memset(buf+offset, 0x00, 2);
        offset += 2;

        TAccountTable    &acc_table = GetDesc()->GetAccountTable();
        memcpy(buf+offset, &acc_table.id, 4);
        offset += 4;

        g_TeenDesc->Packet(buf, offset);
    }

    if (GetDesc())
    {
        GetDesc()->BindCharacter(NULL);
//        BindDesc(NULL);
    }

    CXTrapManager::instance().DestroyClientSession(this);

    M2_DESTROY_CHARACTER(this);
}

bool CHARACTER::Show(long lMapIndex, long x, long y, long z, bool bShowSpawnMotion/* = false */)
{
    LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);

    if (!sectree)
    {
        sys_log(0, "cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex);
        return false;
    }

    SetMapIndex(lMapIndex);

    bool bChangeTree = false;

    if (!GetSectree() || GetSectree() != sectree)
        bChangeTree = true;

    if (bChangeTree)
    {
        if (GetSectree())
            GetSectree()->RemoveEntity(this);

        ViewCleanup();
    }

    if (!IsNPC())
    {
        sys_log(0, "SHOW: %s %dx%dx%d", GetName(), x, y, z);
        if (GetStamina() < GetMaxStamina())
            StartAffectEvent();
    }
    else if (m_pkMobData)
    {
        m_pkMobInst->m_posLastAttacked.x = x;
        m_pkMobInst->m_posLastAttacked.y = y;
        m_pkMobInst->m_posLastAttacked.z = z;
    }

    if (bShowSpawnMotion)
    {
        SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
        m_afAffectFlag.Set(AFF_SPAWN);
    }

    SetXYZ(x, y, z);

    m_posDest.x = x;
    m_posDest.y = y;
    m_posDest.z = z;

    m_posStart.x = x;
    m_posStart.y = y;
    m_posStart.z = z;

    if (bChangeTree)
    {
        EncodeInsertPacket(this);
        sectree->InsertEntity(this);

        UpdateSectree();
    }
    else
    {
        ViewReencode();
        sys_log(0, "      in same sectree");
    }

    REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
    
    SetValidComboInterval(0);
    return true;
}

// BGM_INFO
struct BGMInfo
{
    std::string    name;
    float        vol;
};

typedef std::map<unsigned, BGMInfo> BGMInfoMap;

static BGMInfoMap     gs_bgmInfoMap;
static bool        gs_bgmVolEnable = false;

void CHARACTER_SetBGMVolumeEnable()
{
    gs_bgmVolEnable = true;    
    sys_log(0, "bgm_info.set_bgm_volume_enable");
}

void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol)
{
    BGMInfo newInfo;
    newInfo.name = name;
    newInfo.vol = vol;

    gs_bgmInfoMap[mapIndex] = newInfo;

    sys_log(0, "bgm_info.add_info(%d, '%s', %f)", mapIndex, name, vol);
}

const BGMInfo& CHARACTER_GetBGMInfo(unsigned mapIndex)
{
    BGMInfoMap::iterator f = gs_bgmInfoMap.find(mapIndex);
    if (gs_bgmInfoMap.end() == f)
    {
        static BGMInfo s_empty = {"", 0.0f};
        return s_empty;
    }
    return f->second;
}

bool CHARACTER_IsBGMVolumeEnable()
{
    return gs_bgmVolEnable;
}
// END_OF_BGM_INFO

void CHARACTER::MainCharacterPacket()
{
    const unsigned mapIndex = GetMapIndex();
    const BGMInfo& bgmInfo = CHARACTER_GetBGMInfo(mapIndex);

    // SUPPORT_BGM
    if (!bgmInfo.name.empty())
    {
        if (CHARACTER_IsBGMVolumeEnable())
        {
            sys_log(1, "bgm_info.play_bgm_vol(%d, name='%s', vol=%f)", mapIndex, bgmInfo.name.c_str(), bgmInfo.vol);
            TPacketGCMainCharacter4_BGM_VOL mainChrPacket;
            mainChrPacket.header = HEADER_GC_MAIN_CHARACTER4_BGM_VOL;
            mainChrPacket.dwVID = m_vid;
            mainChrPacket.wRaceNum = GetRaceNum();
            mainChrPacket.lx = GetX();
            mainChrPacket.ly = GetY();
            mainChrPacket.lz = GetZ();
            mainChrPacket.empire = GetDesc()->GetEmpire();
            mainChrPacket.skill_group = GetSkillGroup();
            strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));

            mainChrPacket.fBGMVol = bgmInfo.vol;
            strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
            GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter4_BGM_VOL));
        }
        else
        {
            sys_log(1, "bgm_info.play(%d, '%s')", mapIndex, bgmInfo.name.c_str());
            TPacketGCMainCharacter3_BGM mainChrPacket;
            mainChrPacket.header = HEADER_GC_MAIN_CHARACTER3_BGM;
            mainChrPacket.dwVID = m_vid;
            mainChrPacket.wRaceNum = GetRaceNum();
            mainChrPacket.lx = GetX();
            mainChrPacket.ly = GetY();
            mainChrPacket.lz = GetZ();
            mainChrPacket.empire = GetDesc()->GetEmpire();
            mainChrPacket.skill_group = GetSkillGroup();
            strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
            strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
            GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter3_BGM));
        }
        //if (m_stMobile.length())
        //        ChatPacket(CHAT_TYPE_COMMAND, "sms");
    }
    // END_OF_SUPPORT_BGM
    else
    {
        sys_log(0, "bgm_info.play(%d, DEFAULT_BGM_NAME)", mapIndex);

        TPacketGCMainCharacter pack;
        pack.header = HEADER_GC_MAIN_CHARACTER;
        pack.dwVID = m_vid;
        pack.wRaceNum = GetRaceNum();
        pack.lx = GetX();
        pack.ly = GetY();
        pack.lz = GetZ();
        pack.empire = GetDesc()->GetEmpire();
        pack.skill_group = GetSkillGroup();
        strlcpy(pack.szName, GetName(), sizeof(pack.szName));
        GetDesc()->Packet(&pack, sizeof(TPacketGCMainCharacter));

        if (m_stMobile.length())
            ChatPacket(CHAT_TYPE_COMMAND, "sms");
    }
}

void CHARACTER::PointsPacket()
{
    if (!GetDesc())
        return;

    TPacketGCPoints pack;

    pack.header    = HEADER_GC_CHARACTER_POINTS;

    pack.points[POINT_LEVEL]        = GetLevel();
    pack.points[POINT_EXP]        = GetExp();
    pack.points[POINT_NEXT_EXP]        = GetNextExp();
    pack.points[POINT_HP]        = GetHP();
    pack.points[POINT_MAX_HP]        = GetMaxHP();
    pack.points[POINT_SP]        = GetSP();
    pack.points[POINT_MAX_SP]        = GetMaxSP();
    pack.points[POINT_GOLD]        = GetGold();
    pack.points[POINT_STAMINA]        = GetStamina();
    pack.points[POINT_MAX_STAMINA]    = GetMaxStamina();

    for (int i = POINT_ST; i < POINT_MAX_NUM; ++i)
        pack.points = GetPoint(i);

    GetDesc()->Packet(&pack, sizeof(TPacketGCPoints));
}

bool CHARACTER::ChangeSex()
{
    int src_race = GetRaceNum();

    switch (src_race)
    {
        case MAIN_RACE_WARRIOR_M:
            m_points.job = MAIN_RACE_WARRIOR_W;
            break;

        case MAIN_RACE_WARRIOR_W:
            m_points.job = MAIN_RACE_WARRIOR_M;
            break;

        case MAIN_RACE_ASSASSIN_M:
            m_points.job = MAIN_RACE_ASSASSIN_W;
            break;

        case MAIN_RACE_ASSASSIN_W:
            m_points.job = MAIN_RACE_ASSASSIN_M;
            break;

        case MAIN_RACE_SURA_M:
            m_points.job = MAIN_RACE_SURA_W;
            break;

        case MAIN_RACE_SURA_W:
            m_points.job = MAIN_RACE_SURA_M;
            break;

        case MAIN_RACE_SHAMAN_M:
            m_points.job = MAIN_RACE_SHAMAN_W;
            break;

        case MAIN_RACE_SHAMAN_W:
            m_points.job = MAIN_RACE_SHAMAN_M;
            break;

        default:
            sys_err("CHANGE_SEX: %s unknown race %d", GetName(), src_race);
            return false;
    }

    sys_log(0, "CHANGE_SEX: %s (%d -> %d)", GetName(), src_race, m_points.job);
    return true;
}

WORD CHARACTER::GetRaceNum() const
{
    if (m_dwPolymorphRace)
        return m_dwPolymorphRace;

    if (m_pkMobData)
        return m_pkMobData->m_table.dwVnum;

    return m_points.job;
}

void CHARACTER::SetRace(BYTE race)
{
    if (race >= MAIN_RACE_MAX_NUM)
    {
        sys_err("CHARACTER::SetRace(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
        return;
    }

    m_points.job = race;
}

BYTE CHARACTER::GetJob() const
{
    unsigned race = m_points.job;
    unsigned job;

    if (RaceToJob(race, &job))
        return job;

    sys_err("CHARACTER::GetJob(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
    return JOB_WARRIOR;
}

void CHARACTER::SetLevel(BYTE level)
{
    m_points.level = level;

    if (IsPC())
    {
        if (level < PK_PROTECT_LEVEL)
            SetPKMode(PK_MODE_PROTECT);
        else if (GetGMLevel() != GM_PLAYER)
            SetPKMode(PK_MODE_PROTECT);
        else if (m_bPKMode == PK_MODE_PROTECT)
            SetPKMode(PK_MODE_PEACE);
    }
}

void CHARACTER::SetEmpire(BYTE bEmpire)
{
    m_bEmpire = bEmpire;
}

void CHARACTER::SetPlayerProto(const TPlayerTable * t)
{
    if (!GetDesc() || !*GetDesc()->GetHostName())
        sys_err("cannot get desc or hostname");
    else
        SetGMLevel();

    m_bCharType = CHAR_TYPE_PC;

    m_dwPlayerID = t->id;

    m_iAlignment = t->lAlignment;
    m_iRealAlignment = t->lAlignment;

    m_points.voice = t->voice;

    m_points.skill_group = t->skill_group; 

    m_pointsInstant.bBasePart = t->part_base;
    SetPart(PART_HAIR, t->parts[PART_HAIR]);

    m_points.iRandomHP = t->sRandomHP;
    m_points.iRandomSP = t->sRandomSP;

    // REMOVE_REAL_SKILL_LEVLES
    if (m_pSkillLevels)
        M2_DELETE_ARRAY(m_pSkillLevels);

    m_pSkillLevels = M2_NEW TPlayerSkill[SKILL_MAX_NUM];
    thecore_memcpy(m_pSkillLevels, t->skills, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
    // END_OF_REMOVE_REAL_SKILL_LEVLES

    if (t->lMapIndex >= 10000)
    {
        m_posWarp.x = t->lExitX;
        m_posWarp.y = t->lExitY;
        m_lWarpMapIndex = t->lExitMapIndex;
    }

    SetRealPoint(POINT_PLAYTIME, t->playtime);
    m_dwLoginPlayTime = t->playtime;
    SetRealPoint(POINT_ST, t->st);
    SetRealPoint(POINT_HT, t->ht);
    SetRealPoint(POINT_DX, t->dx);
    SetRealPoint(POINT_IQ, t->iq);

    SetPoint(POINT_ST, t->st);
    SetPoint(POINT_HT, t->ht);
    SetPoint(POINT_DX, t->dx);
    SetPoint(POINT_IQ, t->iq);

    SetPoint(POINT_STAT, t->stat_point);
    SetPoint(POINT_SKILL, t->skill_point);
    SetPoint(POINT_SUB_SKILL, t->sub_skill_point);
    SetPoint(POINT_HORSE_SKILL, t->horse_skill_point);

    SetPoint(POINT_STAT_RESET_COUNT, t->stat_reset_count);

    SetPoint(POINT_LEVEL_STEP, t->level_step);
    SetRealPoint(POINT_LEVEL_STEP, t->level_step);

    SetRace(t->job);

    SetLevel(t->level);
    SetExp(t->exp);
    SetGold(t->gold);

    SetMapIndex(t->lMapIndex);
    SetXYZ(t->x, t->y, t->z);

    ComputePoints();

    SetHP(t->hp);
    SetSP(t->sp);
    SetStamina(t->stamina);

    //GMŔ϶§ ş¸ČŁ¸đµĺ  
    if (!test_server)
    {
        if (GetGMLevel() > GM_LOW_WIZARD)
        {
            m_afAffectFlag.Set(AFF_YMIR);
            m_bPKMode = PK_MODE_PROTECT;
        }
    }

    if (GetLevel() < PK_PROTECT_LEVEL)
        m_bPKMode = PK_MODE_PROTECT;

    m_stMobile = t->szMobile;

    SetHorseData(t->horse);

    if (GetHorseLevel() > 0)
        UpdateHorseDataByLogoff(t->logoff_interval);

    thecore_memcpy(m_aiPremiumTimes, t->aiPremiumTimes, sizeof(t->aiPremiumTimes));

    m_dwLogOffInterval = t->logoff_interval;

    sys_log(0, "PLAYER_LOAD: %s PREMIUM %d %d, LOGGOFF_INTERVAL %u PTR: %p", t->name, m_aiPremiumTimes[0], m_aiPremiumTimes[1], t->logoff_interval, this);

    if (GetGMLevel() != GM_PLAYER) 
    {
        LogManager::instance().CharLog(this, GetGMLevel(), "GM_LOGIN", "");
        sys_log(0, "GM_LOGIN(gmlevel=%d, name=%s(%d), pos=(%d, %d)", GetGMLevel(), GetName(), GetPlayerID(), GetX(), GetY());
    }

#ifdef __PET_SYSTEM__
    // NOTE: ŔĎ´Ü Äł¸ŻĹÍ°ˇ PCŔÎ °ćżěżˇ¸¸ PetSystemŔ» °®µµ·Ď ÇÔ. ŔŻ·´ ¸Ó˝Ĺ´ç ¸Ţ¸đ¸® »çżë·ü¶§ą®żˇ NPC±îÁö Çϱä Á»..
    if (m_petSystem)
    {
        m_petSystem->Destroy();
        delete m_petSystem;
    }

    m_petSystem = M2_NEW CPetSystem(this);
#endif
}

EVENTFUNC(kill_ore_load_event)
{
    char_event_info* info = dynamic_cast<char_event_info*>( event->info );
    if ( info == NULL )
    {
        sys_err( "kill_ore_load_even> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER    ch = info->ch;
    if (ch == NULL) { // <Factor>
        return 0;
    }    

    ch->m_pkMiningEvent = NULL;
    M2_DESTROY_CHARACTER(ch);
    return 0;
}

void CHARACTER::SetProto(const CMob * pkMob)
{
    if (m_pkMobInst)
        M2_DELETE(m_pkMobInst);

    m_pkMobData = pkMob;
    m_pkMobInst = M2_NEW CMobInstance;

    m_bPKMode = PK_MODE_FREE;

    const TMobTable * t = &m_pkMobData->m_table;

    m_bCharType = t->bType;

    SetLevel(t->bLevel);
    SetEmpire(t->bEmpire);

    SetExp(t->dwExp);
    SetRealPoint(POINT_ST, t->bStr);
    SetRealPoint(POINT_DX, t->bDex);
    SetRealPoint(POINT_HT, t->bCon);
    SetRealPoint(POINT_IQ, t->bInt);

    ComputePoints();

    SetHP(GetMaxHP());
    SetSP(GetMaxSP());

    ////////////////////
    m_pointsInstant.dwAIFlag = t->dwAIFlag;
    SetImmuneFlag(t->dwImmuneFlag);

    AssignTriggers(t);

    ApplyMobAttribute(t);

    if (IsStone())
    {
        DetermineDropMetinStone();
    }

    if (IsWarp() || IsGoto())
    {
        StartWarpNPCEvent();
    }

    CHARACTER_MANAGER::instance().RegisterRaceNumMap(this);

    // XXX X-mas santa hardcoding
    if (GetRaceNum() == xmas::MOB_SANTA_VNUM)
    {
        SetPoint(POINT_ATT_GRADE_BONUS, 10);
        if (g_iUseLocale)
            SetPoint(POINT_DEF_GRADE_BONUS, 6);
        else
            SetPoint(POINT_DEF_GRADE_BONUS, 15);

        //»ęŸżë
        //m_dwPlayStartTime = get_dword_time() + 10 * 60 * 1000;
        //˝ĹĽ±ŔÚ łëÇŘ 
        m_dwPlayStartTime = get_dword_time() + 30 * 1000;
        if (test_server)
            m_dwPlayStartTime = get_dword_time() + 30 * 1000;
    }

    // XXX CTF GuildWar hardcoding
    if (warmap::IsWarFlag(GetRaceNum()))
    {
        m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
        m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
        m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
    }

    if (warmap::IsWarFlagBase(GetRaceNum()))
    {
        m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
        m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
        m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
    }

    if (m_bCharType == CHAR_TYPE_HORSE || 
            GetRaceNum() == 20101 ||
            GetRaceNum() == 20102 ||
            GetRaceNum() == 20103 ||
            GetRaceNum() == 20104 ||
            GetRaceNum() == 20105 ||
            GetRaceNum() == 20106 ||
            GetRaceNum() == 20107 ||
            GetRaceNum() == 20108 ||
            GetRaceNum() == 20109
      )
    {
        m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
        m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
        m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
    }

    // MINING
    if (mining::IsVeinOfOre (GetRaceNum()))
    {
        char_event_info* info = AllocEventInfo<char_event_info>();

        info->ch = this;

        m_pkMiningEvent = event_create(kill_ore_load_event, info, PASSES_PER_SEC(number(7 * 60, 15 * 60)));
    }
    // END_OF_MINING
}

const TMobTable & CHARACTER::GetMobTable() const
{
    return m_pkMobData->m_table;
}

bool CHARACTER::IsRaceFlag(DWORD dwBit) const
{
    return m_pkMobData ? IS_SET(m_pkMobData->m_table.dwRaceFlag, dwBit) : 0;
}

DWORD CHARACTER::GetMobDamageMin() const
{
    return m_pkMobData->m_table.dwDamageRange[0];
}

DWORD CHARACTER::GetMobDamageMax() const
{
    return m_pkMobData->m_table.dwDamageRange[1];
}

float CHARACTER::GetMobDamageMultiply() const
{
    float fDamMultiply = GetMobTable().fDamMultiply;

    if (IsBerserk())
        fDamMultiply = fDamMultiply * 2.0f; // BALANCE: ±¤ĆřČ­ ˝Ă µÎąč

    return fDamMultiply;
}

DWORD CHARACTER::GetMobDropItemVnum() const
{
    return m_pkMobData->m_table.dwDropItemVnum;
}

bool CHARACTER::IsSummonMonster() const
{
    return GetSummonVnum() != 0;
}

DWORD CHARACTER::GetSummonVnum() const
{
    return m_pkMobData ? m_pkMobData->m_table.dwSummonVnum : 0;
}

DWORD CHARACTER::GetPolymorphItemVnum() const
{
    return m_pkMobData ? m_pkMobData->m_table.dwPolymorphItemVnum : 0;
}

DWORD CHARACTER::GetMonsterDrainSPPoint() const
{
    return m_pkMobData ? m_pkMobData->m_table.dwDrainSP : 0;
}

BYTE CHARACTER::GetMobRank() const
{
    if (!m_pkMobData)
        return MOB_RANK_KNIGHT;    // PCŔĎ °ćżě KNIGHT±Ţ

    return m_pkMobData->m_table.bRank;
}

BYTE CHARACTER::GetMobSize() const
{
    if (!m_pkMobData)
        return MOBSIZE_MEDIUM;

    return m_pkMobData->m_table.bSize;
}

WORD CHARACTER::GetMobAttackRange() const

    switch (GetMobBattleType())
    {
        case BATTLE_TYPE_RANGE:
        case BATTLE_TYPE_MAGIC:
            return m_pkMobData->m_table.wAttackRange + GetPoint(POINT_BOW_DISTANCE);  
        default:
            return m_pkMobData->m_table.wAttackRange; 
    }
}

BYTE CHARACTER::GetMobBattleType() const
{
    if (!m_pkMobData)
        return BATTLE_TYPE_MELEE;

    return (m_pkMobData->m_table.bBattleType);
}

void CHARACTER::ComputeBattlePoints()
{
    if (IsPolymorphed())
    {
        DWORD dwMobVnum = GetPolymorphVnum();
        const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
        int iAtt = 0;
        int iDef = 0;

        if (pMob)
        {
            iAtt = GetLevel() * 2 + GetPolymorphPoint(POINT_ST) * 2;
            // lev + con
            iDef = GetLevel() + GetPolymorphPoint(POINT_HT) + pMob->m_table.wDef;
        }

        SetPoint(POINT_ATT_GRADE, iAtt);
        SetPoint(POINT_DEF_GRADE, iDef);
        SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE)); 
        SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
    }
    else if (IsPC())
    {
        SetPoint(POINT_ATT_GRADE, 0);
        SetPoint(POINT_DEF_GRADE, 0);
        SetPoint(POINT_CLIENT_DEF_GRADE, 0);
        SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
        SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));

        //
        // ±âş» ATK = 2lev + 2str, Á÷ľ÷żˇ ¸¶´Ů 2strŔş ąŮ˛đ Ľö ŔÖŔ˝
        //
        int iAtk = GetLevel() * 2;
        int iStatAtk = 0;

        switch (GetJob())
        {
            case JOB_WARRIOR:
            case JOB_SURA:
                iStatAtk = (2 * GetPoint(POINT_ST));
                break;

            case JOB_ASSASSIN:
                iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_DX)) / 3;
                break;

            case JOB_SHAMAN:
                iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_IQ)) / 3;
                break;

            default:
                sys_err("invalid job %d", GetJob());
                iStatAtk = (2 * GetPoint(POINT_ST));
                break;
        }

        // ¸»Ŕ» Ÿ°í ŔÖ°í, ˝şĹČŔ¸·Î ŔÎÇŃ °ř°Ý·ÂŔĚ ST*2 ş¸´Ů ł·Ŕ¸¸é ST*2·Î ÇŃ´Ů.
        // ˝şĹČŔ» Ŕ߸ř ÂďŔş »ç¶÷ °ř°Ý·ÂŔĚ ´ő ł·Áö ľĘ°Ô Çϱâ Ŕ§ÇŘĽ­´Ů.
        if (GetMountVnum() && iStatAtk < 2 * GetPoint(POINT_ST))
            iStatAtk = (2 * GetPoint(POINT_ST));

        iAtk += iStatAtk;

        // ˝Â¸¶(¸») : °ËĽö¶ó µĄąĚÁö °¨ĽŇ  
        if (GetMountVnum())
        {
            if (GetJob() == JOB_SURA && GetSkillGroup() == 1)
            {
                iAtk += (iAtk * GetHorseLevel()) / 60;
            }
            else
            {
                iAtk += (iAtk * GetHorseLevel()) / 30;
            }
        }
        
        //
        // ATK Setting
        //
        iAtk += GetPoint(POINT_ATT_GRADE_BONUS);

        PointChange(POINT_ATT_GRADE, iAtk);

        // DEF = LEV + CON + ARMOR
        int iShowDef = GetLevel() + GetPoint(POINT_HT); // For Ymir(õ¸¶)
        int iDef = GetLevel() + (int) (GetPoint(POINT_HT) / 1.25); // For Other
        int iArmor = 0;

        LPITEM pkItem;

        for (int i = 0; i < WEAR_MAX_NUM; ++i)
            if ((pkItem = GetWear(i)) && pkItem->GetType() == ITEM_ARMOR)
            {
                if (pkItem->GetSubType() == ARMOR_BODY || pkItem->GetSubType() == ARMOR_HEAD || pkItem->GetSubType() == ARMOR_FOOTS || pkItem->GetSubType() == ARMOR_SHIELD)
                {
                    iArmor += pkItem->GetValue(1);
                    iArmor += (2 * pkItem->GetValue(5));
                }
            }

        // ¸» Ÿ°í ŔÖŔ» ¶§ ąćľî·ÂŔĚ ¸»ŔÇ ±âÁŘ ąćľî·Âş¸´Ů ł·Ŕ¸¸é ±âÁŘ ąćľî·ÂŔ¸·Î ĽłÁ¤
        if( true == IsHorseRiding() )
        {
            if (iArmor < GetHorseArmor())
                iArmor = GetHorseArmor();

            const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID());

            if (pHorseName != NULL && strlen(pHorseName))
            {
                iArmor += 20;
            }
        }

        iArmor += GetPoint(POINT_DEF_GRADE_BONUS);
        iArmor += GetPoint(POINT_PARTY_DEFENDER_BONUS);

        // INTERNATIONAL_VERSION
        if (LC_IsYMIR())
        {
            PointChange(POINT_DEF_GRADE, iShowDef + iArmor);
        }
        else
        {
            PointChange(POINT_DEF_GRADE, iDef + iArmor);
            PointChange(POINT_CLIENT_DEF_GRADE, (iShowDef + iArmor) - GetPoint(POINT_DEF_GRADE));
        }
        // END_OF_INTERNATIONAL_VERSION

        PointChange(POINT_MAGIC_ATT_GRADE, GetLevel() * 2 + GetPoint(POINT_IQ) * 2 + GetPoint(POINT_MAGIC_ATT_GRADE_BONUS));
        PointChange(POINT_MAGIC_DEF_GRADE, GetLevel() + (GetPoint(POINT_IQ) * 3 + GetPoint(POINT_HT)) / 3 + iArmor / 2 + GetPoint(POINT_MAGIC_DEF_GRADE_BONUS));
    }
    else
    {
        // 2lev + str * 2
        int iAtt = GetLevel() * 2 + GetPoint(POINT_ST) * 2;
        // lev + con
        int iDef = GetLevel() + GetPoint(POINT_HT) + GetMobTable().wDef;

        SetPoint(POINT_ATT_GRADE, iAtt);
        SetPoint(POINT_DEF_GRADE, iDef);
        SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE)); 
        SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
    }
}

void CHARACTER::ComputePoints()
{
    long lStat = GetPoint(POINT_STAT);
    long lStatResetCount = GetPoint(POINT_STAT_RESET_COUNT);
    long lSkillActive = GetPoint(POINT_SKILL);
    long lSkillSub = GetPoint(POINT_SUB_SKILL);
    long lSkillHorse = GetPoint(POINT_HORSE_SKILL);
    long lLevelStep = GetPoint(POINT_LEVEL_STEP);

    long lAttackerBonus = GetPoint(POINT_PARTY_ATTACKER_BONUS);
    long lTankerBonus = GetPoint(POINT_PARTY_TANKER_BONUS);
    long lBufferBonus = GetPoint(POINT_PARTY_BUFFER_BONUS);
    long lSkillMasterBonus = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
    long lHasteBonus = GetPoint(POINT_PARTY_HASTE_BONUS);
    long lDefenderBonus = GetPoint(POINT_PARTY_DEFENDER_BONUS);

    long lHPRecovery = GetPoint(POINT_HP_RECOVERY);
    long lSPRecovery = GetPoint(POINT_SP_RECOVERY);

    memset(m_pointsInstant.points, 0, sizeof(m_pointsInstant.points));
    BuffOnAttr_ClearAll();
    m_SkillDamageBonus.clear();

    SetPoint(POINT_STAT, lStat);
    SetPoint(POINT_SKILL, lSkillActive);
    SetPoint(POINT_SUB_SKILL, lSkillSub);
    SetPoint(POINT_HORSE_SKILL, lSkillHorse);
    SetPoint(POINT_LEVEL_STEP, lLevelStep);
    SetPoint(POINT_STAT_RESET_COUNT, lStatResetCount);

    SetPoint(POINT_ST, GetRealPoint(POINT_ST));
    SetPoint(POINT_HT, GetRealPoint(POINT_HT));
    SetPoint(POINT_DX, GetRealPoint(POINT_DX));
    SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));

    SetPart(PART_MAIN, GetOriginalPart(PART_MAIN));
    SetPart(PART_WEAPON, GetOriginalPart(PART_WEAPON));
    SetPart(PART_HEAD, GetOriginalPart(PART_HEAD));
    SetPart(PART_HAIR, GetOriginalPart(PART_HAIR));

    SetPoint(POINT_PARTY_ATTACKER_BONUS, lAttackerBonus);
    SetPoint(POINT_PARTY_TANKER_BONUS, lTankerBonus);
    SetPoint(POINT_PARTY_BUFFER_BONUS, lBufferBonus);
    SetPoint(POINT_PARTY_SKILL_MASTER_BONUS, lSkillMasterBonus);
    SetPoint(POINT_PARTY_HASTE_BONUS, lHasteBonus);
    SetPoint(POINT_PARTY_DEFENDER_BONUS, lDefenderBonus);

    SetPoint(POINT_HP_RECOVERY, lHPRecovery);
    SetPoint(POINT_SP_RECOVERY, lSPRecovery);

    // PC_BANG_ITEM_ADD
    SetPoint(POINT_PC_BANG_EXP_BONUS, 0);
    SetPoint(POINT_PC_BANG_DROP_BONUS, 0);
    // END_PC_BANG_ITEM_ADD

    int iMaxHP, iMaxSP;
    int iMaxStamina;

    if (IsPC())
    {
        // ĂÖ´ë »ý¸í·Â/Á¤˝Ĺ·Â
        iMaxHP = JobInitialPoints[GetJob()].max_hp + m_points.iRandomHP + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].hp_per_ht;
        iMaxSP = JobInitialPoints[GetJob()].max_sp + m_points.iRandomSP + GetPoint(POINT_IQ) * JobInitialPoints[GetJob()].sp_per_iq;
        iMaxStamina = JobInitialPoints[GetJob()].max_stamina + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].stamina_per_con;

        {
            CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_ADD_HP);

            if (NULL != pkSk)
            {
                pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_ADD_HP) / 100.0f);

                iMaxHP += static_cast<int>(pkSk->kPointPoly.Eval());
            }
        }

        // ±âş» °Şµé
        SetPoint(POINT_MOV_SPEED,    100);
        SetPoint(POINT_ATT_SPEED,    100);
        PointChange(POINT_ATT_SPEED, GetPoint(POINT_PARTY_HASTE_BONUS));
        SetPoint(POINT_CASTING_SPEED,    100);
    }
    else
    {
        iMaxHP = m_pkMobData->m_table.dwMaxHP;
        iMaxSP = 0;
        iMaxStamina = 0;

        SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
        SetPoint(POINT_MOV_SPEED, m_pkMobData->m_table.sMovingSpeed);
        SetPoint(POINT_CASTING_SPEED, m_pkMobData->m_table.sAttackSpeed);
    }

    if (IsPC())
    {
        // ¸» Ÿ°í ŔÖŔ» ¶§´Â ±âş» ˝şĹČŔĚ ¸»ŔÇ ±âÁŘ ˝şĹČş¸´Ů ł·Ŕ¸¸é łô°Ô ¸¸µç´Ů.
        // µű¶óĽ­ ¸»ŔÇ ±âÁŘ ˝şĹČŔĚ ą«»ç ±âÁŘŔ̹ǷÎ, Ľö¶ó/ą«´çŔş ŔüĂĽ ˝şĹČ ÇŐŔĚ
        // ´ëäŔűŔ¸·Î ´ő żĂ¶ó°ˇ°Ô µÉ °ÍŔĚ´Ů.
        if (GetMountVnum()) 
        {
            if (GetHorseST() > GetPoint(POINT_ST))
                PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));

            if (GetHorseDX() > GetPoint(POINT_DX))
                PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));

            if (GetHorseHT() > GetPoint(POINT_HT))
                PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));

            if (GetHorseIQ() > GetPoint(POINT_IQ))
                PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
        }

    }

    ComputeBattlePoints();

    // ±âş» HP/SP ĽłÁ¤
    if (iMaxHP != GetMaxHP())
    {
        SetRealPoint(POINT_MAX_HP, iMaxHP); // ±âş»HP¸¦ RealPointżˇ ŔúŔĺÇŘ łő´Â´Ů.
    }

    PointChange(POINT_MAX_HP, 0);

    if (iMaxSP != GetMaxSP())
    {
        SetRealPoint(POINT_MAX_SP, iMaxSP); // ±âş»SP¸¦ RealPointżˇ ŔúŔĺÇŘ łő´Â´Ů.
    }

    PointChange(POINT_MAX_SP, 0);

    SetMaxStamina(iMaxStamina);

    m_pointsInstant.dwImmuneFlag = 0;

    for (int i = 0 ; i < WEAR_MAX_NUM; i++) 
    {
        LPITEM pItem = GetWear(i);
        if (pItem)
        {
            pItem->ModifyPoints(true);
            SET_BIT(m_pointsInstant.dwImmuneFlag, GetWear(i)->GetImmuneFlag());
        }
    }

    // żëČĄĽ® ˝Ă˝şĹŰ
    // ComputePointsżˇĽ­´Â ÄɸŻĹÍŔÇ ¸đµç ĽÓĽş°ŞŔ» ĂʱâČ­ÇĎ°í,
    // ľĆŔĚĹŰ, ąöÇÁ µîżˇ °ü·ĂµČ ¸đµç ĽÓĽş°ŞŔ» Ŕç°č»ęÇϱ⠶§ą®żˇ,
    // żëČĄĽ® ˝Ă˝şĹ۵µ ActiveDeckżˇ ŔÖ´Â ¸đµç żëČĄĽ®ŔÇ ĽÓĽş°ŞŔ» ´Ů˝Ă Ŕűżë˝ĂÄŃľß ÇŃ´Ů.
    if (DragonSoul_IsDeckActivated())
    {
        for (int i = WEAR_MAX_NUM + DS_SLOT_MAX * DragonSoul_GetActiveDeck(); 
            i < WEAR_MAX_NUM + DS_SLOT_MAX * (DragonSoul_GetActiveDeck() + 1); i++)    
        {
            LPITEM pItem = GetWear(i);
            if (pItem)
            {
                if (DSManager::instance().IsTimeLeftDragonSoul(pItem))
                    pItem->ModifyPoints(true);
            }
        }
    }

    if (GetHP() > GetMaxHP())
        PointChange(POINT_HP, GetMaxHP() - GetHP());

    if (GetSP() > GetMaxSP())
        PointChange(POINT_SP, GetMaxSP() - GetSP());

    ComputeSkillPoints();

    RefreshAffect();
    CPetSystem* pPetSystem = GetPetSystem();
    if (NULL != pPetSystem)
    {
        pPetSystem->RefreshBuff();
    }

    for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
    {
        it->second->GiveAllAttributes();
    }

    UpdatePacket();
}

// m_dwPlayStartTimeŔÇ ´ÜŔ§´Â milisecond´Ů. µĄŔĚĹÍşŁŔĚ˝şżˇ´Â şĐ´ÜŔ§·Î ±â·ĎÇϱâ
// ¶§ą®żˇ ÇĂ·ąŔ̽ðŁŔ» °č»ęÇŇ ¶§ / 60000 Ŕ¸·Î łŞ´˛Ľ­ ÇϴµĄ, ±× łŞ¸ÓÁö °ŞŔĚ ł˛ľŇ
// Ŕ» ¶§ ż©±âżˇ dwTimeRemainŔ¸·Î łÖľîĽ­ Á¦´ë·Î °č»ęµÇµµ·Ď ÇŘÁÖľîľß ÇŃ´Ů.
void CHARACTER::ResetPlayTime(DWORD dwTimeRemain)
{
    m_dwPlayStartTime = get_dword_time() - dwTimeRemain;
}

const int aiRecoveryPercents[10] = { 1, 5, 5, 5, 5, 5, 5, 5, 5, 5 };

EVENTFUNC(recovery_event)
{
    char_event_info* info = dynamic_cast<char_event_info*>( event->info );
    if ( info == NULL )
    {
        sys_err( "recovery_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER    ch = info->ch;

    if (ch == NULL) { // <Factor>
        return 0;
    }    

    if (!ch->IsPC())
    {
        //
        // ¸ó˝şĹÍ Č¸şą
        //
        if (ch->IsAffectFlag(AFF_POISON))
            return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));

        if (2493 == ch->GetMobTable().dwVnum)
        {
            int regenPct = BlueDragon_GetRangeFactor("hp_regen", ch->GetHPPct());
            regenPct += ch->GetMobTable().bRegenPercent;

            for (int i=1 ; i <= 4 ; ++i)
            {
                if (REGEN_PECT_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
                {
                    DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
                    size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
                    size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );

                    regenPct += (val*cnt);

                    break;
                }
            }

            ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * regenPct) / 100));
        }
        else if (!ch->IsDoor())
        {
            ch->MonsterLog("HP_REGEN +%d", MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
            ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
        }

        if (ch->GetHP() >= ch->GetMaxHP())
        {
            ch->m_pkRecoveryEvent = NULL;
            return 0;
        }

        if (2493 == ch->GetMobTable().dwVnum)
        {
            for (int i=1 ; i <= 4 ; ++i)
            {
                if (REGEN_TIME_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
                {
                    DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
                    size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
                    size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );

                    return PASSES_PER_SEC(MAX(1, (ch->GetMobTable().bRegenCycle - (val*cnt))));
                }
            }
        }

        return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
    }
    else
    {
        //
        // PC ȸşą
        //
        ch->CheckTarget();
        //ch->UpdateSectree(); // ż©±âĽ­ ŔĚ°É żÖÇĎÁö?
        ch->UpdateKillerMode();

        if (ch->IsAffectFlag(AFF_POISON) == true)
        {
            // Áßµ¶ŔÎ °ćżě ŔÚµżČ¸şą ±ÝÁö
            // ĆÄąýĽúŔÎ °ćżě ŔÚµżČ¸şą ±ÝÁö
            return 3;
        }

        int iSec = (get_dword_time() - ch->GetLastMoveTime()) / 3000;

        // SP ȸşą ·çĆľ.
        // żÖ ŔĚ°É·Î ÇŘĽ­ ÇÔĽö·Î »©łů´Â°ˇ ?!
        ch->DistributeSP(ch);

        if (ch->GetMaxHP() <= ch->GetHP())
            return PASSES_PER_SEC(3);

        int iPercent = 0;
        int iAmount = 0;
        
        {
            iPercent = aiRecoveryPercents[MIN(9, iSec)];
            iAmount = 15 + (ch->GetMaxHP() * iPercent) / 100;
        }
        
        iAmount += (iAmount * ch->GetPoint(POINT_HP_REGEN)) / 100;

        sys_log(1, "RECOVERY_EVENT: %s %d HP_REGEN %d HP +%d", ch->GetName(), iPercent, ch->GetPoint(POINT_HP_REGEN), iAmount);

        ch->PointChange(POINT_HP, iAmount, false);
        return PASSES_PER_SEC(3);
    }
}

void CHARACTER::StartRecoveryEvent()
{
    if (m_pkRecoveryEvent)
        return;

    if (IsDead() || IsStun())
        return;

    if (IsNPC() && GetHP() >= GetMaxHP()) // ¸ó˝şĹÍ´Â ĂĽ·ÂŔĚ ´Ů Â÷ŔÖŔ¸¸é ˝ĂŔŰ ľČÇŃ´Ů.
        return;

    char_event_info* info = AllocEventInfo<char_event_info>();

    info->ch = this;

    int iSec = IsPC() ? 3 : (MAX(1, GetMobTable().bRegenCycle));
    m_pkRecoveryEvent = event_create(recovery_event, info, PASSES_PER_SEC(iSec));
}

void CHARACTER::Standup()
{
    struct packet_position pack_position;

    if (!IsPosition(POS_SITTING))
        return;

    SetPosition(POS_STANDING);

    sys_log(1, "STANDUP: %s", GetName());

    pack_position.header    = HEADER_GC_CHARACTER_POSITION;
    pack_position.vid        = GetVID();
    pack_position.position    = POSITION_GENERAL;

    PacketAround(&pack_position, sizeof(pack_position));
}

void CHARACTER::Sitdown(int is_ground)
{
    struct packet_position pack_position;

    if (IsPosition(POS_SITTING))
        return;

    SetPosition(POS_SITTING);
    sys_log(1, "SITDOWN: %s", GetName());

    pack_position.header    = HEADER_GC_CHARACTER_POSITION;
    pack_position.vid        = GetVID();
    pack_position.position    = POSITION_SITTING_GROUND;
    PacketAround(&pack_position, sizeof(pack_position));
}

void CHARACTER::SetRotation(float fRot)
{
    m_pointsInstant.fRot = fRot;
}

// x, y ąćÇâŔ¸·Î ş¸°í Ľ±´Ů.
void CHARACTER::SetRotationToXY(long x, long y)
{
    SetRotation(GetDegreeFromPositionXY(GetX(), GetY(), x, y));
}

bool CHARACTER::CannotMoveByAffect() const
{
    return (IsAffectFlag(AFF_STUN));
}

bool CHARACTER::CanMove() const
{
    if (CannotMoveByAffect())
        return false;

    if (GetMyShop())    // »óÁˇ ż¬ »óĹÂżˇĽ­´Â żňÁ÷ŔĎ Ľö ľřŔ˝
        return false;

    // 0.2ĂĘ ŔüŔ̶ó¸é żňÁ÷ŔĎ Ľö ľř´Ů.
    /*
       if (get_float_time() - m_fSyncTime < 0.2f)
       return false;
     */
    return true;
}

// ą«Á¶°Ç x, y Ŕ§Äˇ·Î Ŕ̵ż ˝ĂŲ´Ů.
bool CHARACTER::Sync(long x, long y)
{
    if (!GetSectree())
        return false;

    LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y);

    if (!new_tree)
    {
        if (GetDesc())
        {
            sys_err("cannot find tree at %d %d (name: %s)", x, y, GetName());
            GetDesc()->SetPhase(PHASE_CLOSE);
        }
        else
        {
            sys_err("no tree: %s %d %d %d", GetName(), x, y, GetMapIndex());
            Dead();
        }

        return false;
    }

    SetRotationToXY(x, y);
    SetXYZ(x, y, 0);

    if (GetDungeon())
    {
        // ´řÁŻżë ŔĚşĄĆ® ĽÓĽş şŻČ­
        int iLastEventAttr = m_iEventAttr;
        m_iEventAttr = new_tree->GetEventAttribute(x, y);

        if (m_iEventAttr != iLastEventAttr)
        {
            if (GetParty())
            {
                quest::CQuestManager::instance().AttrOut(GetParty()->GetLeaderPID(), this, iLastEventAttr);
                quest::CQuestManager::instance().AttrIn(GetParty()->GetLeaderPID(), this, m_iEventAttr);
            }
            else
            {
                quest::CQuestManager::instance().AttrOut(GetPlayerID(), this, iLastEventAttr);
                quest::CQuestManager::instance().AttrIn(GetPlayerID(), this, m_iEventAttr);
            }
        }
    }

    if (GetSectree() != new_tree)
    {
        if (!IsNPC())
        {
            SECTREEID id = new_tree->GetID();
            SECTREEID old_id = GetSectree()->GetID();

            sys_log(0, "SECTREE DIFFER: %s %dx%d was %dx%d",
                    GetName(),
                    id.coord.x,
                    id.coord.y,
                    old_id.coord.x,
                    old_id.coord.y);
        }

        new_tree->InsertEntity(this);
    }

    return true;
}

void CHARACTER::Stop()
{
    if (!IsState(m_stateIdle))
        MonsterLog("[IDLE] Á¤Áö");

    GotoState(m_stateIdle);

    m_posDest.x = m_posStart.x = GetX();
    m_posDest.y = m_posStart.y = GetY();
}

bool CHARACTER::Goto(long x, long y)
{
    // TODO °Ĺ¸®ĂĽĹ© ÇĘżä
    // °°Ŕş Ŕ§Äˇ¸é Ŕ̵żÇŇ ÇĘżä ľřŔ˝ (ŔÚµż Ľş°ř)
    if (GetX() == x && GetY() == y)
        return false;

    if (m_posDest.x == x && m_posDest.y == y)
    {
        if (!IsState(m_stateMove))
        {
            m_dwStateDuration = 4;
            GotoState(m_stateMove);
        }
        return false;
    }

    m_posDest.x = x;
    m_posDest.y = y;

    CalculateMoveDuration();

    m_dwStateDuration = 4;

    
    if (!IsState(m_stateMove))
    {
        MonsterLog("[MOVE] %s", GetVictim() ? "´ë»óĂßŔű" : "±×łÉŔ̵ż");

        if (GetVictim())
        {
            //MonsterChat(MONSTER_CHAT_CHASE);
            MonsterChat(MONSTER_CHAT_ATTACK);
        }
    }

    GotoState(m_stateMove);

    return true;
}


DWORD CHARACTER::GetMotionMode() const
{
    DWORD dwMode = MOTION_MODE_GENERAL;

    if (IsPolymorphed())
        return dwMode;

    LPITEM pkItem;

    if ((pkItem = GetWear(WEAR_WEAPON)))
    {
        switch (pkItem->GetProto()->bSubType)
        {
            case WEAPON_SWORD:
                dwMode = MOTION_MODE_ONEHAND_SWORD;
                break;

            case WEAPON_TWO_HANDED:
                dwMode = MOTION_MODE_TWOHAND_SWORD;
                break;

            case WEAPON_DAGGER:
                dwMode = MOTION_MODE_DUALHAND_SWORD;
                break;

            case WEAPON_BOW:
                dwMode = MOTION_MODE_BOW;
                break;

            case WEAPON_BELL:
                dwMode = MOTION_MODE_BELL;
                break;

            case WEAPON_FAN:
                dwMode = MOTION_MODE_FAN;
                break;
        }
    }
    return dwMode;
}

float CHARACTER::GetMoveMotionSpeed() const
{
    DWORD dwMode = GetMotionMode();

    const CMotion * pkMotion = NULL;

    if (!GetMountVnum())
        pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(dwMode, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
    else
    {
        pkMotion = CMotionManager::instance().GetMotion(GetMountVnum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));

        if (!pkMotion)
            pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_HORSE, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
    }

    if (pkMotion)
        return -pkMotion->GetAccumVector().y / pkMotion->GetDuration();
    else
    {
        sys_err("cannot find motion (name %s race %d mode %d)", GetName(), GetRaceNum(), dwMode);
        return 300.0f;
    }
}

float CHARACTER::GetMoveSpeed() const
{
    return GetMoveMotionSpeed() * 10000 / CalculateDuration(GetLimitPoint(POINT_MOV_SPEED), 10000);
}

void CHARACTER::CalculateMoveDuration()
{
    m_posStart.x = GetX();
    m_posStart.y = GetY();

    float fDist = DISTANCE_SQRT(m_posStart.x - m_posDest.x, m_posStart.y - m_posDest.y);

    float motionSpeed = GetMoveMotionSpeed();

    m_dwMoveDuration = CalculateDuration(GetLimitPoint(POINT_MOV_SPEED),
            (int) ((fDist / motionSpeed) * 1000.0f));

    if (IsNPC())
        sys_log(1, "%s: GOTO: distance %f, spd %u, duration %u, motion speed %f pos %d %d -> %d %d",
                GetName(), fDist, GetLimitPoint(POINT_MOV_SPEED), m_dwMoveDuration, motionSpeed,
                m_posStart.x, m_posStart.y, m_posDest.x, m_posDest.y);

    m_dwMoveStartTime = get_dword_time();
}

// x y Ŕ§Äˇ·Î Ŕ̵ż ÇŃ´Ů. (Ŕ̵żÇŇ Ľö ŔÖ´Â °ˇ ľř´Â °ˇ¸¦ Č®ŔÎ ÇĎ°í Sync ¸ŢĽŇµĺ·Î ˝ÇÁ¦ Ŕ̵ż ÇŃ´Ů)
// Ľ­ąö´Â charŔÇ x, y °ŞŔ» ąŮ·Î ąŮ˛ŮÁö¸¸,
// Ŭ¶óżˇĽ­´Â ŔĚŔü Ŕ§ÄˇżˇĽ­ ąŮ˛Ű x, y±îÁö interpolationÇŃ´Ů.
// °Č°ĹłŞ ¶Ů´Â °ÍŔş charŔÇ m_bNowWalkingżˇ ´Ţ·ÁŔÖ´Ů.
// Warp¸¦ ŔǵµÇŃ °ÍŔ̶ó¸é Show¸¦ »çżëÇŇ °Í.
bool CHARACTER::Move(long x, long y)
{
    // °°Ŕş Ŕ§Äˇ¸é Ŕ̵żÇŇ ÇĘżä ľřŔ˝ (ŔÚµż Ľş°ř)
    if (GetX() == x && GetY() == y)
        return true;

    if (test_server)
        if (m_bDetailLog)
            sys_log(0, "%s position %u %u", GetName(), x, y);

    OnMove();
    return Sync(x, y);
}

void CHARACTER::SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, int iRot)
{
    TPacketGCMove pack;

    if (bFunc == FUNC_WAIT)
    {
        x = m_posDest.x;
        y = m_posDest.y;
        dwDuration = m_dwMoveDuration;
    }

    EncodeMovePacket(pack, GetVID(), bFunc, bArg, x, y, dwDuration, dwTime, iRot == -1 ? (int) GetRotation() / 5 : iRot);
    PacketView(&pack, sizeof(TPacketGCMove), this);
}

int CHARACTER::GetRealPoint(BYTE type) const
{
    return m_points.points[type];
}

void CHARACTER::SetRealPoint(BYTE type, int val)
{
    m_points.points[type] = val;
}

int CHARACTER::GetPolymorphPoint(BYTE type) const
{
    if (IsPolymorphed() && !IsPolyMaintainStat())
    {
        DWORD dwMobVnum = GetPolymorphVnum();
        const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
        int iPower = GetPolymorphPower();

        if (pMob)
        {
            switch (type)
            {
                case POINT_ST:
                    if (GetJob() == JOB_SHAMAN || GetJob() == JOB_SURA && GetSkillGroup() == 2)
                        return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_IQ);
                    return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_ST);

                case POINT_HT:
                    return pMob->m_table.bCon * iPower / 100 + GetPoint(POINT_HT);

                case POINT_IQ:
                    return pMob->m_table.bInt * iPower / 100 + GetPoint(POINT_IQ);

                case POINT_DX:
                    return pMob->m_table.bDex * iPower / 100 + GetPoint(POINT_DX);
            }
        }
    }

    return GetPoint(type);
}

int CHARACTER::GetPoint(BYTE type) const
{
    if (type >= POINT_MAX_NUM)
    {
        sys_err("Point type overflow (type %u)", type);
        return 0;
    }

    int val = m_pointsInstant.points[type];
    int max_val = INT_MAX;

    switch (type)
    {
        case POINT_STEAL_HP:
        case POINT_STEAL_SP:
            max_val = 50;
            break;
    }

    if (val > max_val)
        sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);

    return (val);
}

int CHARACTER::GetLimitPoint(BYTE type) const
{
    if (type >= POINT_MAX_NUM)
    {
        sys_err("Point type overflow (type %u)", type);
        return 0;
    }

    int val = m_pointsInstant.points[type];
    int max_val = INT_MAX;
    int limit = INT_MAX;
    int min_limit = -INT_MAX;

    switch (type)
    {
        case POINT_ATT_SPEED:
            min_limit = 0;

            if (IsPC())
                limit = 170;
            else
                limit = 250;
            break;

        case POINT_MOV_SPEED:
            min_limit = 0;

            if (IsPC())
                limit = 200;
            else
                limit = 250;
            break;

        case POINT_STEAL_HP:
        case POINT_STEAL_SP:
            limit = 50;
            max_val = 50;
            break;

        case POINT_MALL_ATTBONUS:
        case POINT_MALL_DEFBONUS:
            limit = 20;
            max_val = 50;
            break;
    }

    if (val > max_val)
        sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);

    if (val > limit)
        val = limit;

    if (val < min_limit)
        val = min_limit;

    return (val);
}

void CHARACTER::SetPoint(BYTE type, int val)
{
    if (type >= POINT_MAX_NUM)
    {
        sys_err("Point type overflow (type %u)", type);
        return;
    }

    m_pointsInstant.points[type] = val;

    // ľĆÁ÷ Ŕ̵żŔĚ ´Ů ľČłˇłµ´Ů¸é Ŕ̵ż ˝Ă°Ł °č»ęŔ» ´Ů˝Ă ÇŘľß ÇŃ´Ů.
    if (type == POINT_MOV_SPEED && get_dword_time() < m_dwMoveStartTime + m_dwMoveDuration)
    {
        CalculateMoveDuration();
    }
}

INT CHARACTER::GetAllowedGold() const
{
    if (GetLevel() <= 10)
        return 100000;
    else if (GetLevel() <= 20)
        return 500000;
    else
        return 50000000;
}

void CHARACTER::CheckMaximumPoints()
{
    if (GetMaxHP() < GetHP())
        PointChange(POINT_HP, GetMaxHP() - GetHP());

    if (GetMaxSP() < GetSP())
        PointChange(POINT_SP, GetMaxSP() - GetSP());
}

void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast)
{
    int val = 0;

    //sys_log(0, "PointChange %d %d | %d -> %d cHP %d mHP %d", type, amount, GetPoint(type), GetPoint(type)+amount, GetHP(), GetMaxHP());

    switch (type)
    {
        case POINT_NONE:
            return;

        case POINT_LEVEL:
            if ((GetLevel() + amount) > gPlayerMaxLevel)
                return;

            SetLevel(GetLevel() + amount);
            val = GetLevel();

            sys_log(0, "LEVELUP: %s %d NEXT EXP %d", GetName(), GetLevel(), GetNextExp());

            PointChange(POINT_NEXT_EXP,    GetNextExp(), false);

            if (amount)
            {
                quest::CQuestManager::instance().LevelUp(GetPlayerID());

                LogManager::instance().LevelLog(this, val, GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000);

                if (GetGuild())
                {
                    GetGuild()->LevelChange(GetPlayerID(), GetLevel());
                }

                if (GetParty())
                {
                    GetParty()->RequestSetMemberLevel(GetPlayerID(), GetLevel());
                }
            }
            break;

        case POINT_NEXT_EXP:
            val = GetNextExp();
            bAmount = false;    // ą«Á¶°Ç bAmount´Â false ż©ľß ÇŃ´Ů.
            break;

        case POINT_EXP:
            {
                DWORD exp = GetExp();
                DWORD next_exp = GetNextExp();

                // Ă»ĽŇłâş¸ČŁ
                if (LC_IsNewCIBN())
                {
                    if (IsOverTime(OT_NONE))
                    {
                        dev_log(LOG_DEB0, "<EXP_LOG> %s = NONE", GetName());
                    }
                    else if (IsOverTime(OT_3HOUR))
                    {
                        amount = (amount / 2);
                        dev_log(LOG_DEB0, "<EXP_LOG> %s = 3HOUR", GetName());
                    }
                    else if (IsOverTime(OT_5HOUR))
                    {
                        amount = 0;
                        dev_log(LOG_DEB0, "<EXP_LOG> %s = 5HOUR", GetName());
                    }
                }

                // exp°ˇ 0 ŔĚÇĎ·Î °ˇÁö ľĘµµ·Ď ÇŃ´Ů
                if (amount < 0 && exp < -amount)
                {
                    sys_log(1, "%s AMOUNT < 0 %d, CUR EXP: %d", GetName(), -amount, exp);
                    amount = -exp;

                    SetExp(exp + amount);
                    val = GetExp();
                }
                else
                {
                    if (gPlayerMaxLevel <= GetLevel())
                        return;

                    if (test_server)
                        ChatPacket(CHAT_TYPE_INFO, "You have gained %d exp.", amount);

                    DWORD iExpBalance = 0;

                    // ·ąş§ ľ÷!
                    if (exp + amount >= next_exp)
                    {
                        iExpBalance = (exp + amount) - next_exp;
                        amount = next_exp - exp;

                        SetExp(0);
                        exp = next_exp;
                    }
                    else
                    {
                        SetExp(exp + amount);
                        exp = GetExp();
                    }

                    DWORD q = DWORD(next_exp / 4.0f);
                    int iLevStep = GetRealPoint(POINT_LEVEL_STEP);

                    // iLevStepŔĚ 4 ŔĚ»óŔĚ¸é ·ąş§ŔĚ żĂ¶úľîľß ÇϹǷΠż©±âżˇ żĂ Ľö ľř´Â °ŞŔĚ´Ů.
                    if (iLevStep >= 4)
                    {
                        sys_err("%s LEVEL_STEP bigger than 4! (%d)", GetName(), iLevStep);
                        iLevStep = 4;
                    }

                    if (exp >= next_exp && iLevStep < 4)
                    {
                        for (int i = 0; i < 4 - iLevStep; ++i)
                            PointChange(POINT_LEVEL_STEP, 1, false, true);
                    }
                    else if (exp >= q * 3 && iLevStep < 3)
                    {
                        for (int i = 0; i < 3 - iLevStep; ++i)
                            PointChange(POINT_LEVEL_STEP, 1, false, true);
                    }
                    else if (exp >= q * 2 && iLevStep < 2)
                    {
                        for (int i = 0; i < 2 - iLevStep; ++i)
                            PointChange(POINT_LEVEL_STEP, 1, false, true);
                    }
                    else if (exp >= q && iLevStep < 1)
                        PointChange(POINT_LEVEL_STEP, 1);

                    if (iExpBalance)
                    {
                        PointChange(POINT_EXP, iExpBalance);
                    }

                    val = GetExp();
                }
            }
            break;

        case POINT_LEVEL_STEP:
            if (amount > 0)
            {
                val = GetPoint(POINT_LEVEL_STEP) + amount;

                switch (val)
                {
                    case 1:
                    case 2:
                    case 3:
                        //if (GetLevel() < 100) PointChange(POINT_STAT, 1);
                        if (GetLevel() < 91) PointChange(POINT_STAT, 1);
                        break;

                    case 4:
                        {
                            int iHP = number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end);
                            int iSP = number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end);

                            m_points.iRandomHP += iHP;
                            m_points.iRandomSP += iSP;

                            if (GetSkillGroup())
                            {
                                if (GetLevel() >= 5)
                                    PointChange(POINT_SKILL, 1);

                                if (GetLevel() >= 9)
                                    PointChange(POINT_SUB_SKILL, 1);
                            }

                            PointChange(POINT_MAX_HP, iHP);
                            PointChange(POINT_MAX_SP, iSP);
                            PointChange(POINT_LEVEL, 1, false, true);

                            val = 0;
                        }
                        break;
                }

                if (GetLevel() <= 10)
                    AutoGiveItem(27001, 2);
                else if (GetLevel() <= 30)
                    AutoGiveItem(27002, 2);
                else
                {
                    AutoGiveItem(27002, 2);
//                    AutoGiveItem(27003, 2);
                }

                PointChange(POINT_HP, GetMaxHP() - GetHP());
                PointChange(POINT_SP, GetMaxSP() - GetSP());
                PointChange(POINT_STAMINA, GetMaxStamina() - GetStamina());

                SetPoint(POINT_LEVEL_STEP, val);
                SetRealPoint(POINT_LEVEL_STEP, val);

                Save();
            }
            else
                val = GetPoint(POINT_LEVEL_STEP);

            break;

        case POINT_HP:
            {
                if (IsDead() || IsStun())
                    return;

                int prev_hp = GetHP();

                amount = MIN(GetMaxHP() - GetHP(), amount);
                SetHP(GetHP() + amount);
                val = GetHP();

                BroadcastTargetPacket();

                if (GetParty() && IsPC() && val != prev_hp)
                    GetParty()->SendPartyInfoOneToAll(this);
            }
            break;

        case POINT_SP:
            {
                if (IsDead() || IsStun())
                    return;

                amount = MIN(GetMaxSP() - GetSP(), amount);
                SetSP(GetSP() + amount);
                val = GetSP();
            }
            break;

        case POINT_STAMINA:
            {
                if (IsDead() || IsStun())
                    return;

                int prev_val = GetStamina();
                amount = MIN(GetMaxStamina() - GetStamina(), amount);
                SetStamina(GetStamina() + amount);
                val = GetStamina();
                
                if (val == 0)
                {
                    // Stamina°ˇ ľřŔ¸´Ď °ČŔÚ!
                    SetNowWalking(true);
                }
                else if (prev_val == 0)
                {
                    // ľř´ř ˝şĹ×ąĚłŞ°ˇ »ý°ĺŔ¸´Ď ŔĚŔü ¸đµĺ şą±Í
                    ResetWalking();
                }

                if (amount < 0 && val != 0) // °¨ĽŇ´Â ş¸ł»ÁöľĘ´Â´Ů.
                    return;
            }
            break;

        case POINT_MAX_HP:
            {
                SetPoint(type, GetPoint(type) + amount);

                //SetMaxHP(GetMaxHP() + amount);
                // ĂÖ´ë »ý¸í·Â = (±âş» ĂÖ´ë »ý¸í·Â + Ăß°ˇ) * ĂÖ´ë»ý¸í·Â%
                int hp = GetRealPoint(POINT_MAX_HP);
                int add_hp = MIN(3500, hp * GetPoint(POINT_MAX_HP_PCT) / 100);
                add_hp += GetPoint(POINT_MAX_HP);
                add_hp += GetPoint(POINT_PARTY_TANKER_BONUS);

                SetMaxHP(hp + add_hp);

                val = GetMaxHP();
            }
            break;

        case POINT_MAX_SP:
            {
                SetPoint(type, GetPoint(type) + amount);

                //SetMaxSP(GetMaxSP() + amount);
                // ĂÖ´ë Á¤˝Ĺ·Â = (±âş» ĂÖ´ë Á¤˝Ĺ·Â + Ăß°ˇ) * ĂÖ´ëÁ¤˝Ĺ·Â%
                int sp = GetRealPoint(POINT_MAX_SP);
                int add_sp = MIN(800, sp * GetPoint(POINT_MAX_SP_PCT) / 100);
                add_sp += GetPoint(POINT_MAX_SP);
                add_sp += GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);

                SetMaxSP(sp + add_sp);

                val = GetMaxSP();
            }
            break;

        case POINT_MAX_HP_PCT:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);

            PointChange(POINT_MAX_HP, 0);
            break;

        case POINT_MAX_SP_PCT:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);

            PointChange(POINT_MAX_SP, 0);
            break;

        case POINT_MAX_STAMINA:
            SetMaxStamina(GetMaxStamina() + amount);
            val = GetMaxStamina();
            break;

        case POINT_GOLD:
            {
                const int64_t nTotalMoney = static_cast<int64_t>(GetGold()) + static_cast<int64_t>(amount);

                if (GOLD_MAX <= nTotalMoney)
                {
                    sys_err("[OVERFLOW_GOLD] OriGold %d AddedGold %d id %u Name %s ", GetGold(), amount, GetPlayerID(), GetName());
                    LogManager::instance().CharLog(this, GetGold() + amount, "OVERFLOW_GOLD", "");
                    return;
                }

                // Ă»ĽŇłâş¸ČŁ
                if (LC_IsNewCIBN() && amount > 0)
                {
                    if (IsOverTime(OT_NONE))
                    {
                        dev_log(LOG_DEB0, "<GOLD_LOG> %s = NONE", GetName());
                    }
                    else if (IsOverTime(OT_3HOUR))
                    {
                        amount = (amount / 2);
                        dev_log(LOG_DEB0, "<GOLD_LOG> %s = 3HOUR", GetName());
                    }
                    else if (IsOverTime(OT_5HOUR))
                    {
                        amount = 0;
                        dev_log(LOG_DEB0, "<GOLD_LOG> %s = 5HOUR", GetName());
                    }
                }

                SetGold(GetGold() + amount);
                val = GetGold();
            }
            break;

        case POINT_SKILL:
        case POINT_STAT:
        case POINT_SUB_SKILL:
        case POINT_STAT_RESET_COUNT:
        case POINT_HORSE_SKILL:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);

            SetRealPoint(type, val);
            break;

        case POINT_DEF_GRADE:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);

            PointChange(POINT_CLIENT_DEF_GRADE, amount);
            break;

        case POINT_CLIENT_DEF_GRADE:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            break;

        case POINT_ST:
        case POINT_HT:
        case POINT_DX:
        case POINT_IQ:
        case POINT_HP_REGEN:
        case POINT_SP_REGEN:
        case POINT_ATT_SPEED:
        case POINT_ATT_GRADE:
        case POINT_MOV_SPEED:
        case POINT_CASTING_SPEED:
        case POINT_MAGIC_ATT_GRADE:
        case POINT_MAGIC_DEF_GRADE:
        case POINT_BOW_DISTANCE:
        case POINT_HP_RECOVERY:
        case POINT_SP_RECOVERY:

        case POINT_ATTBONUS_HUMAN:    // 42 ŔΰŁżˇ°Ô °­ÇÔ
        case POINT_ATTBONUS_ANIMAL:    // 43 µżą°żˇ°Ô µĄąĚÁö % Áő°ˇ
        case POINT_ATTBONUS_ORC:        // 44 żő±Íżˇ°Ô µĄąĚÁö % Áő°ˇ
        case POINT_ATTBONUS_MILGYO:    // 45 ąĐ±łżˇ°Ô µĄąĚÁö % Áő°ˇ
        case POINT_ATTBONUS_UNDEAD:    // 46 ˝ĂĂĽżˇ°Ô µĄąĚÁö % Áő°ˇ
        case POINT_ATTBONUS_DEVIL:    // 47 ¸¶±Í(ľÇ¸¶)żˇ°Ô µĄąĚÁö % Áő°ˇ

        case POINT_ATTBONUS_MONSTER:
        case POINT_ATTBONUS_SURA:
        case POINT_ATTBONUS_ASSASSIN:
        case POINT_ATTBONUS_WARRIOR:
        case POINT_ATTBONUS_SHAMAN:

        case POINT_POISON_PCT:
        case POINT_STUN_PCT:
        case POINT_SLOW_PCT:

        case POINT_BLOCK:
        case POINT_DODGE:

        case POINT_CRITICAL_PCT:
        case POINT_RESIST_CRITICAL:
        case POINT_PENETRATE_PCT:
        case POINT_RESIST_PENETRATE:
        case POINT_CURSE_PCT:

        case POINT_STEAL_HP:        // 48 »ý¸í·Â ČíĽö
        case POINT_STEAL_SP:        // 49 Á¤˝Ĺ·Â ČíĽö

        case POINT_MANA_BURN_PCT:    // 50 ¸¶łŞ ąř
        case POINT_DAMAGE_SP_RECOVER:    // 51 °ř°Ý´çÇŇ ˝Ă Á¤˝Ĺ·Â ȸşą Č®·ü
        case POINT_RESIST_NORMAL_DAMAGE:
        case POINT_RESIST_SWORD:
        case POINT_RESIST_TWOHAND:
        case POINT_RESIST_DAGGER:
        case POINT_RESIST_BELL: 
        case POINT_RESIST_FAN: 
        case POINT_RESIST_BOW:
        case POINT_RESIST_FIRE:
        case POINT_RESIST_ELEC:
        case POINT_RESIST_MAGIC:
        case POINT_RESIST_WIND:
        case POINT_RESIST_ICE:
        case POINT_RESIST_EARTH:
        case POINT_RESIST_DARK:
        case POINT_REFLECT_MELEE:    // 67 °ř°Ý ąÝ»ç
        case POINT_REFLECT_CURSE:    // 68 ŔúÁÖ ąÝ»ç
        case POINT_POISON_REDUCE:    // 69 µ¶µĄąĚÁö °¨ĽŇ
        case POINT_KILL_SP_RECOVER:    // 70 Ŕű ĽŇ¸ę˝Ă MP ȸşą
        case POINT_KILL_HP_RECOVERY:    // 75  
        case POINT_HIT_HP_RECOVERY:
        case POINT_HIT_SP_RECOVERY:
        case POINT_MANASHIELD:
        case POINT_ATT_BONUS:
        case POINT_DEF_BONUS:
        case POINT_SKILL_DAMAGE_BONUS:
        case POINT_NORMAL_HIT_DAMAGE_BONUS:

            // DEPEND_BONUS_ATTRIBUTES 
        case POINT_SKILL_DEFEND_BONUS:
        case POINT_NORMAL_HIT_DEFEND_BONUS:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            break;
            // END_OF_DEPEND_BONUS_ATTRIBUTES

        case POINT_PARTY_ATTACKER_BONUS:
        case POINT_PARTY_TANKER_BONUS:
        case POINT_PARTY_BUFFER_BONUS:
        case POINT_PARTY_SKILL_MASTER_BONUS:
        case POINT_PARTY_HASTE_BONUS:
        case POINT_PARTY_DEFENDER_BONUS:

        case POINT_RESIST_WARRIOR :
        case POINT_RESIST_ASSASSIN :
        case POINT_RESIST_SURA :
        case POINT_RESIST_SHAMAN :

            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            break;

        case POINT_MALL_ATTBONUS:
        case POINT_MALL_DEFBONUS:
        case POINT_MALL_EXPBONUS:
        case POINT_MALL_ITEMBONUS:
        case POINT_MALL_GOLDBONUS:
        case POINT_MELEE_MAGIC_ATT_BONUS_PER:
            if (GetPoint(type) + amount > 100)
            {
                sys_err("MALL_BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
                amount = 100 - GetPoint(type);
            }

            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            break;

            // PC_BANG_ITEM_ADD        
        case POINT_PC_BANG_EXP_BONUS :
        case POINT_PC_BANG_DROP_BONUS :
        case POINT_RAMADAN_CANDY_BONUS_EXP:
            SetPoint(type, amount);
            val = GetPoint(type);
            break;
            // END_PC_BANG_ITEM_ADD        

        case POINT_EXP_DOUBLE_BONUS:    // 71  
        case POINT_GOLD_DOUBLE_BONUS:    // 72  
        case POINT_ITEM_DROP_BONUS:    // 73  
        case POINT_POTION_BONUS:    // 74
            if (GetPoint(type) + amount > 100)
            {
                sys_err("BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
                amount = 100 - GetPoint(type);
            }

            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            break;

        case POINT_IMMUNE_STUN:        // 76 
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            if (val)
            {
                SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
            }
            else
            {
                REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
            }
            break;

        case POINT_IMMUNE_SLOW:        // 77  
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            if (val)
            {
                SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
            }
            else
            {
                REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
            }
            break;

        case POINT_IMMUNE_FALL:    // 78   
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            if (val)
            {
                SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
            }
            else
            {
                REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
            }
            break;

        case POINT_ATT_GRADE_BONUS:
            SetPoint(type, GetPoint(type) + amount);
            PointChange(POINT_ATT_GRADE, amount);
            val = GetPoint(type);
            break;

        case POINT_DEF_GRADE_BONUS:
            SetPoint(type, GetPoint(type) + amount);
            PointChange(POINT_DEF_GRADE, amount);
            val = GetPoint(type);
            break;

        case POINT_MAGIC_ATT_GRADE_BONUS:
            SetPoint(type, GetPoint(type) + amount);
            PointChange(POINT_MAGIC_ATT_GRADE, amount);
            val = GetPoint(type);
            break;

        case POINT_MAGIC_DEF_GRADE_BONUS:
            SetPoint(type, GetPoint(type) + amount);
            PointChange(POINT_MAGIC_DEF_GRADE, amount);
            val = GetPoint(type);
            break;

        case POINT_VOICE:
        case POINT_EMPIRE_POINT:
            //sys_err("CHARACTER::PointChange: %s: point cannot be changed. use SetPoint instead (type: %d)", GetName(), type);
            val = GetRealPoint(type);
            break;

        case POINT_POLYMORPH:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            SetPolymorph(val);
            break;

        case POINT_MOUNT:
            SetPoint(type, GetPoint(type) + amount);
            val = GetPoint(type);
            MountVnum(val);
            break;

        case POINT_ENERGY:
        case POINT_COSTUME_ATTR_BONUS:
            {
                int old_val = GetPoint(type);
                SetPoint(type, old_val + amount);
                val = GetPoint(type);
                BuffOnAttr_ValueChange(type, old_val, val);
            }
            break;

        default:
            sys_err("CHARACTER::PointChange: %s: unknown point change type %d", GetName(), type);
            return;
    }

    switch (type)
    {
        case POINT_LEVEL:
        case POINT_ST:
        case POINT_DX:
        case POINT_IQ:
        case POINT_HT:
            ComputeBattlePoints();
            break;
        case POINT_MAX_HP:
        case POINT_MAX_SP:
        case POINT_MAX_STAMINA:
            break;
    }

    if (type == POINT_HP && amount == 0)
        return;

    if (GetDesc())
    {
        struct packet_point_change pack;

        pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
        pack.dwVID = m_vid;
        pack.type = type;
        pack.value = val;

        if (bAmount)
            pack.amount = amount;
        else
            pack.amount = 0;

        if (!bBroadcast)
            GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
        else
            PacketAround(&pack, sizeof(pack));
    }
}

void CHARACTER::ApplyPoint(BYTE bApplyType, int iVal)
{
    switch (bApplyType)
    {
        case APPLY_NONE:            // 0
            break;;

        case APPLY_CON:
            PointChange(POINT_HT, iVal);
            PointChange(POINT_MAX_HP, (iVal * JobInitialPoints[GetJob()].hp_per_ht));
            PointChange(POINT_MAX_STAMINA, (iVal * JobInitialPoints[GetJob()].stamina_per_con));
            break;

        case APPLY_INT: 
            PointChange(POINT_IQ, iVal);
            PointChange(POINT_MAX_SP, (iVal * JobInitialPoints[GetJob()].sp_per_iq));
            break;

        case APPLY_SKILL:
            // SKILL_DAMAGE_BONUS
            {
                // ĂÖ»óŔ§ şńĆ® ±âÁŘŔ¸·Î 8şńĆ® vnum, 9şńĆ® add, 15şńĆ® change
                // 00000000 00000000 00000000 00000000
                // ^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^
                // vnum     ^ add       change
                BYTE bSkillVnum = (BYTE) (((DWORD)iVal) >> 24);
                int iAdd = iVal & 0x00800000;
                int iChange = iVal & 0x007fffff;

                sys_log(1, "APPLY_SKILL skill %d add? %d change %d", bSkillVnum, iAdd ? 1 : 0, iChange);

                if (0 == iAdd)
                    iChange = -iChange;

                boost::unordered_map<BYTE, int>::iterator iter = m_SkillDamageBonus.find(bSkillVnum);

                if (iter == m_SkillDamageBonus.end())
                    m_SkillDamageBonus.insert(std::make_pair(bSkillVnum, iChange));
                else
                    iter->second += iChange;
            }
            // END_OF_SKILL_DAMAGE_BONUS
            break;

        case APPLY_STR:
        case APPLY_DEX:
        case APPLY_MAX_HP:
        case APPLY_MAX_SP:
        case APPLY_MAX_HP_PCT:
        case APPLY_MAX_SP_PCT:
        case APPLY_ATT_SPEED:
        case APPLY_MOV_SPEED:
        case APPLY_CAST_SPEED:
        case APPLY_HP_REGEN:
        case APPLY_SP_REGEN:
        case APPLY_POISON_PCT:
        case APPLY_STUN_PCT:
        case APPLY_SLOW_PCT:
        case APPLY_CRITICAL_PCT:
        case APPLY_PENETRATE_PCT:
        case APPLY_ATTBONUS_HUMAN:
        case APPLY_ATTBONUS_ANIMAL:
        case APPLY_ATTBONUS_ORC:
        case APPLY_ATTBONUS_MILGYO:
        case APPLY_ATTBONUS_UNDEAD:
        case APPLY_ATTBONUS_DEVIL:
        case APPLY_ATTBONUS_WARRIOR:    // 59
        case APPLY_ATTBONUS_ASSASSIN:    // 60
        case APPLY_ATTBONUS_SURA:    // 61
        case APPLY_ATTBONUS_SHAMAN:    // 62
        case APPLY_ATTBONUS_MONSTER:    // 63
        case APPLY_STEAL_HP:
        case APPLY_STEAL_SP:
        case APPLY_MANA_BURN_PCT:
        case APPLY_DAMAGE_SP_RECOVER:
        case APPLY_BLOCK:
        case APPLY_DODGE:
        case APPLY_RESIST_SWORD:
        case APPLY_RESIST_TWOHAND:
        case APPLY_RESIST_DAGGER:
        case APPLY_RESIST_BELL:
        case APPLY_RESIST_FAN:
        case APPLY_RESIST_BOW:
        case APPLY_RESIST_FIRE:
        case APPLY_RESIST_ELEC:
        case APPLY_RESIST_MAGIC:
        case APPLY_RESIST_WIND:
        case APPLY_RESIST_ICE:
        case APPLY_RESIST_EARTH:
        case APPLY_RESIST_DARK:
        case APPLY_REFLECT_MELEE:
        case APPLY_REFLECT_CURSE:
        case APPLY_ANTI_CRITICAL_PCT:
        case APPLY_ANTI_PENETRATE_PCT:
        case APPLY_POISON_REDUCE:
        case APPLY_KILL_SP_RECOVER:
        case APPLY_EXP_DOUBLE_BONUS:
        case APPLY_GOLD_DOUBLE_BONUS:
        case APPLY_ITEM_DROP_BONUS:
        case APPLY_POTION_BONUS:
        case APPLY_KILL_HP_RECOVER:
        case APPLY_IMMUNE_STUN:    
        case APPLY_IMMUNE_SLOW:    
        case APPLY_IMMUNE_FALL:    
        case APPLY_BOW_DISTANCE:
        case APPLY_ATT_GRADE_BONUS:
        case APPLY_DEF_GRADE_BONUS:
        case APPLY_MAGIC_ATT_GRADE:
        case APPLY_MAGIC_DEF_GRADE:
        case APPLY_CURSE_PCT:
        case APPLY_MAX_STAMINA:
        case APPLY_MALL_ATTBONUS:
        case APPLY_MALL_DEFBONUS:
        case APPLY_MALL_EXPBONUS:
        case APPLY_MALL_ITEMBONUS:
        case APPLY_MALL_GOLDBONUS:
        case APPLY_SKILL_DAMAGE_BONUS:
        case APPLY_NORMAL_HIT_DAMAGE_BONUS:

            // DEPEND_BONUS_ATTRIBUTES
        case APPLY_SKILL_DEFEND_BONUS:
        case APPLY_NORMAL_HIT_DEFEND_BONUS:
            // END_OF_DEPEND_BONUS_ATTRIBUTES

        case APPLY_PC_BANG_EXP_BONUS :
        case APPLY_PC_BANG_DROP_BONUS :

        case APPLY_RESIST_WARRIOR :
        case APPLY_RESIST_ASSASSIN :
        case APPLY_RESIST_SURA :
        case APPLY_RESIST_SHAMAN :    
        case APPLY_ENERGY:                    // 82 ±â·Â
        case APPLY_DEF_GRADE:                // 83 ąćľî·Â. DEF_GRADE_BONUS´Â Ŭ¶óżˇĽ­ µÎąč·Î ş¸ż©Áö´Â ŔǵµµČ ąö±×(...)°ˇ ŔÖ´Ů.
        case APPLY_COSTUME_ATTR_BONUS:        // 84 ÄÚ˝şĆ¬ ľĆŔĚĹŰżˇ şŮŔş ĽÓĽşÄˇ ş¸łĘ˝ş
        case APPLY_MAGIC_ATTBONUS_PER:        // 85 ¸¶ąý °ř°Ý·Â +x%
        case APPLY_MELEE_MAGIC_ATTBONUS_PER:            // 86 ¸¶ąý + ąĐ¸® °ř°Ý·Â +x%
            PointChange(aApplyInfo[bApplyType].bPointType, iVal);
            break;

        default:
            sys_err("Unknown apply type %d name %s", bApplyType, GetName());
            break;
    }
}

void CHARACTER::MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet)
{
    packet->header    = HEADER_GC_MOTION;
    packet->vid        = m_vid;
    packet->motion    = motion;

    if (victim)
        packet->victim_vid = victim->GetVID();
    else
        packet->victim_vid = 0;
}

void CHARACTER::Motion(BYTE motion, LPCHARACTER victim)
{
    struct packet_motion pack_motion;
    MotionPacketEncode(motion, victim, &pack_motion);
    PacketAround(&pack_motion, sizeof(struct packet_motion));
}

EVENTFUNC(save_event)
{
    char_event_info* info = dynamic_cast<char_event_info*>( event->info );
    if ( info == NULL )
    {
        sys_err( "save_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER ch = info->ch;

    if (ch == NULL) { // <Factor>
        return 0;
    }    
    sys_log(1, "SAVE_EVENT: %s", ch->GetName());
    ch->Save();
    ch->FlushDelayedSaveItem();
    return (save_event_second_cycle);
}

void CHARACTER::StartSaveEvent()
{
    if (m_pkSaveEvent)
        return;

    char_event_info* info = AllocEventInfo<char_event_info>();

    info->ch = this;
    m_pkSaveEvent = event_create(save_event, info, save_event_second_cycle);
}

void CHARACTER::MonsterLog(const char* format, ...)
{
    if (!test_server)
        return;

    if (IsPC())
        return;

    char chatbuf[CHAT_MAX_LEN + 1];
    int len = snprintf(chatbuf, sizeof(chatbuf), "%u)", (DWORD)GetVID());

    if (len < 0 || len >= (int) sizeof(chatbuf))
        len = sizeof(chatbuf) - 1;

    va_list args;

    va_start(args, format);

    int len2 = vsnprintf(chatbuf + len, sizeof(chatbuf) - len, format, args);

    if (len2 < 0 || len2 >= (int) sizeof(chatbuf) - len)
        len += (sizeof(chatbuf) - len) - 1;
    else
        len += len2;

    // \0 ą®ŔÚ Ć÷ÇÔ
    ++len;

    va_end(args);

    TPacketGCChat pack_chat;

    pack_chat.header    = HEADER_GC_CHAT;
    pack_chat.size        = sizeof(TPacketGCChat) + len;
    pack_chat.type      = CHAT_TYPE_TALKING;
    pack_chat.id        = (DWORD)GetVID();
    pack_chat.bEmpire    = 0;

    TEMP_BUFFER buf;
    buf.write(&pack_chat, sizeof(TPacketGCChat));
    buf.write(chatbuf, len);

    CHARACTER_MANAGER::instance().PacketMonsterLog(this, buf.read_peek(), buf.size());
}

void CHARACTER::ChatPacket(BYTE type, const char * format, ...)
{
    LPDESC d = GetDesc();

    if (!d || !format)
        return;

    char chatbuf[CHAT_MAX_LEN + 1];
    va_list args;

    va_start(args, format);
    int len = vsnprintf(chatbuf, sizeof(chatbuf), format, args);
    va_end(args);

    struct packet_chat pack_chat;

    pack_chat.header    = HEADER_GC_CHAT;
    pack_chat.size      = sizeof(struct packet_chat) + len;
    pack_chat.type      = type;
    pack_chat.id        = 0;
    pack_chat.bEmpire   = d->GetEmpire();

    TEMP_BUFFER buf;
    buf.write(&pack_chat, sizeof(struct packet_chat));
    buf.write(chatbuf, len);

    d->Packet(buf.read_peek(), buf.size());

    if (type == CHAT_TYPE_COMMAND && test_server)
        sys_log(0, "SEND_COMMAND %s %s", GetName(), chatbuf);
}

// MINING
void CHARACTER::mining_take()
{
    m_pkMiningEvent = NULL;
}

void CHARACTER::mining_cancel()
{
    if (m_pkMiningEvent)
    {
        sys_log(0, "XXX MINING CANCEL");
        event_cancel(&m_pkMiningEvent);
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("䱤Ŕ» Áß´ÜÇĎż´˝Ŕ´Ď´Ů."));
    }
}

void CHARACTER::mining(LPCHARACTER chLoad)
{
    if (m_pkMiningEvent)
    {
        mining_cancel();
        return;
    }

    if (!chLoad)
        return;

    if (mining::GetRawOreFromLoad(chLoad->GetRaceNum()) == 0)
        return;

    LPITEM pick = GetWear(WEAR_WEAPON);

    if (!pick || pick->GetType() != ITEM_PICK)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°î±ŞŔ̸¦ ŔĺÂřÇĎĽĽżä."));
        return;
    }

    int count = number(5, 15); // µżŔŰ Č˝Ľö, ÇŃ µżŔŰ´ç 2ĂĘ

    // 䱤 µżŔŰŔ» ş¸ż©ÁÜ
    TPacketGCDigMotion p;
    p.header = HEADER_GC_DIG_MOTION;
    p.vid = GetVID();
    p.target_vid = chLoad->GetVID();
    p.count = count;

    PacketAround(&p, sizeof(p));

    m_pkMiningEvent = mining::CreateMiningEvent(this, chLoad, count);
}
// END_OF_MINING

void CHARACTER::fishing()
{
    if (m_pkFishingEvent)
    {
        fishing_take();
        return;
    }

    // ¸ř°¨ ĽÓĽşżˇĽ­ ł¬˝Ă¸¦ ˝ĂµµÇŃ´Ů?
    {
        LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex());

        int    x = GetX();
        int y = GetY();

        LPSECTREE tree = pkSectreeMap->Find(x, y);
        DWORD dwAttr = tree->GetAttribute(x, y);

        if (IS_SET(dwAttr, ATTR_BLOCK))
        {
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ł¬˝Ă¸¦ ÇŇ Ľö ŔÖ´Â °÷ŔĚ ľĆ´Ő´Ď´Ů"));
            return;
        }
    }

    LPITEM rod = GetWear(WEAR_WEAPON);

    // ł¬˝Ă´ë ŔĺÂř
    if (!rod || rod->GetType() != ITEM_ROD)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ł¬˝Ă´ë¸¦ ŔĺÂř ÇĎĽĽżä."));
        return;
    }

    if (0 == rod->GetSocket(2))
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ąĚł˘¸¦ ł˘°í ´řÁ® ÁÖĽĽżä."));
        return;
    }

    float fx, fy;
    GetDeltaByDegree(GetRotation(), 400.0f, &fx, &fy);

    m_pkFishingEvent = fishing::CreateFishingEvent(this);
}

void CHARACTER::fishing_take()
{
    LPITEM rod = GetWear(WEAR_WEAPON);
    if (rod && rod->GetType() == ITEM_ROD)
    {
        using fishing::fishing_event_info;
        if (m_pkFishingEvent)
        {
            struct fishing_event_info* info = dynamic_cast<struct fishing_event_info*>(m_pkFishingEvent->info);

            if (info)
                fishing::Take(info, this);
        }
    }
    else
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ł¬˝Ă´ë°ˇ ľĆ´Ń ą°°ÇŔ¸·Î ł¬˝Ă¸¦ ÇŇ Ľö ľř˝Ŕ´Ď´Ů!"));
    }

    event_cancel(&m_pkFishingEvent);
}

bool CHARACTER::StartStateMachine(int iNextPulse)
{
    if (CHARACTER_MANAGER::instance().AddToStateList(this))
    {
        m_dwNextStatePulse = thecore_heart->pulse + iNextPulse;
        return true;
    }

    return false;
}

void CHARACTER::StopStateMachine()
{
    CHARACTER_MANAGER::instance().RemoveFromStateList(this);
}

void CHARACTER::UpdateStateMachine(DWORD dwPulse)
{
    if (dwPulse < m_dwNextStatePulse)
        return;

    if (IsDead())
        return;

    Update();
    m_dwNextStatePulse = dwPulse + m_dwStateDuration;
}

void CHARACTER::SetNextStatePulse(int iNextPulse)
{
    CHARACTER_MANAGER::instance().AddToStateList(this);
    m_dwNextStatePulse = iNextPulse;

    if (iNextPulse < 10)
        MonsterLog("´ŮŔ˝»óĹ·ξ°ˇŔÚ");
}


// Äł¸ŻĹÍ ŔνşĹĎ˝ş ľ÷µĄŔĚĆ® ÇÔĽö.
void CHARACTER::UpdateCharacter(DWORD dwPulse)
{
    CFSM::Update();
}

void CHARACTER::SetShop(LPSHOP pkShop)
{
    if ((m_pkShop = pkShop))
        SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
    else
    {
        REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP); 
        SetShopOwner(NULL);
    }
}

void CHARACTER::SetExchange(CExchange * pkExchange)
{
    m_pkExchange = pkExchange;
}

void CHARACTER::SetPart(BYTE bPartPos, WORD wVal)
{
    assert(bPartPos < PART_MAX_NUM);
    m_pointsInstant.parts[bPartPos] = wVal;
}

WORD CHARACTER::GetPart(BYTE bPartPos) const
{
    assert(bPartPos < PART_MAX_NUM);
    return m_pointsInstant.parts[bPartPos];
}

WORD CHARACTER::GetOriginalPart(BYTE bPartPos) const
{
    switch (bPartPos)
    {
        case PART_MAIN:
            if (!IsPC()) // PC°ˇ ľĆ´Ń °ćżě ÇöŔç ĆÄĆ®¸¦ ±×´ë·Î ¸®ĹĎ
                return GetPart(PART_MAIN);
            else
                return m_pointsInstant.bBasePart;

        case PART_HAIR:
            return GetPart(PART_HAIR);

        default:
            return 0;
    }
}

BYTE CHARACTER::GetCharType() const
{
    return m_bCharType;
}

bool CHARACTER::SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList)
{
    // TRENT_MONSTER
    if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
        return false;
    // END_OF_TRENT_MONSTER

    if (ch == this)
    {
        sys_err("SetSyncOwner owner == this (%p)", this);
        return false;
    }

    if (!ch)
    {
        if (bRemoveFromList && m_pkChrSyncOwner)
        {
            m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
        }

        if (m_pkChrSyncOwner)
            sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());

        // ¸®˝şĆ®żˇĽ­ Á¦°ĹÇĎÁö ľĘ´ő¶óµµ Ć÷ŔÎĹÍ´Â NULL·Î ĽÂĆõǾîľß ÇŃ´Ů.
        m_pkChrSyncOwner = NULL;
    }
    else
    {
        if (!IsSyncOwner(ch))
            return false;

        // °Ĺ¸®°ˇ 200 ŔĚ»óŔ̸é SyncOwner°ˇ µÉ Ľö ľř´Ů.
        if (DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY()) > 250)
        {
            sys_log(1, "SetSyncOwner distance over than 250 %s %s", GetName(), ch->GetName());

            // SyncOwnerŔĎ °ćżě Owner·Î ÇĄ˝ĂÇŃ´Ů.
            if (m_pkChrSyncOwner == ch)
                return true;

            return false;
        }

        if (m_pkChrSyncOwner != ch)
        {
            if (m_pkChrSyncOwner)
            {
                sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
                m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
            }

            m_pkChrSyncOwner = ch;
            m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.push_back(this);

            // SyncOwner°ˇ ąŮ˛î¸é LastSyncTimeŔ» ĂʱâČ­ÇŃ´Ů.
            static const timeval zero_tv = {0, 0};
            SetLastSyncTime(zero_tv);

            sys_log(1, "SetSyncOwner set %s %p to %s", GetName(), this, ch->GetName());
        }

        m_fSyncTime = get_float_time();
    }

    // TODO: Sync Owner°ˇ °°´ő¶óµµ °čĽÓ ĆĐŶŔ» ş¸ł»°í ŔÖŔ¸ąÇ·Î,
    //       µż±âČ­ µČ ˝Ă°ŁŔĚ 3ĂĘ ŔĚ»ó ÁöłµŔ» ¶§ Ç®ľîÁÖ´Â ĆĐŶŔ»
    //       ş¸ł»´Â ąć˝ÄŔ¸·Î Çϸé ĆĐŶŔ» ÁŮŔĎ Ľö ŔÖ´Ů.
    TPacketGCOwnership pack;

    pack.bHeader    = HEADER_GC_OWNERSHIP;
    pack.dwOwnerVID    = ch ? ch->GetVID() : 0;
    pack.dwVictimVID    = GetVID();

    PacketAround(&pack, sizeof(TPacketGCOwnership));
    return true;
}

struct FuncClearSync
{
    void operator () (LPCHARACTER ch)
    {
        assert(ch != NULL);
        ch->SetSyncOwner(NULL, false);    // false ÇĂ·ˇ±×·Î ÇŘľß for_each °ˇ Á¦´ë·Î µ·´Ů.
    }
};

void CHARACTER::ClearSync()
{
    SetSyncOwner(NULL);

    // ľĆ·ˇ for_eachżˇĽ­ łŞ¸¦ m_pkChrSyncOwner·Î °ˇÁř ŔÚµéŔÇ Ć÷ŔÎĹ͸¦ NULL·Î ÇŃ´Ů.
    std::for_each(m_kLst_pkChrSyncOwned.begin(), m_kLst_pkChrSyncOwned.end(), FuncClearSync());
    m_kLst_pkChrSyncOwned.clear();
}

bool CHARACTER::IsSyncOwner(LPCHARACTER ch) const
{
    if (m_pkChrSyncOwner == ch)
        return true;

    // ¸¶Áö¸·Ŕ¸·Î µż±âČ­ µČ ˝Ă°ŁŔĚ 3ĂĘ ŔĚ»ó Áöłµ´Ů¸é ĽŇŔŻ±ÇŔĚ ľĆą«żˇ°Ôµµ
    // ľř´Ů. µű¶óĽ­ ľĆą«łŞ SyncOwnerŔ̹ǷΠtrue ¸®ĹĎ
    if (get_float_time() - m_fSyncTime >= 3.0f)
        return true;

    return false;
}

void CHARACTER::SetParty(LPPARTY pkParty)
{
    if (pkParty == m_pkParty)
        return;

    if (pkParty && m_pkParty)
        sys_err("%s is trying to reassigning party (current %p, new party %p)", GetName(), get_pointer(m_pkParty), get_pointer(pkParty));

    sys_log(1, "PARTY set to %p", get_pointer(pkParty));

    //if (m_pkDungeon && IsPC())
    //SetDungeon(NULL);
    m_pkParty = pkParty;

    if (IsPC())
    {
        if (m_pkParty)
            SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
        else
            REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);

        UpdatePacket();
    }
}

// PARTY_JOIN_BUG_FIX
/// ĆÄĆĽ °ˇŔÔ event Á¤ş¸
EVENTINFO(TPartyJoinEventInfo)
{
    DWORD    dwGuestPID;        ///< ĆÄĆĽżˇ Âüż©ÇŇ Äł¸ŻĹÍŔÇ PID
    DWORD    dwLeaderPID;        ///< ĆÄĆĽ ¸®´őŔÇ PID

    TPartyJoinEventInfo() 
    : dwGuestPID( 0 )
    , dwLeaderPID( 0 )
    {
    }
} ;

EVENTFUNC(party_request_event)
{
    TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(  event->info );

    if ( info == NULL )
    {
        sys_err( "party_request_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->dwGuestPID);

    if (ch)
    {
        sys_log(0, "PartyRequestEvent %s", ch->GetName());
        ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
        ch->SetPartyRequestEvent(NULL);
    }

    return 0;
}

bool CHARACTER::RequestToParty(LPCHARACTER leader)
{
    if (leader->GetParty())
        leader = leader->GetParty()->GetLeaderCharacter();

    if (!leader)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ĆÄĆĽŔĺŔĚ Á˘ĽÓ »óĹ°ˇ ľĆ´Ď¶óĽ­ żäĂ»Ŕ» ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
        return false;
    }

    if (m_pkPartyRequestEvent)
        return false; 

    if (!IsPC() || !leader->IsPC())
        return false;

    if (leader->IsBlockMode(BLOCK_PARTY_REQUEST))
        return false;

    PartyJoinErrCode errcode = IsPartyJoinableCondition(leader, this);

    switch (errcode)
    {
        case PERR_NONE:
            break;

        case PERR_SERVER:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> Ľ­ąö ą®Á¦·Î ĆÄĆĽ °ü·Ă Ăł¸®¸¦ ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return false;

        case PERR_DIFFEMPIRE:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´Ů¸Ą Á¦±ą°ú ĆÄĆĽ¸¦ ŔĚ·ę Ľö ľř˝Ŕ´Ď´Ů."));
            return false;

        case PERR_DUNGEON:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´řŔü ľČżˇĽ­´Â ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); 
            return false;

        case PERR_OBSERVER:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> °üŔü ¸đµĺżˇĽ± ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); 
            return false;

        case PERR_LVBOUNDARY:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> -30 ~ +30 ·ąş§ ŔĚł»ŔÇ »ó´ëąć¸¸ ĂĘ´ëÇŇ Ľö ŔÖ˝Ŕ´Ď´Ů.")); 
            return false;

        case PERR_LOWLEVEL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖ°í ·ąş§ ş¸´Ů 30·ąş§ŔĚ ł·ľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return false;

        case PERR_HILEVEL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖŔú ·ąş§ ş¸´Ů 30·ąş§ŔĚ łôľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); 
            return false;

        case PERR_ALREADYJOIN:     
            return false;

        case PERR_PARTYISFULL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´ő ŔĚ»ó ĆÄĆĽżřŔ» ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); 
            return false;

        default:
            sys_err("Do not process party join error(%d)", errcode); 
            return false;
    }

    TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();

    info->dwGuestPID = GetPlayerID();
    info->dwLeaderPID = leader->GetPlayerID();

    SetPartyRequestEvent(event_create(party_request_event, info, PASSES_PER_SEC(10)));

    leader->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequest %u", (DWORD) GetVID());
    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ´Ôżˇ°Ô ĆÄĆĽ°ˇŔÔ ˝ĹĂ»Ŕ» Çß˝Ŕ´Ď´Ů."), leader->GetName());
    return true;
}

void CHARACTER::DenyToParty(LPCHARACTER member)
{
    sys_log(1, "DenyToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));

    if (!member->m_pkPartyRequestEvent)
        return;

    TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);

    if (!info)
    {
        sys_err( "CHARACTER::DenyToParty> <Factor> Null pointer" );
        return;
    }

    if (info->dwGuestPID != member->GetPlayerID())
        return;

    if (info->dwLeaderPID != GetPlayerID())
        return;

    event_cancel(&member->m_pkPartyRequestEvent);

    member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
}

void CHARACTER::AcceptToParty(LPCHARACTER member)
{
    sys_log(1, "AcceptToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));

    if (!member->m_pkPartyRequestEvent)
        return;

    TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);

    if (!info)
    {
        sys_err( "CHARACTER::AcceptToParty> <Factor> Null pointer" );
        return;
    }

    if (info->dwGuestPID != member->GetPlayerID())
        return;

    if (info->dwLeaderPID != GetPlayerID())
        return;

    event_cancel(&member->m_pkPartyRequestEvent);

    if (!GetParty())
        member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ ĆÄĆĽżˇ ĽÓÇŘŔÖÁö ľĘ˝Ŕ´Ď´Ů."));
    else 
    {
        if (GetPlayerID() != GetParty()->GetLeaderPID())
            return;

        PartyJoinErrCode errcode = IsPartyJoinableCondition(this, member);
        switch (errcode) 
        {
            case PERR_NONE:         member->PartyJoin(this); return;
            case PERR_SERVER:        member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> Ľ­ąö ą®Á¦·Î ĆÄĆĽ °ü·Ă Ăł¸®¸¦ ÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); break;
            case PERR_DUNGEON:        member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´řŔü ľČżˇĽ­´Â ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); break;
            case PERR_OBSERVER:     member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> °üŔü ¸đµĺżˇĽ± ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); break;
            case PERR_LVBOUNDARY:    member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> -30 ~ +30 ·ąş§ ŔĚł»ŔÇ »ó´ëąć¸¸ ĂĘ´ëÇŇ Ľö ŔÖ˝Ŕ´Ď´Ů.")); break;
            case PERR_LOWLEVEL:     member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖ°í ·ąş§ ş¸´Ů 30·ąş§ŔĚ ł·ľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); break;
            case PERR_HILEVEL:         member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖŔú ·ąş§ ş¸´Ů 30·ąş§ŔĚ łôľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů.")); break;
            case PERR_ALREADYJOIN:     break;
            case PERR_PARTYISFULL: {
                                       ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´ő ŔĚ»ó ĆÄĆĽżřŔ» ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                                       member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽŔÇ ŔÎżřÁ¦ÇŃŔĚ ĂĘ°úÇĎż© ĆÄĆĽżˇ Âü°ˇÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                                       break;
                                   }
            default: sys_err("Do not process party join error(%d)", errcode);
        }
    }

    member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
}

/**
 * ĆÄĆĽ ĂĘ´ë event callback ÇÔĽö.
 * event °ˇ ąßµżÇϸé ĂĘ´ë °ĹŔý·Î Ăł¸®ÇŃ´Ů.
 */
EVENTFUNC(party_invite_event)
{
    TPartyJoinEventInfo * pInfo = dynamic_cast<TPartyJoinEventInfo *>(  event->info );

    if ( pInfo == NULL )
    {
        sys_err( "party_invite_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER pchInviter = CHARACTER_MANAGER::instance().FindByPID(pInfo->dwLeaderPID);

    if (pchInviter)
    {
        sys_log(1, "PartyInviteEvent %s", pchInviter->GetName());
        pchInviter->PartyInviteDeny(pInfo->dwGuestPID);
    }

    return 0;
}

void CHARACTER::PartyInvite(LPCHARACTER pchInvitee)
{
    if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽżřŔ» ĂĘ´ëÇŇ Ľö ŔÖ´Â ±ÇÇŃŔĚ ľř˝Ŕ´Ď´Ů."));
        return;
    }
    else if (pchInvitee->IsBlockMode(BLOCK_PARTY_INVITE))
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> %s ´ÔŔĚ ĆÄĆĽ °ĹşÎ »óĹÂŔÔ´Ď´Ů."), pchInvitee->GetName());
        return;
    }

    PartyJoinErrCode errcode = IsPartyJoinableCondition(this, pchInvitee);

    switch (errcode)
    {
        case PERR_NONE:
            break;

        case PERR_SERVER:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> Ľ­ąö ą®Á¦·Î ĆÄĆĽ °ü·Ă Ăł¸®¸¦ ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_DIFFEMPIRE:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´Ů¸Ą Á¦±ą°ú ĆÄĆĽ¸¦ ŔĚ·ę Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_DUNGEON:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´řŔü ľČżˇĽ­´Â ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_OBSERVER:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> °üŔü ¸đµĺżˇĽ± ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_LVBOUNDARY:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> -30 ~ +30 ·ąş§ ŔĚł»ŔÇ »ó´ëąć¸¸ ĂĘ´ëÇŇ Ľö ŔÖ˝Ŕ´Ď´Ů."));
            return;

        case PERR_LOWLEVEL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖ°í ·ąş§ ş¸´Ů 30·ąş§ŔĚ ł·ľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_HILEVEL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖŔú ·ąş§ ş¸´Ů 30·ąş§ŔĚ łôľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_ALREADYJOIN:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ŔĚąĚ %s´ÔŔş ĆÄĆĽżˇ ĽÓÇŘ ŔÖ˝Ŕ´Ď´Ů."), pchInvitee->GetName());
            return;

        case PERR_PARTYISFULL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´ő ŔĚ»ó ĆÄĆĽżřŔ» ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        default:
            sys_err("Do not process party join error(%d)", errcode);
            return;
    }

    if (m_PartyInviteEventMap.end() != m_PartyInviteEventMap.find(pchInvitee->GetPlayerID()))
        return;

    //
    // EventMap żˇ ŔĚşĄĆ® Ăß°ˇ
    // 
    TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();

    info->dwGuestPID = pchInvitee->GetPlayerID();
    info->dwLeaderPID = GetPlayerID();

    m_PartyInviteEventMap.insert(EventMap::value_type(pchInvitee->GetPlayerID(), event_create(party_invite_event, info, PASSES_PER_SEC(10))));

    //
    // ĂĘ´ë ąŢ´Â character żˇ°Ô ĂĘ´ë ĆĐŶ ŔüĽŰ
    // 

    TPacketGCPartyInvite p;
    p.header = HEADER_GC_PARTY_INVITE;
    p.leader_vid = GetVID();
    pchInvitee->GetDesc()->Packet(&p, sizeof(p));
}

void CHARACTER::PartyInviteAccept(LPCHARACTER pchInvitee)
{
    EventMap::iterator itFind = m_PartyInviteEventMap.find(pchInvitee->GetPlayerID());

    if (itFind == m_PartyInviteEventMap.end())
    {
        sys_log(1, "PartyInviteAccept from not invited character(%s)", pchInvitee->GetName());
        return;
    }

    event_cancel(&itFind->second);
    m_PartyInviteEventMap.erase(itFind);

    if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽżřŔ» ĂĘ´ëÇŇ Ľö ŔÖ´Â ±ÇÇŃŔĚ ľř˝Ŕ´Ď´Ů."));
        return;
    }

    PartyJoinErrCode errcode = IsPartyJoinableMutableCondition(this, pchInvitee);

    switch (errcode)
    {
        case PERR_NONE:
            break;

        case PERR_SERVER:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> Ľ­ąö ą®Á¦·Î ĆÄĆĽ °ü·Ă Ăł¸®¸¦ ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_DUNGEON:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´řŔü ľČżˇĽ­´Â ĆÄĆĽ Ăʴ뿡 ŔŔÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_OBSERVER:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> °üŔü ¸đµĺżˇĽ± ĆÄĆĽ Ăʴ븦 ÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_LVBOUNDARY:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> -30 ~ +30 ·ąş§ ŔĚł»ŔÇ »ó´ëąć¸¸ ĂĘ´ëÇŇ Ľö ŔÖ˝Ŕ´Ď´Ů."));
            return;

        case PERR_LOWLEVEL:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖ°í ·ąş§ ş¸´Ů 30·ąş§ŔĚ ł·ľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_HILEVEL:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽł» ĂÖŔú ·ąş§ ş¸´Ů 30·ąş§ŔĚ łôľĆ ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_ALREADYJOIN:
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽ Ăʴ뿡 ŔŔÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        case PERR_PARTYISFULL:
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ´ő ŔĚ»ó ĆÄĆĽżřŔ» ĂĘ´ëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> ĆÄĆĽŔÇ ŔÎżřÁ¦ÇŃŔĚ ĂĘ°úÇĎż© ĆÄĆĽżˇ Âü°ˇÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return;

        default:
            sys_err("ignore party join error(%d)", errcode);
            return;
    }

    //
    // ĆÄĆĽ °ˇŔÔ Ăł¸®
    // 

    if (GetParty())
        pchInvitee->PartyJoin(this);
    else
    {
        LPPARTY pParty = CPartyManager::instance().CreateParty(this);

        pParty->Join(pchInvitee->GetPlayerID());
        pParty->Link(pchInvitee);
        pParty->SendPartyInfoAllToOne(this);
    }
}

void CHARACTER::PartyInviteDeny(DWORD dwPID)
{
    EventMap::iterator itFind = m_PartyInviteEventMap.find(dwPID);

    if (itFind == m_PartyInviteEventMap.end())
    {
        sys_log(1, "PartyInviteDeny to not exist event(inviter PID: %d, invitee PID: %d)", GetPlayerID(), dwPID);
        return;
    }

    event_cancel(&itFind->second);
    m_PartyInviteEventMap.erase(itFind);

    LPCHARACTER pchInvitee = CHARACTER_MANAGER::instance().FindByPID(dwPID);
    if (pchInvitee)
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> %s´ÔŔĚ ĆÄĆĽ Ăʴ븦 °ĹŔýÇĎĽĚ˝Ŕ´Ď´Ů."), pchInvitee->GetName());
}

void CHARACTER::PartyJoin(LPCHARACTER pLeader)
{
    pLeader->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> %s´ÔŔĚ ĆÄĆĽżˇ Âü°ˇÇĎĽĚ˝Ŕ´Ď´Ů."), GetName());
    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ĆÄĆĽ> %s´ÔŔÇ ĆÄĆĽżˇ Âü°ˇÇĎĽĚ˝Ŕ´Ď´Ů."), pLeader->GetName());

    pLeader->GetParty()->Join(GetPlayerID());
    pLeader->GetParty()->Link(this);
}

CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
{
    if (pchLeader->GetEmpire() != pchGuest->GetEmpire())
        return PERR_DIFFEMPIRE;

    return IsPartyJoinableMutableCondition(pchLeader, pchGuest);
}

static bool __party_can_join_by_level(LPCHARACTER leader, LPCHARACTER quest)
{
    int    level_limit = 30;

    if (LC_IsCanada())
        level_limit = 15;
    else if (LC_IsBrazil() == true)
    {
        level_limit = 10;
    }
    else
        level_limit = 30;

    return (abs(leader->GetLevel() - quest->GetLevel()) <= level_limit);
}

CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
{
    if (!CPartyManager::instance().IsEnablePCParty())
        return PERR_SERVER;
    else if (pchLeader->GetDungeon())
        return PERR_DUNGEON;
    else if (pchGuest->IsObserverMode())
        return PERR_OBSERVER;
    else if (false == __party_can_join_by_level(pchLeader, pchGuest))
        return PERR_LVBOUNDARY;
    else if (pchGuest->GetParty())
        return PERR_ALREADYJOIN;
    else if (pchLeader->GetParty())
       {
           if (pchLeader->GetParty()->GetMemberCount() == PARTY_MAX_MEMBER)
            return PERR_PARTYISFULL;
    }

    return PERR_NONE;
}
// END_OF_PARTY_JOIN_BUG_FIX

void CHARACTER::SetDungeon(LPDUNGEON pkDungeon)
{
    if (pkDungeon && m_pkDungeon)
        sys_err("%s is trying to reassigning dungeon (current %p, new party %p)", GetName(), get_pointer(m_pkDungeon), get_pointer(pkDungeon));

    if (m_pkDungeon == pkDungeon) {
        return;
    }

    if (m_pkDungeon)
    {
        if (IsPC())
        {
            if (GetParty())
                m_pkDungeon->DecPartyMember(GetParty(), this);
            else
                m_pkDungeon->DecMember(this);
        }
        else if (IsMonster() || IsStone())
        {
            m_pkDungeon->DecMonster();
        }
    }

    m_pkDungeon = pkDungeon;

    if (pkDungeon)
    {
        sys_log(0, "%s DUNGEON set to %p, PARTY is %p", GetName(), get_pointer(pkDungeon), get_pointer(m_pkParty));

        if (IsPC())
        {
            if (GetParty())
                m_pkDungeon->IncPartyMember(GetParty(), this);
            else
                m_pkDungeon->IncMember(this);
        }
        else if (IsMonster() || IsStone())
        {
            m_pkDungeon->IncMonster();
        }
    }
}

void CHARACTER::SetWarMap(CWarMap * pWarMap)
{
    if (m_pWarMap)
        m_pWarMap->DecMember(this);

    m_pWarMap = pWarMap;

    if (m_pWarMap)
        m_pWarMap->IncMember(this);
}

void CHARACTER::SetWeddingMap(marriage::WeddingMap* pMap)
{
    if (m_pWeddingMap)
        m_pWeddingMap->DecMember(this);

    m_pWeddingMap = pMap;

    if (m_pWeddingMap)
        m_pWeddingMap->IncMember(this);
}

void CHARACTER::SetRegen(LPREGEN pkRegen)
{
    m_pkRegen = pkRegen;
    if (pkRegen != NULL) {
        regen_id_ = pkRegen->id;
    }
    m_fRegenAngle = GetRotation();
    m_posRegen = GetXYZ();
}

bool CHARACTER::OnIdle()
{
    return false;
}

void CHARACTER::OnMove(bool bIsAttack)
{
    m_dwLastMoveTime = get_dword_time();

    if (bIsAttack)
    {
        m_dwLastAttackTime = m_dwLastMoveTime;

        if (IsAffectFlag(AFF_REVIVE_INVISIBLE))
            RemoveAffect(AFFECT_REVIVE_INVISIBLE);

        if (IsAffectFlag(AFF_EUNHYUNG))
        {
            RemoveAffect(SKILL_EUNHYUNG);
            SetAffectedEunhyung();
        }
        else
        {
            ClearAffectedEunhyung();
        }

        /*if (IsAffectFlag(AFF_JEONSIN))
          RemoveAffect(SKILL_JEONSINBANGEO);*/
    }

    /*if (IsAffectFlag(AFF_GUNGON))
      RemoveAffect(SKILL_GUNGON);*/

    // MINING
    mining_cancel();
    // END_OF_MINING
}

void CHARACTER::OnClick(LPCHARACTER pkChrCauser)
{
    if (!pkChrCauser)
    {
        sys_err("OnClick %s by NULL", GetName());
        return;
    }

    DWORD vid = GetVID();
    sys_log(0, "OnClick %s[vnum %d ServerUniqueID %d, pid %d] by %s", GetName(), GetRaceNum(), vid, GetPlayerID(), pkChrCauser->GetName());

    // »óÁˇŔ» ż¬»óĹ·ΠÄů˝şĆ®¸¦ ÁřÇŕÇŇ Ľö ľř´Ů.
    {
        // ´Ü, ŔÚ˝ĹŔş ŔÚ˝ĹŔÇ »óÁˇŔ» Ŭ¸ŻÇŇ Ľö ŔÖ´Ů.
        if (pkChrCauser->GetMyShop() && pkChrCauser != this) 
        {
            sys_err("OnClick Fail (%s->%s) - pc has shop", pkChrCauser->GetName(), GetName());
            return;
        }
    }

    // ±łČŻÁßŔ϶§ Äů˝şĆ®¸¦ ÁřÇŕÇŇ Ľö ľř´Ů.
    {
        if (pkChrCauser->GetExchange())
        {
            sys_err("OnClick Fail (%s->%s) - pc is exchanging", pkChrCauser->GetName(), GetName());
            return;
        }
    }

    if (IsPC())
    {
        // Ÿ°ŮŔ¸·Î ĽłÁ¤µČ °ćżě´Â PCżˇ ŔÇÇŃ Ĺ¬¸Żµµ Äů˝şĆ®·Î Ăł¸®Çϵµ·Ď ÇŐ´Ď´Ů.
        if (!CTargetManager::instance().GetTargetInfo(pkChrCauser->GetPlayerID(), TARGET_TYPE_VID, GetVID()))
        {
            // 2005.03.17.myevan.Ÿ°ŮŔĚ ľĆ´Ń °ćżě´Â °łŔÎ »óÁˇ Ăł¸® ±â´ÉŔ» Ŕ۵ż˝ĂŲ´Ů.
            if (GetMyShop())
            {
                if (pkChrCauser->IsDead() == true) return;

                //PREVENT_TRADE_WINDOW
                if (pkChrCauser == this) // ŔÚ±â´Â °ˇ´É
                {
                    if ((GetExchange() || IsOpenSafebox() || GetShopOwner()) || IsCubeOpen())
                    {
                        pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("´Ů¸Ą °Ĺ·ˇÁß(â°í,±łČŻ,»óÁˇ)żˇ´Â °łŔλóÁˇŔ» »çżëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                        return;
                    }
                }
                else // ´Ů¸Ą »ç¶÷ŔĚ Ĺ¬¸ŻÇßŔ»¶§
                {
                    // Ŭ¸ŻÇŃ »ç¶÷ŔĚ ±łČŻ/â°í/°łŔλóÁˇ/»óÁˇŔĚżëÁßŔ̶ó¸é şŇ°ˇ
                    if ((pkChrCauser->GetExchange() || pkChrCauser->IsOpenSafebox() || pkChrCauser->GetMyShop() || pkChrCauser->GetShopOwner()) || pkChrCauser->IsCubeOpen() )
                    {
                        pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("´Ů¸Ą °Ĺ·ˇÁß(â°í,±łČŻ,»óÁˇ)żˇ´Â °łŔλóÁˇŔ» »çżëÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                        return;
                    }

                    // Ŭ¸ŻÇŃ ´ë»óŔĚ ±łČŻ/â°í/»óÁˇŔĚżëÁßŔ̶ó¸é şŇ°ˇ
                    //if ((GetExchange() || IsOpenSafebox() || GetShopOwner()))
                    if ((GetExchange() || IsOpenSafebox() || IsCubeOpen()))
                    {
                        pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ ´Ů¸Ą °Ĺ·ˇ¸¦ ÇĎ°í ŔÖ´Â ÁßŔÔ´Ď´Ů."));
                        return;
                    }
                }
                //END_PREVENT_TRADE_WINDOW

                if (pkChrCauser->GetShop())
                {
                    pkChrCauser->GetShop()->RemoveGuest(pkChrCauser);
                    pkChrCauser->SetShop(NULL);
                }

                GetMyShop()->AddGuest(pkChrCauser, GetVID(), false);
                pkChrCauser->SetShopOwner(this);
                return;
            }

            if (test_server)
                sys_err("%s.OnClickFailure(%s) - target is PC", pkChrCauser->GetName(), GetName());

            return;
        }
    }

    // Ă»ĽŇłâŔş Äů˝şĆ® ¸řÇÔ
    if (LC_IsNewCIBN())
    {
        if (pkChrCauser->IsOverTime(OT_3HOUR))
        {
            sys_log(0, "Teen OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 3);
            return;
        }
        else if (pkChrCauser->IsOverTime(OT_5HOUR))
        {
            sys_log(0, "Teen OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 5);
            return;
        }
    }


    pkChrCauser->SetQuestNPCID(GetVID());

    if (quest::CQuestManager::instance().Click(pkChrCauser->GetPlayerID(), this))
    {
        return;
    }


    // NPC Ŕüżë ±â´É ĽöÇŕ : »óÁˇ ż­±â µî
    if (!IsPC())
    {
        if (!m_triggerOnClick.pFunc)
        {
            // NPC Ć®¸®°Ĺ ˝Ă˝şĹŰ ·Î±× ş¸±â
            //sys_err("%s.OnClickFailure(%s) : triggerOnClick.pFunc is EMPTY(pid=%d)", 
            //            pkChrCauser->GetName(),
            //            GetName(),
            //            pkChrCauser->GetPlayerID());
            return;
        }

        m_triggerOnClick.pFunc(this, pkChrCauser);
    }

}

BYTE CHARACTER::GetGMLevel() const
{
    if (test_server)
        return GM_IMPLEMENTOR;
    return m_pointsInstant.gm_level;
}

void CHARACTER::SetGMLevel()
{
    if (GetDesc())
    {
        m_pointsInstant.gm_level =  gm_get_level(GetName(), GetDesc()->GetHostName(), GetDesc()->GetAccountTable().login);
    }
    else
    {
        m_pointsInstant.gm_level = GM_PLAYER;
    }
}

BOOL CHARACTER::IsGM() const
{
    if (m_pointsInstant.gm_level != GM_PLAYER)
        return true;
    if (test_server)
        return true;
    return false;
}

void CHARACTER::SetStone(LPCHARACTER pkChrStone)
{
    m_pkChrStone = pkChrStone;

    if (m_pkChrStone)
    {
        if (pkChrStone->m_set_pkChrSpawnedBy.find(this) == pkChrStone->m_set_pkChrSpawnedBy.end())
            pkChrStone->m_set_pkChrSpawnedBy.insert(this);
    }
}

struct FuncDeadSpawnedByStone
{
    void operator () (LPCHARACTER ch)
    {
        ch->Dead(NULL);
        ch->SetStone(NULL);
    }
};

void CHARACTER::ClearStone()
{
    if (!m_set_pkChrSpawnedBy.empty())
    {
        // ł»°ˇ ˝şĆů˝ĂŲ ¸ó˝şĹ͵éŔ» ¸đµÎ Á×ŔδŮ.
        FuncDeadSpawnedByStone f;
        std::for_each(m_set_pkChrSpawnedBy.begin(), m_set_pkChrSpawnedBy.end(), f);
        m_set_pkChrSpawnedBy.clear();
    }

    if (!m_pkChrStone)
        return;

    m_pkChrStone->m_set_pkChrSpawnedBy.erase(this);
    m_pkChrStone = NULL;
}

void CHARACTER::ClearTarget()
{
    if (m_pkChrTarget)
    {
        m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
        m_pkChrTarget = NULL;
    }

    TPacketGCTarget p;

    p.header = HEADER_GC_TARGET;
    p.dwVID = 0;
    p.bHPPercent = 0;

    CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();

    while (it != m_set_pkChrTargetedBy.end())
    {
        LPCHARACTER pkChr = *(it++);
        pkChr->m_pkChrTarget = NULL;

        if (!pkChr->GetDesc())
        {
            sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
            abort();
        }

        pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
    }

    m_set_pkChrTargetedBy.clear();
}

void CHARACTER::SetTarget(LPCHARACTER pkChrTarget)
{
    if (m_pkChrTarget == pkChrTarget)
        return;

    // CASTLE
    if (IS_CASTLE_MAP(GetMapIndex()) && !IsGM())
        return;
    // CASTLE

    if (m_pkChrTarget)
        m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);

    m_pkChrTarget = pkChrTarget;

    TPacketGCTarget p;

    p.header = HEADER_GC_TARGET;

    if (m_pkChrTarget)
    {
        m_pkChrTarget->m_set_pkChrTargetedBy.insert(this);

        p.dwVID    = m_pkChrTarget->GetVID();

        if (m_pkChrTarget->IsPC() && !m_pkChrTarget->IsPolymorphed() || m_pkChrTarget->GetMaxHP() <= 0)
            p.bHPPercent = 0;
        else 
        {
            if (m_pkChrTarget->GetRaceNum() == 20101 ||
                    m_pkChrTarget->GetRaceNum() == 20102 ||
                    m_pkChrTarget->GetRaceNum() == 20103 ||
                    m_pkChrTarget->GetRaceNum() == 20104 ||
                    m_pkChrTarget->GetRaceNum() == 20105 ||
                    m_pkChrTarget->GetRaceNum() == 20106 ||
                    m_pkChrTarget->GetRaceNum() == 20107 ||
                    m_pkChrTarget->GetRaceNum() == 20108 ||
                    m_pkChrTarget->GetRaceNum() == 20109)
            {
                LPCHARACTER owner = m_pkChrTarget->GetVictim();

                if (owner)
                {
                    int iHorseHealth = owner->GetHorseHealth();
                    int iHorseMaxHealth = owner->GetHorseMaxHealth();

                    if (iHorseMaxHealth)
                        p.bHPPercent = MINMAX(0,  iHorseHealth * 100 / iHorseMaxHealth, 100);
                    else
                        p.bHPPercent = 100;
                }
                else
                    p.bHPPercent = 100;
            }
            else
                p.bHPPercent = MINMAX(0, (m_pkChrTarget->GetHP() * 100) / m_pkChrTarget->GetMaxHP(), 100);
        }
    }
    else
    {
        p.dwVID = 0;
        p.bHPPercent = 0;
    }

    GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
}

void CHARACTER::BroadcastTargetPacket()
{
    if (m_set_pkChrTargetedBy.empty())
        return;

    TPacketGCTarget p;

    p.header = HEADER_GC_TARGET;
    p.dwVID = GetVID();

    if (IsPC())
        p.bHPPercent = 0;
    else
        p.bHPPercent = MINMAX(0, (GetHP() * 100) / GetMaxHP(), 100);

    CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();

    while (it != m_set_pkChrTargetedBy.end())
    {
        LPCHARACTER pkChr = *it++;

        if (!pkChr->GetDesc())
        {
            sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
            abort();
        }

        pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
    }
}

void CHARACTER::CheckTarget()
{
    if (!m_pkChrTarget)
        return;

    if (DISTANCE_APPROX(GetX() - m_pkChrTarget->GetX(), GetY() - m_pkChrTarget->GetY()) >= 4800)
        SetTarget(NULL);
}

void CHARACTER::SetWarpLocation(long lMapIndex, long x, long y)
{
    m_posWarp.x = x * 100;
    m_posWarp.y = y * 100;
    m_lWarpMapIndex = lMapIndex;
}

void CHARACTER::SaveExitLocation()
{
    m_posExit = GetXYZ();
    m_lExitMapIndex = GetMapIndex();
}

void CHARACTER::ExitToSavedLocation()
{
    sys_log (0, "ExitToSavedLocation");
    WarpSet(m_posWarp.x, m_posWarp.y, m_lWarpMapIndex);

    m_posExit.x = m_posExit.y = m_posExit.z = 0;
    m_lExitMapIndex = 0;
}

// fixme 
// Áö±Ý±îÁř privateMapIndex °ˇ ÇöŔç ¸Ę Ŕε¦˝şżÍ °°ŔşÁö ĂĽĹ© ÇĎ´Â °ÍŔ» żÜşÎżˇĽ­ ÇĎ°í,
// ´Ů¸Ł¸é warpsetŔ» şŇ·¶´ÂµĄ
// Ŕ̸¦ warpset ľČŔ¸·Î łÖŔÚ.
bool CHARACTER::WarpSet(long x, long y, long lPrivateMapIndex)
{
    if (!IsPC())
        return false;

    long lAddr;
    long lMapIndex;
    WORD wPort;

    if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort))
    {
        sys_err("cannot find map location index %d x %d y %d name %s", lMapIndex, x, y, GetName());
        return false;
    }

    //Send Supplementary Data Block if new map requires security packages in loading this map
    {
        long lCurAddr;
        long lCurMapIndex = 0;
        WORD wCurPort;

        CMapLocation::instance().Get(GetX(), GetY(), lCurMapIndex, lCurAddr, wCurPort);

        //do not send SDB files if char is in the same map
        if( lCurMapIndex != lMapIndex )
        {
            const TMapRegion * rMapRgn = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex);
            {
                DESC_MANAGER::instance().SendClientPackageSDBToLoadMap( GetDesc(), rMapRgn->strMapName.c_str() );    
            }
        }
    }

    if (lPrivateMapIndex >= 10000)
    {
        if (lPrivateMapIndex / 10000 != lMapIndex)
        {
            sys_err("Invalid map inedx %d, must be child of %d", lPrivateMapIndex, lMapIndex);
            return false;
        }

        lMapIndex = lPrivateMapIndex;
    }

    Stop();
    Save();

    if (GetSectree())
    {
        GetSectree()->RemoveEntity(this);
        ViewCleanup();

        EncodeRemovePacket(this);
    }

    m_lWarpMapIndex = lMapIndex;
    m_posWarp.x = x;
    m_posWarp.y = y;

    sys_log(0, "WarpSet %s %d %d current map %d target map %d", GetName(), x, y, GetMapIndex(), lMapIndex);

    TPacketGCWarp p;

    p.bHeader    = HEADER_GC_WARP;
    p.lX    = x;
    p.lY    = y;
    p.lAddr    = lAddr;
    p.wPort    = wPort;

    GetDesc()->Packet(&p, sizeof(TPacketGCWarp));

    //if (!LC_IsNewCIBN())
    {
        char buf[256];
        snprintf(buf, sizeof(buf), "%s MapIdx %ld DestMapIdx%ld DestX%ld DestY%ld Empire%d", GetName(), GetMapIndex(), lPrivateMapIndex, x, y, GetEmpire());
        LogManager::instance().CharLog(this, 0, "WARP", buf);
    }

    return true;
}

void CHARACTER::WarpEnd()
{
    if (test_server)
        sys_log(0, "WarpEnd %s", GetName());

    if (m_posWarp.x == 0 && m_posWarp.y == 0)
        return;

    int index = m_lWarpMapIndex;

    if (index > 10000)
        index /= 10000;

    if (!map_allow_find(index))
    {
        // ŔĚ °÷Ŕ¸·Î żöÇÁÇŇ Ľö ľřŔ¸ąÇ·Î żöÇÁÇϱâ Ŕü ÁÂÇĄ·Î µÇµą¸®ŔÚ.
        sys_err("location %d %d not allowed to login this server", m_posWarp.x, m_posWarp.y);
        GetDesc()->SetPhase(PHASE_CLOSE);
        return;
    }

    sys_log(0, "WarpEnd %s %d %u %u", GetName(), m_lWarpMapIndex, m_posWarp.x, m_posWarp.y);

    Show(m_lWarpMapIndex, m_posWarp.x, m_posWarp.y, 0);
    Stop();

    m_lWarpMapIndex = 0;
    m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;

    {
        // P2P Login
        TPacketGGLogin p;

        p.bHeader = HEADER_GG_LOGIN;
        strlcpy(p.szName, GetName(), sizeof(p.szName));
        p.dwPID = GetPlayerID();
        p.bEmpire = GetEmpire();
        p.lMapIndex = SECTREE_MANAGER::instance().GetMapIndex(GetX(), GetY());
        p.bChannel = g_bChannel;

        P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogin));
    }
}

bool CHARACTER::Return()
{
    if (!IsNPC())
        return false;

    int x, y;
    /*
       float fDist = DISTANCE_SQRT(m_pkMobData->m_posLastAttacked.x - GetX(), m_pkMobData->m_posLastAttacked.y - GetY());
       float fx, fy;
       GetDeltaByDegree(GetRotation(), fDist, &fx, &fy);
       x = GetX() + (int) fx;
       y = GetY() + (int) fy;
     */
    SetVictim(NULL);

    x = m_pkMobInst->m_posLastAttacked.x;
    y = m_pkMobInst->m_posLastAttacked.y;

    SetRotationToXY(x, y);

    if (!Goto(x, y))
        return false;

    SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);

    if (test_server)
        sys_log(0, "%s %p Ć÷±âÇĎ°í µąľĆ°ˇŔÚ! %d %d", GetName(), this, x, y);

    if (GetParty())
        GetParty()->SendMessage(this, PM_RETURN, x, y);

    return true;
}

bool CHARACTER::Follow(LPCHARACTER pkChr, float fMinDistance)
{
    if (IsPC())
    {
        sys_err("CHARACTER::Follow : PC cannot use this method", GetName());
        return false;
    }

    // TRENT_MONSTER
    if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
    {
        if (pkChr->IsPC()) // ÂѾưˇ´Â »ó´ë°ˇ PCŔĎ ¶§
        {
            // If i'm in a party. I must obey party leader's AI.
            if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
            {
                if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // ¸¶Áö¸·Ŕ¸·Î °ř°ÝąŢŔşÁö 15ĂĘ°ˇ Áöłµ°í
                {
                    // ¸¶Áö¸· ¸ÂŔş °÷Ŕ¸·Î şÎĹÍ 50ąĚĹÍ ŔĚ»ó Â÷ŔĚłŞ¸é Ć÷±âÇĎ°í µąľĆ°Ł´Ů.
                    if (m_pkMobData->m_table.wAttackRange < DISTANCE_APPROX(pkChr->GetX() - GetX(), pkChr->GetY() - GetY()))
                        if (Return())
                            return true;
                }
            }
        }
        return false;
    }
    // END_OF_TRENT_MONSTER

    long x = pkChr->GetX();
    long y = pkChr->GetY();

    if (pkChr->IsPC()) // ÂѾưˇ´Â »ó´ë°ˇ PCŔĎ ¶§
    {
        // If i'm in a party. I must obey party leader's AI.
        if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
        {
            if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // ¸¶Áö¸·Ŕ¸·Î °ř°ÝąŢŔşÁö 15ĂĘ°ˇ Áöłµ°í
            {
                // ¸¶Áö¸· ¸ÂŔş °÷Ŕ¸·Î şÎĹÍ 50ąĚĹÍ ŔĚ»ó Â÷ŔĚłŞ¸é Ć÷±âÇĎ°í µąľĆ°Ł´Ů.
                if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
                    if (Return())
                        return true;
            }
        }
    }

    if (IsGuardNPC())
    {
        if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
            if (Return())
                return true;
    }

    if (pkChr->IsState(pkChr->m_stateMove) && 
        GetMobBattleType() != BATTLE_TYPE_RANGE && 
        GetMobBattleType() != BATTLE_TYPE_MAGIC &&
        false == IsPet())
    {
        // ´ë»óŔĚ Ŕ̵żÁßŔĚ¸é żąĂř Ŕ̵żŔ» ÇŃ´Ů
        // łŞżÍ »ó´ëąćŔÇ ĽÓµµÂ÷żÍ °Ĺ¸®·ÎşÎĹÍ ¸¸łŻ ˝Ă°ŁŔ» żą»óÇŃ ČÄ
        // »ó´ëąćŔĚ ±× ˝Ă°Ł±îÁö Á÷Ľ±Ŕ¸·Î Ŕ̵żÇŃ´Ů°í °ˇÁ¤ÇĎż© °Ĺ±â·Î Ŕ̵żÇŃ´Ů.
        float rot = pkChr->GetRotation();
        float rot_delta = GetDegreeDelta(rot, GetDegreeFromPositionXY(GetX(), GetY(), pkChr->GetX(), pkChr->GetY()));

        float yourSpeed = pkChr->GetMoveSpeed();
        float mySpeed = GetMoveSpeed();

        float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
        float fFollowSpeed = mySpeed - yourSpeed * cos(rot_delta * M_PI / 180);

        if (fFollowSpeed >= 0.1f)
        {
            float fMeetTime = fDist / fFollowSpeed;
            float fYourMoveEstimateX, fYourMoveEstimateY;

            if( fMeetTime * yourSpeed <= 100000.0f )
            {
                GetDeltaByDegree(pkChr->GetRotation(), fMeetTime * yourSpeed, &fYourMoveEstimateX, &fYourMoveEstimateY);

                x += (long) fYourMoveEstimateX;
                y += (long) fYourMoveEstimateY;

                float fDistNew = sqrt(((double)x - GetX())*(x-GetX())+((double)y - GetY())*(y-GetY()));
                if (fDist < fDistNew)
                {
                    x = (long)(GetX() + (x - GetX()) * fDist / fDistNew);
                    y = (long)(GetY() + (y - GetY()) * fDist / fDistNew);
                }
            }
        }
    }

    // °ˇ·Á´Â Ŕ§Äˇ¸¦ ąŮ¶óşÁľß ÇŃ´Ů.
    SetRotationToXY(x, y);

    float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());

    if (fDist <= fMinDistance)
        return false;

    float fx, fy;

    if (IsChangeAttackPosition(pkChr) && GetMobRank() < MOB_RANK_BOSS)
    {
        // »ó´ëąć ÁÖşŻ ·Ł´ýÇŃ °÷Ŕ¸·Î Ŕ̵ż
        SetChangeAttackPositionTime();

        int retry = 16;
        int dx, dy;
        int rot = (int) GetDegreeFromPositionXY(x, y, GetX(), GetY());

        while (--retry)
        {
            if (fDist < 500.0f)
                GetDeltaByDegree((rot + number(-90, 90) + number(-90, 90)) % 360, fMinDistance, &fx, &fy);
            else
                GetDeltaByDegree(number(0, 359), fMinDistance, &fx, &fy);

            dx = x + (int) fx;
            dy = y + (int) fy;

            LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), dx, dy);

            if (NULL == tree)
                break;

            if (0 == (tree->GetAttribute(dx, dy) & (ATTR_BLOCK | ATTR_OBJECT)))
                break;
        }

        //sys_log(0, "±ŮĂł ľîµň°ˇ·Î Ŕ̵ż %s retry %d", GetName(), retry);
        if (!Goto(dx, dy))
            return false;
    }
    else
    {
        // Á÷Ľ± µű¶ó°ˇ±â
        float fDistToGo = fDist - fMinDistance;
        GetDeltaByDegree(GetRotation(), fDistToGo, &fx, &fy);

        //sys_log(0, "Á÷Ľ±Ŕ¸·Î Ŕ̵ż %s", GetName());
        if (!Goto(GetX() + (int) fx, GetY() + (int) fy))
            return false;
    }

    SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
    //MonsterLog("ÂѾưˇ±â; %s", pkChr->GetName());
    return true;
}

float CHARACTER::GetDistanceFromSafeboxOpen() const
{
    return DISTANCE_APPROX(GetX() - m_posSafeboxOpen.x, GetY() - m_posSafeboxOpen.y);
}

void CHARACTER::SetSafeboxOpenPosition()
{
    m_posSafeboxOpen = GetXYZ();
}

CSafebox * CHARACTER::GetSafebox() const
{
    return m_pkSafebox;
}

void CHARACTER::ReqSafeboxLoad(const char* pszPassword)
{
    if (!*pszPassword || strlen(pszPassword) > SAFEBOX_PASSWORD_MAX_LEN)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â°í> Ŕ߸řµČ ľĎČŁ¸¦ ŔÔ·ÂÇĎĽĚ˝Ŕ´Ď´Ů."));
        return;
    }
    else if (m_pkSafebox)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â°í> â°í°ˇ ŔĚąĚ ż­·ÁŔÖ˝Ŕ´Ď´Ů."));
        return;
    }

    int iPulse = thecore_pulse();

    if (iPulse - GetSafeboxLoadTime()  < PASSES_PER_SEC(10))
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â°í> â°í¸¦ ´ÝŔşÁö 10ĂĘ ľČżˇ´Â ż­ Ľö ľř˝Ŕ´Ď´Ů."));
        return;
    }
    else if (GetDistanceFromSafeboxOpen() > 1000)
    {
        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<â°í> °Ĺ¸®°ˇ ¸ÖľîĽ­ â°í¸¦ ż­ Ľö ľř˝Ŕ´Ď´Ů."));
        return;
    }
    else if (m_bOpeningSafebox)
    {
        sys_log(0, "Overlapped safebox load request from %s", GetName());
        return;
    }

    SetSafeboxLoadTime();
    m_bOpeningSafebox = true;

    TSafeboxLoadPacket p;
    p.dwID = GetDesc()->GetAccountTable().id;
    strlcpy(p.szLogin, GetDesc()->GetAccountTable().login, sizeof(p.szLogin));
    strlcpy(p.szPassword, pszPassword, sizeof(p.szPassword));

    db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_LOAD, GetDesc()->GetHandle(), &p, sizeof(p));
}

void CHARACTER::LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems)
{
    bool bLoaded = false;

    //PREVENT_TRADE_WINDOW
    SetOpenSafebox(true);
    //END_PREVENT_TRADE_WINDOW

    if (m_pkSafebox)
        bLoaded = true;

    if (!m_pkSafebox)
        m_pkSafebox = M2_NEW CSafebox(this, iSize, dwGold);
    else
        m_pkSafebox->ChangeSize(iSize);

    m_iSafeboxSize = iSize;

    TPacketCGSafeboxSize p;

    p.bHeader = HEADER_GC_SAFEBOX_SIZE;
    p.bSize = iSize;

    GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));

    if (!bLoaded)
    {
        for (int i = 0; i < iItemCount; ++i, ++pItems)
        {
            if (!m_pkSafebox->IsValidPosition(pItems->pos))
                continue;

            LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);

            if (!item)
            {
                sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
                continue;
            }

            item->SetSkipSave(true);
            item->SetSockets(pItems->alSockets);
            item->SetAttributes(pItems->aAttr);

            if (!m_pkSafebox->Add(pItems->pos, item))
            {
                M2_DESTROY_ITEM(item);
            }
            else
                item->SetSkipSave(false);
        }
    }
}

void CHARACTER::ChangeSafeboxSize(BYTE bSize)
{
    //if (!m_pkSafebox)
    //return;

    TPacketCGSafeboxSize p;

    p.bHeader = HEADER_GC_SAFEBOX_SIZE;
    p.bSize = bSize;

    GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));

    if (m_pkSafebox)
        m_pkSafebox->ChangeSize(bSize);

    m_iSafeboxSize = bSize;
}

void CHARACTER::CloseSafebox()
{
    if (!m_pkSafebox)
        return;

    //PREVENT_TRADE_WINDOW
    SetOpenSafebox(false);
    //END_PREVENT_TRADE_WINDOW

    m_pkSafebox->Save();

    M2_DELETE(m_pkSafebox);
    m_pkSafebox = NULL;

    ChatPacket(CHAT_TYPE_COMMAND, "CloseSafebox");

    SetSafeboxLoadTime();
    m_bOpeningSafebox = false;

    Save();
}

CSafebox * CHARACTER::GetMall() const
{
    return m_pkMall;
}

void CHARACTER::LoadMall(int iItemCount, TPlayerItem * pItems)
{
    bool bLoaded = false;

    if (m_pkMall)
        bLoaded = true;

    if (!m_pkMall)
        m_pkMall = M2_NEW CSafebox(this, 3 * SAFEBOX_PAGE_SIZE, 0);
    else
        m_pkMall->ChangeSize(3 * SAFEBOX_PAGE_SIZE);

    m_pkMall->SetWindowMode(MALL);

    TPacketCGSafeboxSize p;

    p.bHeader = HEADER_GC_MALL_OPEN;
    p.bSize = 3 * SAFEBOX_PAGE_SIZE;

    GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));

    if (!bLoaded)
    {
        for (int i = 0; i < iItemCount; ++i, ++pItems)
        {
            if (!m_pkMall->IsValidPosition(pItems->pos))
                continue;

            LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);

            if (!item)
            {
                sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
                continue;
            }

            item->SetSkipSave(true);
            item->SetSockets(pItems->alSockets);
            item->SetAttributes(pItems->aAttr);

            if (!m_pkMall->Add(pItems->pos, item))
                M2_DESTROY_ITEM(item);
            else
                item->SetSkipSave(false);
        }
    }
}

void CHARACTER::CloseMall()
{
    if (!m_pkMall)
        return;

    m_pkMall->Save();

    M2_DELETE(m_pkMall);
    m_pkMall = NULL;

    ChatPacket(CHAT_TYPE_COMMAND, "CloseMall");
}

bool CHARACTER::BuildUpdatePartyPacket(TPacketGCPartyUpdate & out)
{
    if (!GetParty())
        return false;

    memset(&out, 0, sizeof(out));

    out.header        = HEADER_GC_PARTY_UPDATE;
    out.pid        = GetPlayerID();
    out.percent_hp    = MINMAX(0, GetHP() * 100 / GetMaxHP(), 100);
    out.role        = GetParty()->GetRole(GetPlayerID());

    sys_log(1, "PARTY %s role is %d", GetName(), out.role);

    LPCHARACTER l = GetParty()->GetLeaderCharacter();

    if (l && DISTANCE_APPROX(GetX() - l->GetX(), GetY() - l->GetY()) < PARTY_DEFAULT_RANGE)
    {
        if (g_iUseLocale)
            out.affects[0] = GetParty()->GetPartyBonusExpPercent();
        else
            out.affects[0] = GetParty()->GetExpBonusPercent();
        out.affects[1] = GetPoint(POINT_PARTY_ATTACKER_BONUS);
        out.affects[2] = GetPoint(POINT_PARTY_TANKER_BONUS);
        out.affects[3] = GetPoint(POINT_PARTY_BUFFER_BONUS);
        out.affects[4] = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
        out.affects[5] = GetPoint(POINT_PARTY_HASTE_BONUS);
        out.affects[6] = GetPoint(POINT_PARTY_DEFENDER_BONUS);
    }

    return true;
}

int CHARACTER::GetLeadershipSkillLevel() const

    return GetSkillLevel(SKILL_LEADERSHIP);
}

void CHARACTER::QuerySafeboxSize()
{
    if (m_iSafeboxSize == -1)
    {
        DBManager::instance().ReturnQuery(QID_SAFEBOX_SIZE,
                GetPlayerID(),
                NULL, 
                "SELECT size FROM safebox%s WHERE account_id = %u",
                get_table_postfix(),
                GetDesc()->GetAccountTable().id);
    }
}

void CHARACTER::SetSafeboxSize(int iSize)
{
    sys_log(1, "SetSafeboxSize: %s %d", GetName(), iSize);
    m_iSafeboxSize = iSize;
    DBManager::instance().Query("UPDATE safebox%s SET size = %d WHERE account_id = %u", get_table_postfix(), iSize / SAFEBOX_PAGE_SIZE, GetDesc()->GetAccountTable().id);
}

int CHARACTER::GetSafeboxSize() const
{
    return m_iSafeboxSize;
}

void CHARACTER::SetNowWalking(bool bWalkFlag)
{
    //if (m_bNowWalking != bWalkFlag || IsNPC())
    if (m_bNowWalking != bWalkFlag)
    {
        if (bWalkFlag)
        {
            m_bNowWalking = true;
            m_dwWalkStartTime = get_dword_time();
        }
        else
        {
            m_bNowWalking = false;
        }

        //if (m_bNowWalking)
        {
            TPacketGCWalkMode p;
            p.vid = GetVID();
            p.header = HEADER_GC_WALK_MODE;
            p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;

            PacketView(&p, sizeof(p));
        }

        if (IsNPC())
        {
            if (m_bNowWalking)
                MonsterLog("°Č´Â´Ů");
            else
                MonsterLog("¶Ú´Ů");
        }

        //sys_log(0, "%s is now %s", GetName(), m_bNowWalking?"walking.":"running.");
    }
}

void CHARACTER::StartStaminaConsume()
{
    if (m_bStaminaConsume)
        return;
    PointChange(POINT_STAMINA, 0);
    m_bStaminaConsume = true;
    //ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
    if (IsStaminaHalfConsume())
        ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec / 2, GetStamina());
    else
        ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
}

void CHARACTER::StopStaminaConsume()
{
    if (!m_bStaminaConsume)
        return;
    PointChange(POINT_STAMINA, 0);
    m_bStaminaConsume = false;
    ChatPacket(CHAT_TYPE_COMMAND, "StopStaminaConsume %d", GetStamina());
}

bool CHARACTER::IsStaminaConsume() const
{
    return m_bStaminaConsume;
}

bool CHARACTER::IsStaminaHalfConsume() const
{
    return IsEquipUniqueItem(UNIQUE_ITEM_HALF_STAMINA);
}

void CHARACTER::ResetStopTime()
{
    m_dwStopTime = get_dword_time();
}

DWORD CHARACTER::GetStopTime() const
{
    return m_dwStopTime;
}

void CHARACTER::ResetPoint(int iLv)
{
    BYTE bJob = GetJob();

    PointChange(POINT_LEVEL, iLv - GetLevel());

    SetRealPoint(POINT_ST, JobInitialPoints[bJob].st);
    SetPoint(POINT_ST, GetRealPoint(POINT_ST));

    SetRealPoint(POINT_HT, JobInitialPoints[bJob].ht);
    SetPoint(POINT_HT, GetRealPoint(POINT_HT));

    SetRealPoint(POINT_DX, JobInitialPoints[bJob].dx);
    SetPoint(POINT_DX, GetRealPoint(POINT_DX));

    SetRealPoint(POINT_IQ, JobInitialPoints[bJob].iq);
    SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));

    SetRandomHP((iLv - 1) * number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end));
    SetRandomSP((iLv - 1) * number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end));

    //PointChange(POINT_STAT, ((MINMAX(1, iLv, 99) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
    PointChange(POINT_STAT, ((MINMAX(1, iLv, 90) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));

    ComputePoints();

    // ȸşą
    PointChange(POINT_HP, GetMaxHP() - GetHP());
    PointChange(POINT_SP, GetMaxSP() - GetSP());

    PointsPacket();

    LogManager::instance().CharLog(this, 0, "RESET_POINT", "");
}

bool CHARACTER::IsChangeAttackPosition(LPCHARACTER target) const

    if (!IsNPC())
        return true;

    DWORD dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_NEAR;

    if (DISTANCE_APPROX(GetX() - target->GetX(), GetY() - target->GetY()) > 
        AI_CHANGE_ATTACK_POISITION_DISTANCE + GetMobAttackRange())
        dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_FAR;

    return get_dword_time() - m_dwLastChangeAttackPositionTime > dwChangeTime; 
}

void CHARACTER::GiveRandomSkillBook()
{
    LPITEM item = AutoGiveItem(50300);

    if (NULL != item)
    {
        BYTE bJob = 0;

        if (!number(0, 1))
            bJob = GetJob() + 1;

        DWORD dwSkillVnum = 0;

        do
        {
            dwSkillVnum = number(1, 111);
            const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum);

            if (NULL == pkSk)
                continue;

            if (bJob && bJob != pkSk->dwType)
                continue;

            break;
        } while (true);

        item->SetSocket(0, dwSkillVnum);
    }
}

void CHARACTER::ReviveInvisible(int iDur)
{
    AddAffect(AFFECT_REVIVE_INVISIBLE, POINT_NONE, 0, AFF_REVIVE_INVISIBLE, iDur, 0, true);
}

void CHARACTER::ToggleMonsterLog()
{
    m_bMonsterLog = !m_bMonsterLog;

    if (m_bMonsterLog)
    {
        CHARACTER_MANAGER::instance().RegisterForMonsterLog(this);
    }
    else
    {
        CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
    }
}

void CHARACTER::SetGuild(CGuild* pGuild)
{
    if (m_pGuild != pGuild)
    {
        m_pGuild = pGuild;
        UpdatePacket();
    }
}

void CHARACTER::SendGreetMessage()
{
    typeof(DBManager::instance().GetGreetMessage()) v = DBManager::instance().GetGreetMessage();

    for (itertype(v) it = v.begin(); it != v.end(); ++it)
    {
        ChatPacket(CHAT_TYPE_NOTICE, it->c_str());
    }
}

void CHARACTER::BeginStateEmpty()
{
    MonsterLog("!");
}

void CHARACTER::EffectPacket(int enumEffectType)
{
    TPacketGCSpecialEffect p;

    p.header = HEADER_GC_SEPCIAL_EFFECT;
    p.type = enumEffectType;
    p.vid = GetVID();

    PacketAround(&p, sizeof(TPacketGCSpecialEffect));
}

void CHARACTER::SpecificEffectPacket(const char filename[MAX_EFFECT_FILE_NAME])
{
    TPacketGCSpecificEffect p;

    p.header = HEADER_GC_SPECIFIC_EFFECT;
    p.vid = GetVID();
    memcpy (p.effect_file, filename, MAX_EFFECT_FILE_NAME);

    PacketAround(&p, sizeof(TPacketGCSpecificEffect));
}

void CHARACTER::MonsterChat(BYTE bMonsterChatType)
{
    if (IsPC())
        return;

    char sbuf[256+1];

    if (IsMonster())
    {
        if (number(0, 60))
            return;

        snprintf(sbuf, sizeof(sbuf), 
                "(locale.monster_chat[%i] and locale.monster_chat[%i][%d] or '')",
                GetRaceNum(), GetRaceNum(), bMonsterChatType*3 + number(1, 3));
    }
    else
    {
        if (bMonsterChatType != MONSTER_CHAT_WAIT)
            return;

        if (IsGuardNPC())
        {
            if (number(0, 6))
                return;
        }
        else
        {
            if (number(0, 30))
                return;
        }

        snprintf(sbuf, sizeof(sbuf), "(locale.monster_chat[%i] and locale.monster_chat[%i][number(1, table.getn(locale.monster_chat[%i]))] or '')", GetRaceNum(), GetRaceNum(), GetRaceNum());
    }

    std::string text = quest::ScriptToString(sbuf);

    if (text.empty())
        return;

    struct packet_chat pack_chat;

    pack_chat.header    = HEADER_GC_CHAT;
    pack_chat.size    = sizeof(struct packet_chat) + text.size() + 1;
    pack_chat.type      = CHAT_TYPE_TALKING;
    pack_chat.id        = GetVID();
    pack_chat.bEmpire    = 0;

    TEMP_BUFFER buf;
    buf.write(&pack_chat, sizeof(struct packet_chat));
    buf.write(text.c_str(), text.size() + 1);

    PacketAround(buf.read_peek(), buf.size());
}

void CHARACTER::SetQuestNPCID(DWORD vid)
{
    m_dwQuestNPCVID = vid;
}

LPCHARACTER CHARACTER::GetQuestNPC() const
{
    return CHARACTER_MANAGER::instance().Find(m_dwQuestNPCVID);
}

void CHARACTER::SetQuestItemPtr(LPITEM item)
{
    m_pQuestItem = item;
}

void CHARACTER::ClearQuestItemPtr()
{
    m_pQuestItem = NULL;
}

LPITEM CHARACTER::GetQuestItemPtr() const
{
    return m_pQuestItem;
}

LPDUNGEON CHARACTER::GetDungeonForce() const

    if (m_lWarpMapIndex > 10000)
        return CDungeonManager::instance().FindByMapIndex(m_lWarpMapIndex);

    return m_pkDungeon;
}

void CHARACTER::SetBlockMode(BYTE bFlag)
{
    m_pointsInstant.bBlockMode = bFlag;

    ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);

    SetQuestFlag("game_option.block_exchange", bFlag & BLOCK_EXCHANGE ? 1 : 0);
    SetQuestFlag("game_option.block_party_invite", bFlag & BLOCK_PARTY_INVITE ? 1 : 0);
    SetQuestFlag("game_option.block_guild_invite", bFlag & BLOCK_GUILD_INVITE ? 1 : 0);
    SetQuestFlag("game_option.block_whisper", bFlag & BLOCK_WHISPER ? 1 : 0);
    SetQuestFlag("game_option.block_messenger_invite", bFlag & BLOCK_MESSENGER_INVITE ? 1 : 0);
    SetQuestFlag("game_option.block_party_request", bFlag & BLOCK_PARTY_REQUEST ? 1 : 0);
}

void CHARACTER::SetBlockModeForce(BYTE bFlag)
{
    m_pointsInstant.bBlockMode = bFlag;
    ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
}

bool CHARACTER::IsGuardNPC() const
{
    return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
}

int CHARACTER::GetPolymorphPower() const
{
    if (test_server)
    {
        int value = quest::CQuestManager::instance().GetEventFlag("poly");
        if (value)
            return value;
    }
    return aiPolymorphPowerByLevel[MINMAX(0, GetSkillLevel(SKILL_POLYMORPH), 40)];
}

void CHARACTER::SetPolymorph(DWORD dwRaceNum, bool bMaintainStat)
{
    if (dwRaceNum < JOB_MAX_NUM)
    {
        dwRaceNum = 0;
        bMaintainStat = false;
    }

    if (m_dwPolymorphRace == dwRaceNum)
        return;

    m_bPolyMaintainStat = bMaintainStat;
    m_dwPolymorphRace = dwRaceNum;

    sys_log(0, "POLYMORPH: %s race %u ", GetName(), dwRaceNum);

    if (dwRaceNum != 0)
        StopRiding();

    SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
    m_afAffectFlag.Set(AFF_SPAWN);

    ViewReencode();

    REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);

    if (!bMaintainStat)
    {
        PointChange(POINT_ST, 0);
        PointChange(POINT_DX, 0);
        PointChange(POINT_IQ, 0);
        PointChange(POINT_HT, 0);
    }

    // Ćú¸®¸đÇÁ »óĹÂżˇĽ­ Á×´Â °ćżě, Ćú¸®¸đÇÁ°ˇ Ç®¸®°Ô µÇ´ÂµĄ
    // Ćú¸® ¸đÇÁ ŔüČÄ·Î valid combo intervalŔĚ ´Ů¸Ł±â ¶§ą®żˇ
    // Combo ÇŮ ¶Ç´Â Hacker·Î ŔνÄÇĎ´Â °ćżě°ˇ ŔÖ´Ů.
    // µű¶óĽ­ Ćú¸®¸đÇÁ¸¦ Ç®°ĹłŞ Ćú¸®¸đÇÁ ÇĎ°Ô µÇ¸é,
    // valid combo intervalŔ» resetÇŃ´Ů.
    SetValidComboInterval(0);
    SetComboSequence(0);

    ComputeBattlePoints();
}

int CHARACTER::GetQuestFlag(const std::string& flag) const
{
    quest::CQuestManager& q = quest::CQuestManager::instance();
    quest::PC* pPC = q.GetPC(GetPlayerID());
    return pPC->GetFlag(flag);
}

void CHARACTER::SetQuestFlag(const std::string& flag, int value)
{
    quest::CQuestManager& q = quest::CQuestManager::instance();
    quest::PC* pPC = q.GetPC(GetPlayerID());
    pPC->SetFlag(flag, value);
}

void CHARACTER::DetermineDropMetinStone()
{
    const int METIN_STONE_NUM = 14;
    static DWORD c_adwMetin[METIN_STONE_NUM] = 
    {
        28030,
        28031,
        28032,
        28033,
        28034,
        28035,
        28036,
        28037,
        28038,
        28039,
        28040,
        28041,
        28042,
        28043,
    };
    DWORD stone_num = GetRaceNum();
    int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop;
    if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num)
    {
        m_dwDropMetinStone = 0;
    }
    else
    {
        const SStoneDropInfo & info = aStoneDrop[idx];
        m_bDropMetinStonePct = info.iDropPct;
        {
            m_dwDropMetinStone = c_adwMetin[number(0, METIN_STONE_NUM - 1)];
            int iGradePct = number(1, 100);
            for (int iStoneLevel = 0; iStoneLevel < STONE_LEVEL_MAX_NUM; iStoneLevel ++)
            {
                int iLevelGradePortion = info.iLevelPct[iStoneLevel];
                if (iGradePct <= iLevelGradePortion)
                {
                    break;
                }
                else
                {
                    iGradePct -= iLevelGradePortion;
                    m_dwDropMetinStone += 100; // µą +a -> +(a+1)ŔĚ µÉ¶§¸¶´Ů 100ľż Áő°ˇ
                }
            }
        }
    }
}

void CHARACTER::SendEquipment(LPCHARACTER ch)
{
    TPacketViewEquip p;
    p.header = HEADER_GC_VIEW_EQUIP;
    p.vid    = GetVID();
    for (int i = 0; i<WEAR_MAX_NUM; i++)
    {
        LPITEM item = GetWear(i);
        if (item)
        {
            p.equips.vnum = item->GetVnum();
            p.equips.count = item->GetCount();

            thecore_memcpy(p.equips.alSockets, item->GetSockets(), sizeof(p.equips.alSockets));
            thecore_memcpy(p.equips.aAttr, item->GetAttributes(), sizeof(p.equips.aAttr));
        }
        else
        {
            p.equips.vnum = 0;
        }
    }
    ch->GetDesc()->Packet(&p, sizeof(p));
}

bool CHARACTER::CanSummon(int iLeaderShip)
{
    return (iLeaderShip >= 20 || iLeaderShip >= 12 && m_dwLastDeadTime + 180 > get_dword_time());
}
#ifdef ENABLE_BIOLOG_SYSTEM
int CHARACTER::GetBiologState(const std::string& state) const
{
    quest::CQuestManager& q = quest::CQuestManager::instance();
    return pPC->GetFlag(state);
}

void CHARACTER::SetBiologState(const std::string& state, int szValue)
{
    quest::CQuestManager& q = quest::CQuestManager::instance();
    quest::PC* pPC = q.GetPC(GetPlayerID());
    pPC->SetFlag(state, szValue);
}
#endif

void CHARACTER::MountVnum(DWORD vnum)
{
    if (m_dwMountVnum == vnum)
        return;

    m_dwMountVnum = vnum;
    m_dwMountTime = get_dword_time();

    if (m_bIsObserver)
        return;

    //NOTE : MountÇŃ´Ů°í ÇŘĽ­ Client SideŔÇ °´ĂĽ¸¦ »čÁ¦ÇĎÁř ľĘ´Â´Ů.
    //±×¸®°í Ľ­ąöSideżˇĽ­ ĹŔŔ»¶§ Ŕ§Äˇ Ŕ̵żŔş ÇĎÁö ľĘ´Â´Ů. żÖłÄÇϸé Client SideżˇĽ­ Coliision Adjust¸¦ ÇŇĽö ŔִµĄ
    //°´ĂĽ¸¦ ĽŇ¸ę˝ĂÄ×´Ů°ˇ Ľ­ąöŔ§Äˇ·Î Ŕ̵ż˝ĂĹ°¸é Ŕ̶§ collision check¸¦ ÇĎÁö´Â ľĘŔ¸ąÇ·Î ąč°ćżˇ ł˘°ĹłŞ ¶Ő°í łŞ°ˇ´Â ą®Á¦°ˇ Á¸ŔçÇŃ´Ů.
    m_posDest.x = m_posStart.x = GetX();
    m_posDest.y = m_posStart.y = GetY();
    //EncodeRemovePacket(this);
    EncodeInsertPacket(this);

    ENTITY_MAP::iterator it = m_map_view.begin();

    while (it != m_map_view.end())
    {
        LPENTITY entity = (it++)->first;

        //MountÇŃ´Ů°í ÇŘĽ­ Client SideŔÇ °´ĂĽ¸¦ »čÁ¦ÇĎÁř ľĘ´Â´Ů.
        //EncodeRemovePacket(entity);
        //if (!m_bIsObserver)
        EncodeInsertPacket(entity);

        //if (!entity->IsObserverMode())
        //    entity->EncodeInsertPacket(this);
    }

    SetValidComboInterval(0);
    SetComboSequence(0);

    ComputePoints();
}

namespace {
    class FuncCheckWarp
    {
        public:
            FuncCheckWarp(LPCHARACTER pkWarp)
            {
                m_lTargetY = 0;
                m_lTargetX = 0;

                m_lX = pkWarp->GetX();
                m_lY = pkWarp->GetY();

                m_bInvalid = false;
                m_bEmpire = pkWarp->GetEmpire();

                char szTmp[64];

                if (3 != sscanf(pkWarp->GetName(), " %s %ld %ld ", szTmp, &m_lTargetX, &m_lTargetY))
                {
                    if (number(1, 100) < 5)
                        sys_err("Warp NPC name wrong : vnum(%d) name(%s)", pkWarp->GetRaceNum(), pkWarp->GetName());

                    m_bInvalid = true;

                    return;
                }

                m_lTargetX *= 100;
                m_lTargetY *= 100;

                m_bUseWarp = true;

                if (pkWarp->IsGoto())
                {
                    LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(pkWarp->GetMapIndex());
                    m_lTargetX += pkSectreeMap->m_setting.iBaseX;
                    m_lTargetY += pkSectreeMap->m_setting.iBaseY;
                    m_bUseWarp = false;
                }
            }

            bool Valid()
            {
                return !m_bInvalid;
            }

            void operator () (LPENTITY ent)
            {
                if (!Valid())
                    return;

                if (!ent->IsType(ENTITY_CHARACTER))
                    return;

                LPCHARACTER pkChr = (LPCHARACTER) ent;

                if (!pkChr->IsPC())
                    return;

                int iDist = DISTANCE_APPROX(pkChr->GetX() - m_lX, pkChr->GetY() - m_lY);

                if (iDist > 300)
                    return;

                if (m_bEmpire && pkChr->GetEmpire() && m_bEmpire != pkChr->GetEmpire())
                    return;

                if (pkChr->IsHack())
                    return;

                if (!pkChr->CanHandleItem(false, true))
                    return;    
                
                if (m_bUseWarp)
                    pkChr->WarpSet(m_lTargetX, m_lTargetY);
                else
                {
                    pkChr->Show(pkChr->GetMapIndex(), m_lTargetX, m_lTargetY);
                    pkChr->Stop();
                }
            }

            bool m_bInvalid;
            bool m_bUseWarp;

            long m_lX;
            long m_lY;
            long m_lTargetX;
            long m_lTargetY;

            BYTE m_bEmpire;
    };
}

EVENTFUNC(warp_npc_event)
{
    char_event_info* info = dynamic_cast<char_event_info*>( event->info );
    if ( info == NULL )
    {
        sys_err( "warp_npc_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER    ch = info->ch;

    if (ch == NULL) { // <Factor>
        return 0;
    }    

    if (!ch->GetSectree())
    {
        ch->m_pkWarpNPCEvent = NULL;
        return 0;
    }

    FuncCheckWarp f(ch);
    if (f.Valid())
        ch->GetSectree()->ForEachAround(f);

    return passes_per_sec / 2;
}


void CHARACTER::StartWarpNPCEvent()
{
    if (m_pkWarpNPCEvent)
        return;

    if (!IsWarp() && !IsGoto())
        return;

    char_event_info* info = AllocEventInfo<char_event_info>();

    info->ch = this;

    m_pkWarpNPCEvent = event_create(warp_npc_event, info, passes_per_sec / 2);
}

void CHARACTER::SyncPacket()
{
    TEMP_BUFFER buf;

    TPacketCGSyncPositionElement elem;

    elem.dwVID = GetVID();
    elem.lX = GetX();
    elem.lY = GetY();

    TPacketGCSyncPosition pack;

    pack.bHeader = HEADER_GC_SYNC_POSITION;
    pack.wSize = sizeof(TPacketGCSyncPosition) + sizeof(elem);

    buf.write(&pack, sizeof(pack));
    buf.write(&elem, sizeof(elem));

    PacketAround(buf.read_peek(), buf.size());
}

LPCHARACTER CHARACTER::GetMarryPartner() const
{
    return m_pkChrMarried;
}

void CHARACTER::SetMarryPartner(LPCHARACTER ch)
{
    m_pkChrMarried = ch;
}

int CHARACTER::GetMarriageBonus(DWORD dwItemVnum, bool bSum)
{
    if (IsNPC())
        return 0;

    marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());

    if (!pMarriage)
        return 0;

    return pMarriage->GetBonus(dwItemVnum, bSum, this);
}

void CHARACTER::ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID)
{
    if (!IsPC())
        return;

    TPacketGCQuestConfirm p;

    p.header = HEADER_GC_QUEST_CONFIRM;
    p.requestPID = dwRequestPID;
    p.timeout = iTimeout;
    strlcpy(p.msg, szMsg, sizeof(p.msg));

    GetDesc()->Packet(&p, sizeof(p));
}

int CHARACTER::GetPremiumRemainSeconds(BYTE bType) const
{
    if (bType >= PREMIUM_MAX_NUM)
        return 0;

    return m_aiPremiumTimes[bType] - get_global_time();
}

bool CHARACTER::WarpToPID(DWORD dwPID)
{
    LPCHARACTER victim;
    if ((victim = (CHARACTER_MANAGER::instance().FindByPID(dwPID))))
    {
        int mapIdx = victim->GetMapIndex();
        if (IS_SUMMONABLE_ZONE(mapIdx))
        {
            if (CAN_ENTER_ZONE(this, mapIdx))
            {
                WarpSet(victim->GetX(), victim->GetY());
            }
            else
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ ŔÖ´Â °÷Ŕ¸·Î żöÇÁÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                return false;
            }
        }
        else
        {
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ ŔÖ´Â °÷Ŕ¸·Î żöÇÁÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return false;
        }
    }
    else
    {
        // ´Ů¸Ą Ľ­ąöżˇ ·Î±×ŔÎµČ »ç¶÷ŔĚ ŔÖŔ˝ -> ¸Ţ˝ĂÁö ş¸ł» ÁÂÇĄ¸¦ ąŢľĆżŔŔÚ
        // 1. A.pid, B.pid ¸¦ »Ń¸˛
        // 2. B.pid¸¦ °ˇÁř Ľ­ąö°ˇ »Ń¸°Ľ­ąöżˇ°Ô A.pid, ÁÂÇĄ ¸¦ ş¸łż
        // 3. żöÇÁ
        CCI * pcci = P2P_MANAGER::instance().FindByPID(dwPID);

        if (!pcci)
        {
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ żÂ¶óŔÎ »óĹ°ˇ ľĆ´Ő´Ď´Ů."));
            return false;
        }

        if (pcci->bChannel != g_bChannel)
        {
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ %d äłÎżˇ ŔÖ˝Ŕ´Ď´Ů. (ÇöŔç äłÎ %d)"), pcci->bChannel, g_bChannel);
            return false;
        }
        else if (false == IS_SUMMONABLE_ZONE(pcci->lMapIndex))
        {
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ ŔÖ´Â °÷Ŕ¸·Î żöÇÁÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
            return false;
        }
        else
        {
            if (!CAN_ENTER_ZONE(this, pcci->lMapIndex))
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»ó´ëąćŔĚ ŔÖ´Â °÷Ŕ¸·Î żöÇÁÇŇ Ľö ľř˝Ŕ´Ď´Ů."));
                return false;
            }

            TPacketGGFindPosition p;
            p.header = HEADER_GG_FIND_POSITION;
            p.dwFromPID = GetPlayerID();
            p.dwTargetPID = dwPID;
            pcci->pkDesc->Packet(&p, sizeof(TPacketGGFindPosition));

            if (test_server) 
                ChatPacket(CHAT_TYPE_PARTY, "sent find position packet for teleport");
        }
    }
    return true;
}

// ADD_REFINE_BUILDING
CGuild* CHARACTER::GetRefineGuild() const
{
    LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);

    return (chRefineNPC ? chRefineNPC->GetGuild() : NULL);
}

bool CHARACTER::IsRefineThroughGuild() const
{
    return GetRefineGuild() != NULL;
}

int CHARACTER::ComputeRefineFee(int iCost, int iMultiply) const
{
    CGuild* pGuild = GetRefineGuild();
    if (pGuild)
    {
        if (pGuild == GetGuild())
            return iCost * iMultiply * 9 / 10;

        // ´Ů¸Ą Á¦±ą »ç¶÷ŔĚ ˝ĂµµÇĎ´Â °ćżě Ăß°ˇ·Î 3ąč ´ő
        LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
        if (chRefineNPC && chRefineNPC->GetEmpire() != GetEmpire())
            return iCost * iMultiply * 3;

        return iCost * iMultiply;
    }
    else
        return iCost;
}

void CHARACTER::PayRefineFee(int iTotalMoney)
{
    int iFee = iTotalMoney / 10;
    CGuild* pGuild = GetRefineGuild();

    int iRemain = iTotalMoney;

    if (pGuild)
    {
        // Ŕڱ⠱ćµĺŔ̸é iTotalMoneyżˇ ŔĚąĚ 10%°ˇ Á¦żÜµÇľîŔÖ´Ů
        if (pGuild != GetGuild())
        {
            pGuild->RequestDepositMoney(this, iFee);
            iRemain -= iFee;
        }
    }

    PointChange(POINT_GOLD, -iRemain);
}
// END_OF_ADD_REFINE_BUILDING

//Hack ąćÁö¸¦ Ŕ§ÇŃ ĂĽĹ©.
bool CHARACTER::IsHack(bool bSendMsg, bool bCheckShopOwner, int limittime)
{
    const int iPulse = thecore_pulse();

    if (test_server)
        bSendMsg = true;

    //â°í ż¬ČÄ ĂĽĹ©
    if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(limittime))
    {
        if (bSendMsg)
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("â°í¸¦ ż¬ČÄ %dĂĘ ŔĚł»żˇ´Â ´Ů¸Ą°÷Ŕ¸·Î Ŕ̵żÇŇĽö ľř˝Ŕ´Ď´Ů."), limittime);

        if (test_server)
            ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(limittime));
        return true; 
    }

    //°Ĺ·ˇ°ü·Ă â ĂĽĹ©
    if (bCheckShopOwner)
    {
        if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
        {
            if (bSendMsg)
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°Ĺ·ˇĂ˘,â°í µîŔ» ż¬ »óĹÂżˇĽ­´Â ´Ů¸Ą°÷Ŕ¸·Î Ŕ̵ż,Áľ·á ÇŇĽö ľř˝Ŕ´Ď´Ů"));

            return true;
        }
    }
    else
    {
        if (GetExchange() || GetMyShop() || IsOpenSafebox() || IsCubeOpen())
        {
            if (bSendMsg)
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°Ĺ·ˇĂ˘,â°í µîŔ» ż¬ »óĹÂżˇĽ­´Â ´Ů¸Ą°÷Ŕ¸·Î Ŕ̵ż,Áľ·á ÇŇĽö ľř˝Ŕ´Ď´Ů"));

            return true;
        }
    }

    //PREVENT_PORTAL_AFTER_EXCHANGE
    //±łČŻ ČÄ ˝Ă°ŁĂĽĹ©
    if (iPulse - GetExchangeTime()  < PASSES_PER_SEC(limittime))
    {
        if (bSendMsg)
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°Ĺ·ˇ ČÄ %dĂĘ ŔĚł»żˇ´Â ´Ů¸ĄÁöżŞŔ¸·Î Ŕ̵ż ÇŇ Ľö ľř˝Ŕ´Ď´Ů."), limittime );
        return true;
    }
    //END_PREVENT_PORTAL_AFTER_EXCHANGE

    //PREVENT_ITEM_COPY
    if (iPulse - GetMyShopTime() < PASSES_PER_SEC(limittime))
    {
        if (bSendMsg)
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°Ĺ·ˇ ČÄ %dĂĘ ŔĚł»żˇ´Â ´Ů¸ĄÁöżŞŔ¸·Î Ŕ̵ż ÇŇ Ľö ľř˝Ŕ´Ď´Ů."), limittime);
        return true;
    }

    if (iPulse - GetRefineTime() < PASSES_PER_SEC(limittime))
    {
        if (bSendMsg)
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ľĆŔĚĹŰ °ł·®ČÄ %dĂĘ ŔĚł»żˇ´Â ±ÍČŻşÎ,±ÍČŻ±âľďşÎ¸¦ »çżëÇŇ Ľö ľř˝Ŕ´Ď´Ů."), limittime);
        return true; 
    }
    //END_PREVENT_ITEM_COPY

    return false;
}

BOOL CHARACTER::IsMonarch() const
{
    //MONARCH_LIMIT
    if (CMonarch::instance().IsMonarch(GetPlayerID(), GetEmpire()))
        return true;

    return false;

    //END_MONARCH_LIMIT
}
void CHARACTER::Say(const std::string & s)
{
    struct ::packet_script packet_script;

    packet_script.header = HEADER_GC_SCRIPT;
    packet_script.skin = 1;
    packet_script.src_size = s.size();
    packet_script.size = packet_script.src_size + sizeof(struct packet_script);
    
    TEMP_BUFFER buf;

    buf.write(&packet_script, sizeof(struct packet_script));
    buf.write(&s[0], s.size());

    if (IsPC())
    {
        GetDesc()->Packet(buf.read_peek(), buf.size());
    }
}

//
// Monarch
//
void CHARACTER::InitMC()
{
    for (int n = 0; n < MI_MAX; ++n)
    {
        m_dwMonarchCooltime[n] = thecore_pulse(); 
    }

    m_dwMonarchCooltimelimit[MI_HEAL] = PASSES_PER_SEC(MC_HEAL);
    m_dwMonarchCooltimelimit[MI_WARP] = PASSES_PER_SEC(MC_WARP);
    m_dwMonarchCooltimelimit[MI_TRANSFER] = PASSES_PER_SEC(MC_TRANSFER);
    m_dwMonarchCooltimelimit[MI_TAX] = PASSES_PER_SEC(MC_TAX);
    m_dwMonarchCooltimelimit[MI_SUMMON] = PASSES_PER_SEC(MC_SUMMON);

    m_dwMonarchCooltime[MI_HEAL] -= PASSES_PER_SEC(GetMCL(MI_HEAL));
    m_dwMonarchCooltime[MI_WARP] -= PASSES_PER_SEC(GetMCL(MI_WARP));
    m_dwMonarchCooltime[MI_TRANSFER] -= PASSES_PER_SEC(GetMCL(MI_TRANSFER));
    m_dwMonarchCooltime[MI_TAX] -= PASSES_PER_SEC(GetMCL(MI_TAX));
    m_dwMonarchCooltime[MI_SUMMON] -= PASSES_PER_SEC(GetMCL(MI_SUMMON));
}

DWORD CHARACTER::GetMC(enum MONARCH_INDEX e) const
{
    return m_dwMonarchCooltime[e];
}

void CHARACTER::SetMC(enum MONARCH_INDEX e)
{
    m_dwMonarchCooltime[e] = thecore_pulse();
}

bool CHARACTER::IsMCOK(enum MONARCH_INDEX e) const
{
    int iPulse = thecore_pulse();

    if ((iPulse -  GetMC(e)) <  GetMCL(e))
    {
        if (test_server)
            sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
        
        return false;
    }
    
    if (test_server)
        sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));

    return true;
}

DWORD CHARACTER::GetMCL(enum MONARCH_INDEX e) const
{
    return m_dwMonarchCooltimelimit[e];
}

DWORD CHARACTER::GetMCLTime(enum MONARCH_INDEX e) const
{
    int iPulse = thecore_pulse();

    if (test_server)
        sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));

    return  (GetMCL(e)) / passes_per_sec   -  (iPulse - GetMC(e)) / passes_per_sec;
}

bool CHARACTER::IsSiegeNPC() const
{
    return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
}

//------------------------------------------------
void CHARACTER::UpdateDepositPulse()
{
    m_deposit_pulse = thecore_pulse() + PASSES_PER_SEC(60*5);    // 5şĐ
}

bool CHARACTER::CanDeposit() const
{
    return (m_deposit_pulse == 0 || (m_deposit_pulse < thecore_pulse()));
}
//------------------------------------------------

ESex GET_SEX(LPCHARACTER ch)
{
    switch (ch->GetRaceNum())
    {
        case MAIN_RACE_WARRIOR_M:
        case MAIN_RACE_SURA_M:
        case MAIN_RACE_ASSASSIN_M:
        case MAIN_RACE_SHAMAN_M:
            return SEX_MALE;

        case MAIN_RACE_ASSASSIN_W:
        case MAIN_RACE_SHAMAN_W:
        case MAIN_RACE_WARRIOR_W:
        case MAIN_RACE_SURA_W:
            return SEX_FEMALE;
    }

    /* default sex = male */
    return SEX_MALE;
}

int CHARACTER::GetHPPct() const
{
    return (GetHP() * 100) / GetMaxHP();
}

bool CHARACTER::IsBerserk() const
{
    if (m_pkMobInst != NULL)
        return m_pkMobInst->m_IsBerserk;
    else
        return false;
}

void CHARACTER::SetBerserk(bool mode)
{
    if (m_pkMobInst != NULL)
        m_pkMobInst->m_IsBerserk = mode;
}

bool CHARACTER::IsGodSpeed() const
{
    if (m_pkMobInst != NULL)
    {
        return m_pkMobInst->m_IsGodSpeed;
    }
    else
    {
        return false;
    }
}

void CHARACTER::SetGodSpeed(bool mode)
{
    if (m_pkMobInst != NULL)
    {
        m_pkMobInst->m_IsGodSpeed = mode;

        if (mode == true)
        {
            SetPoint(POINT_ATT_SPEED, 250);
        }
        else
        {
            SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
        }
    }
}

bool CHARACTER::IsDeathBlow() const
{
    if (number(1, 100) <= m_pkMobData->m_table.bDeathBlowPoint)
    {
        return true;
    }
    else
    {
        return false;
    }
}

struct FFindReviver
{
    FFindReviver()
    {
        pChar = NULL;
        HasReviver = false;
    }
    
    void operator() (LPCHARACTER ch)
    {
        if (ch->IsMonster() != true)
        {
            return;
        }

        if (ch->IsReviver() == true && pChar != ch && ch->IsDead() != true)
        {
            if (number(1, 100) <= ch->GetMobTable().bRevivePoint)
            {
                HasReviver = true;
                pChar = ch;
            }
        }
    }

    LPCHARACTER pChar;
    bool HasReviver;
};

bool CHARACTER::HasReviverInParty() const
{
    LPPARTY party = GetParty();

    if (party != NULL)
    {
        if (party->GetMemberCount() == 1) return false;

        FFindReviver f;
        party->ForEachMemberPtr(f);
        return f.HasReviver;
    }

    return false;
}

bool CHARACTER::IsRevive() const
{
    if (m_pkMobInst != NULL)
    {
        return m_pkMobInst->m_IsRevive;
    }

    return false;
}

void CHARACTER::SetRevive(bool mode)
{
    if (m_pkMobInst != NULL)
    {
        m_pkMobInst->m_IsRevive = mode;
    }
}

#define IS_SPEED_HACK_PLAYER(ch) (ch->m_speed_hack_count > SPEEDHACK_LIMIT_COUNT)

EVENTFUNC(check_speedhack_event)
{
    char_event_info* info = dynamic_cast<char_event_info*>( event->info );
    if ( info == NULL )
    {
        sys_err( "check_speedhack_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER    ch = info->ch;

    if (NULL == ch || ch->IsNPC())
        return 0;

    if (IS_SPEED_HACK_PLAYER(ch))
    {
        // write hack log
        LogManager::instance().SpeedHackLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(), ch->m_speed_hack_count);

        if (false == LC_IsEurope())
        {
            // close connection
            LPDESC desc = ch->GetDesc();

            if (desc)
            {
                DESC_MANAGER::instance().DestroyDesc(desc);
                return 0;
            }
        }
    }

    ch->m_speed_hack_count = 0;

    ch->ResetComboHackCount();
    return PASSES_PER_SEC(60);
}

void CHARACTER::StartCheckSpeedHackEvent()
{
    if (m_pkCheckSpeedHackEvent)
        return;

    char_event_info* info = AllocEventInfo<char_event_info>();

    info->ch = this;

    m_pkCheckSpeedHackEvent = event_create(check_speedhack_event, info, PASSES_PER_SEC(60));    // 1şĐ
}

void CHARACTER::GoHome()
{
    WarpSet(EMPIRE_START_X(GetEmpire()), EMPIRE_START_Y(GetEmpire()));
}

void CHARACTER::SendGuildName(CGuild* pGuild)
{
    if (NULL == pGuild) return;

    DESC    *desc = GetDesc();

    if (NULL == desc) return;
    if (m_known_guild.find(pGuild->GetID()) != m_known_guild.end()) return;

    m_known_guild.insert(pGuild->GetID());

    TPacketGCGuildName    pack;
    memset(&pack, 0x00, sizeof(pack));

    pack.header        = HEADER_GC_GUILD;
    pack.subheader    = GUILD_SUBHEADER_GC_GUILD_NAME;
    pack.size        = sizeof(TPacketGCGuildName);
    pack.guildID    = pGuild->GetID();
    memcpy(pack.guildName, pGuild->GetName(), GUILD_NAME_MAX_LEN);

    desc->Packet(&pack, sizeof(pack));
}

void CHARACTER::SendGuildName(DWORD dwGuildID)
{
    SendGuildName(CGuildManager::instance().FindGuild(dwGuildID));
}

EVENTFUNC(destroy_when_idle_event)
{
    char_event_info* info = dynamic_cast<char_event_info*>( event->info );
    if ( info == NULL )
    {
        sys_err( "destroy_when_idle_event> <Factor> Null pointer" );
        return 0;
    }

    LPCHARACTER ch = info->ch;
    if (ch == NULL) { // <Factor>
        return 0;
    }    

    if (ch->GetVictim())
    {
        return PASSES_PER_SEC(300);
    }

    sys_log(1, "DESTROY_WHEN_IDLE: %s", ch->GetName());

    ch->m_pkDestroyWhenIdleEvent = NULL;
    M2_DESTROY_CHARACTER(ch);
    return 0;
}

void CHARACTER::StartDestroyWhenIdleEvent()
{
    if (m_pkDestroyWhenIdleEvent)
        return;

    char_event_info* info = AllocEventInfo<char_event_info>();

    info->ch = this;

    m_pkDestroyWhenIdleEvent = event_create(destroy_when_idle_event, info, PASSES_PER_SEC(300));
}

void CHARACTER::SetComboSequence(BYTE seq)
{
    m_bComboSequence = seq;
}

BYTE CHARACTER::GetComboSequence() const
{
    return m_bComboSequence;
}

void CHARACTER::SetLastComboTime(DWORD time)
{
    m_dwLastComboTime = time;
}

DWORD CHARACTER::GetLastComboTime() const
{
    return m_dwLastComboTime;
}

void CHARACTER::SetValidComboInterval(int interval)
{
    m_iValidComboInterval = interval;
}

int CHARACTER::GetValidComboInterval() const
{
    return m_iValidComboInterval;
}

BYTE CHARACTER::GetComboIndex() const
{
    return m_bComboIndex;
}

void CHARACTER::IncreaseComboHackCount(int k)
{
    m_iComboHackCount += k;

    if (m_iComboHackCount >= 10)
    {
        if (GetDesc())
            if (GetDesc()->DelayedDisconnect(number(2, 7)))
            {
                sys_log(0, "COMBO_HACK_DISCONNECT: %s count: %d", GetName(), m_iComboHackCount);
                LogManager::instance().HackLog("Combo", this);
            }
    }
}

void CHARACTER::ResetComboHackCount()
{
    m_iComboHackCount = 0;
}

void CHARACTER::SkipComboAttackByTime(int interval)
{
    m_dwSkipComboAttackByTime = get_dword_time() + interval;
}

DWORD CHARACTER::GetSkipComboAttackByTime() const
{
    return m_dwSkipComboAttackByTime;
}

void CHARACTER::ResetChatCounter()
{
    m_bChatCounter = 0;
}

BYTE CHARACTER::IncreaseChatCounter()
{
    return ++m_bChatCounter;
}

BYTE CHARACTER::GetChatCounter() const
{
    return m_bChatCounter;
}

// ¸»ŔĚłŞ ´Ů¸Ą°ÍŔ» Ÿ°í ŔÖłŞ?
bool CHARACTER::IsRiding() const
{
    return IsHorseRiding() || GetMountVnum();
}

bool CHARACTER::CanWarp() const
{
    const int iPulse = thecore_pulse();
    const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime);

    if ((iPulse - GetSafeboxLoadTime()) < limit_time)
        return false;

    if ((iPulse - GetExchangeTime()) < limit_time)
        return false;

    if ((iPulse - GetMyShopTime()) < limit_time)
        return false;

    if ((iPulse - GetRefineTime()) < limit_time)
        return false;

    if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
        return false;

    return true;
}

DWORD CHARACTER::GetNextExp() const
{
    if (PLAYER_EXP_TABLE_MAX < GetLevel())
        return 2500000000;
    else
        return exp_table[GetLevel()];
}

int    CHARACTER::GetSkillPowerByLevel(int level, bool bMob) const
{
    return CTableBySkill::instance().GetSkillPowerByLevelFromType(GetJob(), GetSkillGroup(), MINMAX(0, level, SKILL_MAX_LEVEL), bMob); 
}

 

Link to post
Share on other sites
12 hours ago, tierrilopes said:

Replace:

Hidden Content

    Give reaction or reply to this topic to see the hidden content.

With:

Hidden Content

    Give reaction or reply to this topic to see the hidden content.

 

Thanks for that did help but can you help with this ? 

Quote

biolog.cpp:30: error: 'AFF_BIO_17' was not declared in this scope
biolog.cpp:30: error: 'AFF_BIO_18' was not declared in this scope
biolog.cpp:30: error: 'AFF_BIO_19' was not declared in this scope
biolog.cpp:30: error: 'AFF_BIO_20' was not declared in this scope
biolog.cpp: In member function 'void BiologManager::SendBonusType(CHARACTER*, int)':
biolog.cpp:76: error: 'SendMessagePopUp' was not declared in this scope
biolog.cpp: In member function 'void BiologManager::SelectBonusType(CHARACTER*, const char*)':
biolog.cpp:135: error: 'SendMessagePopUp' was not declared in this scope
biolog.cpp:140: error: 'SendMessagePopUp' was not declared in this scope
biolog.cpp:145: error: 'SendMessagePopUp' was not declared in this scope
biolog.cpp:155: error: 'SendMessagePopUp' was not declared in this scope
biolog.cpp:160: error: 'SendMessagePopUp' was not declared in this scope
biolog.cpp: In member function 'void BiologManager::SendWindow_SelectType(CHARACTER*, int)':
biolog.cpp:193: error: 'arg3' was not declared in this scope
biolog.cpp:193: error: 'arg4' was not declared in this scope
biolog.cpp: In function 'bool SendBiolog_Data(CHARACTER*, int)':
biolog.cpp:227: error: 'days' was not declared in this scope
biolog.cpp:227: error: 'hours' was not declared in this scope
../../../Extern/include/cryptopp/cryptlib.h: In member function 'bool CryptoPP::NameValuePairs::GetValue(const char*, T&) const [with T = std::string]':
../../../Extern/include/cryptopp/cryptlib.h:277:   instantiated from here
../../../Extern/include/cryptopp/cryptlib.h:264: warning: dereferencing type-punned pointer will break strict-aliasing rules
../../../Extern/include/cryptopp/cryptlib.h: In member function 'bool CryptoPP::NameValuePairs::GetValue(const char*, T&) const [with T = int]':
../../../Extern/include/cryptopp/cryptlib.h:283:   instantiated from here
../../../Extern/include/cryptopp/cryptlib.h:264: warning: dereferencing type-punned pointer will break strict-aliasing rules
gmake: *** [Makefile:133: OBJDIR/biolog.o] Error 1
 

 

Link to post
Share on other sites
1 hour ago, tierrilopes said:

Yes i can, please send as attachment your biolog.cpp and biolog.h

There you go mate attached

@tierrilopes

 

Hidden Content

    Give reaction or reply to this topic to see the hidden content.

Hidden Content

    Give reaction or reply to this topic to see the hidden content.

Edited by frogen1145
Link to post
Share on other sites
30 minutes ago, tierrilopes said:

Replace files and recheck

Hidden Content

    Give reaction or reply to this topic to see the hidden content.
 1   559 B

Hidden Content

    Give reaction or reply to this topic to see the hidden content.
 1   20.86 kB

Compiled correct but client error , see below and I attached my uiinventory.py

Quote

uiInventory.py(line:322) __LoadWindow
ui.py(line:2708) GetChild

CollectInventoryWindow.LoadWindow.BindObject - <type 'exceptions.KeyError'>:'count_value'

0326 16:42:22381 :: ============================================================================================================
0326 16:42:22381 :: Abort!!!!


0326 16:42:22382 :: 
uiInventory.py(line:452) __LoadWindow
uiInventory.py(line:268) __init__
uiInventory.py(line:328) __LoadWindow
exception.py(line:36) Abort

InventoryWindow.LoadWindow.BindObject - <type 'exceptions.SystemExit'>:

0326 16:42:22382 :: ============================================================================================================
0326 16:42:22382 :: Abort!!!!
 

 

Hidden Content

    Give reaction or reply to this topic to see the hidden content.

Link to post
Share on other sites
6 hours ago, tierrilopes said:

Hello, i've been unable to access my Computer on the past few days, maybe someone else with the gui installed sees this before im on my off work day

No worries, No-one else didn't replay to it so I'm assume no one want to help me with it ;) ;) 

Cheers

Link to post
Share on other sites
  • 1 year later...
  • 4 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...