Jump to content

dzdev

Inactive Member
  • Posts

    65
  • Joined

  • Last visited

  • Feedback

    0%

Posts posted by dzdev

  1. hello .. i had a problem after installing new pet system client binary side

    syserr

    Spoiler

    0325 12:12:00466 :: Traceback (most recent call last):

    0325 12:12:00466 ::   File "introLogo.py", line 60, in OnUpdate

    0325 12:12:00466 ::   File "networkModule.py", line 177, in SetLoginPhase

    0325 12:12:00467 ::   File "system.py", line 130, in __pack_import

    0325 12:12:00467 ::   File "system.py", line 110, in _process_result

    0325 12:12:00467 ::   File "introLogin.py", line 21, in <module>

    0325 12:12:00467 ::   File "system.py", line 137, in __pack_import

    0325 12:12:00467 ::   File "Lib\md5.py", line 6, in <module>

    0325 12:12:00467 ::   File "system.py", line 137, in __pack_import

    0325 12:12:00467 ::   File "D:\clnt\clntlib\warnings.py", line 3, in <module>

    0325 12:12:00467 ::     
    0325 12:12:00467 :: import sys, re, types

    0325 12:12:00467 ::   File "system.py", line 137, in __pack_import

    0325 12:12:00467 ::   File "D:\clnt\clntlib\re.py", line 27, in <module>

    0325 12:12:00467 ::     
    0325 12:12:00467 :: from sre import *

    0325 12:12:00467 ::   File "system.py", line 137, in __pack_import

    0325 12:12:00468 ::   File "D:\clnt\clntlib\sre.py", line 97, in <module>

    0325 12:12:00468 ::     
    0325 12:12:00468 :: import sre_compile

    0325 12:12:00468 ::   File "system.py", line 137, in __pack_import

    0325 12:12:00468 ::   File "D:\clnt\clntlib\sre_compile.py", line 17, in <module>

    0325 12:12:00468 ::     
    0325 12:12:00468 :: assert _sre.MAGIC == MAGIC, "SRE module mismatch"

    0325 12:12:00468 :: AssertionError
    0325 12:12:00468 :: : 
    0325 12:12:00468 :: SRE module mismatch
    0325 12:12:00468 :: 

     

  2. Spoiler

    #include "stdafx.h"
    #include "utils.h"
    #include "config.h"
    #include "desc.h"
    #include "desc_manager.h"
    #include "char_manager.h"
    #include "item.h"
    #include "item_manager.h"
    #include "mob_manager.h"
    #include "battle.h"
    #include "pvp.h"
    #include "skill.h"
    #include "start_position.h"
    #include "profiler.h"
    #include "cmd.h"
    #include "dungeon.h"
    #include "log.h"
    #include "unique_item.h"
    #include "priv_manager.h"
    #include "db.h"
    #include "vector.h"
    #include "marriage.h"
    #include "arena.h"
    #include "regen.h"
    #include "monarch.h"
    #include "exchange.h"
    #include "shop_manager.h"
    #include "castle.h"
    #include "dev_log.h"
    #include "ani.h"
    #include "BattleArena.h"
    #include "packet.h"
    #include "party.h"
    #include "affect.h"
    #include "guild.h"
    #include "guild_manager.h"
    #include "questmanager.h"
    #include "questlua.h"
    #include "threeway_war.h"
    #include "BlueDragon.h"
    #include "DragonLair.h"

    DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp)
    {
        if (PLAYER_EXP_TABLE_MAX < ch->GetLevel())
        {
            double ret = 0.95;
            double factor = 0.1;

            for (ssize_t i=0 ; i < ch->GetLevel()-100 ; ++i)
            {
                if ( (i%10) == 0)
                    factor /= 2.0;

                ret *= 1.0 - factor;
            }

            ret = ret * static_cast<double>(exp);

            if (ret < 1.0)
                return 1;

            return static_cast<DWORD>(ret);
        }

        return exp;
    }

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

        return m_pointsInstant.position == POS_STANDING && !IsDead() && !IsStun();
    }

    void CHARACTER::BeginFight(LPCHARACTER pkVictim)
    {
        SetVictim(pkVictim);
        SetPosition(POS_FIGHTING);
        SetNextStatePulse(1);
    }

    bool CHARACTER::CanFight() const
    {
        return m_pointsInstant.position >= POS_FIGHTING ? true : false;
    }

    void CHARACTER::CreateFly(BYTE bType, LPCHARACTER pkVictim)
    {
        TPacketGCCreateFly packFly;

        packFly.bHeader         = HEADER_GC_CREATE_FLY;
        packFly.bType           = bType;
        packFly.dwStartVID      = GetVID();
        packFly.dwEndVID        = pkVictim->GetVID();

        PacketAround(&packFly, sizeof(TPacketGCCreateFly));
    }

    void CHARACTER::DistributeSP(LPCHARACTER pkKiller, int iMethod)
    {
        if (pkKiller->GetSP() >= pkKiller->GetMaxSP())
            return;

        bool bAttacking = (get_dword_time() - GetLastAttackTime()) < 3000;
        bool bMoving = (get_dword_time() - GetLastMoveTime()) < 3000;

        if (iMethod == 1)
        {
            int num = number(0, 3);

            if (!num)
            {
                int iLvDelta = GetLevel() - pkKiller->GetLevel();
                int iAmount = 0;

                if (iLvDelta >= 5)
                    iAmount = 10;
                else if (iLvDelta >= 0)
                    iAmount = 6;
                else if (iLvDelta >= -3)
                    iAmount = 2;

                if (iAmount != 0)
                {
                    iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;

                    if (iAmount >= 11)
                        CreateFly(FLY_SP_BIG, pkKiller);
                    else if (iAmount >= 7)
                        CreateFly(FLY_SP_MEDIUM, pkKiller);
                    else
                        CreateFly(FLY_SP_SMALL, pkKiller);

                    pkKiller->PointChange(POINT_SP, iAmount);
                }
            }
        }
        else
        {
            if (pkKiller->GetJob() == JOB_SHAMAN || (pkKiller->GetJob() == JOB_SURA && pkKiller->GetSkillGroup() == 2))
            {
                int iAmount;

                if (bAttacking)
                    iAmount = 2 + GetMaxSP() / 100;
                else if (bMoving)
                    iAmount = 3 + GetMaxSP() * 2 / 100;
                else
                    iAmount = 10 + GetMaxSP() * 3 / 100; // 평상시

                iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
                pkKiller->PointChange(POINT_SP, iAmount);
            }
            else
            {
                int iAmount;

                if (bAttacking)
                    iAmount = 2 + pkKiller->GetMaxSP() / 200;
                else if (bMoving)
                    iAmount = 2 + pkKiller->GetMaxSP() / 100;
                else
                {
                    // 평상시
                    if (pkKiller->GetHP() < pkKiller->GetMaxHP())
                        iAmount = 2 + (pkKiller->GetMaxSP() / 100); // 피 다 안찼을때
                    else
                        iAmount = 9 + (pkKiller->GetMaxSP() / 100); // 기본
                }

                iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
                pkKiller->PointChange(POINT_SP, iAmount);
            }
        }
    }


    bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType)
    {
        if (test_server)
            sys_log(0, "[TEST_SERVER] Attack : %s type %d, MobBattleType %d", GetName(), bType, !GetMobBattleType() ? 0 : GetMobAttackRange());
        //PROF_UNIT puAttack("Attack");
        if (!CanMove())
            return false;

        // CASTLE
        if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim))
            return false;
        // CASTLE

        DWORD dwCurrentTime = get_dword_time();

        if (IsPC())
        {
            if (IS_SPEED_HACK(this, pkVictim, dwCurrentTime))
                return false;

            if (bType == 0 && dwCurrentTime < GetSkipComboAttackByTime())
                return false;
        }
        else
        {
            MonsterChat(MONSTER_CHAT_ATTACK);
        }

        pkVictim->SetSyncOwner(this);

        if (pkVictim->CanBeginFight())
            pkVictim->BeginFight(this);

        int iRet;

        if (bType == 0)
        {
            //
            // 일반 공격
            //
            switch (GetMobBattleType())
            {
                case BATTLE_TYPE_MELEE:
                case BATTLE_TYPE_POWER:
                case BATTLE_TYPE_TANKER:
                case BATTLE_TYPE_SUPER_POWER:
                case BATTLE_TYPE_SUPER_TANKER:
                    iRet = battle_melee_attack(this, pkVictim);
                    break;

                case BATTLE_TYPE_RANGE:
                    FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING);
                    iRet = Shoot(0) ? BATTLE_DAMAGE : BATTLE_NONE;
                    break;

                case BATTLE_TYPE_MAGIC:
                    FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING);
                    iRet = Shoot(1) ? BATTLE_DAMAGE : BATTLE_NONE;
                    break;

                default:
                    sys_err("Unhandled battle type %d", GetMobBattleType());
                    iRet = BATTLE_NONE;
                    break;
            }
        }
        else
        {
            if (IsPC() == true)
            {
                if (dwCurrentTime - m_dwLastSkillTime > 1500)
                {
                    sys_log(1, "HACK: Too long skill using term. Name(%s) PID(%u) delta(%u)",
                            GetName(), GetPlayerID(), (dwCurrentTime - m_dwLastSkillTime));
                    return false;
                }
            }

            sys_log(1, "Attack call ComputeSkill %d %s", bType, pkVictim?pkVictim->GetName():"");
            iRet = ComputeSkill(bType, pkVictim);
        }

        //if (test_server && IsPC())
        //    sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet);
        if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD)
        {
            OnMove(true);
            pkVictim->OnMove();

            // only pc sets victim null. For npc, state machine will reset this.
            if (BATTLE_DEAD == iRet && IsPC())
                SetVictim(NULL);

            return true;
        }

        return false;
    }

    void CHARACTER::DeathPenalty(BYTE bTown)
    {
        sys_log(1, "DEATH_PERNALY_CHECK(%s) town(%d)", GetName(), bTown);

        Cube_close(this);

        if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true)
        {
            return;
        }
        
        if (GetLevel() < 10)
        {
            sys_log(0, "NO_DEATH_PENALTY_LESS_LV10(%s)", GetName());
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
            return;
        }

           if (number(0, 2))
        {
            sys_log(0, "NO_DEATH_PENALTY_LUCK(%s)", GetName());
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
            return;
        }

        if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY))
        {
            REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);

            // NO_DEATH_PENALTY_BUG_FIX 
            if (LC_IsYMIR()) // 천마 버전에서는 언제나 용신의 가호 아이템을 체크한다.
            {
                if (FindAffect(AFFECT_NO_DEATH_PENALTY))
                {
                    sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName());
                    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
                    RemoveAffect(AFFECT_NO_DEATH_PENALTY);
                    return;
                }
            }
            else if (!bTown) // 국제 버전에서는 제자리 부활시만 용신의 가호를 사용한다. (마을 복귀시는 경험치 패널티 없음)
            {
                if (FindAffect(AFFECT_NO_DEATH_PENALTY))
                {
                    sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName());
                    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
                    RemoveAffect(AFFECT_NO_DEATH_PENALTY);
                    return;
                }
            }
            // END_OF_NO_DEATH_PENALTY_BUG_FIX

            int iLoss = ((GetNextExp() * aiExpLossPercents[MINMAX(1, GetLevel(), PLAYER_EXP_TABLE_MAX)]) / 100);

            if (true == LC_IsYMIR())
            {
                if (PLAYER_EXP_TABLE_MAX < GetLevel())
                {
                    iLoss = MIN(500000, iLoss);
                }
                else
                {
                    iLoss = MIN(200000, iLoss);
                }
            }
            else if (true == LC_IsEurope())
            {
                iLoss = MIN(800000, iLoss);
            }

            if (bTown)
            {
                if (g_iUseLocale)
                {
                    iLoss = 0;
                }
                else
                {
                    iLoss -= iLoss / 3;
                }
            }

            if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS))
                iLoss /= 2;

            sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, aiExpLossPercents[MIN(gPlayerMaxLevel, GetLevel())]);

            PointChange(POINT_EXP, -iLoss, true);
        }
    }

    bool CHARACTER::IsStun() const
    {
        if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN))
            return true;

        return false;
    }

    EVENTFUNC(StunEvent)
    {
        char_event_info* info = dynamic_cast<char_event_info*>( event->info );

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

        LPCHARACTER ch = info->ch;

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

    void CHARACTER::Stun()
    {
        if (IsStun())
            return;

        if (IsDead())
            return;

        if (!IsPC() && m_pkParty)
        {
            m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0);
        }

        sys_log(1, "%s: Stun %p", GetName(), this);

        PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY));
        PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY));

        CloseMyShop();

        event_cancel(&m_pkRecoveryEvent); // 회복 이벤트를 죽인다.

        TPacketGCStun pack;
        pack.header    = HEADER_GC_STUN;
        pack.vid    = m_vid;
        PacketAround(&pack, sizeof(pack));

        SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);

        if (m_pkStunEvent)
            return;

        char_event_info* info = AllocEventInfo<char_event_info>();

        info->ch = this;

        m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3));
    }

    EVENTINFO(SCharDeadEventInfo)
    {
        bool isPC;
        uint32_t dwID;

        SCharDeadEventInfo()
        : isPC(0)
        , dwID(0)
        {
        }
    };

    EVENTFUNC(dead_event)
    {
        const SCharDeadEventInfo* info = dynamic_cast<SCharDeadEventInfo*>(event->info);

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

        LPCHARACTER ch = NULL;

        if (true == info->isPC)
        {
            ch = CHARACTER_MANAGER::instance().FindByPID( info->dwID );
        }
        else
        {
            ch = CHARACTER_MANAGER::instance().Find( info->dwID );
        }

        if (NULL == ch)
        {
            sys_err("DEAD_EVENT: cannot find char pointer with %s id(%d)", info->isPC ? "PC" : "MOB", info->dwID );
            return 0;
        }

        ch->m_pkDeadEvent = NULL;

        if (ch->GetDesc())
        {
            ch->GetDesc()->SetPhase(PHASE_GAME);

            ch->SetPosition(POS_STANDING);

            PIXEL_POSITION pos;

            if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos))
                ch->WarpSet(pos.x, pos.y);
            else
            {
                sys_err("cannot find spawn position (name %s)", ch->GetName());
                ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
            }

            ch->PointChange(POINT_HP, (ch->GetMaxHP() / 2) - ch->GetHP(), true);

            ch->DeathPenalty(0);

            ch->StartRecoveryEvent();

            ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow");
        }
        else
        {
            if (ch->IsMonster() == true)
            {
                if (ch->IsRevive() == false && ch->HasReviverInParty() == true)
                {
                    ch->SetPosition(POS_STANDING);
                    ch->SetHP(ch->GetMaxHP());

                    ch->ViewReencode();

                    ch->SetAggressive();
                    ch->SetRevive(true);

                    return 0;
                }
            }

            M2_DESTROY_CHARACTER(ch);
        }

        return 0;
    }

    bool CHARACTER::IsDead() const
    {
        if (m_pointsInstant.position == POS_DEAD)
            return true;

        return false;
    }

    #define GetGoldMultipler() (distribution_test_server ? 3 : 1)

    void CHARACTER::RewardGold(LPCHARACTER pkAttacker)
    {
        // ADD_PREMIUM
        bool isAutoLoot = 
            (pkAttacker->GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 ||
             pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT))
            ? true : false; // 제3의 손
        // END_OF_ADD_PREMIUM

        PIXEL_POSITION pos;

        if (!isAutoLoot)
            if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos))
                return;

        int iTotalGold = 0;
        //
        // --------- 돈 드롭 확률 계산 ----------
        //
        int iGoldPercent = MobRankStats[GetMobRank()].iGoldPercent;

        if (pkAttacker->IsPC())
            iGoldPercent = iGoldPercent * (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD_DROP)) / 100;

        if (pkAttacker->GetPoint(POINT_MALL_GOLDBONUS))
            iGoldPercent += (iGoldPercent * pkAttacker->GetPoint(POINT_MALL_GOLDBONUS) / 100);

        iGoldPercent = iGoldPercent * CHARACTER_MANAGER::instance().GetMobGoldDropRate(pkAttacker) / 100;

        // ADD_PREMIUM
        if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 ||
                pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD))
            iGoldPercent += iGoldPercent;
        // END_OF_ADD_PREMIUM

        if (iGoldPercent > 100) 
            iGoldPercent = 100;

        int iPercent;

        if (GetMobRank() >= MOB_RANK_BOSS)
            iPercent = ((iGoldPercent * PERCENT_LVDELTA_BOSS(pkAttacker->GetLevel(), GetLevel())) / 100);
        else
            iPercent = ((iGoldPercent * PERCENT_LVDELTA(pkAttacker->GetLevel(), GetLevel())) / 100);
        //int iPercent = CALCULATE_VALUE_LVDELTA(pkAttacker->GetLevel(), GetLevel(), iGoldPercent);

        if (number(1, 100) > iPercent)
            return;

        int iGoldMultipler = GetGoldMultipler();

        if (1 == number(1, 50000)) // 1/50000 확률로 돈이 10배
            iGoldMultipler *= 10;
        else if (1 == number(1, 10000)) // 1/10000 확률로 돈이 5배
            iGoldMultipler *= 5;

        // 개인 적용
        if (pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS))
            if (number(1, 100) <= pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS))
                iGoldMultipler *= 2;

        //
        // --------- 돈 드롭 배수 결정 ----------
        //
        if (test_server)
            pkAttacker->ChatPacket(CHAT_TYPE_PARTY, "gold_mul %d rate %d", iGoldMultipler, CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker));

        //
        // --------- 실제 드롭 처리 -------------
        // 
        LPITEM item;

        int iGold10DropPct = 100;
        iGold10DropPct = (iGold10DropPct * 100) / (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD10_DROP));

        // MOB_RANK가 BOSS보다 높으면 무조건 돈폭탄
        if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0)
        {
            if (1 == number(1, iGold10DropPct))
                iGoldMultipler *= 10; // 1% 확률로 돈 10배

            int iSplitCount = number(25, 35);

            for (int i = 0; i < iSplitCount; ++i)
            {
                int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax) / iSplitCount;
                if (test_server)
                    sys_log(0, "iGold %d", iGold);
                iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
                iGold *= iGoldMultipler;

                if (iGold == 0)
                {
                    continue ;
                }

                if (test_server)
                {
                    sys_log(0, "Drop Moeny MobGoldAmountRate %d %d", CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker), iGoldMultipler);
                    sys_log(0, "Drop Money gold %d GoldMin %d GoldMax %d", iGold, GetMobTable().dwGoldMax, GetMobTable().dwGoldMax);
                }

                // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음
                if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold)))
                {
                    pos.x = GetX() + ((number(-14, 14) + number(-14, 14)) * 23);
                    pos.y = GetY() + ((number(-14, 14) + number(-14, 14)) * 23);

                    item->AddToGround(GetMapIndex(), pos);
                    item->StartDestroyEvent();

                    iTotalGold += iGold; // Total gold
                }
            }
        }
        // 1% 확률로 돈을 10개 떨어 뜨린다. (10배 드롭임)
        else if (1 == number(1, iGold10DropPct))
        {
            //
            // 돈 폭탄식 드롭
            //
            for (int i = 0; i < 10; ++i)
            {
                int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
                iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
                iGold *= iGoldMultipler;

                if (iGold == 0)
                {
                    continue;
                }

                // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음
                if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold)))
                {
                    pos.x = GetX() + (number(-7, 7) * 20);
                    pos.y = GetY() + (number(-7, 7) * 20);

                    item->AddToGround(GetMapIndex(), pos);
                    item->StartDestroyEvent();

                    iTotalGold += iGold; // Total gold
                }
            }
        }
        else
        {
            //
            // 일반적인 방식의 돈 드롭
            //
            int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
            iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
            iGold *= iGoldMultipler;

            int iSplitCount;

            if (iGold >= 3 && !LC_IsYMIR()) 
                iSplitCount = number(1, 3);
            else if (GetMobRank() >= MOB_RANK_BOSS)
            {
                iSplitCount = number(3, 10);

                if ((iGold / iSplitCount) == 0)
                    iSplitCount = 1;
            }
            else
                iSplitCount = 1;

            if (iGold != 0)
            {
                iTotalGold += iGold; // Total gold

                for (int i = 0; i < iSplitCount; ++i)
                {
                    if (isAutoLoot)
                    {
                        pkAttacker->GiveGold(iGold / iSplitCount);
                    }
                    else if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount)))
                    {
                        pos.x = GetX() + (number(-7, 7) * 20);
                        pos.y = GetY() + (number(-7, 7) * 20);

                        item->AddToGround(GetMapIndex(), pos);
                        item->StartDestroyEvent();
                    }
                }
            }
        }

        DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold);
    }

    void CHARACTER::Reward(bool bItemDrop)
    {
        if (GetRaceNum() == 5001) // 왜구는 돈을 무조건 드롭
        {
            PIXEL_POSITION pos;

            if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos))
                return;

            LPITEM item;
            int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
            iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(NULL) / 100;
            iGold *= GetGoldMultipler();
            int iSplitCount = number(25, 35);

            sys_log(0, "WAEGU Dead gold %d split %d", iGold, iSplitCount);

            for (int i = 1; i <= iSplitCount; ++i)
            {
                if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount)))
                {
                    if (i != 0)
                    {
                        pos.x = number(-7, 7) * 20;
                        pos.y = number(-7, 7) * 20;

                        pos.x += GetX();
                        pos.y += GetY();
                    }

                    item->AddToGround(GetMapIndex(), pos);
                    item->StartDestroyEvent();
                }
            }
            return;
        }

        //PROF_UNIT puReward("Reward");
           LPCHARACTER pkAttacker = DistributeExp();

        if (!pkAttacker)
            return;

        //PROF_UNIT pu1("r1");
        if (pkAttacker->IsPC())
        {
            if (GetLevel() - pkAttacker->GetLevel() >= -10)
                if (pkAttacker->GetRealAlignment() < 0)
                {
                    if (pkAttacker->IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_KILL))
                        pkAttacker->UpdateAlignment(14);
                    else
                        pkAttacker->UpdateAlignment(7);
                }
                else
                    pkAttacker->UpdateAlignment(2);

            pkAttacker->SetQuestNPCID(GetVID());
            quest::CQuestManager::instance().Kill(pkAttacker->GetPlayerID(), GetRaceNum());
            CHARACTER_MANAGER::instance().KillLog(GetRaceNum());

            if (!number(0, 9))
            {
                if (pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY))
                {
                    int iHP = pkAttacker->GetMaxHP() * pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY) / 100;
                    pkAttacker->PointChange(POINT_HP, iHP);
                    CreateFly(FLY_HP_SMALL, pkAttacker);
                }

                if (pkAttacker->GetPoint(POINT_KILL_SP_RECOVER))
                {
                    int iSP = pkAttacker->GetMaxSP() * pkAttacker->GetPoint(POINT_KILL_SP_RECOVER) / 100;
                    pkAttacker->PointChange(POINT_SP, iSP);
                    CreateFly(FLY_SP_SMALL, pkAttacker);
                }
            }
        }
        //pu1.Pop();

        if (!bItemDrop)
            return;

        PIXEL_POSITION pos = GetXYZ();

        if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), pos.x, pos.y, pos))
            return;

        //
        // 돈 드롭
        //
        //PROF_UNIT pu2("r2");
        if (test_server)
            sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName());
        RewardGold(pkAttacker);
        //pu2.Pop();

        //
        // 아이템 드롭
        //
        //PROF_UNIT pu3("r3");
        LPITEM item;

        static std::vector<LPITEM> s_vec_item;
        s_vec_item.clear();

        if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item))
        {
            if (s_vec_item.size() == 0);
            else if (s_vec_item.size() == 1)
            {
                item = s_vec_item[0];
                item->AddToGround(GetMapIndex(), pos);

                if (CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()) == false)
                {
                    item->SetOwnership(pkAttacker);
                }

                item->StartDestroyEvent();

                pos.x = number(-7, 7) * 20;
                pos.y = number(-7, 7) * 20;
                pos.x += GetX();
                pos.y += GetY();

                sys_log(0, "DROP_ITEM: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName());
            }
            else
            {
                int iItemIdx = s_vec_item.size() - 1;

                std::priority_queue<std::pair<int, LPCHARACTER> > pq;

                int total_dam = 0;

                for (TDamageMap::iterator it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it)
                {
                    int iDamage = it->second.iTotalDamage;
                    if (iDamage > 0)
                    {
                        LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first);

                        if (ch)
                        {
                            pq.push(std::make_pair(iDamage, ch));
                            total_dam += iDamage;
                        }
                    }
                }

                std::vector<LPCHARACTER> v;

                while (!pq.empty() && pq.top().first * 10 >= total_dam)
                {
                    v.push_back(pq.top().second);
                    pq.pop();
                }

                if (v.empty())
                {
                    // 데미지를 특별히 많이 준 사람이 없으니 소유권 없음
                    while (iItemIdx >= 0)
                    {
                        item = s_vec_item[iItemIdx--];

                        if (!item)
                        {
                            sys_err("item null in vector idx %d", iItemIdx + 1);
                            continue;
                        }

                        item->AddToGround(GetMapIndex(), pos);
                        // 10% 이하 데미지 준 사람끼리는 소유권없음
                        //item->SetOwnership(pkAttacker);
                        item->StartDestroyEvent();

                        pos.x = number(-7, 7) * 20;
                        pos.y = number(-7, 7) * 20;
                        pos.x += GetX();
                        pos.y += GetY();

                        sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName());
                    }
                }
                else
                {
                    // 데미지 많이 준 사람들 끼리만 소유권 나눠가짐
                    std::vector<LPCHARACTER>::iterator it = v.begin();

                    while (iItemIdx >= 0)
                    {
                        item = s_vec_item[iItemIdx--];

                        if (!item)
                        {
                            sys_err("item null in vector idx %d", iItemIdx + 1);
                            continue;
                        }

                        item->AddToGround(GetMapIndex(), pos);

                        LPCHARACTER ch = *it;

                        if (ch->GetParty())
                            ch = ch->GetParty()->GetNextOwnership(ch, GetX(), GetY());

                        ++it;

                        if (it == v.end())
                            it = v.begin();

                        if (CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()) == false)
                        {
                            item->SetOwnership(ch);
                        }

                        item->StartDestroyEvent();

                        pos.x = number(-7, 7) * 20;
                        pos.y = number(-7, 7) * 20;
                        pos.x += GetX();
                        pos.y += GetY();

                        sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName());
                    }
                }
            }
        }

        m_map_kDamage.clear();
    }

    struct TItemDropPenalty
    {
        int iInventoryPct;        // Range: 1 ~ 1000
        int iInventoryQty;        // Range: --
        int iEquipmentPct;        // Range: 1 ~ 100
        int iEquipmentQty;        // Range: --
    };

    TItemDropPenalty aItemDropPenalty_kor[9] =
    {
        {   0,   0,  0,  0 },    // 선왕
        {   0,   0,  0,  0 },    // 영웅
        {   0,   0,  0,  0 },    // 성자
        {   0,   0,  0,  0 },    // 지인
        {   0,   0,  0,  0 },    // 양민
        {  25,   1,  5,  1 },    // 낭인
        {  50,   2, 10,  1 },    // 악인
        {  75,   4, 15,  1 },    // 마두
        { 100,   8, 20,  1 },    // 패왕
    };

    void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller)
    {
        // 개인상점을 연 상태에서는 아이템을 드롭하지 않는다.
        if (GetMyShop())
            return;

        if (false == LC_IsYMIR())
        {
            if (GetLevel() < 50)
                return;
        }
        
        if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true)
        {
            return;
        }

        struct TItemDropPenalty * table = &aItemDropPenalty_kor[0];

        if (GetLevel() < 10)
            return;

        int iAlignIndex;

        if (GetRealAlignment() >= 120000)
            iAlignIndex = 0;
        else if (GetRealAlignment() >= 80000)
            iAlignIndex = 1;
        else if (GetRealAlignment() >= 40000)
            iAlignIndex = 2;
        else if (GetRealAlignment() >= 10000)
            iAlignIndex = 3;
        else if (GetRealAlignment() >= 0)
            iAlignIndex = 4;
        else if (GetRealAlignment() > -40000)
            iAlignIndex = 5;
        else if (GetRealAlignment() > -80000)
            iAlignIndex = 6;
        else if (GetRealAlignment() > -120000)
            iAlignIndex = 7;
        else
            iAlignIndex = 8;

        std::vector<std::pair<LPITEM, int> > vec_item;
        LPITEM pkItem;
        int    i;
        bool isDropAllEquipments = false;

        TItemDropPenalty & r = table[iAlignIndex];
        sys_log(0, "%s align %d inven_pct %d equip_pct %d", GetName(), iAlignIndex, r.iInventoryPct, r.iEquipmentPct);

        bool bDropInventory = r.iInventoryPct >= number(1, 1000);
        bool bDropEquipment = r.iEquipmentPct >= number(1, 100);
        bool bDropAntiDropUniqueItem = false;

        if ((bDropInventory || bDropEquipment) && IsEquipUniqueItem(UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY))
        {
            bDropInventory = false;
            bDropEquipment = false;
            bDropAntiDropUniqueItem = true;
        }

        if (bDropInventory) // Drop Inventory
        {
            std::vector<BYTE> vec_bSlots;

            for (i = 0; i < INVENTORY_MAX_NUM; ++i)
                if (GetInventoryItem(i))
                    vec_bSlots.push_back(i);

            if (!vec_bSlots.empty())
            {
                random_shuffle(vec_bSlots.begin(), vec_bSlots.end());

                int iQty = MIN(vec_bSlots.size(), r.iInventoryQty);

                if (iQty)
                    iQty = number(1, iQty);

                for (i = 0; i < iQty; ++i)
                {
                    pkItem = GetInventoryItem(vec_bSlots);

                    if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP))
                        continue;

                    SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255);
                    vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY));
                }
            }
            else if (iAlignIndex == 8)
                isDropAllEquipments = true;
        }

        if (bDropEquipment) // Drop Equipment
        {
            std::vector<BYTE> vec_bSlots;

            for (i = 0; i < WEAR_MAX_NUM; ++i)
                if (GetWear(i))
                    vec_bSlots.push_back(i);

            if (!vec_bSlots.empty())
            {
                random_shuffle(vec_bSlots.begin(), vec_bSlots.end());
                int iQty;

                if (isDropAllEquipments)
                    iQty = vec_bSlots.size();
                else
                    iQty = MIN(vec_bSlots.size(), number(1, r.iEquipmentQty));

                if (iQty)
                    iQty = number(1, iQty);

                for (i = 0; i < iQty; ++i)
                {
                    pkItem = GetWear(vec_bSlots);

                    if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP))
                        continue;

                    SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255);
                    vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
                }
            }
        }

        if (bDropAntiDropUniqueItem)
        {
            LPITEM pkItem;

            pkItem = GetWear(WEAR_UNIQUE1);

            if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)
            {
                SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE1, 255);
                vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
            }

            pkItem = GetWear(WEAR_UNIQUE2);

            if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)
            {
                SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE2, 255);
                vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
            }
        }

        {
            PIXEL_POSITION pos;
            pos.x = GetX();
            pos.y = GetY();

            unsigned int i;

            for (i = 0; i < vec_item.size(); ++i)
            {
                LPITEM item = vec_item.first;
                int window = vec_item.second;

                item->AddToGround(GetMapIndex(), pos);
                item->StartDestroyEvent();

                sys_log(0, "DROP_ITEM_PK: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName());
                LogManager::instance().ItemLog(this, item, "DEAD_DROP", (window == INVENTORY) ? "INVENTORY" : ((window == EQUIPMENT) ? "EQUIPMENT" : ""));

                pos.x = GetX() + number(-7, 7) * 20;
                pos.y = GetY() + number(-7, 7) * 20;
            }
        }
    }

    class FPartyAlignmentCompute
    {
        public:
            FPartyAlignmentCompute(int iAmount, int x, int y)
            {
                m_iAmount = iAmount;
                m_iCount = 0;
                m_iStep = 0;
                m_iKillerX = x;
                m_iKillerY = y;
            }

            void operator () (LPCHARACTER pkChr)
            {
                if (DISTANCE_APPROX(pkChr->GetX() - m_iKillerX, pkChr->GetY() - m_iKillerY) < PARTY_DEFAULT_RANGE)
                {
                    if (m_iStep == 0)
                    {
                        ++m_iCount;
                    }
                    else
                    {
                        pkChr->UpdateAlignment(m_iAmount / m_iCount);
                    }
                }
            }

            int m_iAmount;
            int m_iCount;
            int m_iStep;

            int m_iKillerX;
            int m_iKillerY;
    };

    void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead)
    {
        if (IsDead())
            return;

        {
            if (IsHorseRiding())
            {
                StopRiding();
            }
            else if (GetMountVnum())
            {
                RemoveAffect(AFFECT_MOUNT_BONUS);
                m_dwMountVnum = 0;
                UnEquipSpecialRideUniqueItem();

                UpdatePacket();
            }
        }

        if (!pkKiller && m_dwKillerPID)
            pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID);

        m_dwKillerPID = 0; // 반드시 초기화 해야함 DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE

        bool isAgreedPVP = false;
        bool isUnderGuildWar = false;
        bool isDuel = false;
        bool isForked = false;

        if (pkKiller && pkKiller->IsPC())
        {
            if (pkKiller->m_pkChrTarget == this)
                pkKiller->SetTarget(NULL);

            if (!IsPC() && pkKiller->GetDungeon())
                pkKiller->GetDungeon()->IncKillCount(pkKiller, this);

            isAgreedPVP = CPVPManager::instance().Dead(this, pkKiller->GetPlayerID());
            isDuel = CArenaManager::instance().OnDead(pkKiller, this);

            if (IsPC())
            {
                CGuild * g1 = GetGuild();
                CGuild * g2 = pkKiller->GetGuild();

                if (g1 && g2)
                    if (g1->UnderWar(g2->GetID()))
                        isUnderGuildWar = true;

                pkKiller->SetQuestNPCID(GetVID());
                quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC);
                CGuildManager::instance().Kill(pkKiller, this);
            }
        }

        //CHECK_FORKEDROAD_WAR
        if (IsPC())
        {
            if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex()))
                isForked = true;
        }
        //END_CHECK_FORKEDROAD_WAR

        if (pkKiller &&
                !isAgreedPVP &&
                !isUnderGuildWar &&
                IsPC() &&
                !isDuel &&
                !isForked &&
                !IS_CASTLE_MAP(GetMapIndex()))
        {
            if (GetGMLevel() == GM_PLAYER || test_server)
            {
                ItemDropPenalty(pkKiller);
            }
        }

        // CASTLE_SIEGE
        if (IS_CASTLE_MAP(GetMapIndex()))
        {
            if (CASTLE_FROG_VNUM == GetRaceNum())
                castle_frog_die(this, pkKiller);
            else if (castle_is_guard_vnum(GetRaceNum()))
                castle_guard_die(this, pkKiller);
            else if (castle_is_tower_vnum(GetRaceNum()))
                castle_tower_die(this, pkKiller);
        }
        // CASTLE_SIEGE

        if (true == isForked)
        {
            CThreeWayWar::instance().onDead( this, pkKiller );
        }

        SetPosition(POS_DEAD);
        ClearAffect(true);

        if (pkKiller && IsPC())
        {
            if (!pkKiller->IsPC())
            {
                if (!isForked)
                {
                    sys_log(1, "DEAD: %s %p WITH PENALTY", GetName(), this);
                    SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
                    LogManager::instance().CharLog(this, pkKiller->GetRaceNum(), "DEAD_BY_NPC", pkKiller->GetName());
                }
            }
            else
            {
                sys_log(1, "DEAD_BY_PC: %s %p KILLER %s %p", GetName(), this, pkKiller->GetName(), get_pointer(pkKiller));
                REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);

                if (GetEmpire() != pkKiller->GetEmpire())
                {
                    int iEP = MIN(GetPoint(POINT_EMPIRE_POINT), pkKiller->GetPoint(POINT_EMPIRE_POINT));

                    PointChange(POINT_EMPIRE_POINT, -(iEP / 10));
                    pkKiller->PointChange(POINT_EMPIRE_POINT, iEP / 5);

                    if (GetPoint(POINT_EMPIRE_POINT) < 10)
                    {
                        // TODO : 입구로 날리는 코드를 넣어야 한다.
                    }

                    char buf[256];
                    snprintf(buf, sizeof(buf),
                            "%d %d %d %s %d %d %d %s",
                            GetEmpire(), GetAlignment(), GetPKMode(), GetName(),
                            pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName());

                    LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf);
                }
                else
                {
                    if (!isAgreedPVP && !isUnderGuildWar && !IsKillerMode() && GetAlignment() >= 0 && !isDuel && !isForked)
                    {
                        int iNoPenaltyProb = 0;

                        if (g_iUseLocale)
                        {
                            if (pkKiller->GetAlignment() >= 0)    // 1/3 percent down
                                iNoPenaltyProb = 33;
                            else                // 4/5 percent down
                                iNoPenaltyProb = 20;
                        }

                        if (number(1, 100) < iNoPenaltyProb)
                            pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 보호로 아이템이 떨어지지 않았습니다."));
                        else
                        {
                            if (g_iUseLocale && pkKiller->GetParty())
                            {
                                FPartyAlignmentCompute f(-20000, pkKiller->GetX(), pkKiller->GetY());
                                pkKiller->GetParty()->ForEachOnlineMember(f);

                                if (f.m_iCount == 0)
                                    pkKiller->UpdateAlignment(-20000);
                                else
                                {
                                    sys_log(0, "ALIGNMENT PARTY count %d amount %d", f.m_iCount, f.m_iAmount);

                                    f.m_iStep = 1;
                                    pkKiller->GetParty()->ForEachOnlineMember(f);
                                }
                            }
                            else
                                pkKiller->UpdateAlignment(-20000);
                        }
                    }

                    char buf[256];
                    snprintf(buf, sizeof(buf),
                            "%d %d %d %s %d %d %d %s",
                            GetEmpire(), GetAlignment(), GetPKMode(), GetName(),
                            pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName());

                    LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf);
                }
            }
        }
        else
        {
            sys_log(1, "DEAD: %s %p", GetName(), this);
            REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
        }

        ClearSync();

        //sys_log(1, "stun cancel %s[%d]", GetName(), (DWORD)GetVID());
        event_cancel(&m_pkStunEvent); // 기절 이벤트는 죽인다.

        if (IsPC())
        {
            m_dwLastDeadTime = get_dword_time();
            SetKillerMode(false);
            GetDesc()->SetPhase(PHASE_DEAD);
        }
        else
        {
            // 가드에게 공격받은 몬스터는 보상이 없어야 한다.
            if (!IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD))
            {
                if (!(pkKiller && pkKiller->IsPC() && pkKiller->GetGuild() && pkKiller->GetGuild()->UnderAnyWar(GUILD_WAR_TYPE_FIELD)))
                {
                    // 부활하는 몬스터는 보상을 주지 않는다.
                    if (GetMobTable().dwResurrectionVnum)
                    {
                        // DUNGEON_MONSTER_REBIRTH_BUG_FIX
                        LPCHARACTER chResurrect = CHARACTER_MANAGER::instance().SpawnMob(GetMobTable().dwResurrectionVnum, GetMapIndex(), GetX(), GetY(), GetZ(), true, (int) GetRotation());
                        if (GetDungeon() && chResurrect)
                        {
                            chResurrect->SetDungeon(GetDungeon());
                        }
                        // END_OF_DUNGEON_MONSTER_REBIRTH_BUG_FIX

                        Reward(false);
                    }
                    else if (IsRevive() == true)
                    {
                        Reward(false);
                    }
                    else
                    {
                        Reward(true); // Drops gold, item, etc..
                    }
                }
                else
                {
                    if (pkKiller->m_dwUnderGuildWarInfoMessageTime < get_dword_time())
                    {
                        pkKiller->m_dwUnderGuildWarInfoMessageTime = get_dword_time() + 60000;
                        pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드전중에는 사냥에 따른 이익이 없습니다."));
                    }
                }
            }
        }

        // BOSS_KILL_LOG
        if (GetMobRank() >= MOB_RANK_BOSS && pkKiller && pkKiller->IsPC())
        {
            char buf[51];
            snprintf(buf, sizeof(buf), "%d %ld", g_bChannel, pkKiller->GetMapIndex());
            if (IsStone())
                LogManager::instance().CharLog(pkKiller, GetRaceNum(), "STONE_KILL", buf);
            else
                LogManager::instance().CharLog(pkKiller, GetRaceNum(), "BOSS_KILL", buf);
        }
        // END_OF_BOSS_KILL_LOG

        TPacketGCDead pack;
        pack.header    = HEADER_GC_DEAD;
        pack.vid    = m_vid;
        PacketAround(&pack, sizeof(pack));

        REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);

        // 플레이어 캐릭터이면
        if (GetDesc() != NULL) {
            //
            // 클라이언트에 에펙트 패킷을 다시 보낸다.
            //
            itertype(m_list_pkAffect) it = m_list_pkAffect.begin();

            while (it != m_list_pkAffect.end())
                SendAffectAddPacket(GetDesc(), *it++);
        }

        //
        // Dead 이벤트 생성,
        //
        // Dead 이벤트에서는 몬스터의 경우 몇초 후에 Destroy 되도록 해주며,
        // PC의 경우 3분 있다가 마을에서 나오도록 해 준다. 3분 내에는 유저로부터
        // 마을에서 시작할 건지, 여기서 시작할 건지 결정을 받는다.
        if (isDuel == false)
        {
            if (m_pkDeadEvent)
            {
                sys_log(1, "DEAD_EVENT_CANCEL: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
                event_cancel(&m_pkDeadEvent);
            }

            if (IsStone())
                ClearStone();

            if (GetDungeon())
            {
                GetDungeon()->DeadCharacter(this);
            }

            SCharDeadEventInfo* pEventInfo = AllocEventInfo<SCharDeadEventInfo>();

            if (IsPC())
            {
                pEventInfo->isPC = true;
                pEventInfo->dwID = this->GetPlayerID();

                m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(180));
            }
            else
            {
                pEventInfo->isPC = false;
                pEventInfo->dwID = this->GetVID();

                if (IsRevive() == false && HasReviverInParty() == true)
                {
                    m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3));
                }
                else
                {
                    m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(10));
                }
            }

            sys_log(1, "DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
        }

        if (m_pkExchange != NULL)
        {
            m_pkExchange->Cancel();
        }

        if (IsCubeOpen() == true)
        {
            Cube_close(this);
        }
        
        CShopManager::instance().StopShopping(this);
        CloseMyShop();
        CloseSafebox();

        if (true == IsMonster() && 2493 == GetMobTable().dwVnum)
        {
            if (NULL != pkKiller && NULL != pkKiller->GetGuild())
            {
                CDragonLairManager::instance().OnDragonDead( this, pkKiller->GetGuild()->GetID() );
            }
            else
            {
                sys_err("DragonLair: Dragon killed by nobody");
            }
        }
    }

    struct FuncSetLastAttacked
    {
        FuncSetLastAttacked(DWORD dwTime) : m_dwTime(dwTime)
        {
        }

        void operator () (LPCHARACTER ch)
        {
            ch->SetLastAttacked(m_dwTime);
        }

        DWORD m_dwTime;
    };

    void CHARACTER::SetLastAttacked(DWORD dwTime)
    {
        assert(m_pkMobInst != NULL);

        m_pkMobInst->m_dwLastAttackedTime = dwTime;
        m_pkMobInst->m_posLastAttacked = GetXYZ();
    }

    void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag)
    {
        if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this))
        {
            TPacketGCDamageInfo damageInfo;
            memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo));

            damageInfo.header = HEADER_GC_DAMAGE_INFO;
            damageInfo.dwVID = (DWORD)GetVID();
            damageInfo.flag = DamageFlag;
            damageInfo.damage = Damage;

            if (GetDesc() != NULL)
            {
                GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
            }

            if (pAttacker->GetDesc() != NULL)
            {
                pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
            }
            /*
               if (GetArenaObserverMode() == false && GetArena() != NULL)
               {
               GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo));
               }
             */        
        }
    }

    //
    // CHARACTER::Damage 메소드는 this가 데미지를 입게 한다.
    //
    // Arguments
    //    pAttacker        : 공격자
    //    dam        : 데미지
    //    EDamageType    : 어떤 형식의 공격인가?
    //    
    // Return value
    //    true        : dead
    //    false        : not dead yet
    // 
    bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead
    {
        if (DAMAGE_TYPE_MAGIC == type)
        {
            dam = (int)((float)dam * (100 + (pAttacker->GetPoint(POINT_MAGIC_ATT_BONUS_PER) + pAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100.f + 0.5f);
        }
        if (GetRaceNum() == 5001)
        {
            bool bDropMoney = false;
            int iPercent = (GetHP() * 100) / GetMaxHP();

            if (iPercent <= 10 && GetMaxSP() < 5)
            {
                SetMaxSP(5);
                bDropMoney = true;
            }
            else if (iPercent <= 20 && GetMaxSP() < 4)
            {
                SetMaxSP(4);
                bDropMoney = true;
            }
            else if (iPercent <= 40 && GetMaxSP() < 3)
            {
                SetMaxSP(3);
                bDropMoney = true;
            }
            else if (iPercent <= 60 && GetMaxSP() < 2)
            {
                SetMaxSP(2);
                bDropMoney = true;
            }
            else if (iPercent <= 80 && GetMaxSP() < 1)
            {
                SetMaxSP(1);
                bDropMoney = true;
            }

            if (bDropMoney)
            {
                DWORD dwGold = 1000;
                int iSplitCount = number(10, 13);

                sys_log(0, "WAEGU DropGoldOnHit %d times", GetMaxSP());

                for (int i = 1; i <= iSplitCount; ++i)
                {
                    PIXEL_POSITION pos;
                    LPITEM item;

                    if ((item = ITEM_MANAGER::instance().CreateItem(1, dwGold / iSplitCount)))
                    {
                        if (i != 0)
                        {
                            pos.x = (number(-14, 14) + number(-14, 14)) * 20;
                            pos.y = (number(-14, 14) + number(-14, 14)) * 20;

                            pos.x += GetX();
                            pos.y += GetY();
                        }

                        item->AddToGround(GetMapIndex(), pos);
                        item->StartDestroyEvent();
                    }
                }
            }
        }

        // 평타가 아닐 때는 공포 처리
        if (type != DAMAGE_TYPE_NORMAL && type != DAMAGE_TYPE_NORMAL_RANGE)
        {
            if (IsAffectFlag(AFF_TERROR))
            {
                int pct = GetSkillPower(SKILL_TERROR) / 400;

                if (number(1, 100) <= pct)
                    return false;
            }
        }

        int iCurHP = GetHP();
        int iCurSP = GetSP();

        bool IsCritical = false;
        bool IsPenetrate = false;
        bool IsDeathBlow = false;

        enum DamageFlag
        {
            DAMAGE_NORMAL    = (1 << 0),
            DAMAGE_POISON    = (1 << 1),
            DAMAGE_DODGE    = (1 << 2),
            DAMAGE_BLOCK    = (1 << 3),
            DAMAGE_PENETRATE= (1 << 4),
            DAMAGE_CRITICAL = (1 << 5),
        };

        //PROF_UNIT puAttr("Attr");

        //
        // 마법형 스킬과, 레인지형 스킬은(궁자객) 크리티컬과, 관통공격 계산을 한다.
        // 원래는 하지 않아야 하는데 Nerf(다운밸런스)패치를 할 수 없어서 크리티컬과
        // 관통공격의 원래 값을 쓰지 않고, /2 이상하여 적용한다.
        // 
        // 무사 이야기가 많아서 밀리 스킬도 추가
        //
        // 20091109 : 무사가 결과적으로 엄청나게 강해진 것으로 결론남, 독일 기준 무사 비율 70% 육박
        //
        if (type == DAMAGE_TYPE_MELEE || type == DAMAGE_TYPE_RANGE || type == DAMAGE_TYPE_MAGIC)
        {
            if (pAttacker)
            {
                // 크리티컬
                int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT);

                if (!IsPC())
                    iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS);

                if (iCriticalPct)
                {
                    if (iCriticalPct >= 10) // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20%
                        iCriticalPct = 5 + (iCriticalPct - 10) / 4;
                    else // 10보다 작으면 단순히 반으로 깎음, 10 = 5%
                        iCriticalPct /= 2;

                    //크리티컬 저항 값 적용.
                    iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL);

                    if (number(1, 100) <= iCriticalPct)
                    {
                        IsCritical = true;
                        dam *= 2;
                        EffectPacket(SE_CRITICAL);

                        if (IsAffectFlag(AFF_MANASHIELD))
                        {
                            RemoveAffect(AFF_MANASHIELD);
                        }
                    }
                }

                // 관통공격
                int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT);

                if (!IsPC())
                    iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS);


                if (iPenetratePct)
                {
                    {
                        CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE);

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

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

                    if (iPenetratePct >= 10)
                    {
                        // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20%
                        iPenetratePct = 5 + (iPenetratePct - 10) / 4;
                    }
                    else
                    {
                        // 10보다 작으면 단순히 반으로 깎음, 10 = 5%
                        iPenetratePct /= 2;
                    }

                    //관통타격 저항 값 적용.
                    iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE);

                    if (number(1, 100) <= iPenetratePct)
                    {
                        IsPenetrate = true;

                        if (test_server)
                            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("관통 추가 데미지 %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100);

                        dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100;

                        if (IsAffectFlag(AFF_MANASHIELD))
                        {
                            RemoveAffect(AFF_MANASHIELD);
                        }
                    }
                }
            }
        }
        // 
        // 콤보 공격, 활 공격, 즉 평타 일 때만 속성값들을 계산을 한다.
        //
        else if (type == DAMAGE_TYPE_NORMAL || type == DAMAGE_TYPE_NORMAL_RANGE)
        {
            if (type == DAMAGE_TYPE_NORMAL)
            {
                // 근접 평타일 경우 막을 수 있음
                if (GetPoint(POINT_BLOCK) && number(1, 100) <= GetPoint(POINT_BLOCK))
                {
                    if (test_server)
                    {
                        pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 블럭! (%d%%)"), GetName(), GetPoint(POINT_BLOCK));
                        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 블럭! (%d%%)"), GetName(), GetPoint(POINT_BLOCK));
                    }

                    SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
                    return false;
                }
            }
            else if (type == DAMAGE_TYPE_NORMAL_RANGE)
            {
                // 원거리 평타의 경우 피할 수 있음
                if (GetPoint(POINT_DODGE) && number(1, 100) <= GetPoint(POINT_DODGE))
                {
                    if (test_server)
                    {
                        pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 회피! (%d%%)"), GetName(), GetPoint(POINT_DODGE));
                        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 회피! (%d%%)"), GetName(), GetPoint(POINT_DODGE));
                    }

                    SendDamagePacket(pAttacker, 0, DAMAGE_DODGE);
                    return false;
                }
            }

            if (IsAffectFlag(AFF_JEONGWIHON))
                dam = (int) (dam * (100 + GetSkillPower(SKILL_JEONGWI) * 25 / 100) / 100);

            if (IsAffectFlag(AFF_TERROR))
                dam = (int) (dam * (95 - GetSkillPower(SKILL_TERROR) / 5) / 100);

            if (IsAffectFlag(AFF_HOSIN))
                dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100;

            //
            // 공격자 속성 적용
            //
            if (pAttacker)
            {
                if (type == DAMAGE_TYPE_NORMAL)
                {
                    // 반사
                    if (GetPoint(POINT_REFLECT_MELEE))
                    {
                        int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100;

                        // NOTE: 공격자가 IMMUNE_REFLECT 속성을 갖고있다면 반사를 안 하는 게 
                        // 아니라 1/3 데미지로 고정해서 들어가도록 기획에서 요청.
                        if (pAttacker->IsImmune(IMMUNE_REFLECT))
                            reflectDamage = int(reflectDamage / 3.0f + 0.5f);

                        pAttacker->Damage(this, reflectDamage, DAMAGE_TYPE_SPECIAL);
                    }
                }

                // 크리티컬
                int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT);

                if (!IsPC())
                    iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS);

                if (iCriticalPct)
                {
                    //크리티컬 저항 값 적용.
                    iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL);

                    if (number(1, 100) <= iCriticalPct)
                    {
                        IsCritical = true;
                        dam *= 2;
                        EffectPacket(SE_CRITICAL);
                    }
                }

                // 관통공격
                int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT);

                if (!IsPC())
                    iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS);

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

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

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


                if (iPenetratePct)
                {
                    
                    //관통타격 저항 값 적용.
                    iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE);

                    if (number(1, 100) <= iPenetratePct)
                    {
                        IsPenetrate = true;

                        if (test_server)
                            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("관통 추가 데미지 %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100);
                        dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100;
                    }
                }

                // HP 스틸
                if (pAttacker->GetPoint(POINT_STEAL_HP))
                {
                    int pct = 1;

                    if (number(1, 10) <= pct)
                    {
                        int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100;

                        if (iHP > 0 && GetHP() >= iHP)
                        {
                            CreateFly(FLY_HP_SMALL, pAttacker);
                            pAttacker->PointChange(POINT_HP, iHP);
                            PointChange(POINT_HP, -iHP);
                        }
                    }
                }

                // SP 스틸
                if (pAttacker->GetPoint(POINT_STEAL_SP))
                {
                    int pct = 1;

                    if (number(1, 10) <= pct)
                    {
                        int iCur;

                        if (IsPC())
                            iCur = iCurSP;
                        else
                            iCur = iCurHP;

                        int iSP = MIN(dam, MAX(0, iCur)) * pAttacker->GetPoint(POINT_STEAL_SP) / 100;

                        if (iSP > 0 && iCur >= iSP)
                        {
                            CreateFly(FLY_SP_SMALL, pAttacker);
                            pAttacker->PointChange(POINT_SP, iSP);

                            if (IsPC())
                                PointChange(POINT_SP, -iSP);
                        }
                    }
                }

                // 돈 스틸
                if (pAttacker->GetPoint(POINT_STEAL_GOLD))
                {
                    if (number(1, 100) <= pAttacker->GetPoint(POINT_STEAL_GOLD))
                    {
                        int iAmount = number(1, GetLevel());
                        pAttacker->PointChange(POINT_GOLD, iAmount);
                        DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 1, iAmount);
                    }
                }

                // 칠 때마다 HP회복
                if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% 확률
                {
                    int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100;

                    if (i)
                    {
                        CreateFly(FLY_HP_SMALL, pAttacker);
                        pAttacker->PointChange(POINT_HP, i);
                    }
                }

                // 칠 때마다 SP회복
                if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) // 80% 확률
                {
                    int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100;

                    if (i)
                    {
                        CreateFly(FLY_SP_SMALL, pAttacker);
                        pAttacker->PointChange(POINT_SP, i);
                    }
                }

                // 상대방의 마나를 없앤다.
                if (pAttacker->GetPoint(POINT_MANA_BURN_PCT))
                {
                    if (number(1, 100) <= pAttacker->GetPoint(POINT_MANA_BURN_PCT))
                        PointChange(POINT_SP, -50);
                }
            }
        }

        //
        // 평타 또는 스킬로 인한 보너스 피해/방어 계산
        // 
        switch (type)
        {
            case DAMAGE_TYPE_NORMAL:
            case DAMAGE_TYPE_NORMAL_RANGE:
                if (pAttacker)
                    if (pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS))
                        dam = dam * (100 + pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) / 100;

                dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100;
                break;

            case DAMAGE_TYPE_MELEE:
            case DAMAGE_TYPE_RANGE:
            case DAMAGE_TYPE_FIRE:
            case DAMAGE_TYPE_ICE:
            case DAMAGE_TYPE_ELEC:
            case DAMAGE_TYPE_MAGIC:
                if (pAttacker)
                    if (pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS))
                        dam = dam * (100 + pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) / 100;

                dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100;
                break;

            default:
                break;
        }

        //
        // 마나쉴드(흑신수호)
        //
        if (IsAffectFlag(AFF_MANASHIELD))
        {
            // POINT_MANASHIELD 는 작아질수록 좋다
            int iDamageSPPart = dam / 3;
            int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100;
            int iSP = GetSP();

            // SP가 있으면 무조건 데미지 절반 감소
            if (iDamageToSP <= iSP)
            {
                PointChange(POINT_SP, -iDamageToSP);
                dam -= iDamageSPPart;
            }
            else
            {
                // 정신력이 모자라서 피가 더 깍여야할
                PointChange(POINT_SP, -GetSP());
                dam -= iSP * 100 / MAX(GetPoint(POINT_MANASHIELD), 1);
            }
        }

        //
        // 전체 방어력 상승 (몰 아이템)
        // 
        if (GetPoint(POINT_MALL_DEFBONUS) > 0)
        {
            int dec_dam = MIN(200, dam * GetPoint(POINT_MALL_DEFBONUS) / 100);
            dam -= dec_dam;
        }

        if (pAttacker)
        {
            //
            // 전체 공격력 상승 (몰 아이템)
            //
            if (pAttacker->GetPoint(POINT_MALL_ATTBONUS) > 0)
            {
                int add_dam = MIN(300, dam * pAttacker->GetLimitPoint(POINT_MALL_ATTBONUS) / 100);
                dam += add_dam;
            }

            //
            // 제국으로 인한 보너스 (한국 올드 버전만 적용)
            //
            int iEmpire = GetEmpire();
            long lMapIndex = GetMapIndex();
            int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex);

            if (LC_IsYMIR() == true)
            {
                if (iEmpire && iMapEmpire && iEmpire != iMapEmpire)
                {
                    dam += (dam * 30) / 100;
                }
            }

            if (pAttacker->IsPC())
            {
                iEmpire = pAttacker->GetEmpire();
                lMapIndex = pAttacker->GetMapIndex();
                iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex);

                // 다른 제국 사람인 경우 데미지 10% 감소
                if (iEmpire && iMapEmpire && iEmpire != iMapEmpire)
                {
                    int percent = 10;
                    
                    if (184 <= lMapIndex && lMapIndex <= 189)
                    {
                        if (LC_IsYMIR() == true)
                            percent = 7;
                        else
                            percent = 9;
                    }
                    else
                    {
                        if (LC_IsYMIR() == true)
                            percent = 8;
                        else
                            percent = 9;
                    }

                    dam = dam * percent / 10;
                }

                if (!IsPC() && GetMonsterDrainSPPoint())
                {
                    int iDrain = GetMonsterDrainSPPoint();

                    if (iDrain <= pAttacker->GetSP())
                        pAttacker->PointChange(POINT_SP, -iDrain);
                    else
                    {
                        int iSP = pAttacker->GetSP();
                        pAttacker->PointChange(POINT_SP, -iSP);
                    }
                }

            }
            else if (pAttacker->IsGuardNPC())
            {
                SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD);
                Stun();
                return true;
            }

            //
            // 군주의 금강권 & 사자후 
            //
            if (pAttacker->IsPC() && CMonarch::instance().IsPowerUp(pAttacker->GetEmpire()))
            {
                // 10% 피해 증가
                dam += dam / 10;
            }

            if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire()))
            {
                // 10% 피해 감소
                dam -= dam / 10;
            }
        }
        //puAttr.Pop();

        if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
            return false;

        if (!IsPC())
        {
            if (m_pkParty && m_pkParty->GetLeader())
                m_pkParty->GetLeader()->SetLastAttacked(get_dword_time());
            else
                SetLastAttacked(get_dword_time());

            // 몬스터 대사 : 맞을 때
            MonsterChat(MONSTER_CHAT_ATTACKED);
        }

        if (IsStun())
        {
            Dead(pAttacker);
            return true;
        }

        if (IsDead())
            return true;

        // 독 공격으로 죽지 않도록 함.
        if (type == DAMAGE_TYPE_POISON)
        {
            if (GetHP() - dam <= 0)
            {
                dam = GetHP() - 1;
            }
        }

        // ------------------------
        // 독일 프리미엄 모드 
        // -----------------------
        if (LC_IsGermany() && pAttacker && pAttacker->IsPC())
        {
            int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker);
            dam = dam * iDmgPct / 100;
        }

        // STONE SKIN : 피해 반으로 감소
        if (IsMonster() && IsStoneSkinner())
        {
            if (GetHPPct() < GetMobTable().bStoneSkinPoint)
                dam /= 2;
        }

        //PROF_UNIT puRest1("Rest1");
        if (pAttacker)
        {
            // DEATH BLOW : 확률 적으로 4배 피해 (!? 현재 이벤트나 공성전용 몬스터만 사용함)
            if (pAttacker->IsMonster() && pAttacker->IsDeathBlower())
            {
                if (pAttacker->IsDeathBlow())
                {
                    if (number(1, 4) == GetJob())
                    {
                        IsDeathBlow = true;
                        dam = dam * 4;
                    }
                }
            }

            dam = BlueDragon_Damage(this, pAttacker, dam);

            BYTE damageFlag = 0;

            if (type == DAMAGE_TYPE_POISON)
                damageFlag = DAMAGE_POISON;
            else
                damageFlag = DAMAGE_NORMAL;

            if (IsCritical == true)
                damageFlag |= DAMAGE_CRITICAL;

            if (IsPenetrate == true)
                damageFlag |= DAMAGE_PENETRATE;


            //최종 데미지 보정
            float damMul = this->GetDamMul();
            float tempDam = dam;
            dam = tempDam * damMul + 0.5f;


            if (pAttacker)
                SendDamagePacket(pAttacker, dam, damageFlag);

            if (test_server)
            {
                if(pAttacker)
                {
                    pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s",
                            GetName(), 
                            dam, 
                            GetHP(),
                            (GetHP() * 100) / GetMaxHP(),
                            IsCritical ? "crit " : "",
                            IsPenetrate ? "pene " : "",
                            IsDeathBlow ? "deathblow " : "");
                }

                ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s",
                        pAttacker ? pAttacker->GetName() : 0,
                        dam, 
                        GetHP(),
                        (GetHP() * 100) / GetMaxHP(),
                        IsCritical ? "crit " : "",
                        IsPenetrate ? "pene " : "",
                        IsDeathBlow ? "deathblow " : "");
            }

            if (m_bDetailLog)
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s[%d]가 공격 위치: %d %d"), pAttacker->GetName(), (DWORD) pAttacker->GetVID(), pAttacker->GetX(), pAttacker->GetY());
            }
        }

        //
        // !!!!!!!!! 실제 HP를 줄이는 부분 !!!!!!!!!
        //
        if (!cannot_dead)
        {
            PointChange(POINT_HP, -dam, false);
        }

        //puRest1.Pop();

        //PROF_UNIT puRest2("Rest2");
        if (pAttacker && dam > 0 && IsNPC())
        {
            //PROF_UNIT puRest20("Rest20");
            TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID());

            if (it == m_map_kDamage.end())
            {
                m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0)));
                it = m_map_kDamage.find(pAttacker->GetVID());
            }
            else
            {
                it->second.iTotalDamage += dam;
            }
            //puRest20.Pop();

            //PROF_UNIT puRest21("Rest21");
            StartRecoveryEvent(); // 몬스터는 데미지를 입으면 회복을 시작한다.
            //puRest21.Pop();

            //PROF_UNIT puRest22("Rest22");
            UpdateAggrPointEx(pAttacker, type, dam, it->second);
            //puRest22.Pop();
        }
        //puRest2.Pop();

        //PROF_UNIT puRest3("Rest3");
        if (GetHP() <= 0)
        {
            Stun();

            if (pAttacker && !pAttacker->IsNPC())
                m_dwKillerPID = pAttacker->GetPlayerID();
            else
                m_dwKillerPID = 0;
        }

        return false;
    }

    void CHARACTER::DistributeHP(LPCHARACTER pkKiller)
    {
        if (pkKiller->GetDungeon()) // 던젼내에선 만두가나오지않는다
            return;
    }

    static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp)
    {
        // 레벨차 경험치 가감비율
        iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp);

        // 외부 테스트 서버 경험치 3배 보너스
        if (distribution_test_server)
            iExp *= 3;

        int iBaseExp = iExp;

        // 점술, 회사 경험치 이벤트 적용
        iExp = iExp * (100 + CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT)) / 100;

        // 게임내 기본 제공되는 경험치 보너스
        {
            // 노동절 메달
            if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL))
                iExp += iExp * 20 /100;

            // 사귀타워 경험치 보너스
            if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000) 
                iExp += iExp * 20 / 100; // 1.2배 (20%)

            // 아이템 경험치 두배 속성
            if (to->GetPoint(POINT_EXP_DOUBLE_BONUS))
                if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS))
                    iExp += iExp * 30 / 100; // 1.3배 (30%)

            // 경험의 반지 (2시간짜리)
            if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP))
                iExp += iExp * 50 / 100;

            switch (to->GetMountVnum())
            {
                case 20110:
                case 20111:
                case 20112:
                case 20113:
                    if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) ||
                            to->IsEquipUniqueItem(71121) )
                    {
                        iExp += iExp * 10 / 100;
                    }
                    break;

                case 20114:
                case 20120:
                case 20121:
                case 20122:
                case 20123:
                case 20124:
                case 20125:
                    // 백사자 경험치 보너스
                    iExp += iExp * 30 / 100;
                    break;
            }
        }

        // 아이템 몰 판매 경험치 보너스
        if (LC_IsHongKong() || LC_IsEurope() || LC_IsCanada())
        {
            // 아이템 몰: 경험치 결제
            if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
            {
                iExp += (iExp * 50 / 100);
            }

            if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
            {
                iExp += (iExp * 50 / 100);
            }

            // PC방 아템 경치 보너스
            if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
            {
                if (to->IsPCBang() == true)
                    iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
            }

            // 결혼 보너스
            iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
        }
        else if (/*LC_IsNewCIBN() || */LC_IsBrazil())
        {
            // 아이템 몰: 경험치 결제
            if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
            {
                iExp += iExp;
            }

            if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
            {
                iExp += iExp;
            }

            // PC방 아템 경치 보너스
            if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
            {
                if (to->IsPCBang() == true)
                    iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
            }

            // 결혼 보너스
            iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
        }
        else
        {
            // 아이템 몰: 경험치 결제
            if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
            {
                iExp += (iExp * 20 / 100);
            }

            if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
            {
                iExp += (iExp * 20 / 100);
            }

            // PC방 아템 경치 보너스
            if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
            {
                if (to->IsPCBang() == true)
                    iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
            }

            // 결혼 보너스
            iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
        }

        iExp += (iExp * to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP)/100);
        iExp += (iExp * to->GetPoint(POINT_MALL_EXPBONUS)/100);
        iExp += (iExp * to->GetPoint(POINT_EXP)/100);

    /*    if (speed_server)
        {
            iExp += iExp * CSpeedServerManager::ExpBonus();

        }
    */
        if (test_server)
        {
            sys_log(0, "Bonus Exp : Ramadan Candy: %d MallExp: %d PointExp: %d", 
                    to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP),
                    to->GetPoint(POINT_MALL_EXPBONUS),
                    to->GetPoint(POINT_EXP)
                   );
        }

        // 기획측 조정값 2005.04.21 현재 85%
        iExp = iExp * CHARACTER_MANAGER::instance().GetMobExpRate(to) / 100;

        // 경험치 한번 획득량 제한
        iExp = MIN(to->GetNextExp() / 10, iExp);

        if (test_server)
        {
            if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && iBaseExp>0)
                to->ChatPacket(CHAT_TYPE_INFO, "exp bonus %d%%", (iExp-iBaseExp)*100/iBaseExp);
        }

        iExp = AdjustExpByLevel(to, iExp);

        to->PointChange(POINT_EXP, iExp, true);
        from->CreateFly(FLY_EXP, to);

        {
            LPCHARACTER you = to->GetMarryPartner();
            // 부부가 서로 파티중이면 금슬이 오른다
            if (you)
            {
                // 1억이 100%
                DWORD dwUpdatePoint = 2000*iExp/to->GetLevel()/to->GetLevel()/3;

                if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 || 
                        you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0)
                    dwUpdatePoint = (DWORD)(dwUpdatePoint * 3);

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

                // DIVORCE_NULL_BUG_FIX
                if (pMarriage && pMarriage->IsNear())
                    pMarriage->Update(dwUpdatePoint);
                // END_OF_DIVORCE_NULL_BUG_FIX
            }
        }
    }

    namespace NPartyExpDistribute
    {
        struct FPartyTotaler
        {
            int        total;
            int        member_count;
            int        x, y;

            FPartyTotaler(LPCHARACTER center)
                : total(0), member_count(0), x(center->GetX()), y(center->GetY())
            {};

            void operator () (LPCHARACTER ch)
            {
                if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
                {
                    if (LC_IsYMIR())
                        total += ch->GetLevel();
                    else
                        total += party_exp_distribute_table[ch->GetLevel()];

                    ++member_count;
                }
            }
        };

        struct FPartyDistributor
        {
            int        total;
            LPCHARACTER    c;
            int        x, y;
            DWORD        _iExp;
            int        m_iMode;
            int        m_iMemberCount;

            FPartyDistributor(LPCHARACTER center, int member_count, int total, DWORD iExp, int iMode) 
                : total(total), c(center), x(center->GetX()), y(center->GetY()), _iExp(iExp), m_iMode(iMode), m_iMemberCount(member_count)
                {
                    if (m_iMemberCount == 0)
                        m_iMemberCount = 1;
                };

            void operator () (LPCHARACTER ch)
            {
                if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
                {
                    DWORD iExp2 = 0;

                    switch (m_iMode)
                    {
                        case PARTY_EXP_DISTRIBUTION_NON_PARITY:
                            if (LC_IsYMIR())
                                iExp2 = (DWORD) ((_iExp * ch->GetLevel()) / total);
                            else
                                iExp2 = (DWORD) (_iExp * (float) party_exp_distribute_table[ch->GetLevel()] / total);
                            break;

                        case PARTY_EXP_DISTRIBUTION_PARITY:
                            iExp2 = _iExp / m_iMemberCount;
                            break;

                        default:
                            sys_err("Unknown party exp distribution mode %d", m_iMode);
                            return;
                    }

                    GiveExp(c, ch, iExp2);
                }
            }
        };
    }

    typedef struct SDamageInfo
    {
        int iDam;
        LPCHARACTER pAttacker;
        LPPARTY pParty;

        void Clear()
        {
            pAttacker = NULL;
            pParty = NULL;
        }

        inline void Distribute(LPCHARACTER ch, int iExp)
        {
            if (pAttacker)
                GiveExp(ch, pAttacker, iExp);
            else if (pParty)
            {
                NPartyExpDistribute::FPartyTotaler f(ch);
                pParty->ForEachOnlineMember(f);

                if (pParty->IsPositionNearLeader(ch))
                    iExp = iExp * (100 + pParty->GetExpBonusPercent()) / 100;

                if (test_server)
                {
                    if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && pParty->GetExpBonusPercent())
                        pParty->ChatPacketToAllMember(CHAT_TYPE_INFO, "exp party bonus %d%%", pParty->GetExpBonusPercent());
                }

                // 경험치 몰아주기 (파티가 획득한 경험치를 5% 빼서 먼저 줌)
                if (pParty->GetExpCentralizeCharacter())
                {
                    LPCHARACTER tch = pParty->GetExpCentralizeCharacter();

                    if (DISTANCE_APPROX(ch->GetX() - tch->GetX(), ch->GetY() - tch->GetY()) <= PARTY_DEFAULT_RANGE)
                    {
                        int iExpCenteralize = (int) (iExp * 0.05f);
                        iExp -= iExpCenteralize;

                        GiveExp(ch, pParty->GetExpCentralizeCharacter(), iExpCenteralize);
                    }
                }

                NPartyExpDistribute::FPartyDistributor fDist(ch, f.member_count, f.total, iExp, pParty->GetExpDistributionMode());
                pParty->ForEachOnlineMember(fDist);
            }
        }
    } TDamageInfo;

    LPCHARACTER CHARACTER::DistributeExp()
    {
        int iExpToDistribute = GetExp();

        if (iExpToDistribute <= 0)
            return NULL;

        int    iTotalDam = 0;
        LPCHARACTER pkChrMostAttacked = NULL;
        int iMostDam = 0;

        typedef std::vector<TDamageInfo> TDamageInfoTable;
        TDamageInfoTable damage_info_table;
        std::map<LPPARTY, TDamageInfo> map_party_damage;

        damage_info_table.reserve(m_map_kDamage.size());

        TDamageMap::iterator it = m_map_kDamage.begin();

        // 일단 주위에 없는 사람을 걸러 낸다. (50m)
        while (it != m_map_kDamage.end())
        {
            const VID & c_VID = it->first;
            int iDam = it->second.iTotalDamage;

            ++it;

            LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

            // NPC가 때리기도 하나? -.-;
            if (!pAttacker || pAttacker->IsNPC() || DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000)
                continue;

            iTotalDam += iDam;
            if (!pkChrMostAttacked || iDam > iMostDam)
            {
                pkChrMostAttacked = pAttacker;
                iMostDam = iDam;
            }

            if (pAttacker->GetParty())
            {
                std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.find(pAttacker->GetParty());
                if (it == map_party_damage.end())
                {
                    TDamageInfo di;
                    di.iDam = iDam;
                    di.pAttacker = NULL;
                    di.pParty = pAttacker->GetParty();
                    map_party_damage.insert(std::make_pair(di.pParty, di));
                }
                else
                {
                    it->second.iDam += iDam;
                }
            }
            else
            {
                TDamageInfo di;

                di.iDam = iDam;
                di.pAttacker = pAttacker;
                di.pParty = NULL;

                //sys_log(0, "__ pq_damage %s %d", pAttacker->GetName(), iDam);
                //pq_damage.push(di);
                damage_info_table.push_back(di);
            }
        }

        for (std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it)
        {
            damage_info_table.push_back(it->second);
            //sys_log(0, "__ pq_damage_party [%u] %d", it->second.pParty->GetLeaderPID(), it->second.iDam);
        }

        SetExp(0);
        //m_map_kDamage.clear();

        if (iTotalDam == 0)    // 데미지 준게 0이면 리턴
            return NULL;

        if (m_pkChrStone)    // 돌이 있을 경우 경험치의 반을 돌에게 넘긴다.
        {
            //sys_log(0, "__ Give half to Stone : %d", iExpToDistribute>>1);
            int iExp = iExpToDistribute >> 1;
            m_pkChrStone->SetExp(m_pkChrStone->GetExp() + iExp);
            iExpToDistribute -= iExp;
        }

        sys_log(1, "%s total exp: %d, damage_info_table.size() == %d, TotalDam %d",
                GetName(), iExpToDistribute, damage_info_table.size(), iTotalDam);
        //sys_log(1, "%s total exp: %d, pq_damage.size() == %d, TotalDam %d",
        //GetName(), iExpToDistribute, pq_damage.size(), iTotalDam);

        if (damage_info_table.empty())
            return NULL;

        // 제일 데미지를 많이 준 사람이 HP 회복을 한다.
        DistributeHP(pkChrMostAttacked);    // 만두 시스템

        {
            // 제일 데미지를 많이 준 사람이나 파티가 총 경험치의 20% + 자기가 때린만큼의 경험치를 먹는다.
            TDamageInfoTable::iterator di = damage_info_table.begin();
            {
                TDamageInfoTable::iterator it;

                for (it = damage_info_table.begin(); it != damage_info_table.end();++it)
                {
                    if (it->iDam > di->iDam)
                        di = it;
                }
            }

            int    iExp = iExpToDistribute / 5;
            iExpToDistribute -= iExp;

            float fPercent = (float) di->iDam / iTotalDam;

            if (fPercent > 1.0f)
            {
                sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di->pAttacker->GetName());
                fPercent = 1.0f;
            }

            iExp += (int) (iExpToDistribute * fPercent);

            //sys_log(0, "%s given exp percent %.1f + 20 dam %d", GetName(), fPercent * 100.0f, di.iDam);

            di->Distribute(this, iExp);

            // 100% 다 먹었으면 리턴한다.
            if (fPercent == 1.0f)
                return pkChrMostAttacked;

            di->Clear();
        }

        {
            // 남은 80%의 경험치를 분배한다.
            TDamageInfoTable::iterator it;

            for (it = damage_info_table.begin(); it != damage_info_table.end(); ++it)
            {
                TDamageInfo & di = *it;

                float fPercent = (float) di.iDam / iTotalDam;

                if (fPercent > 1.0f)
                {
                    sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di.pAttacker->GetName());
                    fPercent = 1.0f;
                }

                //sys_log(0, "%s given exp percent %.1f dam %d", GetName(), fPercent * 100.0f, di.iDam);
                di.Distribute(this, (int) (iExpToDistribute * fPercent));
            }
        }

        return pkChrMostAttacked;
    }

    // 화살 개수를 리턴해 줌
    int CHARACTER::GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount/* = 1 */)
    {
        LPITEM pkBow;

        if (!(pkBow = GetWear(WEAR_WEAPON)) || pkBow->GetProto()->bSubType != WEAPON_BOW)
        {
            return 0;
        }

        LPITEM pkArrow;

        if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON ||
                pkArrow->GetProto()->bSubType != WEAPON_ARROW)
        {
            return 0;
        }

        iArrowCount = MIN(iArrowCount, pkArrow->GetCount());

        *ppkBow = pkBow;
        *ppkArrow = pkArrow;

        return iArrowCount;
    }

    void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount)
    {
        int iCount = pkArrow->GetCount();
        DWORD dwVnum = pkArrow->GetVnum();
        iCount = iCount - MIN(iCount, dwArrowCount);
        pkArrow->SetCount(iCount);

        if (iCount == 0)
        {
            LPITEM pkNewArrow = FindSpecifyItem(dwVnum);

            sys_log(0, "UseArrow : FindSpecifyItem %u %p", dwVnum, get_pointer(pkNewArrow));

            if (pkNewArrow)
                EquipItem(pkNewArrow);
        }
    }

    class CFuncShoot
    {
        public:
            LPCHARACTER    m_me;
            BYTE        m_bType;
            bool        m_bSucceed;

            CFuncShoot(LPCHARACTER ch, BYTE bType) : m_me(ch), m_bType(bType), m_bSucceed(FALSE)
            {
            }

            void operator () (DWORD dwTargetVID)
            {
                if (m_bType > 1)
                {
                    if (g_bSkillDisable)
                        return;

                    m_me->m_SkillUseInfo[m_bType].SetMainTargetVID(dwTargetVID);
                    /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK)
                      m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/
                }

                LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);

                if (!pkVictim)
                    return;

                // 공격 불가
                if (!battle_is_attackable(m_me, pkVictim))
                    return;

                if (m_me->IsNPC())
                {
                    if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000)
                        return;
                }

                LPITEM pkBow, pkArrow;

                switch (m_bType)
                {
                    case 0: // 일반활
                        {
                            int iDam = 0;

                            if (m_me->IsPC())
                            {
                                if (m_me->GetJob() != JOB_ASSASSIN)
                                    return;

                                if (0 == m_me->GetArrowAndBow(&pkBow, &pkArrow))
                                    return;

                                if (m_me->GetSkillGroup() != 0)
                                    if (!m_me->IsNPC() && m_me->GetSkillGroup() != 2)
                                    {
                                        if (m_me->GetSP() < 5)
                                            return;

                                        m_me->PointChange(POINT_SP, -5);
                                    }

                                iDam = CalcArrowDamage(m_me, pkVictim, pkBow, pkArrow);
                                m_me->UseArrow(pkArrow, 1);

                                // check speed hack
                                DWORD    dwCurrentTime    = get_dword_time();
                                if (IS_SPEED_HACK(m_me, pkVictim, dwCurrentTime))
                                    iDam    = 0;
                            }
                            else
                                iDam = CalcMeleeDamage(m_me, pkVictim);

                            NormalAttackAffect(m_me, pkVictim);

                            // 데미지 계산
                            iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100;

                            //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam);

                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me);

                            pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_NORMAL_RANGE);
                            // 타격치 계산부 끝
                        }
                        break;

                    case 1: // 일반 마법
                        {
                            int iDam;

                            if (m_me->IsPC())
                                return;

                            iDam = CalcMagicDamage(m_me, pkVictim);

                            NormalAttackAffect(m_me, pkVictim);

                            // 데미지 계산
                            iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100;

                            //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam);

                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me); 

                            pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_MAGIC);
                            // 타격치 계산부 끝
                        }
                        break;

                    case SKILL_YEONSA:    // 연사
                        {
                            //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100);
                            int iUseArrow = 1;

                            // 토탈만 계산하는경우
                            {
                                if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                                {
                                    m_me->OnMove(true);
                                    pkVictim->OnMove();

                                    if (pkVictim->CanBeginFight())
                                        pkVictim->BeginFight(m_me); 

                                    m_me->ComputeSkill(m_bType, pkVictim);
                                    m_me->UseArrow(pkArrow, iUseArrow);

                                    if (pkVictim->IsDead())
                                        break;

                                }
                                else
                                    break;
                            }
                        }
                        break;


                    case SKILL_KWANKYEOK:
                        {
                            int iUseArrow = 1;

                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me); 

                                sys_log(0, "%s kwankeyok %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }
                        break;

                    case SKILL_GIGUNG:
                        {
                            int iUseArrow = 1;
                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me);

                                sys_log(0, "%s gigung %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }

                        break;
                    case SKILL_HWAJO:
                        {
                            int iUseArrow = 1;
                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me);

                                sys_log(0, "%s hwajo %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }

                        break;

                    case SKILL_HORSE_WILDATTACK_RANGE:
                        {
                            int iUseArrow = 1;
                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me);

                                sys_log(0, "%s horse_wildattack %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }

                        break;

                    case SKILL_MARYUNG:
                        //case SKILL_GUMHWAN:
                    case SKILL_TUSOK:
                    case SKILL_BIPABU:
                    case SKILL_NOEJEON:
                    case SKILL_GEOMPUNG:
                    case SKILL_SANGONG:
                    case SKILL_MAHWAN:
                    case SKILL_PABEOB:
                        //case SKILL_CURSE:
                        {
                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me);

                            sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
                            m_me->ComputeSkill(m_bType, pkVictim);
                        }
                        break;

                    case SKILL_CHAIN:
                        {
                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me); 

                            sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
                            m_me->ComputeSkill(m_bType, pkVictim);

                            // TODO 여러명에게 슉 슉 슉 하기
                        }
                        break;

                    case SKILL_YONGBI:
                        {
                            m_me->OnMove(true);
                        }
                        break;

                        /*case SKILL_BUDONG:
                          {
                          m_me->OnMove(true);
                          pkVictim->OnMove();

                          DWORD * pdw;
                          DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw);
                          pdw[0] = m_me->GetVID();
                          pdw[1] = pkVictim->GetVID();

                          event_create(budong_event_func, dwEI, PASSES_PER_SEC(1));
                          }
                          break;*/

                    default:
                        sys_err("CFuncShoot: I don't know this type [%d] of range attack.", (int) m_bType);
                        break;
                }

                m_bSucceed = TRUE;
            }
    };

    bool CHARACTER::Shoot(BYTE bType)
    {
        sys_log(1, "Shoot %s type %u flyTargets.size %zu", GetName(), bType, m_vec_dwFlyTargets.size());

        if (!CanMove())
        {
            return false;
        }    

        CFuncShoot f(this, bType);

        if (m_dwFlyTargetID != 0)
        {
            f(m_dwFlyTargetID);
            m_dwFlyTargetID = 0;
        }

        f = std::for_each(m_vec_dwFlyTargets.begin(), m_vec_dwFlyTargets.end(), f);
        m_vec_dwFlyTargets.clear();

        return f.m_bSucceed;
    }

    void CHARACTER::FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader)
    {
        LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);
        TPacketGCFlyTargeting pack;

        //pack.bHeader    = HEADER_GC_FLY_TARGETING;
        pack.bHeader    = (bHeader == HEADER_CG_FLY_TARGETING) ? HEADER_GC_FLY_TARGETING : HEADER_GC_ADD_FLY_TARGETING;
        pack.dwShooterVID    = GetVID();

        if (pkVictim)
        {
            pack.dwTargetVID = pkVictim->GetVID();
            pack.x = pkVictim->GetX();
            pack.y = pkVictim->GetY();

            if (bHeader == HEADER_CG_FLY_TARGETING)
                m_dwFlyTargetID = dwTargetVID;
            else
                m_vec_dwFlyTargets.push_back(dwTargetVID);
        }
        else
        {
            pack.dwTargetVID = 0;
            pack.x = x;
            pack.y = y;
        }

        sys_log(1, "FlyTarget %s vid %d x %d y %d", GetName(), pack.dwTargetVID, pack.x, pack.y);
        PacketAround(&pack, sizeof(pack), this);
    }

    LPCHARACTER CHARACTER::GetNearestVictim(LPCHARACTER pkChr)
    {
        if (NULL == pkChr)
            pkChr = this;

        float fMinDist = 99999.0f;
        LPCHARACTER pkVictim = NULL;

        TDamageMap::iterator it = m_map_kDamage.begin();

        // 일단 주위에 없는 사람을 걸러 낸다.
        while (it != m_map_kDamage.end())
        {
            const VID & c_VID = it->first;
            ++it;

            LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

            if (!pAttacker)
                continue;

            if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || 
                    pAttacker->IsAffectFlag(AFF_INVISIBILITY) ||
                    pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE))
                continue;

            float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY());

            if (fDist < fMinDist)
            {
                pkVictim = pAttacker;
                fMinDist = fDist;
            }
        }

        return pkVictim;
    }

    void CHARACTER::SetVictim(LPCHARACTER pkVictim)
    {
        if (!pkVictim)
        {
            if (0 != (DWORD)m_kVIDVictim)
                MonsterLog("공격 대상을 해제");

            m_kVIDVictim.Reset();
            battle_end(this);
        }
        else
        {
            if (m_kVIDVictim != pkVictim->GetVID())
                MonsterLog("공격 대상을 설정: %s", pkVictim->GetName());

            m_kVIDVictim = pkVictim->GetVID();
            m_dwLastVictimSetTime = get_dword_time();
        }
    }

    LPCHARACTER CHARACTER::GetVictim() const
    {
        return CHARACTER_MANAGER::instance().Find(m_kVIDVictim);
    }

    LPCHARACTER CHARACTER::GetProtege() const // 보호해야 할 대상을 리턴
    {
        if (m_pkChrStone)
            return m_pkChrStone;

        if (m_pkParty)
            return m_pkParty->GetLeader();

        return NULL;
    }

    int CHARACTER::GetAlignment() const
    {
        return m_iAlignment;
    }

    int CHARACTER::GetRealAlignment() const
    {
        return m_iRealAlignment;
    }

    void CHARACTER::ShowAlignment(bool bShow)
    {
        if (bShow)
        {
            if (m_iAlignment != m_iRealAlignment)
            {
                m_iAlignment = m_iRealAlignment;
                UpdatePacket();
            }
        }
        else
        {
            if (m_iAlignment != 0)
            {
                m_iAlignment = 0;
                UpdatePacket();
            }
        }
    }

    void CHARACTER::UpdateAlignment(int iAmount)
    {
        bool bShow = false;

        if (m_iAlignment == m_iRealAlignment)
            bShow = true;

        int i = m_iAlignment / 10;

        m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000);

        if (bShow)
        {
            m_iAlignment = m_iRealAlignment;

            if (i != m_iAlignment / 10)
                UpdatePacket();
        }
    }

    void CHARACTER::SetKillerMode(bool isOn)
    {
        if ((isOn ? ADD_CHARACTER_STATE_KILLER : 0) == IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER))
            return;

        if (isOn)
            SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
        else
            REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);

        m_iKillerModePulse = thecore_pulse();
        UpdatePacket();
        sys_log(0, "SetKillerMode Update %s[%d]", GetName(), GetPlayerID());
    }

    bool CHARACTER::IsKillerMode() const
    {
        return IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
    }

    void CHARACTER::UpdateKillerMode()
    {
        if (!IsKillerMode())
            return;

        int iKillerSeconds = ! LC_IsYMIR() ? 30 : 60;

        if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(iKillerSeconds))
            SetKillerMode(false);
    }

    void CHARACTER::SetPKMode(BYTE bPKMode)
    {
        if (bPKMode >= PK_MODE_MAX_NUM)
            return;

        if (m_bPKMode == bPKMode)
            return;

        if (bPKMode == PK_MODE_GUILD && !GetGuild())
            bPKMode = PK_MODE_FREE;

        m_bPKMode = bPKMode;
        UpdatePacket();

        sys_log(0, "PK_MODE: %s %d", GetName(), m_bPKMode);
    }

    BYTE CHARACTER::GetPKMode() const
    {
        return m_bPKMode;
    }

    struct FuncForgetMyAttacker
    {
        LPCHARACTER m_ch;
        FuncForgetMyAttacker(LPCHARACTER ch)
        {
            m_ch = ch;
        }
        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (ch->m_kVIDVictim == m_ch->GetVID())
                    ch->SetVictim(NULL);
            }
        }
    };

    struct FuncAggregateMonster
    {
        LPCHARACTER m_ch;
        FuncAggregateMonster(LPCHARACTER ch)
        {
            m_ch = ch;
        }
        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (!ch->IsMonster())
                    return;
                if (ch->GetVictim())
                    return;

                if (number(1, 100) <= 50) // 임시로 50% 확률로 적을 끌어온다
                    if (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000)
                        if (ch->CanBeginFight())
                            ch->BeginFight(m_ch);
            }
        }
    };

    struct FuncAttractRanger
    {
        LPCHARACTER m_ch;
        FuncAttractRanger(LPCHARACTER ch)
        {
            m_ch = ch;
        }

        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (!ch->IsMonster())
                    return;
                if (ch->GetVictim() && ch->GetVictim() != m_ch)
                    return;
                if (ch->GetMobAttackRange() > 150)
                {
                    int iNewRange = 150;//(int)(ch->GetMobAttackRange() * 0.2);
                    if (iNewRange < 150)
                        iNewRange = 150;

                    ch->AddAffect(AFFECT_BOW_DISTANCE, POINT_BOW_DISTANCE, iNewRange - ch->GetMobAttackRange(), AFF_NONE, 3*60, 0, false);
                }
            }
        }
    };

    struct FuncPullMonster
    {
        LPCHARACTER m_ch;
        int m_iLength;
        FuncPullMonster(LPCHARACTER ch, int iLength = 300)
        {
            m_ch = ch;
            m_iLength = iLength;
        }

        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (!ch->IsMonster())
                    return;
                //if (ch->GetVictim() && ch->GetVictim() != m_ch)
                //return;
                float fDist = DISTANCE_APPROX(m_ch->GetX() - ch->GetX(), m_ch->GetY() - ch->GetY());
                if (fDist > 3000 || fDist < 100)
                    return;

                float fNewDist = fDist - m_iLength;
                if (fNewDist < 100) 
                    fNewDist = 100;

                float degree = GetDegreeFromPositionXY(ch->GetX(), ch->GetY(), m_ch->GetX(), m_ch->GetY());
                float fx;
                float fy;

                GetDeltaByDegree(degree, fDist - fNewDist, &fx, &fy);
                long tx = (long)(ch->GetX() + fx);
                long ty = (long)(ch->GetY() + fy);

                ch->Sync(tx, ty);
                ch->Goto(tx, ty);
                ch->CalculateMoveDuration();

                ch->SyncPacket();
            }
        }
    };

    void CHARACTER::ForgetMyAttacker()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncForgetMyAttacker f(this);
            pSec->ForEachAround(f);
        }
        ReviveInvisible(5);
    }

    void CHARACTER::AggregateMonster()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncAggregateMonster f(this);
            pSec->ForEachAround(f);
        }
    }

    void CHARACTER::AttractRanger()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncAttractRanger f(this);
            pSec->ForEachAround(f);
        }
    }

    void CHARACTER::PullMonster()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncPullMonster f(this);
            pSec->ForEachAround(f);
        }
    }

    void CHARACTER::UpdateAggrPointEx(LPCHARACTER pAttacker, EDamageType type, int dam, CHARACTER::TBattleInfo & info)
    {
        // 특정 공격타입에 따라 더 올라간다
        switch (type)
        {
            case DAMAGE_TYPE_NORMAL_RANGE:
                dam = (int) (dam*1.2f);
                break;

            case DAMAGE_TYPE_RANGE:
                dam = (int) (dam*1.5f);
                break;

            case DAMAGE_TYPE_MAGIC:
                dam = (int) (dam*1.2f);
                break;

            default:
                break;
        }

        // 공격자가 현재 대상인 경우 보너스를 준다.
        if (pAttacker == GetVictim())
            dam = (int) (dam * 1.2f);

        info.iAggro += dam;

        if (info.iAggro < 0)
            info.iAggro = 0;

        //sys_log(0, "UpdateAggrPointEx for %s by %s dam %d total %d", GetName(), pAttacker->GetName(), dam, total);
        if (GetParty() && dam > 0 && type != DAMAGE_TYPE_SPECIAL)
        {
            LPPARTY pParty = GetParty();

            // 리더인 경우 영향력이 좀더 강하다
            int iPartyAggroDist = dam;

            if (pParty->GetLeaderPID() == GetVID())
                iPartyAggroDist /= 2;
            else
                iPartyAggroDist /= 3;

            pParty->SendMessage(this, PM_AGGRO_INCREASE, iPartyAggroDist, pAttacker->GetVID());
        }

        ChangeVictimByAggro(info.iAggro, pAttacker);
    }

    void CHARACTER::UpdateAggrPoint(LPCHARACTER pAttacker, EDamageType type, int dam)
    {
        if (IsDead() || IsStun())
            return;

        TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID());

        if (it == m_map_kDamage.end())
        {
            m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(0, dam)));
            it = m_map_kDamage.find(pAttacker->GetVID());
        }

        UpdateAggrPointEx(pAttacker, type, dam, it->second);
    }

    void CHARACTER::ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim)
    {
        if (get_dword_time() - m_dwLastVictimSetTime < 3000) // 3초는 기다려야한다
            return;

        if (pNewVictim == GetVictim())
        {
            if (m_iMaxAggro < iNewAggro)
            {
                m_iMaxAggro = iNewAggro;
                return;
            }

            // Aggro가 감소한 경우
            TDamageMap::iterator it;
            TDamageMap::iterator itFind = m_map_kDamage.end();

            for (it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it)
            {
                if (it->second.iAggro > iNewAggro)
                {
                    LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first);

                    if (ch && !ch->IsDead() && DISTANCE_APPROX(ch->GetX() - GetX(), ch->GetY() - GetY()) < 5000)
                    {
                        itFind = it;
                        iNewAggro = it->second.iAggro;
                    }
                }
            }

            if (itFind != m_map_kDamage.end())
            {
                m_iMaxAggro = iNewAggro;
                SetVictim(CHARACTER_MANAGER::instance().Find(itFind->first));
                m_dwStateDuration = 1;
            }
        }
        else
        {
            if (m_iMaxAggro < iNewAggro)
            {
                m_iMaxAggro = iNewAggro;
                SetVictim(pNewVictim);
                m_dwStateDuration = 1;
            }
        }
    }

    here is it

    Spoiler

    #include "stdafx.h"
    #include "utils.h"
    #include "config.h"
    #include "desc.h"
    #include "desc_manager.h"
    #include "char_manager.h"
    #include "item.h"
    #include "item_manager.h"
    #include "mob_manager.h"
    #include "battle.h"
    #include "pvp.h"
    #include "skill.h"
    #include "start_position.h"
    #include "profiler.h"
    #include "cmd.h"
    #include "dungeon.h"
    #include "log.h"
    #include "unique_item.h"
    #include "priv_manager.h"
    #include "db.h"
    #include "vector.h"
    #include "marriage.h"
    #include "arena.h"
    #include "regen.h"
    #include "monarch.h"
    #include "exchange.h"
    #include "shop_manager.h"
    #include "castle.h"
    #include "dev_log.h"
    #include "ani.h"
    #include "BattleArena.h"
    #include "packet.h"
    #include "party.h"
    #include "affect.h"
    #include "guild.h"
    #include "guild_manager.h"
    #include "questmanager.h"
    #include "questlua.h"
    #include "threeway_war.h"
    #include "BlueDragon.h"
    #include "DragonLair.h"

    DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp)
    {
        if (PLAYER_EXP_TABLE_MAX < ch->GetLevel())
        {
            double ret = 0.95;
            double factor = 0.1;

            for (ssize_t i=0 ; i < ch->GetLevel()-100 ; ++i)
            {
                if ( (i%10) == 0)
                    factor /= 2.0;

                ret *= 1.0 - factor;
            }

            ret = ret * static_cast<double>(exp);

            if (ret < 1.0)
                return 1;

            return static_cast<DWORD>(ret);
        }

        return exp;
    }

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

        return m_pointsInstant.position == POS_STANDING && !IsDead() && !IsStun();
    }

    void CHARACTER::BeginFight(LPCHARACTER pkVictim)
    {
        SetVictim(pkVictim);
        SetPosition(POS_FIGHTING);
        SetNextStatePulse(1);
    }

    bool CHARACTER::CanFight() const
    {
        return m_pointsInstant.position >= POS_FIGHTING ? true : false;
    }

    void CHARACTER::CreateFly(BYTE bType, LPCHARACTER pkVictim)
    {
        TPacketGCCreateFly packFly;

        packFly.bHeader         = HEADER_GC_CREATE_FLY;
        packFly.bType           = bType;
        packFly.dwStartVID      = GetVID();
        packFly.dwEndVID        = pkVictim->GetVID();

        PacketAround(&packFly, sizeof(TPacketGCCreateFly));
    }

    void CHARACTER::DistributeSP(LPCHARACTER pkKiller, int iMethod)
    {
        if (pkKiller->GetSP() >= pkKiller->GetMaxSP())
            return;

        bool bAttacking = (get_dword_time() - GetLastAttackTime()) < 3000;
        bool bMoving = (get_dword_time() - GetLastMoveTime()) < 3000;

        if (iMethod == 1)
        {
            int num = number(0, 3);

            if (!num)
            {
                int iLvDelta = GetLevel() - pkKiller->GetLevel();
                int iAmount = 0;

                if (iLvDelta >= 5)
                    iAmount = 10;
                else if (iLvDelta >= 0)
                    iAmount = 6;
                else if (iLvDelta >= -3)
                    iAmount = 2;

                if (iAmount != 0)
                {
                    iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;

                    if (iAmount >= 11)
                        CreateFly(FLY_SP_BIG, pkKiller);
                    else if (iAmount >= 7)
                        CreateFly(FLY_SP_MEDIUM, pkKiller);
                    else
                        CreateFly(FLY_SP_SMALL, pkKiller);

                    pkKiller->PointChange(POINT_SP, iAmount);
                }
            }
        }
        else
        {
            if (pkKiller->GetJob() == JOB_SHAMAN || (pkKiller->GetJob() == JOB_SURA && pkKiller->GetSkillGroup() == 2))
            {
                int iAmount;

                if (bAttacking)
                    iAmount = 2 + GetMaxSP() / 100;
                else if (bMoving)
                    iAmount = 3 + GetMaxSP() * 2 / 100;
                else
                    iAmount = 10 + GetMaxSP() * 3 / 100; // 평상시

                iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
                pkKiller->PointChange(POINT_SP, iAmount);
            }
            else
            {
                int iAmount;

                if (bAttacking)
                    iAmount = 2 + pkKiller->GetMaxSP() / 200;
                else if (bMoving)
                    iAmount = 2 + pkKiller->GetMaxSP() / 100;
                else
                {
                    // 평상시
                    if (pkKiller->GetHP() < pkKiller->GetMaxHP())
                        iAmount = 2 + (pkKiller->GetMaxSP() / 100); // 피 다 안찼을때
                    else
                        iAmount = 9 + (pkKiller->GetMaxSP() / 100); // 기본
                }

                iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
                pkKiller->PointChange(POINT_SP, iAmount);
            }
        }
    }


    bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType)
    {
        if (test_server)
            sys_log(0, "[TEST_SERVER] Attack : %s type %d, MobBattleType %d", GetName(), bType, !GetMobBattleType() ? 0 : GetMobAttackRange());
        //PROF_UNIT puAttack("Attack");
        if (!CanMove())
            return false;

        // CASTLE
        if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim))
            return false;
        // CASTLE

        DWORD dwCurrentTime = get_dword_time();

        if (IsPC())
        {
            if (IS_SPEED_HACK(this, pkVictim, dwCurrentTime))
                return false;

            if (bType == 0 && dwCurrentTime < GetSkipComboAttackByTime())
                return false;
        }
        else
        {
            MonsterChat(MONSTER_CHAT_ATTACK);
        }

        pkVictim->SetSyncOwner(this);

        if (pkVictim->CanBeginFight())
            pkVictim->BeginFight(this);

        int iRet;

        if (bType == 0)
        {
            //
            // 일반 공격
            //
            switch (GetMobBattleType())
            {
                case BATTLE_TYPE_MELEE:
                case BATTLE_TYPE_POWER:
                case BATTLE_TYPE_TANKER:
                case BATTLE_TYPE_SUPER_POWER:
                case BATTLE_TYPE_SUPER_TANKER:
                    iRet = battle_melee_attack(this, pkVictim);
                    break;

                case BATTLE_TYPE_RANGE:
                    FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING);
                    iRet = Shoot(0) ? BATTLE_DAMAGE : BATTLE_NONE;
                    break;

                case BATTLE_TYPE_MAGIC:
                    FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING);
                    iRet = Shoot(1) ? BATTLE_DAMAGE : BATTLE_NONE;
                    break;

                default:
                    sys_err("Unhandled battle type %d", GetMobBattleType());
                    iRet = BATTLE_NONE;
                    break;
            }
        }
        else
        {
            if (IsPC() == true)
            {
                if (dwCurrentTime - m_dwLastSkillTime > 1500)
                {
                    sys_log(1, "HACK: Too long skill using term. Name(%s) PID(%u) delta(%u)",
                            GetName(), GetPlayerID(), (dwCurrentTime - m_dwLastSkillTime));
                    return false;
                }
            }

            sys_log(1, "Attack call ComputeSkill %d %s", bType, pkVictim?pkVictim->GetName():"");
            iRet = ComputeSkill(bType, pkVictim);
        }

        //if (test_server && IsPC())
        //    sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet);
        if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD)
        {
            OnMove(true);
            pkVictim->OnMove();

            // only pc sets victim null. For npc, state machine will reset this.
            if (BATTLE_DEAD == iRet && IsPC())
                SetVictim(NULL);

            return true;
        }

        return false;
    }

    void CHARACTER::DeathPenalty(BYTE bTown)
    {
        sys_log(1, "DEATH_PERNALY_CHECK(%s) town(%d)", GetName(), bTown);

        Cube_close(this);

        if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true)
        {
            return;
        }
        
        if (GetLevel() < 10)
        {
            sys_log(0, "NO_DEATH_PENALTY_LESS_LV10(%s)", GetName());
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
            return;
        }

           if (number(0, 2))
        {
            sys_log(0, "NO_DEATH_PENALTY_LUCK(%s)", GetName());
            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
            return;
        }

        if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY))
        {
            REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);

            // NO_DEATH_PENALTY_BUG_FIX 
            if (LC_IsYMIR()) // 천마 버전에서는 언제나 용신의 가호 아이템을 체크한다.
            {
                if (FindAffect(AFFECT_NO_DEATH_PENALTY))
                {
                    sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName());
                    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
                    RemoveAffect(AFFECT_NO_DEATH_PENALTY);
                    return;
                }
            }
            else if (!bTown) // 국제 버전에서는 제자리 부활시만 용신의 가호를 사용한다. (마을 복귀시는 경험치 패널티 없음)
            {
                if (FindAffect(AFFECT_NO_DEATH_PENALTY))
                {
                    sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName());
                    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
                    RemoveAffect(AFFECT_NO_DEATH_PENALTY);
                    return;
                }
            }
            // END_OF_NO_DEATH_PENALTY_BUG_FIX

            int iLoss = ((GetNextExp() * aiExpLossPercents[MINMAX(1, GetLevel(), PLAYER_EXP_TABLE_MAX)]) / 100);

            if (true == LC_IsYMIR())
            {
                if (PLAYER_EXP_TABLE_MAX < GetLevel())
                {
                    iLoss = MIN(500000, iLoss);
                }
                else
                {
                    iLoss = MIN(200000, iLoss);
                }
            }
            else if (true == LC_IsEurope())
            {
                iLoss = MIN(800000, iLoss);
            }

            if (bTown)
            {
                if (g_iUseLocale)
                {
                    iLoss = 0;
                }
                else
                {
                    iLoss -= iLoss / 3;
                }
            }

            if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS))
                iLoss /= 2;

            sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, aiExpLossPercents[MIN(gPlayerMaxLevel, GetLevel())]);

            PointChange(POINT_EXP, -iLoss, true);
        }
    }

    bool CHARACTER::IsStun() const
    {
        if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN))
            return true;

        return false;
    }

    EVENTFUNC(StunEvent)
    {
        char_event_info* info = dynamic_cast<char_event_info*>( event->info );

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

        LPCHARACTER ch = info->ch;

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

    void CHARACTER::Stun()
    {
        if (IsStun())
            return;

        if (IsDead())
            return;

        if (!IsPC() && m_pkParty)
        {
            m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0);
        }

        sys_log(1, "%s: Stun %p", GetName(), this);

        PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY));
        PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY));

        CloseMyShop();

        event_cancel(&m_pkRecoveryEvent); // 회복 이벤트를 죽인다.

        TPacketGCStun pack;
        pack.header    = HEADER_GC_STUN;
        pack.vid    = m_vid;
        PacketAround(&pack, sizeof(pack));

        SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);

        if (m_pkStunEvent)
            return;

        char_event_info* info = AllocEventInfo<char_event_info>();

        info->ch = this;

        m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3));
    }

    EVENTINFO(SCharDeadEventInfo)
    {
        bool isPC;
        uint32_t dwID;

        SCharDeadEventInfo()
        : isPC(0)
        , dwID(0)
        {
        }
    };

    EVENTFUNC(dead_event)
    {
        const SCharDeadEventInfo* info = dynamic_cast<SCharDeadEventInfo*>(event->info);

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

        LPCHARACTER ch = NULL;

        if (true == info->isPC)
        {
            ch = CHARACTER_MANAGER::instance().FindByPID( info->dwID );
        }
        else
        {
            ch = CHARACTER_MANAGER::instance().Find( info->dwID );
        }

        if (NULL == ch)
        {
            sys_err("DEAD_EVENT: cannot find char pointer with %s id(%d)", info->isPC ? "PC" : "MOB", info->dwID );
            return 0;
        }

        ch->m_pkDeadEvent = NULL;

        if (ch->GetDesc())
        {
            ch->GetDesc()->SetPhase(PHASE_GAME);

            ch->SetPosition(POS_STANDING);

            PIXEL_POSITION pos;

            if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos))
                ch->WarpSet(pos.x, pos.y);
            else
            {
                sys_err("cannot find spawn position (name %s)", ch->GetName());
                ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
            }

            ch->PointChange(POINT_HP, (ch->GetMaxHP() / 2) - ch->GetHP(), true);

            ch->DeathPenalty(0);

            ch->StartRecoveryEvent();

            ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow");
        }
        else
        {
            if (ch->IsMonster() == true)
            {
                if (ch->IsRevive() == false && ch->HasReviverInParty() == true)
                {
                    ch->SetPosition(POS_STANDING);
                    ch->SetHP(ch->GetMaxHP());

                    ch->ViewReencode();

                    ch->SetAggressive();
                    ch->SetRevive(true);

                    return 0;
                }
            }

            M2_DESTROY_CHARACTER(ch);
        }

        return 0;
    }

    bool CHARACTER::IsDead() const
    {
        if (m_pointsInstant.position == POS_DEAD)
            return true;

        return false;
    }

    #define GetGoldMultipler() (distribution_test_server ? 3 : 1)

    void CHARACTER::RewardGold(LPCHARACTER pkAttacker)
    {
        // ADD_PREMIUM
        bool isAutoLoot = 
            (pkAttacker->GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 ||
             pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT))
            ? true : false; // 제3의 손
        // END_OF_ADD_PREMIUM

        PIXEL_POSITION pos;

        if (!isAutoLoot)
            if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos))
                return;

        int iTotalGold = 0;
        //
        // --------- 돈 드롭 확률 계산 ----------
        //
        int iGoldPercent = MobRankStats[GetMobRank()].iGoldPercent;

        if (pkAttacker->IsPC())
            iGoldPercent = iGoldPercent * (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD_DROP)) / 100;

        if (pkAttacker->GetPoint(POINT_MALL_GOLDBONUS))
            iGoldPercent += (iGoldPercent * pkAttacker->GetPoint(POINT_MALL_GOLDBONUS) / 100);

        iGoldPercent = iGoldPercent * CHARACTER_MANAGER::instance().GetMobGoldDropRate(pkAttacker) / 100;

        // ADD_PREMIUM
        if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 ||
                pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD))
            iGoldPercent += iGoldPercent;
        // END_OF_ADD_PREMIUM

        if (iGoldPercent > 100) 
            iGoldPercent = 100;

        int iPercent;

        if (GetMobRank() >= MOB_RANK_BOSS)
            iPercent = ((iGoldPercent * PERCENT_LVDELTA_BOSS(pkAttacker->GetLevel(), GetLevel())) / 100);
        else
            iPercent = ((iGoldPercent * PERCENT_LVDELTA(pkAttacker->GetLevel(), GetLevel())) / 100);
        //int iPercent = CALCULATE_VALUE_LVDELTA(pkAttacker->GetLevel(), GetLevel(), iGoldPercent);

        if (number(1, 100) > iPercent)
            return;

        int iGoldMultipler = GetGoldMultipler();

        if (1 == number(1, 50000)) // 1/50000 확률로 돈이 10배
            iGoldMultipler *= 10;
        else if (1 == number(1, 10000)) // 1/10000 확률로 돈이 5배
            iGoldMultipler *= 5;

        // 개인 적용
        if (pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS))
            if (number(1, 100) <= pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS))
                iGoldMultipler *= 2;

        //
        // --------- 돈 드롭 배수 결정 ----------
        //
        if (test_server)
            pkAttacker->ChatPacket(CHAT_TYPE_PARTY, "gold_mul %d rate %d", iGoldMultipler, CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker));

        //
        // --------- 실제 드롭 처리 -------------
        // 
        LPITEM item;

        int iGold10DropPct = 100;
        iGold10DropPct = (iGold10DropPct * 100) / (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD10_DROP));

        // MOB_RANK가 BOSS보다 높으면 무조건 돈폭탄
        if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0)
        {
            if (1 == number(1, iGold10DropPct))
                iGoldMultipler *= 10; // 1% 확률로 돈 10배

            int iSplitCount = number(25, 35);

            for (int i = 0; i < iSplitCount; ++i)
            {
                int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax) / iSplitCount;
                if (test_server)
                    sys_log(0, "iGold %d", iGold);
                iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
                iGold *= iGoldMultipler;

                if (iGold == 0)
                {
                    continue ;
                }

                if (test_server)
                {
                    sys_log(0, "Drop Moeny MobGoldAmountRate %d %d", CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker), iGoldMultipler);
                    sys_log(0, "Drop Money gold %d GoldMin %d GoldMax %d", iGold, GetMobTable().dwGoldMax, GetMobTable().dwGoldMax);
                }

                // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음
                if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold)))
                {
                    pos.x = GetX() + ((number(-14, 14) + number(-14, 14)) * 23);
                    pos.y = GetY() + ((number(-14, 14) + number(-14, 14)) * 23);

                    item->AddToGround(GetMapIndex(), pos);
                    item->StartDestroyEvent();

                    iTotalGold += iGold; // Total gold
                }
            }
        }
        // 1% 확률로 돈을 10개 떨어 뜨린다. (10배 드롭임)
        else if (1 == number(1, iGold10DropPct))
        {
            //
            // 돈 폭탄식 드롭
            //
            for (int i = 0; i < 10; ++i)
            {
                int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
                iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
                iGold *= iGoldMultipler;

                if (iGold == 0)
                {
                    continue;
                }

                // NOTE: 돈 폭탄은 제 3의 손 처리를 하지 않음
                if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold)))
                {
                    pos.x = GetX() + (number(-7, 7) * 20);
                    pos.y = GetY() + (number(-7, 7) * 20);

                    item->AddToGround(GetMapIndex(), pos);
                    item->StartDestroyEvent();

                    iTotalGold += iGold; // Total gold
                }
            }
        }
        else
        {
            //
            // 일반적인 방식의 돈 드롭
            //
            int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
            iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
            iGold *= iGoldMultipler;

            int iSplitCount;

            if (iGold >= 3 && !LC_IsYMIR()) 
                iSplitCount = number(1, 3);
            else if (GetMobRank() >= MOB_RANK_BOSS)
            {
                iSplitCount = number(3, 10);

                if ((iGold / iSplitCount) == 0)
                    iSplitCount = 1;
            }
            else
                iSplitCount = 1;

            if (iGold != 0)
            {
                iTotalGold += iGold; // Total gold

                for (int i = 0; i < iSplitCount; ++i)
                {
                    if (isAutoLoot)
                    {
                        pkAttacker->GiveGold(iGold / iSplitCount);
                    }
                    else if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount)))
                    {
                        pos.x = GetX() + (number(-7, 7) * 20);
                        pos.y = GetY() + (number(-7, 7) * 20);

                        item->AddToGround(GetMapIndex(), pos);
                        item->StartDestroyEvent();
                    }
                }
            }
        }

        DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold);
    }

    void CHARACTER::Reward(bool bItemDrop)
    {
        if (GetRaceNum() == 5001) // 왜구는 돈을 무조건 드롭
        {
            PIXEL_POSITION pos;

            if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos))
                return;

            LPITEM item;
            int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
            iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(NULL) / 100;
            iGold *= GetGoldMultipler();
            int iSplitCount = number(25, 35);

            sys_log(0, "WAEGU Dead gold %d split %d", iGold, iSplitCount);

            for (int i = 1; i <= iSplitCount; ++i)
            {
                if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount)))
                {
                    if (i != 0)
                    {
                        pos.x = number(-7, 7) * 20;
                        pos.y = number(-7, 7) * 20;

                        pos.x += GetX();
                        pos.y += GetY();
                    }

                    item->AddToGround(GetMapIndex(), pos);
                    item->StartDestroyEvent();
                }
            }
            return;
        }

        //PROF_UNIT puReward("Reward");
           LPCHARACTER pkAttacker = DistributeExp();

        if (!pkAttacker)
            return;

        //PROF_UNIT pu1("r1");
        if (pkAttacker->IsPC())
        {
            if (GetLevel() - pkAttacker->GetLevel() >= -10)
                if (pkAttacker->GetRealAlignment() < 0)
                {
                    if (pkAttacker->IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_KILL))
                        pkAttacker->UpdateAlignment(14);
                    else
                        pkAttacker->UpdateAlignment(7);
                }
                else
                    pkAttacker->UpdateAlignment(2);

            pkAttacker->SetQuestNPCID(GetVID());
            quest::CQuestManager::instance().Kill(pkAttacker->GetPlayerID(), GetRaceNum());
            CHARACTER_MANAGER::instance().KillLog(GetRaceNum());

            if (!number(0, 9))
            {
                if (pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY))
                {
                    int iHP = pkAttacker->GetMaxHP() * pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY) / 100;
                    pkAttacker->PointChange(POINT_HP, iHP);
                    CreateFly(FLY_HP_SMALL, pkAttacker);
                }

                if (pkAttacker->GetPoint(POINT_KILL_SP_RECOVER))
                {
                    int iSP = pkAttacker->GetMaxSP() * pkAttacker->GetPoint(POINT_KILL_SP_RECOVER) / 100;
                    pkAttacker->PointChange(POINT_SP, iSP);
                    CreateFly(FLY_SP_SMALL, pkAttacker);
                }
            }
        }
        //pu1.Pop();

        if (!bItemDrop)
            return;

        PIXEL_POSITION pos = GetXYZ();

        if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), pos.x, pos.y, pos))
            return;

        //
        // 돈 드롭
        //
        //PROF_UNIT pu2("r2");
        if (test_server)
            sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName());
        RewardGold(pkAttacker);
        //pu2.Pop();

        //
        // 아이템 드롭
        //
        //PROF_UNIT pu3("r3");
        LPITEM item;

        static std::vector<LPITEM> s_vec_item;
        s_vec_item.clear();

        if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item))
        {
            if (s_vec_item.size() == 0);
            else if (s_vec_item.size() == 1)
            {
                item = s_vec_item[0];
                item->AddToGround(GetMapIndex(), pos);

                if (CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()) == false)
                {
                    item->SetOwnership(pkAttacker);
                }

                item->StartDestroyEvent();

                pos.x = number(-7, 7) * 20;
                pos.y = number(-7, 7) * 20;
                pos.x += GetX();
                pos.y += GetY();

                sys_log(0, "DROP_ITEM: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName());
            }
            else
            {
                int iItemIdx = s_vec_item.size() - 1;

                std::priority_queue<std::pair<int, LPCHARACTER> > pq;

                int total_dam = 0;

                for (TDamageMap::iterator it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it)
                {
                    int iDamage = it->second.iTotalDamage;
                    if (iDamage > 0)
                    {
                        LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first);

                        if (ch)
                        {
                            pq.push(std::make_pair(iDamage, ch));
                            total_dam += iDamage;
                        }
                    }
                }

                std::vector<LPCHARACTER> v;

                while (!pq.empty() && pq.top().first * 10 >= total_dam)
                {
                    v.push_back(pq.top().second);
                    pq.pop();
                }

                if (v.empty())
                {
                    // 데미지를 특별히 많이 준 사람이 없으니 소유권 없음
                    while (iItemIdx >= 0)
                    {
                        item = s_vec_item[iItemIdx--];

                        if (!item)
                        {
                            sys_err("item null in vector idx %d", iItemIdx + 1);
                            continue;
                        }

                        item->AddToGround(GetMapIndex(), pos);
                        // 10% 이하 데미지 준 사람끼리는 소유권없음
                        //item->SetOwnership(pkAttacker);
                        item->StartDestroyEvent();

                        pos.x = number(-7, 7) * 20;
                        pos.y = number(-7, 7) * 20;
                        pos.x += GetX();
                        pos.y += GetY();

                        sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName());
                    }
                }
                else
                {
                    // 데미지 많이 준 사람들 끼리만 소유권 나눠가짐
                    std::vector<LPCHARACTER>::iterator it = v.begin();

                    while (iItemIdx >= 0)
                    {
                        item = s_vec_item[iItemIdx--];

                        if (!item)
                        {
                            sys_err("item null in vector idx %d", iItemIdx + 1);
                            continue;
                        }

                        item->AddToGround(GetMapIndex(), pos);

                        LPCHARACTER ch = *it;

                        if (ch->GetParty())
                            ch = ch->GetParty()->GetNextOwnership(ch, GetX(), GetY());

                        ++it;

                        if (it == v.end())
                            it = v.begin();

                        if (CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()) == false)
                        {
                            item->SetOwnership(ch);
                        }

                        item->StartDestroyEvent();

                        pos.x = number(-7, 7) * 20;
                        pos.y = number(-7, 7) * 20;
                        pos.x += GetX();
                        pos.y += GetY();

                        sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName());
                    }
                }
            }
        }

        m_map_kDamage.clear();
    }

    struct TItemDropPenalty
    {
        int iInventoryPct;        // Range: 1 ~ 1000
        int iInventoryQty;        // Range: --
        int iEquipmentPct;        // Range: 1 ~ 100
        int iEquipmentQty;        // Range: --
    };

    TItemDropPenalty aItemDropPenalty_kor[9] =
    {
        {   0,   0,  0,  0 },    // 선왕
        {   0,   0,  0,  0 },    // 영웅
        {   0,   0,  0,  0 },    // 성자
        {   0,   0,  0,  0 },    // 지인
        {   0,   0,  0,  0 },    // 양민
        {  25,   1,  5,  1 },    // 낭인
        {  50,   2, 10,  1 },    // 악인
        {  75,   4, 15,  1 },    // 마두
        { 100,   8, 20,  1 },    // 패왕
    };

    void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller)
    {
        // 개인상점을 연 상태에서는 아이템을 드롭하지 않는다.
        if (GetMyShop())
            return;

        if (false == LC_IsYMIR())
        {
            if (GetLevel() < 50)
                return;
        }
        
        if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true)
        {
            return;
        }

        struct TItemDropPenalty * table = &aItemDropPenalty_kor[0];

        if (GetLevel() < 10)
            return;

        int iAlignIndex;

        if (GetRealAlignment() >= 120000)
            iAlignIndex = 0;
        else if (GetRealAlignment() >= 80000)
            iAlignIndex = 1;
        else if (GetRealAlignment() >= 40000)
            iAlignIndex = 2;
        else if (GetRealAlignment() >= 10000)
            iAlignIndex = 3;
        else if (GetRealAlignment() >= 0)
            iAlignIndex = 4;
        else if (GetRealAlignment() > -40000)
            iAlignIndex = 5;
        else if (GetRealAlignment() > -80000)
            iAlignIndex = 6;
        else if (GetRealAlignment() > -120000)
            iAlignIndex = 7;
        else
            iAlignIndex = 8;

        std::vector<std::pair<LPITEM, int> > vec_item;
        LPITEM pkItem;
        int    i;
        bool isDropAllEquipments = false;

        TItemDropPenalty & r = table[iAlignIndex];
        sys_log(0, "%s align %d inven_pct %d equip_pct %d", GetName(), iAlignIndex, r.iInventoryPct, r.iEquipmentPct);

        bool bDropInventory = r.iInventoryPct >= number(1, 1000);
        bool bDropEquipment = r.iEquipmentPct >= number(1, 100);
        bool bDropAntiDropUniqueItem = false;

        if ((bDropInventory || bDropEquipment) && IsEquipUniqueItem(UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY))
        {
            bDropInventory = false;
            bDropEquipment = false;
            bDropAntiDropUniqueItem = true;
        }

        if (bDropInventory) // Drop Inventory
        {
            std::vector<BYTE> vec_bSlots;

            for (i = 0; i < INVENTORY_MAX_NUM; ++i)
                if (GetInventoryItem(i))
                    vec_bSlots.push_back(i);

            if (!vec_bSlots.empty())
            {
                random_shuffle(vec_bSlots.begin(), vec_bSlots.end());

                int iQty = MIN(vec_bSlots.size(), r.iInventoryQty);

                if (iQty)
                    iQty = number(1, iQty);

                for (i = 0; i < iQty; ++i)
                {
                    pkItem = GetInventoryItem(vec_bSlots);

                    if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP))
                        continue;

                    SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255);
                    vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY));
                }
            }
            else if (iAlignIndex == 8)
                isDropAllEquipments = true;
        }

        if (bDropEquipment) // Drop Equipment
        {
            std::vector<BYTE> vec_bSlots;

            for (i = 0; i < WEAR_MAX_NUM; ++i)
                if (GetWear(i))
                    vec_bSlots.push_back(i);

            if (!vec_bSlots.empty())
            {
                random_shuffle(vec_bSlots.begin(), vec_bSlots.end());
                int iQty;

                if (isDropAllEquipments)
                    iQty = vec_bSlots.size();
                else
                    iQty = MIN(vec_bSlots.size(), number(1, r.iEquipmentQty));

                if (iQty)
                    iQty = number(1, iQty);

                for (i = 0; i < iQty; ++i)
                {
                    pkItem = GetWear(vec_bSlots);

                    if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP))
                        continue;

                    SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots, 255);
                    vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
                }
            }
        }

        if (bDropAntiDropUniqueItem)
        {
            LPITEM pkItem;

            pkItem = GetWear(WEAR_UNIQUE1);

            if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)
            {
                SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE1, 255);
                vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
            }

            pkItem = GetWear(WEAR_UNIQUE2);

            if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)
            {
                SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE2, 255);
                vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
            }
        }

        {
            PIXEL_POSITION pos;
            pos.x = GetX();
            pos.y = GetY();

            unsigned int i;

            for (i = 0; i < vec_item.size(); ++i)
            {
                LPITEM item = vec_item.first;
                int window = vec_item.second;

                item->AddToGround(GetMapIndex(), pos);
                item->StartDestroyEvent();

                sys_log(0, "DROP_ITEM_PK: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName());
                LogManager::instance().ItemLog(this, item, "DEAD_DROP", (window == INVENTORY) ? "INVENTORY" : ((window == EQUIPMENT) ? "EQUIPMENT" : ""));

                pos.x = GetX() + number(-7, 7) * 20;
                pos.y = GetY() + number(-7, 7) * 20;
            }
        }
    }

    class FPartyAlignmentCompute
    {
        public:
            FPartyAlignmentCompute(int iAmount, int x, int y)
            {
                m_iAmount = iAmount;
                m_iCount = 0;
                m_iStep = 0;
                m_iKillerX = x;
                m_iKillerY = y;
            }

            void operator () (LPCHARACTER pkChr)
            {
                if (DISTANCE_APPROX(pkChr->GetX() - m_iKillerX, pkChr->GetY() - m_iKillerY) < PARTY_DEFAULT_RANGE)
                {
                    if (m_iStep == 0)
                    {
                        ++m_iCount;
                    }
                    else
                    {
                        pkChr->UpdateAlignment(m_iAmount / m_iCount);
                    }
                }
            }

            int m_iAmount;
            int m_iCount;
            int m_iStep;

            int m_iKillerX;
            int m_iKillerY;
    };

    void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead)
    {
        if (IsDead())
            return;

        {
            if (IsHorseRiding())
            {
                StopRiding();
            }
            else if (GetMountVnum())
            {
                RemoveAffect(AFFECT_MOUNT_BONUS);
                m_dwMountVnum = 0;
                UnEquipSpecialRideUniqueItem();

                UpdatePacket();
            }
        }

        if (!pkKiller && m_dwKillerPID)
            pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID);

        m_dwKillerPID = 0; // 반드시 초기화 해야함 DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE

        bool isAgreedPVP = false;
        bool isUnderGuildWar = false;
        bool isDuel = false;
        bool isForked = false;

        if (pkKiller && pkKiller->IsPC())
        {
            if (pkKiller->m_pkChrTarget == this)
                pkKiller->SetTarget(NULL);

            if (!IsPC() && pkKiller->GetDungeon())
                pkKiller->GetDungeon()->IncKillCount(pkKiller, this);

            isAgreedPVP = CPVPManager::instance().Dead(this, pkKiller->GetPlayerID());
            isDuel = CArenaManager::instance().OnDead(pkKiller, this);

            if (IsPC())
            {
                CGuild * g1 = GetGuild();
                CGuild * g2 = pkKiller->GetGuild();

                if (g1 && g2)
                    if (g1->UnderWar(g2->GetID()))
                        isUnderGuildWar = true;

                pkKiller->SetQuestNPCID(GetVID());
                quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC);
                CGuildManager::instance().Kill(pkKiller, this);
            }
        }

        //CHECK_FORKEDROAD_WAR
        if (IsPC())
        {
            if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex()))
                isForked = true;
        }
        //END_CHECK_FORKEDROAD_WAR

        if (pkKiller &&
                !isAgreedPVP &&
                !isUnderGuildWar &&
                IsPC() &&
                !isDuel &&
                !isForked &&
                !IS_CASTLE_MAP(GetMapIndex()))
        {
            if (GetGMLevel() == GM_PLAYER || test_server)
            {
                ItemDropPenalty(pkKiller);
            }
        }

        // CASTLE_SIEGE
        if (IS_CASTLE_MAP(GetMapIndex()))
        {
            if (CASTLE_FROG_VNUM == GetRaceNum())
                castle_frog_die(this, pkKiller);
            else if (castle_is_guard_vnum(GetRaceNum()))
                castle_guard_die(this, pkKiller);
            else if (castle_is_tower_vnum(GetRaceNum()))
                castle_tower_die(this, pkKiller);
        }
        // CASTLE_SIEGE

        if (true == isForked)
        {
            CThreeWayWar::instance().onDead( this, pkKiller );
        }

        SetPosition(POS_DEAD);
        ClearAffect(true);

        if (pkKiller && IsPC())
        {
            if (!pkKiller->IsPC())
            {
                if (!isForked)
                {
                    sys_log(1, "DEAD: %s %p WITH PENALTY", GetName(), this);
                    SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
                    LogManager::instance().CharLog(this, pkKiller->GetRaceNum(), "DEAD_BY_NPC", pkKiller->GetName());
                }
            }
            else
            {
                sys_log(1, "DEAD_BY_PC: %s %p KILLER %s %p", GetName(), this, pkKiller->GetName(), get_pointer(pkKiller));
                REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);

                if (GetEmpire() != pkKiller->GetEmpire())
                {
                    int iEP = MIN(GetPoint(POINT_EMPIRE_POINT), pkKiller->GetPoint(POINT_EMPIRE_POINT));

                    PointChange(POINT_EMPIRE_POINT, -(iEP / 10));
                    pkKiller->PointChange(POINT_EMPIRE_POINT, iEP / 5);

                    if (GetPoint(POINT_EMPIRE_POINT) < 10)
                    {
                        // TODO : 입구로 날리는 코드를 넣어야 한다.
                    }

                    char buf[256];
                    snprintf(buf, sizeof(buf),
                            "%d %d %d %s %d %d %d %s",
                            GetEmpire(), GetAlignment(), GetPKMode(), GetName(),
                            pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName());

                    LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf);
                }
                else
                {
                    if (!isAgreedPVP && !isUnderGuildWar && !IsKillerMode() && GetAlignment() >= 0 && !isDuel && !isForked)
                    {
                        int iNoPenaltyProb = 0;

                        if (g_iUseLocale)
                        {
                            if (pkKiller->GetAlignment() >= 0)    // 1/3 percent down
                                iNoPenaltyProb = 33;
                            else                // 4/5 percent down
                                iNoPenaltyProb = 20;
                        }

                        if (number(1, 100) < iNoPenaltyProb)
                            pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 보호로 아이템이 떨어지지 않았습니다."));
                        else
                        {
                            if (g_iUseLocale && pkKiller->GetParty())
                            {
                                FPartyAlignmentCompute f(-20000, pkKiller->GetX(), pkKiller->GetY());
                                pkKiller->GetParty()->ForEachOnlineMember(f);

                                if (f.m_iCount == 0)
                                    pkKiller->UpdateAlignment(-20000);
                                else
                                {
                                    sys_log(0, "ALIGNMENT PARTY count %d amount %d", f.m_iCount, f.m_iAmount);

                                    f.m_iStep = 1;
                                    pkKiller->GetParty()->ForEachOnlineMember(f);
                                }
                            }
                            else
                                pkKiller->UpdateAlignment(-20000);
                        }
                    }

                    char buf[256];
                    snprintf(buf, sizeof(buf),
                            "%d %d %d %s %d %d %d %s",
                            GetEmpire(), GetAlignment(), GetPKMode(), GetName(),
                            pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName());

                    LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf);
                }
            }
        }
        else
        {
            sys_log(1, "DEAD: %s %p", GetName(), this);
            REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
        }

        ClearSync();

        //sys_log(1, "stun cancel %s[%d]", GetName(), (DWORD)GetVID());
        event_cancel(&m_pkStunEvent); // 기절 이벤트는 죽인다.

        if (IsPC())
        {
            m_dwLastDeadTime = get_dword_time();
            SetKillerMode(false);
            GetDesc()->SetPhase(PHASE_DEAD);
        }
        else
        {
            // 가드에게 공격받은 몬스터는 보상이 없어야 한다.
            if (!IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD))
            {
                if (!(pkKiller && pkKiller->IsPC() && pkKiller->GetGuild() && pkKiller->GetGuild()->UnderAnyWar(GUILD_WAR_TYPE_FIELD)))
                {
                    // 부활하는 몬스터는 보상을 주지 않는다.
                    if (GetMobTable().dwResurrectionVnum)
                    {
                        // DUNGEON_MONSTER_REBIRTH_BUG_FIX
                        LPCHARACTER chResurrect = CHARACTER_MANAGER::instance().SpawnMob(GetMobTable().dwResurrectionVnum, GetMapIndex(), GetX(), GetY(), GetZ(), true, (int) GetRotation());
                        if (GetDungeon() && chResurrect)
                        {
                            chResurrect->SetDungeon(GetDungeon());
                        }
                        // END_OF_DUNGEON_MONSTER_REBIRTH_BUG_FIX

                        Reward(false);
                    }
                    else if (IsRevive() == true)
                    {
                        Reward(false);
                    }
                    else
                    {
                        Reward(true); // Drops gold, item, etc..
                    }
                }
                else
                {
                    if (pkKiller->m_dwUnderGuildWarInfoMessageTime < get_dword_time())
                    {
                        pkKiller->m_dwUnderGuildWarInfoMessageTime = get_dword_time() + 60000;
                        pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드전중에는 사냥에 따른 이익이 없습니다."));
                    }
                }
            }
        }

        // BOSS_KILL_LOG
        if (GetMobRank() >= MOB_RANK_BOSS && pkKiller && pkKiller->IsPC())
        {
            char buf[51];
            snprintf(buf, sizeof(buf), "%d %ld", g_bChannel, pkKiller->GetMapIndex());
            if (IsStone())
                LogManager::instance().CharLog(pkKiller, GetRaceNum(), "STONE_KILL", buf);
            else
                LogManager::instance().CharLog(pkKiller, GetRaceNum(), "BOSS_KILL", buf);
        }
        // END_OF_BOSS_KILL_LOG

        TPacketGCDead pack;
        pack.header    = HEADER_GC_DEAD;
        pack.vid    = m_vid;
        PacketAround(&pack, sizeof(pack));

        REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);

        // 플레이어 캐릭터이면
        if (GetDesc() != NULL) {
            //
            // 클라이언트에 에펙트 패킷을 다시 보낸다.
            //
            itertype(m_list_pkAffect) it = m_list_pkAffect.begin();

            while (it != m_list_pkAffect.end())
                SendAffectAddPacket(GetDesc(), *it++);
        }

        //
        // Dead 이벤트 생성,
        //
        // Dead 이벤트에서는 몬스터의 경우 몇초 후에 Destroy 되도록 해주며,
        // PC의 경우 3분 있다가 마을에서 나오도록 해 준다. 3분 내에는 유저로부터
        // 마을에서 시작할 건지, 여기서 시작할 건지 결정을 받는다.
        if (isDuel == false)
        {
            if (m_pkDeadEvent)
            {
                sys_log(1, "DEAD_EVENT_CANCEL: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
                event_cancel(&m_pkDeadEvent);
            }

            if (IsStone())
                ClearStone();

            if (GetDungeon())
            {
                GetDungeon()->DeadCharacter(this);
            }

            SCharDeadEventInfo* pEventInfo = AllocEventInfo<SCharDeadEventInfo>();

            if (IsPC())
            {
                pEventInfo->isPC = true;
                pEventInfo->dwID = this->GetPlayerID();

                m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(180));
            }
            else
            {
                pEventInfo->isPC = false;
                pEventInfo->dwID = this->GetVID();

                if (IsRevive() == false && HasReviverInParty() == true)
                {
                    m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3));
                }
                else
                {
                    m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(10));
                }
            }

            sys_log(1, "DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
        }

        if (m_pkExchange != NULL)
        {
            m_pkExchange->Cancel();
        }

        if (IsCubeOpen() == true)
        {
            Cube_close(this);
        }
        
        CShopManager::instance().StopShopping(this);
        CloseMyShop();
        CloseSafebox();

        if (true == IsMonster() && 2493 == GetMobTable().dwVnum)
        {
            if (NULL != pkKiller && NULL != pkKiller->GetGuild())
            {
                CDragonLairManager::instance().OnDragonDead( this, pkKiller->GetGuild()->GetID() );
            }
            else
            {
                sys_err("DragonLair: Dragon killed by nobody");
            }
        }
    }

    struct FuncSetLastAttacked
    {
        FuncSetLastAttacked(DWORD dwTime) : m_dwTime(dwTime)
        {
        }

        void operator () (LPCHARACTER ch)
        {
            ch->SetLastAttacked(m_dwTime);
        }

        DWORD m_dwTime;
    };

    void CHARACTER::SetLastAttacked(DWORD dwTime)
    {
        assert(m_pkMobInst != NULL);

        m_pkMobInst->m_dwLastAttackedTime = dwTime;
        m_pkMobInst->m_posLastAttacked = GetXYZ();
    }

    void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag)
    {
        if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this))
        {
            TPacketGCDamageInfo damageInfo;
            memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo));

            damageInfo.header = HEADER_GC_DAMAGE_INFO;
            damageInfo.dwVID = (DWORD)GetVID();
            damageInfo.flag = DamageFlag;
            damageInfo.damage = Damage;

            if (GetDesc() != NULL)
            {
                GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
            }

            if (pAttacker->GetDesc() != NULL)
            {
                pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
            }
            /*
               if (GetArenaObserverMode() == false && GetArena() != NULL)
               {
               GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo));
               }
             */        
        }
    }

    //
    // CHARACTER::Damage 메소드는 this가 데미지를 입게 한다.
    //
    // Arguments
    //    pAttacker        : 공격자
    //    dam        : 데미지
    //    EDamageType    : 어떤 형식의 공격인가?
    //    
    // Return value
    //    true        : dead
    //    false        : not dead yet
    // 
    bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead
    {
        if (DAMAGE_TYPE_MAGIC == type)
        {
            dam = (int)((float)dam * (100 + (pAttacker->GetPoint(POINT_MAGIC_ATT_BONUS_PER) + pAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100.f + 0.5f);
        }
        if (GetRaceNum() == 5001)
        {
            bool bDropMoney = false;
            int iPercent = (GetHP() * 100) / GetMaxHP();

            if (iPercent <= 10 && GetMaxSP() < 5)
            {
                SetMaxSP(5);
                bDropMoney = true;
            }
            else if (iPercent <= 20 && GetMaxSP() < 4)
            {
                SetMaxSP(4);
                bDropMoney = true;
            }
            else if (iPercent <= 40 && GetMaxSP() < 3)
            {
                SetMaxSP(3);
                bDropMoney = true;
            }
            else if (iPercent <= 60 && GetMaxSP() < 2)
            {
                SetMaxSP(2);
                bDropMoney = true;
            }
            else if (iPercent <= 80 && GetMaxSP() < 1)
            {
                SetMaxSP(1);
                bDropMoney = true;
            }

            if (bDropMoney)
            {
                DWORD dwGold = 1000;
                int iSplitCount = number(10, 13);

                sys_log(0, "WAEGU DropGoldOnHit %d times", GetMaxSP());

                for (int i = 1; i <= iSplitCount; ++i)
                {
                    PIXEL_POSITION pos;
                    LPITEM item;

                    if ((item = ITEM_MANAGER::instance().CreateItem(1, dwGold / iSplitCount)))
                    {
                        if (i != 0)
                        {
                            pos.x = (number(-14, 14) + number(-14, 14)) * 20;
                            pos.y = (number(-14, 14) + number(-14, 14)) * 20;

                            pos.x += GetX();
                            pos.y += GetY();
                        }

                        item->AddToGround(GetMapIndex(), pos);
                        item->StartDestroyEvent();
                    }
                }
            }
        }

        // 평타가 아닐 때는 공포 처리
        if (type != DAMAGE_TYPE_NORMAL && type != DAMAGE_TYPE_NORMAL_RANGE)
        {
            if (IsAffectFlag(AFF_TERROR))
            {
                int pct = GetSkillPower(SKILL_TERROR) / 400;

                if (number(1, 100) <= pct)
                    return false;
            }
        }

        int iCurHP = GetHP();
        int iCurSP = GetSP();

        bool IsCritical = false;
        bool IsPenetrate = false;
        bool IsDeathBlow = false;

        enum DamageFlag
        {
            DAMAGE_NORMAL    = (1 << 0),
            DAMAGE_POISON    = (1 << 1),
            DAMAGE_DODGE    = (1 << 2),
            DAMAGE_BLOCK    = (1 << 3),
            DAMAGE_PENETRATE= (1 << 4),
            DAMAGE_CRITICAL = (1 << 5),
        };

        //PROF_UNIT puAttr("Attr");

        //
        // 마법형 스킬과, 레인지형 스킬은(궁자객) 크리티컬과, 관통공격 계산을 한다.
        // 원래는 하지 않아야 하는데 Nerf(다운밸런스)패치를 할 수 없어서 크리티컬과
        // 관통공격의 원래 값을 쓰지 않고, /2 이상하여 적용한다.
        // 
        // 무사 이야기가 많아서 밀리 스킬도 추가
        //
        // 20091109 : 무사가 결과적으로 엄청나게 강해진 것으로 결론남, 독일 기준 무사 비율 70% 육박
        //
        if (type == DAMAGE_TYPE_MELEE || type == DAMAGE_TYPE_RANGE || type == DAMAGE_TYPE_MAGIC)
        {
            if (pAttacker)
            {
                // 크리티컬
                int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT);

                if (!IsPC())
                    iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS);

                if (iCriticalPct)
                {
                    if (iCriticalPct >= 10) // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20%
                        iCriticalPct = 5 + (iCriticalPct - 10) / 4;
                    else // 10보다 작으면 단순히 반으로 깎음, 10 = 5%
                        iCriticalPct /= 2;

                    //크리티컬 저항 값 적용.
                    iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL);

                    if (number(1, 100) <= iCriticalPct)
                    {
                        IsCritical = true;
                        dam *= 2;
                        EffectPacket(SE_CRITICAL);

                        if (IsAffectFlag(AFF_MANASHIELD))
                        {
                            RemoveAffect(AFF_MANASHIELD);
                        }
                    }
                }

                // 관통공격
                int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT);

                if (!IsPC())
                    iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS);


                if (iPenetratePct)
                {
                    {
                        CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE);

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

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

                    if (iPenetratePct >= 10)
                    {
                        // 10보다 크면 5% + (4마다 1%씩 증가), 따라서 수치가 50이면 20%
                        iPenetratePct = 5 + (iPenetratePct - 10) / 4;
                    }
                    else
                    {
                        // 10보다 작으면 단순히 반으로 깎음, 10 = 5%
                        iPenetratePct /= 2;
                    }

                    //관통타격 저항 값 적용.
                    iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE);

                    if (number(1, 100) <= iPenetratePct)
                    {
                        IsPenetrate = true;

                        if (test_server)
                            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("관통 추가 데미지 %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100);

                        dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100;

                        if (IsAffectFlag(AFF_MANASHIELD))
                        {
                            RemoveAffect(AFF_MANASHIELD);
                        }
                    }
                }
            }
        }
        // 
        // 콤보 공격, 활 공격, 즉 평타 일 때만 속성값들을 계산을 한다.
        //
        else if (type == DAMAGE_TYPE_NORMAL || type == DAMAGE_TYPE_NORMAL_RANGE)
        {
            if (type == DAMAGE_TYPE_NORMAL)
            {
                // 근접 평타일 경우 막을 수 있음
                if (GetPoint(POINT_BLOCK) && number(1, 100) <= GetPoint(POINT_BLOCK))
                {
                    if (test_server)
                    {
                        pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 블럭! (%d%%)"), GetName(), GetPoint(POINT_BLOCK));
                        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 블럭! (%d%%)"), GetName(), GetPoint(POINT_BLOCK));
                    }

                    SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
                    return false;
                }
            }
            else if (type == DAMAGE_TYPE_NORMAL_RANGE)
            {
                // 원거리 평타의 경우 피할 수 있음
                if (GetPoint(POINT_DODGE) && number(1, 100) <= GetPoint(POINT_DODGE))
                {
                    if (test_server)
                    {
                        pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 회피! (%d%%)"), GetName(), GetPoint(POINT_DODGE));
                        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 회피! (%d%%)"), GetName(), GetPoint(POINT_DODGE));
                    }

                    SendDamagePacket(pAttacker, 0, DAMAGE_DODGE);
                    return false;
                }
            }

            if (IsAffectFlag(AFF_JEONGWIHON))
                dam = (int) (dam * (100 + GetSkillPower(SKILL_JEONGWI) * 25 / 100) / 100);

            if (IsAffectFlag(AFF_TERROR))
                dam = (int) (dam * (95 - GetSkillPower(SKILL_TERROR) / 5) / 100);

            if (IsAffectFlag(AFF_HOSIN))
                dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100;

            //
            // 공격자 속성 적용
            //
            if (pAttacker)
            {
                if (type == DAMAGE_TYPE_NORMAL)
                {
                    // 반사
                    if (GetPoint(POINT_REFLECT_MELEE))
                    {
                        int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100;

                        // NOTE: 공격자가 IMMUNE_REFLECT 속성을 갖고있다면 반사를 안 하는 게 
                        // 아니라 1/3 데미지로 고정해서 들어가도록 기획에서 요청.
                        if (pAttacker->IsImmune(IMMUNE_REFLECT))
                            reflectDamage = int(reflectDamage / 3.0f + 0.5f);

                        pAttacker->Damage(this, reflectDamage, DAMAGE_TYPE_SPECIAL);
                    }
                }

                // 크리티컬
                int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT);

                if (!IsPC())
                    iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS);

                if (iCriticalPct)
                {
                    //크리티컬 저항 값 적용.
                    iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL);

                    if (number(1, 100) <= iCriticalPct)
                    {
                        IsCritical = true;
                        dam *= 2;
                        EffectPacket(SE_CRITICAL);
                    }
                }

                // 관통공격
                int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT);

                if (!IsPC())
                    iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS);

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

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

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


                if (iPenetratePct)
                {
                    
                    //관통타격 저항 값 적용.
                    iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE);

                    if (number(1, 100) <= iPenetratePct)
                    {
                        IsPenetrate = true;

                        if (test_server)
                            ChatPacket(CHAT_TYPE_INFO, LC_TEXT("관통 추가 데미지 %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100);
                        dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100;
                    }
                }

                // HP 스틸
                if (pAttacker->GetPoint(POINT_STEAL_HP))
                {
                    int pct = 1;

                    if (number(1, 10) <= pct)
                    {
                        int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100;

                        if (iHP > 0 && GetHP() >= iHP)
                        {
                            CreateFly(FLY_HP_SMALL, pAttacker);
                            pAttacker->PointChange(POINT_HP, iHP);
                            PointChange(POINT_HP, -iHP);
                        }
                    }
                }

                // SP 스틸
                if (pAttacker->GetPoint(POINT_STEAL_SP))
                {
                    int pct = 1;

                    if (number(1, 10) <= pct)
                    {
                        int iCur;

                        if (IsPC())
                            iCur = iCurSP;
                        else
                            iCur = iCurHP;

                        int iSP = MIN(dam, MAX(0, iCur)) * pAttacker->GetPoint(POINT_STEAL_SP) / 100;

                        if (iSP > 0 && iCur >= iSP)
                        {
                            CreateFly(FLY_SP_SMALL, pAttacker);
                            pAttacker->PointChange(POINT_SP, iSP);

                            if (IsPC())
                                PointChange(POINT_SP, -iSP);
                        }
                    }
                }

                // 돈 스틸
                if (pAttacker->GetPoint(POINT_STEAL_GOLD))
                {
                    if (number(1, 100) <= pAttacker->GetPoint(POINT_STEAL_GOLD))
                    {
                        int iAmount = number(1, GetLevel());
                        pAttacker->PointChange(POINT_GOLD, iAmount);
                        DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 1, iAmount);
                    }
                }

                // 칠 때마다 HP회복
                if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% 확률
                {
                    int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100;

                    if (i)
                    {
                        CreateFly(FLY_HP_SMALL, pAttacker);
                        pAttacker->PointChange(POINT_HP, i);
                    }
                }

                // 칠 때마다 SP회복
                if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) // 80% 확률
                {
                    int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100;

                    if (i)
                    {
                        CreateFly(FLY_SP_SMALL, pAttacker);
                        pAttacker->PointChange(POINT_SP, i);
                    }
                }

                // 상대방의 마나를 없앤다.
                if (pAttacker->GetPoint(POINT_MANA_BURN_PCT))
                {
                    if (number(1, 100) <= pAttacker->GetPoint(POINT_MANA_BURN_PCT))
                        PointChange(POINT_SP, -50);
                }
            }
        }

        //
        // 평타 또는 스킬로 인한 보너스 피해/방어 계산
        // 
        switch (type)
        {
            case DAMAGE_TYPE_NORMAL:
            case DAMAGE_TYPE_NORMAL_RANGE:
                if (pAttacker)
                    if (pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS))
                        dam = dam * (100 + pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) / 100;

                dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100;
                break;

            case DAMAGE_TYPE_MELEE:
            case DAMAGE_TYPE_RANGE:
            case DAMAGE_TYPE_FIRE:
            case DAMAGE_TYPE_ICE:
            case DAMAGE_TYPE_ELEC:
            case DAMAGE_TYPE_MAGIC:
                if (pAttacker)
                    if (pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS))
                        dam = dam * (100 + pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) / 100;

                dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100;
                break;

            default:
                break;
        }

        //
        // 마나쉴드(흑신수호)
        //
        if (IsAffectFlag(AFF_MANASHIELD))
        {
            // POINT_MANASHIELD 는 작아질수록 좋다
            int iDamageSPPart = dam / 3;
            int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100;
            int iSP = GetSP();

            // SP가 있으면 무조건 데미지 절반 감소
            if (iDamageToSP <= iSP)
            {
                PointChange(POINT_SP, -iDamageToSP);
                dam -= iDamageSPPart;
            }
            else
            {
                // 정신력이 모자라서 피가 더 깍여야할
                PointChange(POINT_SP, -GetSP());
                dam -= iSP * 100 / MAX(GetPoint(POINT_MANASHIELD), 1);
            }
        }

        //
        // 전체 방어력 상승 (몰 아이템)
        // 
        if (GetPoint(POINT_MALL_DEFBONUS) > 0)
        {
            int dec_dam = MIN(200, dam * GetPoint(POINT_MALL_DEFBONUS) / 100);
            dam -= dec_dam;
        }

        if (pAttacker)
        {
            //
            // 전체 공격력 상승 (몰 아이템)
            //
            if (pAttacker->GetPoint(POINT_MALL_ATTBONUS) > 0)
            {
                int add_dam = MIN(300, dam * pAttacker->GetLimitPoint(POINT_MALL_ATTBONUS) / 100);
                dam += add_dam;
            }

            //
            // 제국으로 인한 보너스 (한국 올드 버전만 적용)
            //
            int iEmpire = GetEmpire();
            long lMapIndex = GetMapIndex();
            int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex);

            if (LC_IsYMIR() == true)
            {
                if (iEmpire && iMapEmpire && iEmpire != iMapEmpire)
                {
                    dam += (dam * 30) / 100;
                }
            }

            if (pAttacker->IsPC())
            {
                iEmpire = pAttacker->GetEmpire();
                lMapIndex = pAttacker->GetMapIndex();
                iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex);

                // 다른 제국 사람인 경우 데미지 10% 감소
                if (iEmpire && iMapEmpire && iEmpire != iMapEmpire)
                {
                    int percent = 10;
                    
                    if (184 <= lMapIndex && lMapIndex <= 189)
                    {
                        if (LC_IsYMIR() == true)
                            percent = 7;
                        else
                            percent = 9;
                    }
                    else
                    {
                        if (LC_IsYMIR() == true)
                            percent = 8;
                        else
                            percent = 9;
                    }

                    dam = dam * percent / 10;
                }

                if (!IsPC() && GetMonsterDrainSPPoint())
                {
                    int iDrain = GetMonsterDrainSPPoint();

                    if (iDrain <= pAttacker->GetSP())
                        pAttacker->PointChange(POINT_SP, -iDrain);
                    else
                    {
                        int iSP = pAttacker->GetSP();
                        pAttacker->PointChange(POINT_SP, -iSP);
                    }
                }

            }
            else if (pAttacker->IsGuardNPC())
            {
                SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD);
                Stun();
                return true;
            }

            //
            // 군주의 금강권 & 사자후 
            //
            if (pAttacker->IsPC() && CMonarch::instance().IsPowerUp(pAttacker->GetEmpire()))
            {
                // 10% 피해 증가
                dam += dam / 10;
            }

            if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire()))
            {
                // 10% 피해 감소
                dam -= dam / 10;
            }
        }
        //puAttr.Pop();

        if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
            return false;

        if (!IsPC())
        {
            if (m_pkParty && m_pkParty->GetLeader())
                m_pkParty->GetLeader()->SetLastAttacked(get_dword_time());
            else
                SetLastAttacked(get_dword_time());

            // 몬스터 대사 : 맞을 때
            MonsterChat(MONSTER_CHAT_ATTACKED);
        }

        if (IsStun())
        {
            Dead(pAttacker);
            return true;
        }

        if (IsDead())
            return true;

        // 독 공격으로 죽지 않도록 함.
        if (type == DAMAGE_TYPE_POISON)
        {
            if (GetHP() - dam <= 0)
            {
                dam = GetHP() - 1;
            }
        }

        // ------------------------
        // 독일 프리미엄 모드 
        // -----------------------
        if (LC_IsGermany() && pAttacker && pAttacker->IsPC())
        {
            int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker);
            dam = dam * iDmgPct / 100;
        }

        // STONE SKIN : 피해 반으로 감소
        if (IsMonster() && IsStoneSkinner())
        {
            if (GetHPPct() < GetMobTable().bStoneSkinPoint)
                dam /= 2;
        }

        //PROF_UNIT puRest1("Rest1");
        if (pAttacker)
        {
            // DEATH BLOW : 확률 적으로 4배 피해 (!? 현재 이벤트나 공성전용 몬스터만 사용함)
            if (pAttacker->IsMonster() && pAttacker->IsDeathBlower())
            {
                if (pAttacker->IsDeathBlow())
                {
                    if (number(1, 4) == GetJob())
                    {
                        IsDeathBlow = true;
                        dam = dam * 4;
                    }
                }
            }

            dam = BlueDragon_Damage(this, pAttacker, dam);

            BYTE damageFlag = 0;

            if (type == DAMAGE_TYPE_POISON)
                damageFlag = DAMAGE_POISON;
            else
                damageFlag = DAMAGE_NORMAL;

            if (IsCritical == true)
                damageFlag |= DAMAGE_CRITICAL;

            if (IsPenetrate == true)
                damageFlag |= DAMAGE_PENETRATE;


            //최종 데미지 보정
            float damMul = this->GetDamMul();
            float tempDam = dam;
            dam = tempDam * damMul + 0.5f;


            if (pAttacker)
                SendDamagePacket(pAttacker, dam, damageFlag);

            if (test_server)
            {
                if(pAttacker)
                {
                    pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s",
                            GetName(), 
                            dam, 
                            GetHP(),
                            (GetHP() * 100) / GetMaxHP(),
                            IsCritical ? "crit " : "",
                            IsPenetrate ? "pene " : "",
                            IsDeathBlow ? "deathblow " : "");
                }

                ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s",
                        pAttacker ? pAttacker->GetName() : 0,
                        dam, 
                        GetHP(),
                        (GetHP() * 100) / GetMaxHP(),
                        IsCritical ? "crit " : "",
                        IsPenetrate ? "pene " : "",
                        IsDeathBlow ? "deathblow " : "");
            }

            if (m_bDetailLog)
            {
                ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s[%d]가 공격 위치: %d %d"), pAttacker->GetName(), (DWORD) pAttacker->GetVID(), pAttacker->GetX(), pAttacker->GetY());
            }
        }

        //
        // !!!!!!!!! 실제 HP를 줄이는 부분 !!!!!!!!!
        //
        if (!cannot_dead)
        {
            PointChange(POINT_HP, -dam, false);
        }

        //puRest1.Pop();

        //PROF_UNIT puRest2("Rest2");
        if (pAttacker && dam > 0 && IsNPC())
        {
            //PROF_UNIT puRest20("Rest20");
            TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID());

            if (it == m_map_kDamage.end())
            {
                m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0)));
                it = m_map_kDamage.find(pAttacker->GetVID());
            }
            else
            {
                it->second.iTotalDamage += dam;
            }
            //puRest20.Pop();

            //PROF_UNIT puRest21("Rest21");
            StartRecoveryEvent(); // 몬스터는 데미지를 입으면 회복을 시작한다.
            //puRest21.Pop();

            //PROF_UNIT puRest22("Rest22");
            UpdateAggrPointEx(pAttacker, type, dam, it->second);
            //puRest22.Pop();
        }
        //puRest2.Pop();

        //PROF_UNIT puRest3("Rest3");
        if (GetHP() <= 0)
        {
            Stun();

            if (pAttacker && !pAttacker->IsNPC())
                m_dwKillerPID = pAttacker->GetPlayerID();
            else
                m_dwKillerPID = 0;
        }

        return false;
    }

    void CHARACTER::DistributeHP(LPCHARACTER pkKiller)
    {
        if (pkKiller->GetDungeon()) // 던젼내에선 만두가나오지않는다
            return;
    }

    static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp)
    {
        // 레벨차 경험치 가감비율
        iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp);

        // 외부 테스트 서버 경험치 3배 보너스
        if (distribution_test_server)
            iExp *= 3;

        int iBaseExp = iExp;

        // 점술, 회사 경험치 이벤트 적용
        iExp = iExp * (100 + CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT)) / 100;

        // 게임내 기본 제공되는 경험치 보너스
        {
            // 노동절 메달
            if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL))
                iExp += iExp * 20 /100;

            // 사귀타워 경험치 보너스
            if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000) 
                iExp += iExp * 20 / 100; // 1.2배 (20%)

            // 아이템 경험치 두배 속성
            if (to->GetPoint(POINT_EXP_DOUBLE_BONUS))
                if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS))
                    iExp += iExp * 30 / 100; // 1.3배 (30%)

            // 경험의 반지 (2시간짜리)
            if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP))
                iExp += iExp * 50 / 100;

            switch (to->GetMountVnum())
            {
                case 20110:
                case 20111:
                case 20112:
                case 20113:
                    if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) ||
                            to->IsEquipUniqueItem(71121) )
                    {
                        iExp += iExp * 10 / 100;
                    }
                    break;

                case 20114:
                case 20120:
                case 20121:
                case 20122:
                case 20123:
                case 20124:
                case 20125:
                    // 백사자 경험치 보너스
                    iExp += iExp * 30 / 100;
                    break;
            }
        }

        // 아이템 몰 판매 경험치 보너스
        if (LC_IsHongKong() || LC_IsEurope() || LC_IsCanada())
        {
            // 아이템 몰: 경험치 결제
            if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
            {
                iExp += (iExp * 50 / 100);
            }

            if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
            {
                iExp += (iExp * 50 / 100);
            }

            // PC방 아템 경치 보너스
            if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
            {
                if (to->IsPCBang() == true)
                    iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
            }

            // 결혼 보너스
            iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
        }
        else if (/*LC_IsNewCIBN() || */LC_IsBrazil())
        {
            // 아이템 몰: 경험치 결제
            if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
            {
                iExp += iExp;
            }

            if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
            {
                iExp += iExp;
            }

            // PC방 아템 경치 보너스
            if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
            {
                if (to->IsPCBang() == true)
                    iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
            }

            // 결혼 보너스
            iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
        }
        else
        {
            // 아이템 몰: 경험치 결제
            if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
            {
                iExp += (iExp * 20 / 100);
            }

            if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
            {
                iExp += (iExp * 20 / 100);
            }

            // PC방 아템 경치 보너스
            if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
            {
                if (to->IsPCBang() == true)
                    iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
            }

            // 결혼 보너스
            iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
        }

        iExp += (iExp * to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP)/100);
        iExp += (iExp * to->GetPoint(POINT_MALL_EXPBONUS)/100);
        iExp += (iExp * to->GetPoint(POINT_EXP)/100);

    /*    if (speed_server)
        {
            iExp += iExp * CSpeedServerManager::ExpBonus();

        }
    */
        if (test_server)
        {
            sys_log(0, "Bonus Exp : Ramadan Candy: %d MallExp: %d PointExp: %d", 
                    to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP),
                    to->GetPoint(POINT_MALL_EXPBONUS),
                    to->GetPoint(POINT_EXP)
                   );
        }

        // 기획측 조정값 2005.04.21 현재 85%
        iExp = iExp * CHARACTER_MANAGER::instance().GetMobExpRate(to) / 100;

        // 경험치 한번 획득량 제한
        iExp = MIN(to->GetNextExp() / 10, iExp);

        if (test_server)
        {
            if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && iBaseExp>0)
                to->ChatPacket(CHAT_TYPE_INFO, "exp bonus %d%%", (iExp-iBaseExp)*100/iBaseExp);
        }

        iExp = AdjustExpByLevel(to, iExp);

        to->PointChange(POINT_EXP, iExp, true);
        from->CreateFly(FLY_EXP, to);

        {
            LPCHARACTER you = to->GetMarryPartner();
            // 부부가 서로 파티중이면 금슬이 오른다
            if (you)
            {
                // 1억이 100%
                DWORD dwUpdatePoint = 2000*iExp/to->GetLevel()/to->GetLevel()/3;

                if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 || 
                        you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0)
                    dwUpdatePoint = (DWORD)(dwUpdatePoint * 3);

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

                // DIVORCE_NULL_BUG_FIX
                if (pMarriage && pMarriage->IsNear())
                    pMarriage->Update(dwUpdatePoint);
                // END_OF_DIVORCE_NULL_BUG_FIX
            }
        }
    }

    namespace NPartyExpDistribute
    {
        struct FPartyTotaler
        {
            int        total;
            int        member_count;
            int        x, y;

            FPartyTotaler(LPCHARACTER center)
                : total(0), member_count(0), x(center->GetX()), y(center->GetY())
            {};

            void operator () (LPCHARACTER ch)
            {
                if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
                {
                    if (LC_IsYMIR())
                        total += ch->GetLevel();
                    else
                        total += party_exp_distribute_table[ch->GetLevel()];

                    ++member_count;
                }
            }
        };

        struct FPartyDistributor
        {
            int        total;
            LPCHARACTER    c;
            int        x, y;
            DWORD        _iExp;
            int        m_iMode;
            int        m_iMemberCount;

            FPartyDistributor(LPCHARACTER center, int member_count, int total, DWORD iExp, int iMode) 
                : total(total), c(center), x(center->GetX()), y(center->GetY()), _iExp(iExp), m_iMode(iMode), m_iMemberCount(member_count)
                {
                    if (m_iMemberCount == 0)
                        m_iMemberCount = 1;
                };

            void operator () (LPCHARACTER ch)
            {
                if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
                {
                    DWORD iExp2 = 0;

                    switch (m_iMode)
                    {
                        case PARTY_EXP_DISTRIBUTION_NON_PARITY:
                            if (LC_IsYMIR())
                                iExp2 = (DWORD) ((_iExp * ch->GetLevel()) / total);
                            else
                                iExp2 = (DWORD) (_iExp * (float) party_exp_distribute_table[ch->GetLevel()] / total);
                            break;

                        case PARTY_EXP_DISTRIBUTION_PARITY:
                            iExp2 = _iExp / m_iMemberCount;
                            break;

                        default:
                            sys_err("Unknown party exp distribution mode %d", m_iMode);
                            return;
                    }

                    GiveExp(c, ch, iExp2);
                }
            }
        };
    }

    typedef struct SDamageInfo
    {
        int iDam;
        LPCHARACTER pAttacker;
        LPPARTY pParty;

        void Clear()
        {
            pAttacker = NULL;
            pParty = NULL;
        }

        inline void Distribute(LPCHARACTER ch, int iExp)
        {
            if (pAttacker)
                GiveExp(ch, pAttacker, iExp);
            else if (pParty)
            {
                NPartyExpDistribute::FPartyTotaler f(ch);
                pParty->ForEachOnlineMember(f);

                if (pParty->IsPositionNearLeader(ch))
                    iExp = iExp * (100 + pParty->GetExpBonusPercent()) / 100;

                if (test_server)
                {
                    if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && pParty->GetExpBonusPercent())
                        pParty->ChatPacketToAllMember(CHAT_TYPE_INFO, "exp party bonus %d%%", pParty->GetExpBonusPercent());
                }

                // 경험치 몰아주기 (파티가 획득한 경험치를 5% 빼서 먼저 줌)
                if (pParty->GetExpCentralizeCharacter())
                {
                    LPCHARACTER tch = pParty->GetExpCentralizeCharacter();

                    if (DISTANCE_APPROX(ch->GetX() - tch->GetX(), ch->GetY() - tch->GetY()) <= PARTY_DEFAULT_RANGE)
                    {
                        int iExpCenteralize = (int) (iExp * 0.05f);
                        iExp -= iExpCenteralize;

                        GiveExp(ch, pParty->GetExpCentralizeCharacter(), iExpCenteralize);
                    }
                }

                NPartyExpDistribute::FPartyDistributor fDist(ch, f.member_count, f.total, iExp, pParty->GetExpDistributionMode());
                pParty->ForEachOnlineMember(fDist);
            }
        }
    } TDamageInfo;

    LPCHARACTER CHARACTER::DistributeExp()
    {
        int iExpToDistribute = GetExp();

        if (iExpToDistribute <= 0)
            return NULL;

        int    iTotalDam = 0;
        LPCHARACTER pkChrMostAttacked = NULL;
        int iMostDam = 0;

        typedef std::vector<TDamageInfo> TDamageInfoTable;
        TDamageInfoTable damage_info_table;
        std::map<LPPARTY, TDamageInfo> map_party_damage;

        damage_info_table.reserve(m_map_kDamage.size());

        TDamageMap::iterator it = m_map_kDamage.begin();

        // 일단 주위에 없는 사람을 걸러 낸다. (50m)
        while (it != m_map_kDamage.end())
        {
            const VID & c_VID = it->first;
            int iDam = it->second.iTotalDamage;

            ++it;

            LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

            // NPC가 때리기도 하나? -.-;
            if (!pAttacker || pAttacker->IsNPC() || DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000)
                continue;

            iTotalDam += iDam;
            if (!pkChrMostAttacked || iDam > iMostDam)
            {
                pkChrMostAttacked = pAttacker;
                iMostDam = iDam;
            }

            if (pAttacker->GetParty())
            {
                std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.find(pAttacker->GetParty());
                if (it == map_party_damage.end())
                {
                    TDamageInfo di;
                    di.iDam = iDam;
                    di.pAttacker = NULL;
                    di.pParty = pAttacker->GetParty();
                    map_party_damage.insert(std::make_pair(di.pParty, di));
                }
                else
                {
                    it->second.iDam += iDam;
                }
            }
            else
            {
                TDamageInfo di;

                di.iDam = iDam;
                di.pAttacker = pAttacker;
                di.pParty = NULL;

                //sys_log(0, "__ pq_damage %s %d", pAttacker->GetName(), iDam);
                //pq_damage.push(di);
                damage_info_table.push_back(di);
            }
        }

        for (std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it)
        {
            damage_info_table.push_back(it->second);
            //sys_log(0, "__ pq_damage_party [%u] %d", it->second.pParty->GetLeaderPID(), it->second.iDam);
        }

        SetExp(0);
        //m_map_kDamage.clear();

        if (iTotalDam == 0)    // 데미지 준게 0이면 리턴
            return NULL;

        if (m_pkChrStone)    // 돌이 있을 경우 경험치의 반을 돌에게 넘긴다.
        {
            //sys_log(0, "__ Give half to Stone : %d", iExpToDistribute>>1);
            int iExp = iExpToDistribute >> 1;
            m_pkChrStone->SetExp(m_pkChrStone->GetExp() + iExp);
            iExpToDistribute -= iExp;
        }

        sys_log(1, "%s total exp: %d, damage_info_table.size() == %d, TotalDam %d",
                GetName(), iExpToDistribute, damage_info_table.size(), iTotalDam);
        //sys_log(1, "%s total exp: %d, pq_damage.size() == %d, TotalDam %d",
        //GetName(), iExpToDistribute, pq_damage.size(), iTotalDam);

        if (damage_info_table.empty())
            return NULL;

        // 제일 데미지를 많이 준 사람이 HP 회복을 한다.
        DistributeHP(pkChrMostAttacked);    // 만두 시스템

        {
            // 제일 데미지를 많이 준 사람이나 파티가 총 경험치의 20% + 자기가 때린만큼의 경험치를 먹는다.
            TDamageInfoTable::iterator di = damage_info_table.begin();
            {
                TDamageInfoTable::iterator it;

                for (it = damage_info_table.begin(); it != damage_info_table.end();++it)
                {
                    if (it->iDam > di->iDam)
                        di = it;
                }
            }

            int    iExp = iExpToDistribute / 5;
            iExpToDistribute -= iExp;

            float fPercent = (float) di->iDam / iTotalDam;

            if (fPercent > 1.0f)
            {
                sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di->pAttacker->GetName());
                fPercent = 1.0f;
            }

            iExp += (int) (iExpToDistribute * fPercent);

            //sys_log(0, "%s given exp percent %.1f + 20 dam %d", GetName(), fPercent * 100.0f, di.iDam);

            di->Distribute(this, iExp);

            // 100% 다 먹었으면 리턴한다.
            if (fPercent == 1.0f)
                return pkChrMostAttacked;

            di->Clear();
        }

        {
            // 남은 80%의 경험치를 분배한다.
            TDamageInfoTable::iterator it;

            for (it = damage_info_table.begin(); it != damage_info_table.end(); ++it)
            {
                TDamageInfo & di = *it;

                float fPercent = (float) di.iDam / iTotalDam;

                if (fPercent > 1.0f)
                {
                    sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di.pAttacker->GetName());
                    fPercent = 1.0f;
                }

                //sys_log(0, "%s given exp percent %.1f dam %d", GetName(), fPercent * 100.0f, di.iDam);
                di.Distribute(this, (int) (iExpToDistribute * fPercent));
            }
        }

        return pkChrMostAttacked;
    }

    // 화살 개수를 리턴해 줌
    int CHARACTER::GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount/* = 1 */)
    {
        LPITEM pkBow;

        if (!(pkBow = GetWear(WEAR_WEAPON)) || pkBow->GetProto()->bSubType != WEAPON_BOW)
        {
            return 0;
        }

        LPITEM pkArrow;

        if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON ||
                pkArrow->GetProto()->bSubType != WEAPON_ARROW)
        {
            return 0;
        }

        iArrowCount = MIN(iArrowCount, pkArrow->GetCount());

        *ppkBow = pkBow;
        *ppkArrow = pkArrow;

        return iArrowCount;
    }

    void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount)
    {
        int iCount = pkArrow->GetCount();
        DWORD dwVnum = pkArrow->GetVnum();
        iCount = iCount - MIN(iCount, dwArrowCount);
        pkArrow->SetCount(iCount);

        if (iCount == 0)
        {
            LPITEM pkNewArrow = FindSpecifyItem(dwVnum);

            sys_log(0, "UseArrow : FindSpecifyItem %u %p", dwVnum, get_pointer(pkNewArrow));

            if (pkNewArrow)
                EquipItem(pkNewArrow);
        }
    }

    class CFuncShoot
    {
        public:
            LPCHARACTER    m_me;
            BYTE        m_bType;
            bool        m_bSucceed;

            CFuncShoot(LPCHARACTER ch, BYTE bType) : m_me(ch), m_bType(bType), m_bSucceed(FALSE)
            {
            }

            void operator () (DWORD dwTargetVID)
            {
                if (m_bType > 1)
                {
                    if (g_bSkillDisable)
                        return;

                    m_me->m_SkillUseInfo[m_bType].SetMainTargetVID(dwTargetVID);
                    /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK)
                      m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/
                }

                LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);

                if (!pkVictim)
                    return;

                // 공격 불가
                if (!battle_is_attackable(m_me, pkVictim))
                    return;

                if (m_me->IsNPC())
                {
                    if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000)
                        return;
                }

                LPITEM pkBow, pkArrow;

                switch (m_bType)
                {
                    case 0: // 일반활
                        {
                            int iDam = 0;

                            if (m_me->IsPC())
                            {
                                if (m_me->GetJob() != JOB_ASSASSIN)
                                    return;

                                if (0 == m_me->GetArrowAndBow(&pkBow, &pkArrow))
                                    return;

                                if (m_me->GetSkillGroup() != 0)
                                    if (!m_me->IsNPC() && m_me->GetSkillGroup() != 2)
                                    {
                                        if (m_me->GetSP() < 5)
                                            return;

                                        m_me->PointChange(POINT_SP, -5);
                                    }

                                iDam = CalcArrowDamage(m_me, pkVictim, pkBow, pkArrow);
                                m_me->UseArrow(pkArrow, 1);

                                // check speed hack
                                DWORD    dwCurrentTime    = get_dword_time();
                                if (IS_SPEED_HACK(m_me, pkVictim, dwCurrentTime))
                                    iDam    = 0;
                            }
                            else
                                iDam = CalcMeleeDamage(m_me, pkVictim);

                            NormalAttackAffect(m_me, pkVictim);

                            // 데미지 계산
                            iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100;

                            //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam);

                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me);

                            pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_NORMAL_RANGE);
                            // 타격치 계산부 끝
                        }
                        break;

                    case 1: // 일반 마법
                        {
                            int iDam;

                            if (m_me->IsPC())
                                return;

                            iDam = CalcMagicDamage(m_me, pkVictim);

                            NormalAttackAffect(m_me, pkVictim);

                            // 데미지 계산
                            iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100;

                            //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam);

                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me); 

                            pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_MAGIC);
                            // 타격치 계산부 끝
                        }
                        break;

                    case SKILL_YEONSA:    // 연사
                        {
                            //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100);
                            int iUseArrow = 1;

                            // 토탈만 계산하는경우
                            {
                                if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                                {
                                    m_me->OnMove(true);
                                    pkVictim->OnMove();

                                    if (pkVictim->CanBeginFight())
                                        pkVictim->BeginFight(m_me); 

                                    m_me->ComputeSkill(m_bType, pkVictim);
                                    m_me->UseArrow(pkArrow, iUseArrow);

                                    if (pkVictim->IsDead())
                                        break;

                                }
                                else
                                    break;
                            }
                        }
                        break;


                    case SKILL_KWANKYEOK:
                        {
                            int iUseArrow = 1;

                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me); 

                                sys_log(0, "%s kwankeyok %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }
                        break;

                    case SKILL_GIGUNG:
                        {
                            int iUseArrow = 1;
                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me);

                                sys_log(0, "%s gigung %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }

                        break;
                    case SKILL_HWAJO:
                        {
                            int iUseArrow = 1;
                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me);

                                sys_log(0, "%s hwajo %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }

                        break;

                    case SKILL_HORSE_WILDATTACK_RANGE:
                        {
                            int iUseArrow = 1;
                            if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
                            {
                                m_me->OnMove(true);
                                pkVictim->OnMove();

                                if (pkVictim->CanBeginFight())
                                    pkVictim->BeginFight(m_me);

                                sys_log(0, "%s horse_wildattack %s", m_me->GetName(), pkVictim->GetName());
                                m_me->ComputeSkill(m_bType, pkVictim);
                                m_me->UseArrow(pkArrow, iUseArrow);
                            }
                        }

                        break;

                    case SKILL_MARYUNG:
                        //case SKILL_GUMHWAN:
                    case SKILL_TUSOK:
                    case SKILL_BIPABU:
                    case SKILL_NOEJEON:
                    case SKILL_GEOMPUNG:
                    case SKILL_SANGONG:
                    case SKILL_MAHWAN:
                    case SKILL_PABEOB:
                        //case SKILL_CURSE:
                        {
                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me);

                            sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
                            m_me->ComputeSkill(m_bType, pkVictim);
                        }
                        break;

                    case SKILL_CHAIN:
                        {
                            m_me->OnMove(true);
                            pkVictim->OnMove();

                            if (pkVictim->CanBeginFight())
                                pkVictim->BeginFight(m_me); 

                            sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
                            m_me->ComputeSkill(m_bType, pkVictim);

                            // TODO 여러명에게 슉 슉 슉 하기
                        }
                        break;

                    case SKILL_YONGBI:
                        {
                            m_me->OnMove(true);
                        }
                        break;

                        /*case SKILL_BUDONG:
                          {
                          m_me->OnMove(true);
                          pkVictim->OnMove();

                          DWORD * pdw;
                          DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw);
                          pdw[0] = m_me->GetVID();
                          pdw[1] = pkVictim->GetVID();

                          event_create(budong_event_func, dwEI, PASSES_PER_SEC(1));
                          }
                          break;*/

                    default:
                        sys_err("CFuncShoot: I don't know this type [%d] of range attack.", (int) m_bType);
                        break;
                }

                m_bSucceed = TRUE;
            }
    };

    bool CHARACTER::Shoot(BYTE bType)
    {
        sys_log(1, "Shoot %s type %u flyTargets.size %zu", GetName(), bType, m_vec_dwFlyTargets.size());

        if (!CanMove())
        {
            return false;
        }    

        CFuncShoot f(this, bType);

        if (m_dwFlyTargetID != 0)
        {
            f(m_dwFlyTargetID);
            m_dwFlyTargetID = 0;
        }

        f = std::for_each(m_vec_dwFlyTargets.begin(), m_vec_dwFlyTargets.end(), f);
        m_vec_dwFlyTargets.clear();

        return f.m_bSucceed;
    }

    void CHARACTER::FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader)
    {
        LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);
        TPacketGCFlyTargeting pack;

        //pack.bHeader    = HEADER_GC_FLY_TARGETING;
        pack.bHeader    = (bHeader == HEADER_CG_FLY_TARGETING) ? HEADER_GC_FLY_TARGETING : HEADER_GC_ADD_FLY_TARGETING;
        pack.dwShooterVID    = GetVID();

        if (pkVictim)
        {
            pack.dwTargetVID = pkVictim->GetVID();
            pack.x = pkVictim->GetX();
            pack.y = pkVictim->GetY();

            if (bHeader == HEADER_CG_FLY_TARGETING)
                m_dwFlyTargetID = dwTargetVID;
            else
                m_vec_dwFlyTargets.push_back(dwTargetVID);
        }
        else
        {
            pack.dwTargetVID = 0;
            pack.x = x;
            pack.y = y;
        }

        sys_log(1, "FlyTarget %s vid %d x %d y %d", GetName(), pack.dwTargetVID, pack.x, pack.y);
        PacketAround(&pack, sizeof(pack), this);
    }

    LPCHARACTER CHARACTER::GetNearestVictim(LPCHARACTER pkChr)
    {
        if (NULL == pkChr)
            pkChr = this;

        float fMinDist = 99999.0f;
        LPCHARACTER pkVictim = NULL;

        TDamageMap::iterator it = m_map_kDamage.begin();

        // 일단 주위에 없는 사람을 걸러 낸다.
        while (it != m_map_kDamage.end())
        {
            const VID & c_VID = it->first;
            ++it;

            LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

            if (!pAttacker)
                continue;

            if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || 
                    pAttacker->IsAffectFlag(AFF_INVISIBILITY) ||
                    pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE))
                continue;

            float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY());

            if (fDist < fMinDist)
            {
                pkVictim = pAttacker;
                fMinDist = fDist;
            }
        }

        return pkVictim;
    }

    void CHARACTER::SetVictim(LPCHARACTER pkVictim)
    {
        if (!pkVictim)
        {
            if (0 != (DWORD)m_kVIDVictim)
                MonsterLog("공격 대상을 해제");

            m_kVIDVictim.Reset();
            battle_end(this);
        }
        else
        {
            if (m_kVIDVictim != pkVictim->GetVID())
                MonsterLog("공격 대상을 설정: %s", pkVictim->GetName());

            m_kVIDVictim = pkVictim->GetVID();
            m_dwLastVictimSetTime = get_dword_time();
        }
    }

    LPCHARACTER CHARACTER::GetVictim() const
    {
        return CHARACTER_MANAGER::instance().Find(m_kVIDVictim);
    }

    LPCHARACTER CHARACTER::GetProtege() const // 보호해야 할 대상을 리턴
    {
        if (m_pkChrStone)
            return m_pkChrStone;

        if (m_pkParty)
            return m_pkParty->GetLeader();

        return NULL;
    }

    int CHARACTER::GetAlignment() const
    {
        return m_iAlignment;
    }

    int CHARACTER::GetRealAlignment() const
    {
        return m_iRealAlignment;
    }

    void CHARACTER::ShowAlignment(bool bShow)
    {
        if (bShow)
        {
            if (m_iAlignment != m_iRealAlignment)
            {
                m_iAlignment = m_iRealAlignment;
                UpdatePacket();
            }
        }
        else
        {
            if (m_iAlignment != 0)
            {
                m_iAlignment = 0;
                UpdatePacket();
            }
        }
    }

    void CHARACTER::UpdateAlignment(int iAmount)
    {
        bool bShow = false;

        if (m_iAlignment == m_iRealAlignment)
            bShow = true;

        int i = m_iAlignment / 10;

        m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000);

        if (bShow)
        {
            m_iAlignment = m_iRealAlignment;

            if (i != m_iAlignment / 10)
                UpdatePacket();
        }
    }

    void CHARACTER::SetKillerMode(bool isOn)
    {
        if ((isOn ? ADD_CHARACTER_STATE_KILLER : 0) == IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER))
            return;

        if (isOn)
            SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
        else
            REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);

        m_iKillerModePulse = thecore_pulse();
        UpdatePacket();
        sys_log(0, "SetKillerMode Update %s[%d]", GetName(), GetPlayerID());
    }

    bool CHARACTER::IsKillerMode() const
    {
        return IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
    }

    void CHARACTER::UpdateKillerMode()
    {
        if (!IsKillerMode())
            return;

        int iKillerSeconds = ! LC_IsYMIR() ? 30 : 60;

        if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(iKillerSeconds))
            SetKillerMode(false);
    }

    void CHARACTER::SetPKMode(BYTE bPKMode)
    {
        if (bPKMode >= PK_MODE_MAX_NUM)
            return;

        if (m_bPKMode == bPKMode)
            return;

        if (bPKMode == PK_MODE_GUILD && !GetGuild())
            bPKMode = PK_MODE_FREE;

        m_bPKMode = bPKMode;
        UpdatePacket();

        sys_log(0, "PK_MODE: %s %d", GetName(), m_bPKMode);
    }

    BYTE CHARACTER::GetPKMode() const
    {
        return m_bPKMode;
    }

    struct FuncForgetMyAttacker
    {
        LPCHARACTER m_ch;
        FuncForgetMyAttacker(LPCHARACTER ch)
        {
            m_ch = ch;
        }
        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (ch->m_kVIDVictim == m_ch->GetVID())
                    ch->SetVictim(NULL);
            }
        }
    };

    struct FuncAggregateMonster
    {
        LPCHARACTER m_ch;
        FuncAggregateMonster(LPCHARACTER ch)
        {
            m_ch = ch;
        }
        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (!ch->IsMonster())
                    return;
                if (ch->GetVictim())
                    return;

                if (number(1, 100) <= 50) // 임시로 50% 확률로 적을 끌어온다
                    if (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000)
                        if (ch->CanBeginFight())
                            ch->BeginFight(m_ch);
            }
        }
    };

    struct FuncAttractRanger
    {
        LPCHARACTER m_ch;
        FuncAttractRanger(LPCHARACTER ch)
        {
            m_ch = ch;
        }

        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (!ch->IsMonster())
                    return;
                if (ch->GetVictim() && ch->GetVictim() != m_ch)
                    return;
                if (ch->GetMobAttackRange() > 150)
                {
                    int iNewRange = 150;//(int)(ch->GetMobAttackRange() * 0.2);
                    if (iNewRange < 150)
                        iNewRange = 150;

                    ch->AddAffect(AFFECT_BOW_DISTANCE, POINT_BOW_DISTANCE, iNewRange - ch->GetMobAttackRange(), AFF_NONE, 3*60, 0, false);
                }
            }
        }
    };

    struct FuncPullMonster
    {
        LPCHARACTER m_ch;
        int m_iLength;
        FuncPullMonster(LPCHARACTER ch, int iLength = 300)
        {
            m_ch = ch;
            m_iLength = iLength;
        }

        void operator()(LPENTITY ent)
        {
            if (ent->IsType(ENTITY_CHARACTER))
            {
                LPCHARACTER ch = (LPCHARACTER) ent;
                if (ch->IsPC())
                    return;
                if (!ch->IsMonster())
                    return;
                //if (ch->GetVictim() && ch->GetVictim() != m_ch)
                //return;
                float fDist = DISTANCE_APPROX(m_ch->GetX() - ch->GetX(), m_ch->GetY() - ch->GetY());
                if (fDist > 3000 || fDist < 100)
                    return;

                float fNewDist = fDist - m_iLength;
                if (fNewDist < 100) 
                    fNewDist = 100;

                float degree = GetDegreeFromPositionXY(ch->GetX(), ch->GetY(), m_ch->GetX(), m_ch->GetY());
                float fx;
                float fy;

                GetDeltaByDegree(degree, fDist - fNewDist, &fx, &fy);
                long tx = (long)(ch->GetX() + fx);
                long ty = (long)(ch->GetY() + fy);

                ch->Sync(tx, ty);
                ch->Goto(tx, ty);
                ch->CalculateMoveDuration();

                ch->SyncPacket();
            }
        }
    };

    void CHARACTER::ForgetMyAttacker()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncForgetMyAttacker f(this);
            pSec->ForEachAround(f);
        }
        ReviveInvisible(5);
    }

    void CHARACTER::AggregateMonster()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncAggregateMonster f(this);
            pSec->ForEachAround(f);
        }
    }

    void CHARACTER::AttractRanger()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncAttractRanger f(this);
            pSec->ForEachAround(f);
        }
    }

    void CHARACTER::PullMonster()
    {
        LPSECTREE pSec = GetSectree();
        if (pSec)
        {
            FuncPullMonster f(this);
            pSec->ForEachAround(f);
        }
    }

    void CHARACTER::UpdateAggrPointEx(LPCHARACTER pAttacker, EDamageType type, int dam, CHARACTER::TBattleInfo & info)
    {
        // 특정 공격타입에 따라 더 올라간다
        switch (type)
        {
            case DAMAGE_TYPE_NORMAL_RANGE:
                dam = (int) (dam*1.2f);
                break;

            case DAMAGE_TYPE_RANGE:
                dam = (int) (dam*1.5f);
                break;

            case DAMAGE_TYPE_MAGIC:
                dam = (int) (dam*1.2f);
                break;

            default:
                break;
        }

        // 공격자가 현재 대상인 경우 보너스를 준다.
        if (pAttacker == GetVictim())
            dam = (int) (dam * 1.2f);

        info.iAggro += dam;

        if (info.iAggro < 0)
            info.iAggro = 0;

        //sys_log(0, "UpdateAggrPointEx for %s by %s dam %d total %d", GetName(), pAttacker->GetName(), dam, total);
        if (GetParty() && dam > 0 && type != DAMAGE_TYPE_SPECIAL)
        {
            LPPARTY pParty = GetParty();

            // 리더인 경우 영향력이 좀더 강하다
            int iPartyAggroDist = dam;

            if (pParty->GetLeaderPID() == GetVID())
                iPartyAggroDist /= 2;
            else
                iPartyAggroDist /= 3;

            pParty->SendMessage(this, PM_AGGRO_INCREASE, iPartyAggroDist, pAttacker->GetVID());
        }

        ChangeVictimByAggro(info.iAggro, pAttacker);
    }

    void CHARACTER::UpdateAggrPoint(LPCHARACTER pAttacker, EDamageType type, int dam)
    {
        if (IsDead() || IsStun())
            return;

        TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID());

        if (it == m_map_kDamage.end())
        {
            m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(0, dam)));
            it = m_map_kDamage.find(pAttacker->GetVID());
        }

        UpdateAggrPointEx(pAttacker, type, dam, it->second);
    }

    void CHARACTER::ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim)
    {
        if (get_dword_time() - m_dwLastVictimSetTime < 3000) // 3초는 기다려야한다
            return;

        if (pNewVictim == GetVictim())
        {
            if (m_iMaxAggro < iNewAggro)
            {
                m_iMaxAggro = iNewAggro;
                return;
            }

            // Aggro가 감소한 경우
            TDamageMap::iterator it;
            TDamageMap::iterator itFind = m_map_kDamage.end();

            for (it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it)
            {
                if (it->second.iAggro > iNewAggro)
                {
                    LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first);

                    if (ch && !ch->IsDead() && DISTANCE_APPROX(ch->GetX() - GetX(), ch->GetY() - GetY()) < 5000)
                    {
                        itFind = it;
                        iNewAggro = it->second.iAggro;
                    }
                }
            }

            if (itFind != m_map_kDamage.end())
            {
                m_iMaxAggro = iNewAggro;
                SetVictim(CHARACTER_MANAGER::instance().Find(itFind->first));
                m_dwStateDuration = 1;
            }
        }
        else
        {
            if (m_iMaxAggro < iNewAggro)
            {
                m_iMaxAggro = iNewAggro;
                SetVictim(pNewVictim);
                m_dwStateDuration = 1;
            }
        }
    }

    here is it

    • Good 1
  3. this is my sysser

    Spoiler

    SYSERR: Feb  5 16:48:23.399697 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_sg1.dat
    SYSERR: Feb  5 16:48:23.400283 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_ds.dat
    SYSERR: Feb  5 16:48:23.400343 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_dss_box.dat
    SYSERR: Feb  5 16:48:23.401205 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_w20_sound.dat
    SYSERR: Feb  5 16:48:23.401271 :: LoadPackageCryptInfo: [PackageCryptInfo] Failed to load package/cshybridcrypt_metin2_patch_w21_etc.dat
    SYSERR: Feb  5 16:49:16.548289 :: LoadSkillMotion: Motion: Skill exist but no motion data for index 0 mob 3596 skill 257
    SYSERR: Feb  5 16:49:16.548339 :: LoadSkillMotion: Motion: Skill exist but no motion data for index 1 mob 3596 skill 258
    SYSERR: Feb  5 16:49:16.606729 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun)
    SYSERR: Feb  5 16:49:16.606762 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun)
    SYSERR: Feb  5 16:49:16.606799 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun)
    SYSERR: Feb  5 16:49:16.606826 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun)
    SYSERR: Feb  5 16:49:16.606858 :: GetMotionFileName: Motion: yamachun have not motlist.txt vnum(6091) folder(yamachun)
    SYSERR: Feb  5 16:49:16.686299 :: LoadMotion: cannot find accumulation data in file 'data/monster/horse_event1/03.msa'
    SYSERR: Feb  5 16:49:16.687837 :: LoadMotion: cannot find accumulation data in file 'data/monster/boar/run.msa'
    SYSERR: Feb  5 16:49:16.688110 :: LoadMotion: cannot find accumulation data in file 'data/monster/dog_god/run.msa'
    SYSERR: Feb  5 16:49:16.688355 :: LoadMotion: cannot find accumulation data in file 'data/monster/lion/run.msa'
    SYSERR: Feb  5 16:49:16.688571 :: LoadMotion: cannot find accumulation data in file 'data/monster/boar/run.msa'
    SYSERR: Feb  5 16:49:16.688807 :: LoadMotion: cannot find accumulation data in file 'data/monster/dog_god/run.msa'
    SYSERR: Feb  5 16:49:16.689092 :: LoadMotion: cannot find accumulation data in file 'data/monster/lion/run.msa'
    SYSERR: Feb  5 16:49:16.689372 :: LoadMotion: cannot find accumulation data in file 'data/monster/boar/run.msa'
    SYSERR: Feb  5 16:49:16.689601 :: LoadMotion: cannot find accumulation data in file 'data/monster/dog_god/run.msa'
    SYSERR: Feb  5 16:49:16.689838 :: LoadMotion: cannot find accumulation data in file 'data/monster/lion/run.msa'
    SYSERR: Feb  5 16:49:16.690118 :: LoadMotion: cannot find accumulation data in file 'data/monster/horse_event1/03.msa'
    SYSERR: Feb  5 16:49:16.690426 :: GetMotionFileName: Motion: leechung have not motlist.txt vnum(20369) folder(leechung)
    SYSERR: Feb  5 16:49:16.690454 :: GetMotionFileName: Motion: leechung have not motlist.txt vnum(20369) folder(leechung)
    SYSERR: Feb  5 16:49:16.690480 :: GetMotionFileName: Motion: leechung have not motlist.txt vnum(20369) folder(leechung)
    SYSERR: Feb  5 16:49:16.690531 :: GetMotionFileName: Motion: spy1 have not motlist.txt vnum(20370) folder(spy1)
    SYSERR: Feb  5 16:49:16.690605 :: GetMotionFileName: Motion: spy1 have not motlist.txt vnum(20370) folder(spy1)
    SYSERR: Feb  5 16:49:16.690631 :: GetMotionFileName: Motion: spy1 have not motlist.txt vnum(20370) folder(spy1)
    SYSERR: Feb  5 16:49:16.690694 :: GetMotionFileName: Motion: zombie_key_stone have not motlist.txt vnum(30101) folder(zombie_key_stone)
    SYSERR: Feb  5 16:49:16.690768 :: GetMotionFileName: Motion: zombie_key_stone have not motlist.txt vnum(30101) folder(zombie_key_stone)
    SYSERR: Feb  5 16:49:16.690797 :: GetMotionFileName: Motion: zombie_key_stone have not motlist.txt vnum(30101) folder(zombie_key_stone)
    SYSERR: Feb  5 16:49:16.690848 :: GetMotionFileName: Motion: zombie_god_stone have not motlist.txt vnum(30102) folder(zombie_god_stone)
    SYSERR: Feb  5 16:49:16.690874 :: GetMotionFileName: Motion: zombie_god_stone have not motlist.txt vnum(30102) folder(zombie_god_stone)
    SYSERR: Feb  5 16:49:16.690899 :: GetMotionFileName: Motion: zombie_god_stone have not motlist.txt vnum(30102) folder(zombie_god_stone)
    SYSERR: Feb  5 16:49:16.690949 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30103) folder(zombie_security_stone)
    SYSERR: Feb  5 16:49:16.691021 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30103) folder(zombie_security_stone)
    SYSERR: Feb  5 16:49:16.691057 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30103) folder(zombie_security_stone)
    SYSERR: Feb  5 16:49:16.691132 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30104) folder(zombie_security_stone)
    SYSERR: Feb  5 16:49:16.691166 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30104) folder(zombie_security_stone)
    SYSERR: Feb  5 16:49:16.691198 :: GetMotionFileName: Motion: zombie_security_stone have not motlist.txt vnum(30104) folder(zombie_security_stone)
    SYSERR: Feb  5 16:49:16.694497 :: GetMotionFileName: Motion: pwahuang1_2 have not motlist.txt vnum(34009) folder(pwahuang1_2)
    SYSERR: Feb  5 16:49:16.694539 :: GetMotionFileName: Motion: pwahuang1_2 have not motlist.txt vnum(34009) folder(pwahuang1_2)
    SYSERR: Feb  5 16:49:16.694577 :: GetMotionFileName: Motion: pwahuang1_2 have not motlist.txt vnum(34009) folder(pwahuang1_2)
    SYSERR: Feb  5 16:49:16.694650 :: GetMotionFileName: Motion: halloween_pumpkin_pet have not motlist.txt vnum(34026) folder(halloween_pumpkin_pet)
    SYSERR: Feb  5 16:49:16.694686 :: GetMotionFileName: Motion: halloween_pumpkin_pet have not motlist.txt vnum(34026) folder(halloween_pumpkin_pet)
    SYSERR: Feb  5 16:49:16.694726 :: GetMotionFileName: Motion: halloween_pumpkin_pet have not motlist.txt vnum(34026) folder(halloween_pumpkin_pet)
    SYSERR: Feb  5 16:49:16.694795 :: GetMotionFileName: Motion: halloween_broom have not motlist.txt vnum(34027) folder(halloween_broom)
    SYSERR: Feb  5 16:49:16.694848 :: GetMotionFileName: Motion: halloween_broom have not motlist.txt vnum(34027) folder(halloween_broom)
    SYSERR: Feb  5 16:49:16.694886 :: GetMotionFileName: Motion: halloween_broom have not motlist.txt vnum(34027) folder(halloween_broom)
    SYSERR: Feb  5 16:49:16.694971 :: GetMotionFileName: Motion: chrismas_recallcard_pet1 have not motlist.txt vnum(34028) folder(chrismas_recallcard_pet1)
    SYSERR: Feb  5 16:49:16.695097 :: GetMotionFileName: Motion: chrismas_recallcard_pet1 have not motlist.txt vnum(34028) folder(chrismas_recallcard_pet1)
    SYSERR: Feb  5 16:49:16.695131 :: GetMotionFileName: Motion: chrismas_recallcard_pet1 have not motlist.txt vnum(34028) folder(chrismas_recallcard_pet1)
    SYSERR: Feb  5 16:49:16.695205 :: GetMotionFileName: Motion: chrismas_recallcard_pet2 have not motlist.txt vnum(34029) folder(chrismas_recallcard_pet2)
    SYSERR: Feb  5 16:49:16.695606 :: GetMotionFileName: Motion: chrismas_recallcard_pet2 have not motlist.txt vnum(34029) folder(chrismas_recallcard_pet2)
    SYSERR: Feb  5 16:49:16.695644 :: GetMotionFileName: Motion: chrismas_recallcard_pet2 have not motlist.txt vnum(34029) folder(chrismas_recallcard_pet2)
    SYSERR: Feb  5 16:49:16.705119 :: heart_idle: losing 52 seconds. (lag occured)
    SYSERR: Feb  5 16:50:04.710145 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:08.70530 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:08.73308 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686
    SYSERR: Feb  5 16:50:08.589246 :: number_ex: number(): first argument is bigger than second argument 3200 -> 49, char_battle.cpp 686
    SYSERR: Feb  5 16:50:08.869788 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686
    SYSERR: Feb  5 16:50:09.712095 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686
    SYSERR: Feb  5 16:50:10.550954 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:50:11.948144 :: number_ex: number(): first argument is bigger than second argument 3700 -> 56, char_battle.cpp 686
    SYSERR: Feb  5 16:50:12.749163 :: number_ex: number(): first argument is bigger than second argument 3700 -> 56, char_battle.cpp 686
    SYSERR: Feb  5 16:50:15.29466 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:50:15.588165 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:50:21.230816 :: number_ex: number(): first argument is bigger than second argument 5600 -> 84, char_battle.cpp 686
    SYSERR: Feb  5 16:50:33.187739 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:33.192580 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:33.194831 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:33.197404 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:34.429579 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:34.908669 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:36.587687 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:38.267537 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:41.27835 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:50:41.508544 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:50:43.790214 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:51:36.65917 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:51:40.785924 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:51:44.67621 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:51:46.145853 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:51:47.465605 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:51:50.943615 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:51:51.68109 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:51:56.304151 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:51:59.65309 :: number_ex: number(): first argument is bigger than second argument 3200 -> 49, char_battle.cpp 686
    SYSERR: Feb  5 16:52:01.144331 :: number_ex: number(): first argument is bigger than second argument 10000 -> 150, char_battle.cpp 686
    SYSERR: Feb  5 16:52:01.225078 :: number_ex: number(): first argument is bigger than second argument 10000 -> 150, char_battle.cpp 686
    SYSERR: Feb  5 16:52:24.861057 :: number_ex: number(): first argument is bigger than second argument 6400 -> 97, char_battle.cpp 686
    SYSERR: Feb  5 16:52:25.141586 :: number_ex: number(): first argument is bigger than second argument 6400 -> 97, char_battle.cpp 686
    SYSERR: Feb  5 16:52:31.224743 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:53:01.837879 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:53:04.472344 :: number_ex: number(): first argument is bigger than second argument 4400 -> 67, char_battle.cpp 686
    SYSERR: Feb  5 16:53:06.433788 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:53:07.831522 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:53:09.714158 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:55:03.572948 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:03.812498 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:06.975961 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:07.130624 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:07.370970 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:07.374042 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:07.850839 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:07.853864 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.854011 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.854111 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.854193 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.854430 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.855185 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.855406 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.855508 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.855602 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:07.855730 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 659
    SYSERR: Feb  5 16:55:08.331137 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:08.572950 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:10.730899 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:11.51885 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:14.250761 :: number_ex: number(): first argument is bigger than second argument 110000 -> 1550, char_battle.cpp 686
    SYSERR: Feb  5 16:55:22.851039 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:55:32.23276 :: number_ex: number(): first argument is bigger than second argument 4700 -> 70, char_battle.cpp 686
    SYSERR: Feb  5 16:55:34.385680 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:34.388597 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:34.943246 :: number_ex: number(): first argument is bigger than second argument 6100 -> 92, char_battle.cpp 686
    SYSERR: Feb  5 16:55:35.863349 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:37.589930 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:38.424862 :: number_ex: number(): first argument is bigger than second argument 6100 -> 92, char_battle.cpp 686
    SYSERR: Feb  5 16:55:41.461731 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:55:41.464455 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:43.985510 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:55:49.226485 :: number_ex: number(): first argument is bigger than second argument 7600 -> 114, char_battle.cpp 686
    SYSERR: Feb  5 16:55:50.711143 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:55:51.557464 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:52.387847 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:52.505562 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:55:53.947900 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686
    SYSERR: Feb  5 16:55:54.946552 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:55:59.65571 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:56:00.426924 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:56:00.433965 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:56:03.425745 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:56:04.825561 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:56:06.829597 :: number_ex: number(): first argument is bigger than second argument 5900 -> 88, char_battle.cpp 686
    SYSERR: Feb  5 16:56:07.26973 :: number_ex: number(): first argument is bigger than second argument 6100 -> 92, char_battle.cpp 686
    SYSERR: Feb  5 16:56:08.149937 :: number_ex: number(): first argument is bigger than second argument 19600 -> 294, char_battle.cpp 686
    SYSERR: Feb  5 16:56:08.160292 :: number_ex: number(): first argument is bigger than second argument 8000 -> 120, char_battle.cpp 686

    i want to fix 

    Spoiler

    number_ex: number(): first argument is bigger than second argument

    help

  4. hello

    the channel crash randomly

    syserr

    Spoiler

    SYSERR: Feb  2 17:46:35.435684 :: RunState: LUA_ERROR: [string "event_altigen_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:35.435721 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_altigen_2014.start letter
    SYSERR: Feb  2 17:46:35.435767 :: RunState: LUA_ERROR: [string "event_ayisigi_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:35.435788 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_ayisigi_2014.start letter
    SYSERR: Feb  2 17:46:43.677128 :: RunState: LUA_ERROR: [string "event_altigen_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:43.677160 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_altigen_2014.start letter
    SYSERR: Feb  2 17:46:43.677207 :: RunState: LUA_ERROR: [string "event_ayisigi_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:43.677228 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_ayisigi_2014.start letter
    SYSERR: Feb  2 17:46:46.756749 :: RunState: LUA_ERROR: [string "event_altigen_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:46.756780 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_altigen_2014.start letter
    SYSERR: Feb  2 17:46:46.756824 :: RunState: LUA_ERROR: [string "event_ayisigi_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:46.756845 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_ayisigi_2014.start letter
    SYSERR: Feb  2 17:46:46.756991 :: number_ex: number(): first argument is bigger than second argument 110400 -> 1656, char_battle.cpp 686
    SYSERR: Feb  2 17:46:52.275787 :: RunState: LUA_ERROR: [string "event_altigen_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:52.275815 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_altigen_2014.start letter
    SYSERR: Feb  2 17:46:52.275859 :: RunState: LUA_ERROR: [string "event_ayisigi_2014"]:1: attempt to call field `get_level' (a nil value)
    SYSERR: Feb  2 17:46:52.275879 :: WriteRunningStateToSyserr: LUA_ERROR: quest event_ayisigi_2014.start letter
    SYSERR: Feb  2 17:46:52.276031 :: number_ex: number(): first argument is bigger than second argument 110400 -> 1656, char_battle.cpp 686
    SYSERR: Feb  2 17:49:08.420680 :: checkpointing: CHECKPOINT shutdown: tics did not updated.

     

  5. hi all

    i'm having a problem in energy crystal timer

    Spoiler

    50021    "ܳƁ࠸ݒڴ"    ITEM_USE    USE_SPECIAL    1    NONE    NONE    NONE    NONE    0    0    0    0    0    LIMIT_NONE    0    LIMIT_NONE    0    APPLY_NONE    0    APPLY_NONE    0    APPLY_NONE    0    0    0    0    0    0    0    0    0    0
     

    this is it's line in item_proto.txt

    help !!

  6. hello all

    i've try a switch system

    in a 40k client

    when i try to enter the game the screen exit

    syserr :

    Spoiler

    0112 19:08:27949 ::   File "networkModule.py", line 245, in SetGamePhase

    0112 19:08:27950 ::   File "system.py", line 130, in __pack_import

    0112 19:08:27950 ::   File "system.py", line 110, in _process_result

    0112 19:08:27950 ::   File "game.py", line 36, in <module>

    0112 19:08:27950 ::   File "system.py", line 130, in __pack_import

    0112 19:08:27951 ::   File "system.py", line 110, in _process_result

    0112 19:08:27951 ::   File "uiswitch.py", line 24, in <module>

    0112 19:08:27951 ::   File "uiswitch.py", line 163, in Switcher

    0112 19:08:27951 :: AttributeError
    0112 19:08:27951 :: : 
    0112 19:08:27952 :: 'module' object has no attribute 'TOOLTIP_MAX_HP'
    0112 19:08:27952 :: 
     

     

×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.