Guest User

Untitled

a guest
May 9th, 2025
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 97.24 KB | None | 0 0
  1. #include "stdafx.h"
  2. #include "utils.h"
  3. #include "config.h"
  4. #include "desc.h"
  5. #include "desc_manager.h"
  6. #include "char_manager.h"
  7. #include "item.h"
  8. #include "item_manager.h"
  9. #include "mob_manager.h"
  10. #include "battle.h"
  11. #include "pvp.h"
  12. #include "skill.h"
  13. #include "start_position.h"
  14. #include "profiler.h"
  15. #include "cmd.h"
  16. #include "dungeon.h"
  17. #include "log.h"
  18. #include "unique_item.h"
  19. #include "priv_manager.h"
  20. #include "db.h"
  21. #include "vector.h"
  22. #include "marriage.h"
  23. #include "arena.h"
  24. #include "regen.h"
  25. #include "monarch.h"
  26. #include "exchange.h"
  27. #include "shop_manager.h"
  28. #include "castle.h"
  29. #include "ani.h"
  30. #include "BattleArena.h"
  31. #include "packet.h"
  32. #include "party.h"
  33. #include "affect.h"
  34. #include "guild.h"
  35. #include "guild_manager.h"
  36. #include "war_map.h"
  37. #include "questmanager.h"
  38. #include "questlua.h"
  39. #include "threeway_war.h"
  40. #include "BlueDragon.h"
  41. #include "DragonLair.h"
  42. #ifdef ENABLE_DAILY_BOSS
  43. #include "DailyBoss.h"
  44. #endif
  45. #if defined(__SHIP_DEFENSE__)
  46. #   include "ShipDefense.h"
  47. #endif
  48. #include "../../common/CommonDefines.h"
  49. #ifdef GUILD_WAR_COUNTER
  50. #include "war_map.h"
  51. #endif
  52.  
  53. #define ENABLE_EFFECT_PENETRATE
  54. static DWORD __GetPartyExpNP(const DWORD level)
  55. {
  56.     if (!level || level > PLAYER_EXP_TABLE_MAX)
  57.         return 14000;
  58.     return party_exp_distribute_table[level];
  59. }
  60.  
  61. static int __GetExpLossPerc(const DWORD level)
  62. {
  63.     if (!level || level > PLAYER_EXP_TABLE_MAX)
  64.         return 1;
  65.     return aiExpLossPercents[level];
  66. }
  67.  
  68. DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp)
  69. {
  70.     if (PLAYER_MAX_LEVEL_CONST < ch->GetLevel())
  71.     {
  72.         double ret = 0.95;
  73.         double factor = 0.1;
  74.  
  75.         for (ssize_t i=0 ; i < ch->GetLevel()-100 ; ++i)
  76.         {
  77.             if ( (i%10) == 0)
  78.                 factor /= 2.0;
  79.  
  80.             ret *= 1.0 - factor;
  81.         }
  82.  
  83.         ret = ret * static_cast<double>(exp);
  84.  
  85.         if (ret < 1.0)
  86.             return 1;
  87.  
  88.         return static_cast<DWORD>(ret);
  89.     }
  90.  
  91.     return exp;
  92. }
  93.  
  94. bool CHARACTER::CanBeginFight() const
  95. {
  96.     if (!CanMove())
  97.         return false;
  98.  
  99.     return m_pointsInstant.position == POS_STANDING && !IsDead() && !IsStun();
  100. }
  101.  
  102. void CHARACTER::BeginFight(LPCHARACTER pkVictim)
  103. {
  104.     SetVictim(pkVictim);
  105.     SetPosition(POS_FIGHTING);
  106.     SetNextStatePulse(1);
  107. }
  108.  
  109. bool CHARACTER::CanFight() const
  110. {
  111.     return m_pointsInstant.position >= POS_FIGHTING ? true : false;
  112. }
  113.  
  114. void CHARACTER::CreateFly(BYTE bType, LPCHARACTER pkVictim)
  115. {
  116.     TPacketGCCreateFly packFly;
  117.  
  118.     packFly.bHeader      = HEADER_GC_CREATE_FLY;
  119.     packFly.bType          = bType;
  120.     packFly.dwStartVID    = GetVID();
  121.     packFly.dwEndVID        = pkVictim->GetVID();
  122.  
  123.     PacketAround(&packFly, sizeof(TPacketGCCreateFly));
  124. }
  125.  
  126. void CHARACTER::DistributeSP(LPCHARACTER pkKiller, int iMethod)
  127. {
  128.     if (pkKiller->GetSP() >= pkKiller->GetMaxSP())
  129.         return;
  130.  
  131.     bool bAttacking = (get_dword_time() - GetLastAttackTime()) < 3000;
  132.     bool bMoving = (get_dword_time() - GetLastMoveTime()) < 3000;
  133.  
  134.     if (iMethod == 1)
  135.     {
  136.         int num = number(0, 3);
  137.  
  138.         if (!num)
  139.         {
  140.             int iLvDelta = GetLevel() - pkKiller->GetLevel();
  141.             int iAmount = 0;
  142.  
  143.             if (iLvDelta >= 5)
  144.                 iAmount = 10;
  145.             else if (iLvDelta >= 0)
  146.                 iAmount = 6;
  147.             else if (iLvDelta >= -3)
  148.                 iAmount = 2;
  149.  
  150.             if (iAmount != 0)
  151.             {
  152.                 iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
  153.  
  154.                 if (iAmount >= 11)
  155.                     CreateFly(FLY_SP_BIG, pkKiller);
  156.                 else if (iAmount >= 7)
  157.                     CreateFly(FLY_SP_MEDIUM, pkKiller);
  158.                 else
  159.                     CreateFly(FLY_SP_SMALL, pkKiller);
  160.  
  161.                 pkKiller->PointChange(POINT_SP, iAmount);
  162.             }
  163.         }
  164.     }
  165.     else
  166.     {
  167.         if (pkKiller->GetJob() == JOB_SHAMAN || (pkKiller->GetJob() == JOB_SURA && pkKiller->GetSkillGroup() == 2))
  168.         {
  169.             int iAmount;
  170.  
  171.             if (bAttacking)
  172.                 iAmount = 2 + GetMaxSP() / 100;
  173.             else if (bMoving)
  174.                 iAmount = 3 + GetMaxSP() * 2 / 100;
  175.             else
  176.                 iAmount = 10 + GetMaxSP() * 3 / 100;
  177.  
  178.             iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
  179.             pkKiller->PointChange(POINT_SP, iAmount);
  180.         }
  181.         else
  182.         {
  183.             int iAmount;
  184.  
  185.             if (bAttacking)
  186.                 iAmount = 2 + pkKiller->GetMaxSP() / 200;
  187.             else if (bMoving)
  188.                 iAmount = 2 + pkKiller->GetMaxSP() / 100;
  189.             else
  190.             {
  191.                
  192.                 if (pkKiller->GetHP() < pkKiller->GetMaxHP())
  193.                     iAmount = 2 + (pkKiller->GetMaxSP() / 100);
  194.                 else
  195.                     iAmount = 9 + (pkKiller->GetMaxSP() / 100);
  196.             }
  197.  
  198.             iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100;
  199.             pkKiller->PointChange(POINT_SP, iAmount);
  200.         }
  201.     }
  202. }
  203.  
  204.  
  205. bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType)
  206. {
  207. #ifdef ENABLE_AFTERDEATH_SHIELD
  208.     RemoveAfterdeathShield();
  209. #endif
  210.  
  211.     if (test_server)
  212.         sys_log(0, "[TEST_SERVER] Attack : %s type %d, MobBattleType %d", GetName(), bType, !GetMobBattleType() ? 0 : GetMobAttackRange());
  213.     //PROF_UNIT puAttack("Attack");
  214.     if (!CanMove())
  215.         return false;
  216.  
  217.     // CASTLE
  218.     if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim))
  219.         return false;
  220.     // CASTLE
  221.  
  222.     // @fixme131
  223.     if (!battle_is_attackable(this, pkVictim))
  224.         return false;
  225.  
  226.     DWORD dwCurrentTime = get_dword_time();
  227.  
  228.     if (IsPC())
  229.     {
  230.         if (IS_SPEED_HACK(this, pkVictim, dwCurrentTime))
  231.             return false;
  232.  
  233.         if (bType == 0 && dwCurrentTime < GetSkipComboAttackByTime())
  234.             return false;
  235.     }
  236.     else
  237.     {
  238.         MonsterChat(MONSTER_CHAT_ATTACK);
  239.     }
  240.  
  241.     int iRet;
  242.  
  243.     pkVictim->SetSyncOwner(this);
  244.  
  245.     if (pkVictim->CanBeginFight())
  246.         pkVictim->BeginFight(this);
  247.  
  248.     if (bType == 0)
  249.     {
  250.         //
  251.        
  252.         //
  253.         switch (GetMobBattleType())
  254.         {
  255.             case BATTLE_TYPE_MELEE:
  256.             case BATTLE_TYPE_POWER:
  257.             case BATTLE_TYPE_TANKER:
  258.             case BATTLE_TYPE_SUPER_POWER:
  259.             case BATTLE_TYPE_SUPER_TANKER:
  260.                 iRet = battle_melee_attack(this, pkVictim);
  261.                 break;
  262.  
  263.             case BATTLE_TYPE_RANGE:
  264.                 FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING);
  265.                 iRet = Shoot(0) ? BATTLE_DAMAGE : BATTLE_NONE;
  266.                 break;
  267.  
  268.             case BATTLE_TYPE_MAGIC:
  269.                 FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING);
  270.                 iRet = Shoot(1) ? BATTLE_DAMAGE : BATTLE_NONE;
  271.                 break;
  272.  
  273.             default:
  274.                 sys_err("Unhandled battle type %d", GetMobBattleType());
  275.                 iRet = BATTLE_NONE;
  276.                 break;
  277.         }
  278.  
  279. #if defined(__SOUL_SYSTEM__)
  280.         if (iRet != BATTLE_NONE)
  281.             SoulItemProcess(RED_SOUL);
  282. #endif
  283.     }
  284.     else
  285.     {
  286.         if (IsPC() == true)
  287.         {
  288.             if (dwCurrentTime - m_dwLastSkillTime > 1500)
  289.             {
  290.                 sys_log(1, "HACK: Too long skill using term. Name(%s) PID(%u) delta(%u)",
  291.                         GetName(), GetPlayerID(), (dwCurrentTime - m_dwLastSkillTime));
  292.                 return false;
  293.             }
  294.         }
  295.  
  296.         sys_log(1, "Attack call ComputeSkill %d %s", bType, pkVictim?pkVictim->GetName():"");
  297.         iRet = ComputeSkill(bType, pkVictim);
  298.  
  299. #if defined(__SOUL_SYSTEM__)
  300.         if (iRet != BATTLE_NONE)
  301.             SoulItemProcess(BLUE_SOUL);
  302. #endif
  303.     }
  304.  
  305.     //if (test_server && IsPC())
  306.     //  sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet);
  307.     if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD)
  308.     {
  309.         OnMove(true);
  310.         pkVictim->OnMove();
  311.  
  312.         // only pc sets victim null. For npc, state machine will reset this.
  313.         if (BATTLE_DEAD == iRet && IsPC())
  314.             SetVictim(NULL);
  315.  
  316.         return true;
  317.     }
  318.  
  319.     return false;
  320. }
  321.  
  322. void CHARACTER::DeathPenalty(BYTE bTown)
  323. {
  324.     sys_log(1, "DEATH_PERNALY_CHECK(%s) town(%d)", GetName(), bTown);
  325.  
  326.     Cube_close(this);
  327. #ifdef ENABLE_ACCE_SYSTEM
  328.     CloseAcce();
  329. #endif
  330.     if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true)
  331.     {
  332.         return;
  333.     }
  334.  
  335.     if (GetLevel() < 10)
  336.     {
  337.         sys_log(0, "NO_DEATH_PENALTY_LESS_LV10(%s)", GetName());
  338.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
  339.         return;
  340.     }
  341.  
  342.     if (number(0, 2))
  343.     {
  344.         sys_log(0, "NO_DEATH_PENALTY_LUCK(%s)", GetName());
  345.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
  346.         return;
  347.     }
  348.  
  349.     if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY))
  350.     {
  351.         REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
  352.  
  353.         // NO_DEATH_PENALTY_BUG_FIX
  354.         if (!bTown)
  355.         {
  356.             if (FindAffect(AFFECT_NO_DEATH_PENALTY))
  357.             {
  358.                 sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName());
  359.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 가호로 경험치가 떨어지지 않았습니다."));
  360.                 RemoveAffect(AFFECT_NO_DEATH_PENALTY);
  361.                 return;
  362.             }
  363.         }
  364.         // END_OF_NO_DEATH_PENALTY_BUG_FIX
  365.  
  366.         int iLoss = ((GetNextExp() * __GetExpLossPerc(GetLevel())) / 100);
  367.  
  368.         iLoss = MIN(800000, iLoss);
  369.  
  370.         if (bTown)
  371.             iLoss = 0;
  372.  
  373.         if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS))
  374.             iLoss /= 2;
  375.  
  376.         sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, __GetExpLossPerc(GetLevel()));
  377.  
  378.         PointChange(POINT_EXP, -iLoss, true);
  379.     }
  380. }
  381.  
  382. bool CHARACTER::IsStun() const
  383. {
  384.     if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN))
  385.         return true;
  386.  
  387.     return false;
  388. }
  389.  
  390. EVENTFUNC(StunEvent)
  391. {
  392.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  393.  
  394.     if ( info == NULL )
  395.     {
  396.         sys_err( "StunEvent> <Factor> Null pointer" );
  397.         return 0;
  398.     }
  399.  
  400.     LPCHARACTER ch = info->ch;
  401.  
  402.     if (ch == NULL) { // <Factor>
  403.         return 0;
  404.     }
  405.     ch->m_pkStunEvent = NULL;
  406.     ch->Dead();
  407.     return 0;
  408. }
  409.  
  410. /*
  411. void CHARACTER::Stun()
  412. {
  413.     if (IsStun())
  414.         return;
  415.  
  416.     if (IsDead())
  417.         return;
  418.  
  419.     if (!IsPC() && m_pkParty)
  420.     {
  421.         m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0);
  422.     }
  423.  
  424.     sys_log(1, "%s: Stun %p", GetName(), this);
  425.  
  426.     PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY));
  427.     PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY));
  428.  
  429.     CloseMyShop();
  430.  
  431.     event_cancel(&m_pkRecoveryEvent);
  432.  
  433.     TPacketGCStun pack;
  434.     pack.header = HEADER_GC_STUN;
  435.     pack.vid    = m_vid;
  436.     PacketAround(&pack, sizeof(pack));
  437.  
  438.     SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  439.  
  440.     if (m_pkStunEvent)
  441.         return;
  442.  
  443.     char_event_info* info = AllocEventInfo<char_event_info>();
  444.  
  445.     info->ch = this;
  446.  
  447.     m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3));
  448. }
  449. */
  450. void CHARACTER::Stun(bool bJustDie)
  451. {
  452.     if (IsStun())
  453.         return;
  454.  
  455.     if (IsDead())
  456.         return;
  457.  
  458.     if (!IsPC() && m_pkParty)
  459.     {
  460.         m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0);
  461.     }
  462.  
  463.     sys_log(1, "%s: Stun %p", GetName(), this);
  464.  
  465.     PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY));
  466.     PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY));
  467.    
  468.     CloseMyShop();
  469.  
  470.     event_cancel(&m_pkRecoveryEvent);
  471.    
  472.     if(bJustDie)
  473.     {
  474.         SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  475.         Dead();
  476.     }
  477.     else
  478.     {
  479.         TPacketGCStun pack;
  480.         pack.header = HEADER_GC_STUN;
  481.         pack.vid    = m_vid;
  482.         PacketAround(&pack, sizeof(pack));
  483.    
  484.         SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  485.    
  486.         if (m_pkStunEvent)
  487.             return;
  488.    
  489.         char_event_info* info = AllocEventInfo<char_event_info>();
  490.    
  491.         info->ch = this;
  492.    
  493.         m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3));
  494.     }
  495. }
  496.  
  497. EVENTINFO(SCharDeadEventInfo)
  498. {
  499.     bool isPC;
  500.     uint32_t dwID;
  501.  
  502.     SCharDeadEventInfo()
  503.     : isPC(0)
  504.     , dwID(0)
  505.     {
  506.     }
  507. };
  508.  
  509. EVENTFUNC(dead_event)
  510. {
  511.     const SCharDeadEventInfo* info = dynamic_cast<SCharDeadEventInfo*>(event->info);
  512.  
  513.     if ( info == NULL )
  514.     {
  515.         sys_err( "dead_event> <Factor> Null pointer" );
  516.         return 0;
  517.     }
  518.  
  519.     LPCHARACTER ch = NULL;
  520.  
  521.     if (true == info->isPC)
  522.     {
  523.         ch = CHARACTER_MANAGER::instance().FindByPID( info->dwID );
  524.     }
  525.     else
  526.     {
  527.         ch = CHARACTER_MANAGER::instance().Find( info->dwID );
  528.     }
  529.  
  530.     if (NULL == ch)
  531.     {
  532.         sys_err("DEAD_EVENT: cannot find char pointer with %s id(%d)", info->isPC ? "PC" : "MOB", info->dwID );
  533.         return 0;
  534.     }
  535.  
  536.     ch->m_pkDeadEvent = NULL;
  537.  
  538.     if (ch->GetDesc())
  539.     {
  540.         ch->GetDesc()->SetPhase(PHASE_GAME);
  541.  
  542.         ch->SetPosition(POS_STANDING);
  543.  
  544.         PIXEL_POSITION pos;
  545.  
  546.         if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos))
  547.             ch->WarpSet(pos.x, pos.y);
  548.         else
  549.         {
  550.             sys_err("cannot find spawn position (name %s)", ch->GetName());
  551.             ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
  552.         }
  553.  
  554.         ch->PointChange(POINT_HP, (ch->GetMaxHP() / 2) - ch->GetHP(), true);
  555.  
  556.         ch->DeathPenalty(0);
  557.  
  558.         ch->StartRecoveryEvent();
  559.  
  560.         ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow");
  561.     }
  562.     else
  563.     {
  564.         if (ch->IsMonster() == true)
  565.         {
  566.             if (ch->IsRevive() == false && ch->HasReviverInParty() == true)
  567.             {
  568.                 ch->SetPosition(POS_STANDING);
  569.                 ch->SetHP(ch->GetMaxHP());
  570.  
  571.                 ch->ViewReencode();
  572.  
  573.                 ch->SetAggressive();
  574.                 ch->SetRevive(true);
  575.  
  576.                 return 0;
  577.             }
  578.         }
  579.  
  580.         M2_DESTROY_CHARACTER(ch);
  581.     }
  582.  
  583.     return 0;
  584. }
  585.  
  586. bool CHARACTER::IsDead() const
  587. {
  588.     if (m_pointsInstant.position == POS_DEAD)
  589.         return true;
  590.  
  591.     return false;
  592. }
  593.  
  594. #define GetGoldMultipler() (distribution_test_server ? 3 : 1)
  595.  
  596. void CHARACTER::RewardGold(LPCHARACTER pkAttacker)
  597. {
  598.     // ADD_PREMIUM
  599.     bool isAutoLoot = true;
  600.         // (pkAttacker->GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 ||
  601.         //  pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT))
  602.         // ? true : false;
  603.     // END_OF_ADD_PREMIUM
  604.  
  605.     PIXEL_POSITION pos = GetXYZ(); // @fixme194 GetMovablePosition is useless here
  606.  
  607.     int iTotalGold = 0;
  608.     //
  609.    
  610.     //
  611.     int iGoldPercent = MobRankStats[GetMobRank()].iGoldPercent;
  612.  
  613.     if (pkAttacker->IsPC())
  614.         iGoldPercent = iGoldPercent * (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD_DROP)) / 100;
  615.  
  616.     if (pkAttacker->GetPoint(POINT_MALL_GOLDBONUS))
  617.         iGoldPercent += (iGoldPercent * pkAttacker->GetPoint(POINT_MALL_GOLDBONUS) / 100);
  618.  
  619.     iGoldPercent = iGoldPercent * CHARACTER_MANAGER::instance().GetMobGoldDropRate(pkAttacker) / 100;
  620.  
  621.     // ADD_PREMIUM
  622.     if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 ||
  623.             pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD))
  624.         iGoldPercent += iGoldPercent;
  625.     // END_OF_ADD_PREMIUM
  626.  
  627.     if (iGoldPercent > 100)
  628.         iGoldPercent = 100;
  629.  
  630.     // int iPercent = 100;
  631.  
  632.     // if (GetMobRank() >= MOB_RANK_BOSS)
  633.     //  iPercent = ((iGoldPercent * PERCENT_LVDELTA_BOSS(pkAttacker->GetLevel(), GetLevel())) / 100);
  634.     // else
  635.     //  iPercent = ((iGoldPercent * PERCENT_LVDELTA(pkAttacker->GetLevel(), GetLevel())) / 100);
  636.  
  637.     // if (number(1, 100) > iPercent)
  638.     //  return;
  639.  
  640.     int iGoldMultipler = GetGoldMultipler();
  641.  
  642.     if (1 == number(1, 50000))
  643.         iGoldMultipler *= 10;
  644.     else if (1 == number(1, 10000))
  645.         iGoldMultipler *= 5;
  646.  
  647.    
  648.     if (pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS))
  649.         if (number(1, 100) <= pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS))
  650.             iGoldMultipler *= 2;
  651.  
  652.     //
  653.    
  654.     //
  655.     if (test_server)
  656.         pkAttacker->ChatPacket(CHAT_TYPE_PARTY, "gold_mul %d rate %d", iGoldMultipler, CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker));
  657.  
  658.     //
  659.     // --------- 실제 드롭 처리 -------------
  660.     //
  661.     LPITEM item;
  662.  
  663.     int iGold10DropPct = 100;
  664.     iGold10DropPct = (iGold10DropPct * 100) / (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD10_DROP));
  665.  
  666.    
  667.     // if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0)
  668.     // {
  669.     //  if (1 == number(1, iGold10DropPct))
  670.     //      iGoldMultipler *= 10;
  671.  
  672.     //  int iSplitCount = number(25, 35);
  673.  
  674.     //  for (int i = 0; i < iSplitCount; ++i)
  675.     //  {
  676.     //      int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax) / iSplitCount;
  677.     //      if (test_server)
  678.     //          sys_log(0, "iGold %d", iGold);
  679.     //      iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
  680.     //      iGold *= iGoldMultipler;
  681.  
  682.     //      if (iGold == 0)
  683.     //      {
  684.     //          continue ;
  685.     //      }
  686.  
  687.     //      if (test_server)
  688.     //      {
  689.     //          sys_log(0, "Drop Moeny MobGoldAmountRate %d %d", CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker), iGoldMultipler);
  690.     //          sys_log(0, "Drop Money gold %d GoldMin %d GoldMax %d", iGold, GetMobTable().dwGoldMax, GetMobTable().dwGoldMax);
  691.     //      }
  692.  
  693.            
  694.     //      if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold)))
  695.     //      {
  696.     //          pos.x = GetX() + ((number(-14, 14) + number(-14, 14)) * 23);
  697.     //          pos.y = GetY() + ((number(-14, 14) + number(-14, 14)) * 23);
  698.  
  699.     //          item->AddToGround(GetMapIndex(), pos);
  700.     //          item->StartDestroyEvent();
  701.  
  702.     //          iTotalGold += iGold; // Total gold
  703.     //      }
  704.     //  }
  705.     // }
  706.    
  707.     // else if (1 == number(1, iGold10DropPct))
  708.     // {
  709.     //  //
  710.        
  711.     //  //
  712.     //  for (int i = 0; i < 10; ++i)
  713.     //  {
  714.     //      int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
  715.     //      iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
  716.     //      iGold *= iGoldMultipler;
  717.  
  718.     //      if (iGold == 0)
  719.     //      {
  720.     //          continue;
  721.     //      }
  722.  
  723.            
  724.     //      if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold)))
  725.     //      {
  726.     //          pos.x = GetX() + (number(-7, 7) * 20);
  727.     //          pos.y = GetY() + (number(-7, 7) * 20);
  728.  
  729.     //          item->AddToGround(GetMapIndex(), pos);
  730.     //          item->StartDestroyEvent();
  731.  
  732.     //          iTotalGold += iGold; // Total gold
  733.     //      }
  734.     //  }
  735.     // }
  736.     // else
  737.     {
  738.         //
  739.        
  740.         //
  741.         int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
  742.         iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
  743.         iGold *= iGoldMultipler;
  744.  
  745.         int iSplitCount;
  746.  
  747.         if (iGold >= 3)
  748.             iSplitCount = number(1, 3);
  749.         else if (GetMobRank() >= MOB_RANK_BOSS)
  750.         {
  751.             iSplitCount = number(3, 10);
  752.  
  753.             if ((iGold / iSplitCount) == 0)
  754.                 iSplitCount = 1;
  755.         }
  756.         else
  757.             iSplitCount = 1;
  758.  
  759.         if (iGold != 0)
  760.         {
  761.             iTotalGold += iGold; // Total gold
  762.  
  763.             for (int i = 0; i < iSplitCount; ++i)
  764.             {
  765.                 if (isAutoLoot)
  766.                 {
  767.                     pkAttacker->GiveGold(iGold / iSplitCount);
  768.                 }
  769.                 else if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount)))
  770.                 {
  771.                     pos.x = GetX() + (number(-7, 7) * 20);
  772.                     pos.y = GetY() + (number(-7, 7) * 20);
  773.  
  774.                     item->AddToGround(GetMapIndex(), pos);
  775.                     item->StartDestroyEvent();
  776.                 }
  777.             }
  778.         }
  779.     }
  780.  
  781.     DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold);
  782. }
  783.  
  784. void CHARACTER::Reward(bool bItemDrop)
  785. {
  786.     if (GetRaceNum() == 5001)
  787.     {
  788.         // @fixme194 BEGIN GetMovablePosition should not return
  789.         PIXEL_POSITION pos = GetXYZ();
  790.         SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos);
  791.         // @fixme194 END
  792.  
  793.         LPITEM item;
  794.         int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
  795.         iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(NULL) / 100;
  796.         iGold *= GetGoldMultipler();
  797.         int iSplitCount = number(25, 35);
  798.  
  799.         sys_log(0, "WAEGU Dead gold %d split %d", iGold, iSplitCount);
  800.  
  801.         for (int i = 1; i <= iSplitCount; ++i)
  802.         {
  803.             if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount)))
  804.             {
  805.                 if (i != 0)
  806.                 {
  807.                     pos.x = number(-7, 7) * 20;
  808.                     pos.y = number(-7, 7) * 20;
  809.  
  810.                     pos.x += GetX();
  811.                     pos.y += GetY();
  812.                 }
  813.  
  814.                 item->AddToGround(GetMapIndex(), pos);
  815.                 item->StartDestroyEvent();
  816.             }
  817.         }
  818.         return;
  819.     }
  820.  
  821.     LPCHARACTER pkAttacker = DistributeExp();
  822.  
  823. #ifdef ENABLE_KILL_EVENT_FIX
  824.     if (!pkAttacker && !(pkAttacker = GetMostAttacked()))
  825.         return;
  826. #else
  827.     if (!pkAttacker)
  828.         return;
  829. #endif
  830.  
  831.     if (pkAttacker->IsPC())
  832.     {
  833.         if ((GetLevel() - pkAttacker->GetLevel()) >= -10)
  834.         {
  835.             if (pkAttacker->GetRealAlignment() < 0)
  836.             {
  837.                 if (pkAttacker->IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_KILL))
  838.                     pkAttacker->UpdateAlignment(14);
  839.                 else
  840.                     pkAttacker->UpdateAlignment(7);
  841.             }
  842.             else
  843.                 pkAttacker->UpdateAlignment(2);
  844.         }
  845.  
  846.         pkAttacker->SetQuestNPCID(GetVID());
  847.         quest::CQuestManager::instance().Kill(pkAttacker->GetPlayerID(), GetRaceNum());
  848.         CHARACTER_MANAGER::instance().KillLog(GetRaceNum());
  849.  
  850.         if (!number(0, 9))
  851.         {
  852.             if (pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY))
  853.             {
  854.                 int iHP = pkAttacker->GetMaxHP() * pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY) / 100;
  855.                 pkAttacker->PointChange(POINT_HP, iHP);
  856.                 CreateFly(FLY_HP_SMALL, pkAttacker);
  857.             }
  858.  
  859.             if (pkAttacker->GetPoint(POINT_KILL_SP_RECOVER))
  860.             {
  861.                 int iSP = pkAttacker->GetMaxSP() * pkAttacker->GetPoint(POINT_KILL_SP_RECOVER) / 100;
  862.                 pkAttacker->PointChange(POINT_SP, iSP);
  863.                 CreateFly(FLY_SP_SMALL, pkAttacker);
  864.             }
  865.         }
  866.     }
  867.  
  868.     if (!bItemDrop)
  869.         return;
  870.  
  871.     // @fixme194 BEGIN GetMovablePosition should not return
  872.     PIXEL_POSITION pos = GetXYZ();
  873.     SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos);
  874.     // @fixme194 END
  875.  
  876.     if (test_server)
  877.         sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName());
  878.     RewardGold(pkAttacker);
  879.  
  880.     LPITEM item;
  881.     static std::vector<LPITEM> s_vec_item;
  882.     s_vec_item.clear();
  883.  
  884.     if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item))
  885.     {
  886.         if (s_vec_item.size() == 0)
  887.         {
  888.             // no items dropped
  889.         }
  890.         else if (s_vec_item.size() == 1)
  891.         {
  892.             // single item drop
  893.             item = s_vec_item[0];
  894.             item->AddToGround(GetMapIndex(), pos);
  895.  
  896.             if (!CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()))
  897.             {
  898. #ifdef ENABLE_DICE_SYSTEM
  899.                 if (pkAttacker->GetParty())
  900.                 {
  901.                     FPartyDropDiceRoll f(item, pkAttacker);
  902.                     f.Process(this);
  903.                 }
  904.                 else
  905.                     item->SetOwnership(pkAttacker);
  906. #else
  907.                 item->SetOwnership(pkAttacker);
  908. #endif
  909.             }
  910.  
  911.             item->StartDestroyEvent();
  912.  
  913.             pos.x = number(-7, 7) * 20;
  914.             pos.y = number(-7, 7) * 20;
  915.             pos.x += GetX();
  916.             pos.y += GetY();
  917.  
  918.             sys_log(0, "DROP_ITEM: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName());
  919.         }
  920.         else
  921.         {
  922.             // multiple items: distribute by damage share
  923.             int iItemIdx = s_vec_item.size() - 1;
  924.             std::priority_queue<std::pair<int, LPCHARACTER>> pq;
  925.  
  926.             // ---- FIX: use 64-bit accumulator for total damage ----
  927.             long long total_dam = 0;
  928.  
  929.             for (TDamageMap::iterator it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it)
  930.             {
  931.                 int iDamage = it->second.iTotalDamage;
  932.                 if (iDamage > 0)
  933.                 {
  934.                     LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first);
  935.                     if (ch)
  936.                     {
  937.                         pq.push(std::make_pair(iDamage, ch));
  938.                         total_dam += iDamage;  // now in 64 bits
  939.                     }
  940.                 }
  941.             }
  942.  
  943.             std::vector<LPCHARACTER> v;
  944.  
  945.             // ---- FIX: cast to long long before multiplying to avoid 32-bit overflow ----
  946.             while (!pq.empty() &&
  947.                    static_cast<long long>(pq.top().first) * 10 >= total_dam)
  948.             {
  949.                 v.push_back(pq.top().second);
  950.                 pq.pop();
  951.             }
  952.  
  953.             if (v.empty())
  954.             {
  955.                 // everyone gets a chance if nobody passes the 10% threshold
  956.                 while (iItemIdx >= 0)
  957.                 {
  958.                     item = s_vec_item[iItemIdx--];
  959.                     if (!item)
  960.                     {
  961.                         sys_err("item null in vector idx %d", iItemIdx + 1);
  962.                         continue;
  963.                     }
  964.                     item->AddToGround(GetMapIndex(), pos);
  965.                     item->StartDestroyEvent();
  966.  
  967.                     pos.x = number(-7, 7) * 20;
  968.                     pos.y = number(-7, 7) * 20;
  969.                     pos.x += GetX();
  970.                     pos.y += GetY();
  971.  
  972.                     sys_log(0, "DROP_ITEM: %s %d %d by %s",
  973.                             item->GetName(), pos.x, pos.y, GetName());
  974.                 }
  975.             }
  976.             else
  977.             {
  978.                 // distribute among top damage-dealers
  979.                 auto it = v.begin();
  980.                 while (iItemIdx >= 0)
  981.                 {
  982.                     item = s_vec_item[iItemIdx--];
  983.                     if (!item)
  984.                     {
  985.                         sys_err("item null in vector idx %d", iItemIdx + 1);
  986.                         continue;
  987.                     }
  988.  
  989.                     LPCHARACTER ch = *it++;
  990.                     if (it == v.end())
  991.                         it = v.begin();
  992.  
  993.                     item->AddToGround(GetMapIndex(), pos);
  994.  
  995.                     if (ch->GetParty())
  996.                         ch = ch->GetParty()->GetNextOwnership(ch, GetX(), GetY());
  997.  
  998.                     if (!CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()))
  999.                     {
  1000. #ifdef ENABLE_DICE_SYSTEM
  1001.                         if (ch->GetParty())
  1002.                         {
  1003.                             FPartyDropDiceRoll f(item, ch);
  1004.                             f.Process(this);
  1005.                         }
  1006.                         else
  1007.                             item->SetOwnership(ch);
  1008. #else
  1009.                         item->SetOwnership(ch);
  1010. #endif
  1011.                     }
  1012.  
  1013.                     item->StartDestroyEvent();
  1014.  
  1015.                     pos.x = number(-7, 7) * 20;
  1016.                     pos.y = number(-7, 7) * 20;
  1017.                     pos.x += GetX();
  1018.                     pos.y += GetY();
  1019.  
  1020.                     sys_log(0, "DROP_ITEM: %s %d %d by %s",
  1021.                             item->GetName(), pos.x, pos.y, GetName());
  1022.                 }
  1023.             }
  1024.         }
  1025.     }
  1026.  
  1027.     m_map_kDamage.clear();
  1028. }
  1029.  
  1030. struct TItemDropPenalty
  1031. {
  1032.     int iInventoryPct;      // Range: 1 ~ 1000
  1033.     int iInventoryQty;      // Range: --
  1034.     int iEquipmentPct;      // Range: 1 ~ 100
  1035.     int iEquipmentQty;      // Range: --
  1036. };
  1037.  
  1038. TItemDropPenalty aItemDropPenalty_kor[9] =
  1039. {
  1040.     {   0,   0,  0,  0 },  
  1041.     {   0,   0,  0,  0 },  
  1042.     {   0,   0,  0,  0 },  
  1043.     {   0,   0,  0,  0 },  
  1044.     {   0,   0,  0,  0 },  
  1045.     {  25,   1,  5,  1 },  
  1046.     {  50,   2, 10,  1 },  
  1047.     {  75,   4, 15,  1 },  
  1048.     { 100,   8, 20,  1 },  
  1049. };
  1050.  
  1051. void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller)
  1052. {
  1053.     if (GetMyShop())
  1054.         return;
  1055.  
  1056.     if (GetLevel() < 50)
  1057.         return;
  1058.  
  1059.     if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true)
  1060.     {
  1061.         return;
  1062.     }
  1063.  
  1064.     struct TItemDropPenalty * table = &aItemDropPenalty_kor[0];
  1065.  
  1066.     if (GetLevel() < 10)
  1067.         return;
  1068.  
  1069.     int iAlignIndex;
  1070.  
  1071.     if (GetRealAlignment() >= 120000)
  1072.         iAlignIndex = 0;
  1073.     else if (GetRealAlignment() >= 80000)
  1074.         iAlignIndex = 1;
  1075.     else if (GetRealAlignment() >= 40000)
  1076.         iAlignIndex = 2;
  1077.     else if (GetRealAlignment() >= 10000)
  1078.         iAlignIndex = 3;
  1079.     else if (GetRealAlignment() >= 0)
  1080.         iAlignIndex = 4;
  1081.     else if (GetRealAlignment() > -40000)
  1082.         iAlignIndex = 5;
  1083.     else if (GetRealAlignment() > -80000)
  1084.         iAlignIndex = 6;
  1085.     else if (GetRealAlignment() > -120000)
  1086.         iAlignIndex = 7;
  1087.     else
  1088.         iAlignIndex = 8;
  1089.  
  1090.     std::vector<std::pair<LPITEM, int> > vec_item;
  1091.     LPITEM pkItem;
  1092.     int i;
  1093.     bool isDropAllEquipments = false;
  1094.  
  1095.     TItemDropPenalty & r = table[iAlignIndex];
  1096.     sys_log(0, "%s align %d inven_pct %d equip_pct %d", GetName(), iAlignIndex, r.iInventoryPct, r.iEquipmentPct);
  1097.  
  1098.     bool bDropInventory = r.iInventoryPct >= number(1, 1000);
  1099.     bool bDropEquipment = r.iEquipmentPct >= number(1, 100);
  1100.     bool bDropAntiDropUniqueItem = false;
  1101.  
  1102.     if ((bDropInventory || bDropEquipment) && IsEquipUniqueItem(UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY))
  1103.     {
  1104.         bDropInventory = false;
  1105.         bDropEquipment = false;
  1106.         bDropAntiDropUniqueItem = true;
  1107.     }
  1108.  
  1109.     // @fixme198 BEGIN
  1110.     if (bDropInventory || bDropEquipment) {
  1111.         if (pkKiller)
  1112.             pkKiller->SetExchangeTime();
  1113.         SetExchangeTime();
  1114.     }
  1115.     // @fixme198 END
  1116.  
  1117.     if (bDropInventory) // Drop Inventory
  1118.     {
  1119.         std::vector<BYTE> vec_bSlots;
  1120.         const auto isQuestRunning = quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning();
  1121.  
  1122.         for (i = 0; i < INVENTORY_MAX_NUM; ++i)
  1123.         {
  1124.             auto pkItem = GetInventoryItem(i);
  1125.             if (!pkItem || (isQuestRunning && pkItem->GetType() == ITEM_QUEST)) // @fixme198 (item_quest items can be used without being consumed)
  1126.                 continue;
  1127.             vec_bSlots.push_back(i);
  1128.         }
  1129.  
  1130.         if (!vec_bSlots.empty())
  1131.         {
  1132.             random_shuffle(vec_bSlots.begin(), vec_bSlots.end());
  1133.  
  1134.             int iQty = MIN(vec_bSlots.size(), r.iInventoryQty);
  1135.  
  1136.             if (iQty)
  1137.                 iQty = number(1, iQty);
  1138.  
  1139.             for (i = 0; i < iQty; ++i)
  1140.             {
  1141.                 pkItem = GetInventoryItem(vec_bSlots[i]);
  1142.  
  1143.                 if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP))
  1144.                     continue;
  1145.  
  1146.                 SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255);
  1147.                 vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY));
  1148.             }
  1149.         }
  1150.         else if (iAlignIndex == 8)
  1151.             isDropAllEquipments = true;
  1152.     }
  1153.  
  1154.     if (bDropEquipment) // Drop Equipment
  1155.     {
  1156.         std::vector<BYTE> vec_bSlots;
  1157.  
  1158.         for (i = 0; i < WEAR_MAX_NUM; ++i)
  1159.             if (GetWear(i))
  1160.                 vec_bSlots.push_back(i);
  1161.  
  1162.         if (!vec_bSlots.empty())
  1163.         {
  1164.             random_shuffle(vec_bSlots.begin(), vec_bSlots.end());
  1165.             int iQty;
  1166.  
  1167.             if (isDropAllEquipments)
  1168.                 iQty = vec_bSlots.size();
  1169.             else
  1170.                 iQty = MIN(vec_bSlots.size(), number(1, r.iEquipmentQty));
  1171.  
  1172.             if (iQty)
  1173.                 iQty = number(1, iQty);
  1174.  
  1175.             for (i = 0; i < iQty; ++i)
  1176.             {
  1177.                 pkItem = GetWear(vec_bSlots[i]);
  1178.  
  1179.                 if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP))
  1180.                     continue;
  1181.  
  1182.                 SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255);
  1183.                 vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
  1184.             }
  1185.         }
  1186.     }
  1187.  
  1188.     if (bDropAntiDropUniqueItem)
  1189.     {
  1190.         LPITEM pkItem;
  1191.  
  1192.         pkItem = GetWear(WEAR_UNIQUE1);
  1193.  
  1194.         if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)
  1195.         {
  1196.             SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE1, 255);
  1197.             vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
  1198.         }
  1199.  
  1200.         pkItem = GetWear(WEAR_UNIQUE2);
  1201.  
  1202.         if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)
  1203.         {
  1204.             SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE2, 255);
  1205.             vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT));
  1206.         }
  1207.     }
  1208.  
  1209.     {
  1210.         PIXEL_POSITION pos;
  1211.         pos.x = GetX();
  1212.         pos.y = GetY();
  1213.  
  1214.         unsigned int i;
  1215.  
  1216.         for (i = 0; i < vec_item.size(); ++i)
  1217.         {
  1218.             LPITEM item = vec_item[i].first;
  1219.             int window = vec_item[i].second;
  1220.  
  1221.             item->AddToGround(GetMapIndex(), pos);
  1222.             item->StartDestroyEvent();
  1223.  
  1224.             sys_log(0, "DROP_ITEM_PK: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName());
  1225.             LogManager::instance().ItemLog(this, item, "DEAD_DROP", (window == INVENTORY) ? "INVENTORY" : ((window == EQUIPMENT) ? "EQUIPMENT" : ""));
  1226.  
  1227.             pos.x = GetX() + number(-7, 7) * 20;
  1228.             pos.y = GetY() + number(-7, 7) * 20;
  1229.         }
  1230.     }
  1231. }
  1232.  
  1233. class FPartyAlignmentCompute
  1234. {
  1235.     public:
  1236.         FPartyAlignmentCompute(int iAmount, int x, int y)
  1237.         {
  1238.             m_iAmount = iAmount;
  1239.             m_iCount = 0;
  1240.             m_iStep = 0;
  1241.             m_iKillerX = x;
  1242.             m_iKillerY = y;
  1243.         }
  1244.  
  1245.         void operator () (LPCHARACTER pkChr)
  1246.         {
  1247.             if (DISTANCE_APPROX(pkChr->GetX() - m_iKillerX, pkChr->GetY() - m_iKillerY) < PARTY_DEFAULT_RANGE)
  1248.             {
  1249.                 if (m_iStep == 0)
  1250.                 {
  1251.                     ++m_iCount;
  1252.                 }
  1253.                 else
  1254.                 {
  1255.                     pkChr->UpdateAlignment(m_iAmount / m_iCount);
  1256.                 }
  1257.             }
  1258.         }
  1259.  
  1260.         int m_iAmount;
  1261.         int m_iCount;
  1262.         int m_iStep;
  1263.  
  1264.         int m_iKillerX;
  1265.         int m_iKillerY;
  1266. };
  1267.  
  1268.  
  1269.  
  1270. void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead)
  1271. {
  1272.     if (IsDead())
  1273.         return;
  1274. #ifndef DISABLE_STOP_RIDING_WHEN_DIE
  1275.     {
  1276.        
  1277.         if (IsHorseRiding())
  1278.         {
  1279.             StopRiding();
  1280.         }
  1281.         else if (GetMountVnum())
  1282.         {
  1283.             RemoveAffect(AFFECT_MOUNT_BONUS);
  1284.             m_dwMountVnum = 0;
  1285.             UnEquipSpecialRideUniqueItem();
  1286.  
  1287.             UpdatePacket();
  1288.         }
  1289.        
  1290.     }
  1291. #endif
  1292.  
  1293.     if (!pkKiller && m_dwKillerPID)
  1294.         pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID);
  1295.  
  1296.     m_dwKillerPID = 0; // 반드시 초기화 해야함 DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE
  1297.  
  1298.     bool isAgreedPVP = false;
  1299.     bool isUnderGuildWar = false;
  1300.     bool isDuel = false;
  1301.     bool isForked = false;
  1302.  
  1303.     if (pkKiller && pkKiller->IsPC())
  1304.     {
  1305.         if (pkKiller->m_pkChrTarget == this)
  1306.             pkKiller->SetTarget(NULL);
  1307.  
  1308.         if (!IsPC() && pkKiller->GetDungeon())
  1309.             pkKiller->GetDungeon()->IncKillCount(pkKiller, this);
  1310.  
  1311.         isAgreedPVP = CPVPManager::instance().Dead(this, pkKiller->GetPlayerID());
  1312.         isDuel = CArenaManager::instance().OnDead(pkKiller, this);
  1313.  
  1314.         if (IsPC())
  1315.         {
  1316.             CGuild * g1 = GetGuild();
  1317.             CGuild * g2 = pkKiller->GetGuild();
  1318.  
  1319.             if (g1 && g2)
  1320.                 if (g1->UnderWar(g2->GetID()))
  1321.                     isUnderGuildWar = true;
  1322.  
  1323.             pkKiller->SetQuestNPCID(GetVID());
  1324.             quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC);
  1325.             CGuildManager::instance().Kill(pkKiller, this);
  1326.  
  1327. #if defined(__BL_KILL_BAR__)
  1328.             TPacketGCKillBar kb;
  1329.            
  1330.             kb.bHeader = HEADER_GC_KILLBAR;
  1331.             kb.bKillerRace = static_cast<BYTE>(pkKiller->GetRaceNum());
  1332.             LPITEM KillerWeapon = pkKiller->GetWear(WEAR_WEAPON);
  1333.             kb.bKillerWeaponType = KillerWeapon ? KillerWeapon->GetSubType() : 255;
  1334.             kb.bVictimRace = static_cast<BYTE>(GetRaceNum());
  1335.  
  1336.             strlcpy(kb.szKiller, pkKiller->GetName(), sizeof(kb.szKiller));
  1337.             strlcpy(kb.szVictim, GetName(), sizeof(kb.szVictim));
  1338.  
  1339.             const DESC_MANAGER::DESC_SET& c_set_desc = DESC_MANAGER::instance().GetClientSet();
  1340.             for (DESC_MANAGER::DESC_SET::const_iterator it = c_set_desc.begin(); it != c_set_desc.end(); ++it)
  1341.             {
  1342.                 LPDESC d_c = *it;
  1343.                 if (!d_c)
  1344.                     continue;
  1345.  
  1346.                 LPCHARACTER c_c = d_c->GetCharacter();
  1347.                 if (!c_c)
  1348.                     continue;
  1349.  
  1350.                 if (pkKiller->GetMapIndex() != c_c->GetMapIndex())
  1351.                     continue;
  1352.  
  1353.                 d_c->Packet(&kb, sizeof(kb));
  1354.             }
  1355. #endif
  1356.         }
  1357.     }
  1358.  
  1359. #ifdef ENABLE_QUEST_DIE_EVENT
  1360.     if (IsPC())
  1361.     {
  1362.         if (pkKiller)
  1363.             SetQuestNPCID(pkKiller->GetVID());
  1364.         // quest::CQuestManager::instance().Die(GetPlayerID(), quest::QUEST_NO_NPC);
  1365.         quest::CQuestManager::instance().Die(GetPlayerID(), (pkKiller)?pkKiller->GetRaceNum():quest::QUEST_NO_NPC);
  1366.     }
  1367. #endif
  1368.  
  1369.     //CHECK_FORKEDROAD_WAR
  1370.     if (IsPC())
  1371.     {
  1372.         if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex()))
  1373.             isForked = true;
  1374.     }
  1375.     //END_CHECK_FORKEDROAD_WAR
  1376.  
  1377.     if (pkKiller &&
  1378.             !isAgreedPVP &&
  1379.             !isUnderGuildWar &&
  1380.             IsPC() &&
  1381.             !isDuel &&
  1382.             !isForked &&
  1383.             !IS_CASTLE_MAP(GetMapIndex()))
  1384.     {
  1385.         if (GetGMLevel() == GM_PLAYER || test_server)
  1386.         {
  1387.             ItemDropPenalty(pkKiller);
  1388.         }
  1389.     }
  1390.  
  1391.     // CASTLE_SIEGE
  1392.     if (IS_CASTLE_MAP(GetMapIndex()))
  1393.     {
  1394.         if (CASTLE_FROG_VNUM == GetRaceNum())
  1395.             castle_frog_die(this, pkKiller);
  1396.         else if (castle_is_guard_vnum(GetRaceNum()))
  1397.             castle_guard_die(this, pkKiller);
  1398.         else if (castle_is_tower_vnum(GetRaceNum()))
  1399.             castle_tower_die(this, pkKiller);
  1400.     }
  1401.     // CASTLE_SIEGE
  1402.  
  1403. #ifdef ENABLE_DAILY_BOSS
  1404.     // if (CDailyBoss::instance().IsBossMap(pkKiller->GetMapIndex()) == true)
  1405.     if (CDailyBoss::instance().IsBossMap(GetMapIndex()) == true)
  1406.     {
  1407.         if (CDailyBoss::instance().BossVnum() == GetRaceNum())
  1408.             CDailyBoss::instance().OnKill(pkKiller);
  1409.     }
  1410. #endif
  1411.  
  1412.     if (true == isForked)
  1413.     {
  1414.         CThreeWayWar::instance().onDead( this, pkKiller );
  1415.     }
  1416.  
  1417. #ifdef __BOSS_TRACK__
  1418.     if (!IsPC())
  1419.     {
  1420.         for (auto itBoss = m_mapBossTrack.begin(); itBoss != m_mapBossTrack.end(); ++itBoss)
  1421.         {
  1422.             const TBossTrack* bossTrack = &itBoss->second;
  1423.             if (std::find(bossTrack->vecChannels.begin(), bossTrack->vecChannels.end(), g_bChannel) == bossTrack->vecChannels.end())
  1424.                 continue;
  1425.             if (GetRaceNum() == itBoss->second.mobIdx && GetMapIndex() == itBoss->second.mapIdx)
  1426.             {
  1427.                 LPREGEN regen = GetRegen();
  1428.                 if (regen && regen->ix == bossTrack->x && regen->iy == bossTrack->y)
  1429.                 {
  1430.                     char buf[254];
  1431.                     snprintf(buf, sizeof(buf), "track_%u_%u", itBoss->first, g_bChannel);
  1432.                     quest::CQuestManager::instance().RequestSetEventFlag(buf, time(0) + regen->time);
  1433.  
  1434.                     snprintf(buf, sizeof(buf), "SetBossTrack start 0");
  1435.                     CHARACTER_MANAGER::Instance().SendBossTrackInfo(true, buf);
  1436.  
  1437.                     snprintf(buf, sizeof(buf), "SetBossTrack %u:%u?%d! 0", itBoss->first, g_bChannel, regen->time);
  1438.                     CHARACTER_MANAGER::Instance().SendBossTrackInfo(true, buf);
  1439.  
  1440.                     snprintf(buf, sizeof(buf), "SetBossTrack end 0");
  1441.                     CHARACTER_MANAGER::Instance().SendBossTrackInfo(true, buf);
  1442.                 }
  1443.             }
  1444.         }
  1445.     }
  1446. #endif
  1447.  
  1448.     SetPosition(POS_DEAD);
  1449.  
  1450. #ifdef KEEP_BUFFS_PVM_AFTER_DIE
  1451.     if (pkKiller && IsPC() && !pkKiller->IsPC())
  1452.         ClearAffect(true, true);
  1453.     else
  1454.         ClearAffect(true);
  1455. #else
  1456.     ClearAffect(true);
  1457. #endif
  1458.  
  1459.     if (pkKiller && IsPC())
  1460.     {
  1461.         if (!pkKiller->IsPC())
  1462.         {
  1463.             if (!isForked)
  1464.             {
  1465.                 sys_log(1, "DEAD: %s %p WITH PENALTY", GetName(), this);
  1466.                 SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
  1467.                 LogManager::instance().CharLog(this, pkKiller->GetRaceNum(), "DEAD_BY_NPC", pkKiller->GetName());
  1468.             }
  1469.         }
  1470.         else
  1471.         {
  1472.             sys_log(1, "DEAD_BY_PC: %s %p KILLER %s %p", GetName(), this, pkKiller->GetName(), get_pointer(pkKiller));
  1473.             REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
  1474.  
  1475.             if (GetEmpire() != pkKiller->GetEmpire())
  1476.             {
  1477.                 int iEP = MIN(GetPoint(POINT_EMPIRE_POINT), pkKiller->GetPoint(POINT_EMPIRE_POINT));
  1478.  
  1479.                 PointChange(POINT_EMPIRE_POINT, -(iEP / 10));
  1480.                 pkKiller->PointChange(POINT_EMPIRE_POINT, iEP / 5);
  1481.  
  1482.                 if (GetPoint(POINT_EMPIRE_POINT) < 10)
  1483.                 {
  1484.                    
  1485.                 }
  1486.  
  1487.                 char buf[256];
  1488.                 snprintf(buf, sizeof(buf),
  1489.                         "%d %d %d %s %d %d %d %s",
  1490.                         GetEmpire(), GetAlignment(), GetPKMode(), GetName(),
  1491.                         pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName());
  1492.  
  1493.                 LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf);
  1494.             }
  1495.             else
  1496.             {
  1497.                 if (!isAgreedPVP && !isUnderGuildWar && !IsKillerMode() && GetAlignment() >= 0 && !isDuel && !isForked)
  1498.                 {
  1499.                     int iNoPenaltyProb = 0;
  1500.  
  1501.                     if (pkKiller->GetAlignment() >= 0)  // 1/3 percent down
  1502.                         iNoPenaltyProb = 33;
  1503.                     else                // 4/5 percent down
  1504.                         iNoPenaltyProb = 20;
  1505.  
  1506.                     if (number(1, 100) < iNoPenaltyProb)
  1507.                         pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용신의 보호로 아이템이 떨어지지 않았습니다."));
  1508.                     else
  1509.                     {
  1510.                         if (pkKiller->GetParty())
  1511.                         {
  1512.                             FPartyAlignmentCompute f(-20000, pkKiller->GetX(), pkKiller->GetY());
  1513.                             pkKiller->GetParty()->ForEachOnlineMember(f);
  1514.  
  1515.                             if (f.m_iCount == 0)
  1516.                                 pkKiller->UpdateAlignment(-20000);
  1517.                             else
  1518.                             {
  1519.                                 sys_log(0, "ALIGNMENT PARTY count %d amount %d", f.m_iCount, f.m_iAmount);
  1520.  
  1521.                                 f.m_iStep = 1;
  1522.                                 pkKiller->GetParty()->ForEachOnlineMember(f);
  1523.                             }
  1524.                         }
  1525.                         else
  1526.                             pkKiller->UpdateAlignment(-20000);
  1527.                     }
  1528.                 }
  1529.  
  1530.                 char buf[256];
  1531.                 snprintf(buf, sizeof(buf),
  1532.                         "%d %d %d %s %d %d %d %s",
  1533.                         GetEmpire(), GetAlignment(), GetPKMode(), GetName(),
  1534.                         pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName());
  1535.  
  1536.                 LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf);
  1537.             }
  1538.         }
  1539.     }
  1540.     else
  1541.     {
  1542.         sys_log(1, "DEAD: %s %p", GetName(), this);
  1543.         REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY);
  1544.     }
  1545.  
  1546.     ClearSync();
  1547.  
  1548.     //sys_log(1, "stun cancel %s[%d]", GetName(), (DWORD)GetVID());
  1549.     event_cancel(&m_pkStunEvent);
  1550.  
  1551.     if (IsPC())
  1552.     {
  1553.         m_dwLastDeadTime = get_dword_time();
  1554.         SetKillerMode(false);
  1555.         GetDesc()->SetPhase(PHASE_DEAD);
  1556.     }
  1557.     else
  1558.     {
  1559.        
  1560.         if (!IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD))
  1561.         {
  1562.             if (!(pkKiller && pkKiller->IsPC() && pkKiller->GetGuild() && pkKiller->GetGuild()->UnderAnyWar(GUILD_WAR_TYPE_FIELD)))
  1563.             {
  1564.                
  1565.                 if (GetMobTable().dwResurrectionVnum)
  1566.                 {
  1567.                     // DUNGEON_MONSTER_REBIRTH_BUG_FIX
  1568.                     LPCHARACTER chResurrect = CHARACTER_MANAGER::instance().SpawnMob(GetMobTable().dwResurrectionVnum, GetMapIndex(), GetX(), GetY(), GetZ(), true, (int) GetRotation());
  1569.                     if (GetDungeon() && chResurrect)
  1570.                     {
  1571.                         chResurrect->SetDungeon(GetDungeon());
  1572.                     }
  1573.                     // END_OF_DUNGEON_MONSTER_REBIRTH_BUG_FIX
  1574.  
  1575.                     Reward(false);
  1576.                 }
  1577.                 else if (IsRevive() == true)
  1578.                 {
  1579.                     Reward(false);
  1580.                 }
  1581.                 else
  1582.                 {
  1583.                     Reward(true); // Drops gold, item, etc..
  1584.                 }
  1585.             }
  1586.             else
  1587.             {
  1588.                 if (pkKiller->m_dwUnderGuildWarInfoMessageTime < get_dword_time())
  1589.                 {
  1590.                     pkKiller->m_dwUnderGuildWarInfoMessageTime = get_dword_time() + 60000;
  1591.                     pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드전중에는 사냥에 따른 이익이 없습니다."));
  1592.                 }
  1593.             }
  1594.         }
  1595.     }
  1596.  
  1597.     // BOSS_KILL_LOG
  1598.     if (GetMobRank() >= MOB_RANK_BOSS && pkKiller && pkKiller->IsPC())
  1599.     {
  1600.         char buf[51];
  1601.         snprintf(buf, sizeof(buf), "%d %ld", g_bChannel, pkKiller->GetMapIndex());
  1602.         if (IsStone())
  1603.             LogManager::instance().CharLog(pkKiller, GetRaceNum(), "STONE_KILL", buf);
  1604.         else
  1605.             LogManager::instance().CharLog(pkKiller, GetRaceNum(), "BOSS_KILL", buf);
  1606.     }
  1607.     // END_OF_BOSS_KILL_LOG
  1608.  
  1609. #ifndef RENEWAL_DEAD_PACKET
  1610.     TPacketGCDead pack;
  1611.     pack.header = HEADER_GC_DEAD;
  1612.     pack.vid    = m_vid;
  1613.     PacketAround(&pack, sizeof(pack));
  1614. #endif
  1615.  
  1616.     REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  1617.  
  1618.    
  1619.     if (GetDesc() != NULL) {
  1620.         //
  1621.        
  1622.         //
  1623.         itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
  1624.  
  1625.         while (it != m_list_pkAffect.end())
  1626.             SendAffectAddPacket(GetDesc(), *it++);
  1627.     }
  1628.  
  1629.     //
  1630.    
  1631.     //
  1632.    
  1633.    
  1634.    
  1635.     if (isDuel == false)
  1636.     {
  1637.         if (m_pkDeadEvent)
  1638.         {
  1639.             sys_log(1, "DEAD_EVENT_CANCEL: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
  1640.             event_cancel(&m_pkDeadEvent);
  1641.         }
  1642.  
  1643.         if (IsStone())
  1644.             ClearStone();
  1645.  
  1646.         if (GetDungeon())
  1647.         {
  1648.             GetDungeon()->DeadCharacter(this);
  1649.         }
  1650.  
  1651.         SCharDeadEventInfo* pEventInfo = AllocEventInfo<SCharDeadEventInfo>();
  1652.  
  1653.         if (IsPC())
  1654.         {
  1655.             pEventInfo->isPC = true;
  1656.             pEventInfo->dwID = this->GetPlayerID();
  1657.  
  1658.             m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(180));
  1659.         }
  1660.         else
  1661.         {
  1662.             pEventInfo->isPC = false;
  1663.             pEventInfo->dwID = this->GetVID();
  1664.  
  1665.             if (IsRevive() == false && HasReviverInParty() == true)
  1666.             {
  1667.                 m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3));
  1668.             }
  1669.             else
  1670.             {
  1671.                 m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(2)); //default: 10
  1672.             }
  1673.         }
  1674.  
  1675.         sys_log(1, "DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
  1676.     }
  1677.  
  1678.     if (m_pkExchange != NULL)
  1679.     {
  1680.         m_pkExchange->Cancel();
  1681.     }
  1682.  
  1683. #ifdef RENEWAL_DEAD_PACKET
  1684.     TPacketGCDead pack;
  1685.     pack.header = HEADER_GC_DEAD;
  1686.     pack.vid    = m_vid;
  1687.     for (BYTE i = REVIVE_TYPE_HERE; i < REVIVE_TYPE_MAX; i++)
  1688.         pack.t_d[i] = CalculateDeadTime(i);
  1689.     PacketAround(&pack, sizeof(pack));
  1690. #endif
  1691.  
  1692.     if (IsCubeOpen() == true)
  1693.     {
  1694.         Cube_close(this);
  1695.     }
  1696.    
  1697. #ifdef ENABLE_ACCE_SYSTEM
  1698.     if (IsPC())
  1699.         CloseAcce();
  1700. #endif
  1701.     CShopManager::instance().StopShopping(this);
  1702.     CloseMyShop();
  1703.     CloseSafebox();
  1704.  
  1705.     if (true == IsMonster() && 2493 == GetMobTable().dwVnum)
  1706.     {
  1707.         if (NULL != pkKiller && NULL != pkKiller->GetGuild())
  1708.         {
  1709.             CDragonLairManager::instance().OnDragonDead( this, pkKiller->GetGuild()->GetID() );
  1710.         }
  1711.         else
  1712.         {
  1713.             sys_err("DragonLair: Dragon killed by nobody");
  1714.         }
  1715.     }
  1716.  
  1717. #if defined(__SHIP_DEFENSE__)
  1718.     CShipDefenseManager& rkShipDefenseMgr = CShipDefenseManager::Instance();
  1719.     if (rkShipDefenseMgr.IsDungeon(this->GetMapIndex()))
  1720.         rkShipDefenseMgr.OnKill(this, pkKiller);
  1721. #endif
  1722. }
  1723.  
  1724. #ifdef SYNC_RENEWAL_FIX
  1725. void CHARACTER::RewardlessDead()  // DevFix 29
  1726. {
  1727.     if (IsPC())  // This function only for mobs or stones, so if IsPC() has triggered this, redirect it regular Dead() function. - [MT2Dev Note] - 18/10/2024
  1728.     {
  1729.         sys_log (0, "<CHARACTER::RewardlessDead> Wrong Usage! It was a PC, redirect it to regular Dead()");
  1730.         Dead();  // Just to prevent, not needed if you use it properly. - [Mitachi] - 18/10/2024
  1731.         return;
  1732.     }
  1733.     if (IsDead())
  1734.     {
  1735.         return;
  1736.     }
  1737.     if (IsMonster() || IsStone())  // Dead is only possible when victim is mob or stone. - [MT2Dev Note] - 10/10/2024
  1738.     {
  1739.         m_dwKillerPID = 0;  // Remove the killer to avoid quest triggers may in dead. - [Mitachi] - 18/10/2024
  1740.         SET_BIT (m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD);  // Set no reward. - [Mitachi] - 18/10/2024
  1741.         SetPosition (POS_DEAD);
  1742.         ClearAffect (true);
  1743.         ClearSync();
  1744.         if (m_pkStunEvent)
  1745.         {
  1746.             event_cancel (&m_pkStunEvent);
  1747.         }
  1748.         TPacketGCDead pack;
  1749.         pack.header = HEADER_GC_DEAD;
  1750.         pack.vid = m_vid;
  1751.         PacketAround (&pack, sizeof (pack));
  1752.         REMOVE_BIT (m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  1753.         sys_log (0, "Rewardless_DEAD: %s %p", GetName(), this);
  1754.         // Create Dead event.. In the Dead event, for monsters, make them destroy after a few seconds. - [Ymir Dev Note]
  1755.         if (m_pkDeadEvent)
  1756.         {
  1757.             event_cancel (&m_pkDeadEvent);
  1758.         }
  1759.         if (IsStone())
  1760.         {
  1761.             ClearStone();
  1762.         }
  1763.         if (GetDungeon())
  1764.         {
  1765.             GetDungeon()->DeadCharacter (this);
  1766.         }
  1767.         SCharDeadEventInfo* pEventInfo = AllocEventInfo<SCharDeadEventInfo>();
  1768.         pEventInfo->isPC = false;
  1769.         pEventInfo->dwID = this->GetVID();
  1770.         m_pkDeadEvent = event_create (dead_event, pEventInfo, PASSES_PER_SEC (0));
  1771.         sys_log (0, "Rewardless_DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer (m_pkDeadEvent));
  1772.     }
  1773. }
  1774. #endif
  1775.  
  1776. struct FuncSetLastAttacked
  1777. {
  1778.     FuncSetLastAttacked(DWORD dwTime) : m_dwTime(dwTime)
  1779.     {
  1780.     }
  1781.  
  1782.     void operator () (LPCHARACTER ch)
  1783.     {
  1784.         ch->SetLastAttacked(m_dwTime);
  1785.     }
  1786.  
  1787.     DWORD m_dwTime;
  1788. };
  1789.  
  1790. void CHARACTER::SetLastAttacked(DWORD dwTime)
  1791. {
  1792.     assert(m_pkMobInst != NULL);
  1793.  
  1794.     m_pkMobInst->m_dwLastAttackedTime = dwTime;
  1795.     m_pkMobInst->m_posLastAttacked = GetXYZ();
  1796. }
  1797.  
  1798. void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag)
  1799. {
  1800.     if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this))
  1801.     {
  1802.         TPacketGCDamageInfo damageInfo;
  1803.         memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo));
  1804.  
  1805.         damageInfo.header = HEADER_GC_DAMAGE_INFO;
  1806.         damageInfo.dwVID = (DWORD)GetVID();
  1807.         damageInfo.flag = DamageFlag;
  1808.         damageInfo.damage = Damage;
  1809.  
  1810.         if (GetDesc() != NULL)
  1811.         {
  1812.             GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
  1813.         }
  1814.  
  1815.         if (pAttacker->GetDesc() != NULL)
  1816.         {
  1817.             pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
  1818.         }
  1819.         /*
  1820.            if (GetArenaObserverMode() == false && GetArena() != NULL)
  1821.            {
  1822.            GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo));
  1823.            }
  1824.          */
  1825.     }
  1826. }
  1827.  
  1828. //
  1829.  
  1830. //
  1831. // Arguments
  1832. //  pAttacker       : 공격자
  1833.  
  1834.  
  1835. //
  1836. // Return value
  1837. //  true        : dead
  1838. //  false       : not dead yet
  1839. //
  1840.  
  1841. bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead
  1842. {
  1843.     if (!pAttacker)
  1844.         return false;
  1845.  
  1846.     if (IsPC() && IsDead())
  1847.         return false;
  1848.  
  1849.     if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
  1850.         return false;
  1851.    
  1852. #ifdef ENABLE_AFTERDEATH_SHIELD
  1853.     if(IsAfterdeathShieldProtected())
  1854.         return false;
  1855. #endif
  1856.  
  1857.     // Patch for Bouquet Sync Damage Exploit
  1858.     if (pAttacker && pAttacker->IsPC() && pAttacker->GetWear(WEAR_WEAPON) && pAttacker->GetWear(WEAR_WEAPON)->GetSubType() >= WEAPON_NUM_TYPES)
  1859.     {
  1860.         SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
  1861.         return false;
  1862.     }
  1863.     // end
  1864.    
  1865.     if (pAttacker && pAttacker->IsPC() && quest::CQuestManager::instance().GetPCForce(pAttacker->GetPlayerID())->IsRunning())
  1866.     {
  1867.         SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
  1868.         return false;
  1869.     }
  1870.  
  1871. #ifdef ENABLE_NEWSTUFF
  1872.     if (pAttacker && IsStone() && pAttacker->IsPC())
  1873.     {
  1874.         if (GetEmpire() && GetEmpire() == pAttacker->GetEmpire())
  1875.         {
  1876.             SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
  1877.             return false;
  1878.         }
  1879.     }
  1880. #endif
  1881.     if (DAMAGE_TYPE_MAGIC == type)
  1882.     {
  1883.         dam = (int)((float)dam * (100 + (pAttacker->GetPoint(POINT_MAGIC_ATT_BONUS_PER) + pAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100.f + 0.5f);
  1884.     }
  1885.     if (GetRaceNum() == 5001)
  1886.     {
  1887.         bool bDropMoney = false;
  1888.  
  1889.         int iPercent = 0; // @fixme136
  1890.         if (GetMaxHP() >= 0)
  1891.             iPercent = (GetHP() * 100) / GetMaxHP();
  1892.  
  1893.         if (iPercent <= 10 && GetMaxSP() < 5)
  1894.         {
  1895.             SetMaxSP(5);
  1896.             bDropMoney = true;
  1897.         }
  1898.         else if (iPercent <= 20 && GetMaxSP() < 4)
  1899.         {
  1900.             SetMaxSP(4);
  1901.             bDropMoney = true;
  1902.         }
  1903.         else if (iPercent <= 40 && GetMaxSP() < 3)
  1904.         {
  1905.             SetMaxSP(3);
  1906.             bDropMoney = true;
  1907.         }
  1908.         else if (iPercent <= 60 && GetMaxSP() < 2)
  1909.         {
  1910.             SetMaxSP(2);
  1911.             bDropMoney = true;
  1912.         }
  1913.         else if (iPercent <= 80 && GetMaxSP() < 1)
  1914.         {
  1915.             SetMaxSP(1);
  1916.             bDropMoney = true;
  1917.         }
  1918.  
  1919.         if (bDropMoney)
  1920.         {
  1921.             DWORD dwGold = 1000;
  1922.             int iSplitCount = number(10, 13);
  1923.  
  1924.             sys_log(0, "WAEGU DropGoldOnHit %d times", GetMaxSP());
  1925.  
  1926.             for (int i = 1; i <= iSplitCount; ++i)
  1927.             {
  1928.                 PIXEL_POSITION pos;
  1929.                 LPITEM item;
  1930.  
  1931.                 if ((item = ITEM_MANAGER::instance().CreateItem(1, dwGold / iSplitCount)))
  1932.                 {
  1933.                     if (i != 0)
  1934.                     {
  1935.                         pos.x = (number(-14, 14) + number(-14, 14)) * 20;
  1936.                         pos.y = (number(-14, 14) + number(-14, 14)) * 20;
  1937.  
  1938.                         pos.x += GetX();
  1939.                         pos.y += GetY();
  1940.                     }
  1941.  
  1942.                     item->AddToGround(GetMapIndex(), pos);
  1943.                     item->StartDestroyEvent();
  1944.                 }
  1945.             }
  1946.         }
  1947.     }
  1948.  
  1949.     if (type != DAMAGE_TYPE_NORMAL && type != DAMAGE_TYPE_NORMAL_RANGE)
  1950.     {
  1951.         if (IsAffectFlag(AFF_TERROR))
  1952.         {
  1953.             int pct = GetSkillPower(SKILL_TERROR) / 400;
  1954.  
  1955.             if (number(1, 100) <= pct)
  1956.                 return false;
  1957.         }
  1958.     }
  1959.  
  1960.     int iCurHP = GetHP();
  1961.     int iCurSP = GetSP();
  1962.  
  1963.     bool IsCritical = false;
  1964.     bool IsPenetrate = false;
  1965.     bool IsDeathBlow = false;
  1966.  
  1967.     //PROF_UNIT puAttr("Attr");
  1968.  
  1969.     if (type == DAMAGE_TYPE_MELEE || type == DAMAGE_TYPE_RANGE || type == DAMAGE_TYPE_MAGIC)
  1970.     {
  1971.         if (pAttacker)
  1972.         {
  1973.            
  1974.             int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT);
  1975.  
  1976.             if (!IsPC())
  1977.                 iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS);
  1978.  
  1979.             if (iCriticalPct)
  1980.             {
  1981.                 if (iCriticalPct >= 10)
  1982.                     iCriticalPct = 5 + (iCriticalPct - 10) / 4;
  1983.                 else
  1984.                     iCriticalPct /= 2;
  1985.  
  1986.                
  1987.                 iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL);
  1988. #ifdef ENABLE_PVP_TALISMAN
  1989.                 if ((number(1, 100) <= iCriticalPct) && (!this->HasPvPTalisman()))
  1990. #else
  1991.                 if (number(1, 100) <= iCriticalPct)
  1992. #endif
  1993.                 {
  1994.                     IsCritical = true;
  1995.                     dam *= 2;
  1996.                     EffectPacket(SE_CRITICAL);
  1997.  
  1998.                     if (IsAffectFlag(AFF_MANASHIELD))
  1999.                     {
  2000.                         RemoveAffect(AFF_MANASHIELD);
  2001.                     }
  2002.                 }
  2003.             }
  2004.  
  2005.             int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT);
  2006.  
  2007.             if (!IsPC())
  2008.                 iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS);
  2009.  
  2010.             if (iPenetratePct)
  2011.             {
  2012.                 {
  2013.                     CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE);
  2014.  
  2015.                     if (NULL != pkSk)
  2016.                     {
  2017.                         pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f);
  2018.  
  2019.                         iPenetratePct -= static_cast<int>(pkSk->kPointPoly.Eval());
  2020.                     }
  2021.                 }
  2022.  
  2023.                 if (iPenetratePct >= 10)
  2024.                 {
  2025.                    
  2026.                     iPenetratePct = 5 + (iPenetratePct - 10) / 4;
  2027.                 }
  2028.                 else
  2029.                 {
  2030.                    
  2031.                     iPenetratePct /= 2;
  2032.                 }
  2033.  
  2034.                 iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE);
  2035. #ifdef ENABLE_PVP_TALISMAN
  2036.                 if ((number(1, 100) <= iPenetratePct) && (!this->HasPvPTalisman()))
  2037. #else
  2038.                 if (number(1, 100) <= iPenetratePct)
  2039. #endif
  2040.                 {
  2041.                     IsPenetrate = true;
  2042.  
  2043.                     if (test_server)
  2044.                         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("관통 추가 데미지 %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100);
  2045.  
  2046.                     dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100;
  2047.  
  2048.                     if (IsAffectFlag(AFF_MANASHIELD))
  2049.                     {
  2050.                         RemoveAffect(AFF_MANASHIELD);
  2051.                     }
  2052. #ifdef ENABLE_EFFECT_PENETRATE
  2053.                     EffectPacket(SE_PENETRATE);
  2054. #endif
  2055.                 }
  2056.             }
  2057.         }
  2058.     }
  2059.     //
  2060.     else if (type == DAMAGE_TYPE_NORMAL || type == DAMAGE_TYPE_NORMAL_RANGE)
  2061.     {
  2062.         if (type == DAMAGE_TYPE_NORMAL)
  2063.         {
  2064.            
  2065.             if (GetPoint(POINT_BLOCK) && number(1, 100) <= GetPoint(POINT_BLOCK))
  2066.             {
  2067.                 if (test_server)
  2068.                 {
  2069.                     pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 블럭! (%d%%)"), GetName(), GetPoint(POINT_BLOCK));
  2070.                     ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 블럭! (%d%%)"), GetName(), GetPoint(POINT_BLOCK));
  2071.                 }
  2072.  
  2073.                 SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
  2074.                 return false;
  2075.             }
  2076.         }
  2077.         else if (type == DAMAGE_TYPE_NORMAL_RANGE)
  2078.         {
  2079.            
  2080.             if (GetPoint(POINT_DODGE) && number(1, 100) <= GetPoint(POINT_DODGE))
  2081.             {
  2082.                 if (test_server)
  2083.                 {
  2084.                     pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 회피! (%d%%)"), GetName(), GetPoint(POINT_DODGE));
  2085.                     ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 회피! (%d%%)"), GetName(), GetPoint(POINT_DODGE));
  2086.                 }
  2087.  
  2088.                 SendDamagePacket(pAttacker, 0, DAMAGE_DODGE);
  2089.                 return false;
  2090.             }
  2091.         }
  2092.  
  2093.         if (IsAffectFlag(AFF_JEONGWIHON))
  2094.             dam = (int) (dam * (100 + GetSkillPower(SKILL_JEONGWI) * 25 / 100) / 100);
  2095.  
  2096.         if (IsAffectFlag(AFF_TERROR))
  2097.             dam = (int) (dam * (95 - GetSkillPower(SKILL_TERROR) / 5) / 100);
  2098.  
  2099.         if (IsAffectFlag(AFF_HOSIN))
  2100.             dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100;
  2101.  
  2102.         if (pAttacker)
  2103.         {
  2104.             if (type == DAMAGE_TYPE_NORMAL)
  2105.             {
  2106.                 if (GetPoint(POINT_REFLECT_MELEE))
  2107.                 {
  2108.                     int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100;
  2109.  
  2110.                     if (pAttacker->IsImmune(IMMUNE_REFLECT))
  2111.                         reflectDamage = int(reflectDamage / 3.0f + 0.5f);
  2112.  
  2113.                     pAttacker->Damage(this, reflectDamage, DAMAGE_TYPE_SPECIAL);
  2114.                 }
  2115.                 if (IsMonster() && IsReflecter())
  2116.                 {
  2117.                     pAttacker->Damage(this, (dam * 5 / 100), DAMAGE_TYPE_SPECIAL);
  2118.                 }
  2119.             }
  2120.  
  2121.             int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT);
  2122.  
  2123.             if (!IsPC())
  2124.                 iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS);
  2125.  
  2126.             if (iCriticalPct)
  2127.             {
  2128.                
  2129.                 iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL);
  2130. #ifdef ENABLE_PVP_TALISMAN
  2131.                 if ((number(1, 100) <= iCriticalPct) && (!this->HasPvPTalisman()))
  2132. #else
  2133.                 if (number(1, 100) <= iCriticalPct)
  2134. #endif
  2135.                 {
  2136.                     IsCritical = true;
  2137.                     dam *= 2;
  2138.                     EffectPacket(SE_CRITICAL);
  2139.                 }
  2140.             }
  2141.  
  2142.             int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT);
  2143.  
  2144.             if (!IsPC())
  2145.                 iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS);
  2146.  
  2147.             {
  2148.                 CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE);
  2149.  
  2150.                 if (NULL != pkSk)
  2151.                 {
  2152.                     pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f);
  2153.  
  2154.                     iPenetratePct -= static_cast<int>(pkSk->kPointPoly.Eval());
  2155.                 }
  2156.             }
  2157.  
  2158.             if (iPenetratePct)
  2159.             {
  2160.                 iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE);
  2161. #ifdef ENABLE_PVP_TALISMAN
  2162.                 if ((number(1, 100) <= iPenetratePct) && (!this->HasPvPTalisman()))
  2163. #else
  2164.                 if (number(1, 100) <= iPenetratePct)
  2165. #endif
  2166.                 {
  2167.                     IsPenetrate = true;
  2168.  
  2169.                     if (test_server)
  2170.                         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("관통 추가 데미지 %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100);
  2171.                     dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100;
  2172. #ifdef ENABLE_EFFECT_PENETRATE
  2173.                     EffectPacket(SE_PENETRATE);
  2174. #endif
  2175.                 }
  2176.             }
  2177.  
  2178.             if (pAttacker->GetPoint(POINT_STEAL_HP))
  2179.             {
  2180.                 int pct = 1;
  2181.  
  2182.                 if (number(1, 10) <= pct)
  2183.                 {
  2184.                     int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100;
  2185.  
  2186.                     if (iHP > 0 && GetHP() >= iHP)
  2187.                     {
  2188.                         CreateFly(FLY_HP_SMALL, pAttacker);
  2189.                         pAttacker->PointChange(POINT_HP, iHP);
  2190.                         PointChange(POINT_HP, -iHP);
  2191.                     }
  2192.                 }
  2193.             }
  2194.  
  2195.             if (pAttacker->GetPoint(POINT_STEAL_SP))
  2196.             {
  2197.                 int pct = 1;
  2198.  
  2199.                 if (number(1, 10) <= pct)
  2200.                 {
  2201.                     int iCur;
  2202.  
  2203.                     if (IsPC())
  2204.                         iCur = iCurSP;
  2205.                     else
  2206.                         iCur = iCurHP;
  2207.  
  2208.                     int iSP = MIN(dam, MAX(0, iCur)) * pAttacker->GetPoint(POINT_STEAL_SP) / 100;
  2209.  
  2210.                     if (iSP > 0 && iCur >= iSP)
  2211.                     {
  2212.                         CreateFly(FLY_SP_SMALL, pAttacker);
  2213.                         pAttacker->PointChange(POINT_SP, iSP);
  2214.  
  2215.                         if (IsPC())
  2216.                             PointChange(POINT_SP, -iSP);
  2217.                     }
  2218.                 }
  2219.             }
  2220.  
  2221.             if (pAttacker->GetPoint(POINT_STEAL_GOLD))
  2222.             {
  2223.                 if (number(1, 100) <= pAttacker->GetPoint(POINT_STEAL_GOLD))
  2224.                 {
  2225.                     int iAmount = number(1, GetLevel());
  2226.                     pAttacker->PointChange(POINT_GOLD, iAmount);
  2227.                     DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 1, iAmount);
  2228.                 }
  2229.             }
  2230.  
  2231.             if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0)
  2232.             {
  2233.                 int i = ((iCurHP>=0)?MIN(dam, iCurHP):dam) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; //@fixme107
  2234.  
  2235.                 if (i)
  2236.                 {
  2237.                     CreateFly(FLY_HP_SMALL, pAttacker);
  2238.                     pAttacker->PointChange(POINT_HP, i);
  2239.                 }
  2240.             }
  2241.  
  2242.             if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0)
  2243.             {
  2244.                 int i = ((iCurHP>=0)?MIN(dam, iCurHP):dam) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; //@fixme107
  2245.  
  2246.                 if (i)
  2247.                 {
  2248.                     CreateFly(FLY_SP_SMALL, pAttacker);
  2249.                     pAttacker->PointChange(POINT_SP, i);
  2250.                 }
  2251.             }
  2252.  
  2253.            
  2254.             if (pAttacker->GetPoint(POINT_MANA_BURN_PCT))
  2255.             {
  2256.                 if (number(1, 100) <= pAttacker->GetPoint(POINT_MANA_BURN_PCT))
  2257.                     PointChange(POINT_SP, -50);
  2258.             }
  2259.         }
  2260.     }
  2261.  
  2262.     switch (type)
  2263.     {
  2264.         case DAMAGE_TYPE_NORMAL:
  2265.         case DAMAGE_TYPE_NORMAL_RANGE:
  2266.         {
  2267.             if (pAttacker)
  2268.             {
  2269.                 if (pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS))
  2270.                     dam = dam * (100 + pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) / 100;
  2271.  
  2272. #if defined(__SOUL_SYSTEM__)
  2273.                 if (pAttacker->FindAffect(AFFECT_SOUL, AFF_SOUL_RED))
  2274.                     dam = dam * (100 + pAttacker->GetSoulDamage(RED_SOUL)) / 100;
  2275. #endif
  2276.             }
  2277.  
  2278. #ifdef BONUS_PCT
  2279.             if (GetHPPct() >= 90)
  2280.                 dam = dam * (100 - (MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) - (MIN(99, GetPoint(POINT_RESIST_COMBAT)))) / 100;
  2281.             else
  2282.                 dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100;
  2283. #else
  2284.             dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100;
  2285. #endif
  2286.         }
  2287.         break;
  2288.  
  2289.         case DAMAGE_TYPE_MELEE:
  2290.         case DAMAGE_TYPE_RANGE:
  2291.         case DAMAGE_TYPE_FIRE:
  2292.         case DAMAGE_TYPE_ICE:
  2293.         case DAMAGE_TYPE_ELEC:
  2294.         case DAMAGE_TYPE_MAGIC:
  2295.         //case DAMAGE_TYPE_EARTH:
  2296.         {
  2297.             if (pAttacker)
  2298.             {
  2299.                 if (pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS))
  2300.                     dam = dam * (100 + pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) / 100;
  2301.  
  2302. #if defined(__SOUL_SYSTEM__)
  2303.                 if (pAttacker->FindAffect(AFFECT_SOUL, AFF_SOUL_BLUE))
  2304.                     dam = dam * (100 + pAttacker->GetSoulDamage(BLUE_SOUL)) / 100;
  2305. #endif
  2306.             }
  2307.  
  2308. #ifdef BONUS_PCT
  2309.             if (GetHPPct() >= 90)
  2310.                 dam = dam * (100 - (MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) - (MIN(99, GetPoint(POINT_RESIST_COMBAT)))) / 100;
  2311.             else
  2312.                 dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100;
  2313.            
  2314. #else      
  2315.             dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100;
  2316. #endif
  2317.         }
  2318.         break;
  2319.  
  2320.         default:
  2321.             break;
  2322.     }
  2323.  
  2324.     //
  2325.    
  2326.     //
  2327.     if (IsAffectFlag(AFF_MANASHIELD))
  2328.     {
  2329.        
  2330.         int iDamageSPPart = dam / 3;
  2331.         int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100;
  2332.         int iSP = GetSP();
  2333.  
  2334.        
  2335.         if (iDamageToSP <= iSP)
  2336.         {
  2337.             PointChange(POINT_SP, -iDamageToSP);
  2338.             dam -= iDamageSPPart;
  2339.         }
  2340.         else
  2341.         {
  2342.            
  2343.             PointChange(POINT_SP, -GetSP());
  2344.             dam -= iSP * 100 / MAX(GetPoint(POINT_MANASHIELD), 1);
  2345.         }
  2346.     }
  2347.  
  2348.     if (GetPoint(POINT_MALL_DEFBONUS) > 0)
  2349.     {
  2350.         int dec_dam = MIN(200, dam * GetPoint(POINT_MALL_DEFBONUS) / 100);
  2351.         dam -= dec_dam;
  2352.     }
  2353.  
  2354.     if (pAttacker)
  2355.     {
  2356.         if (pAttacker->GetPoint(POINT_MALL_ATTBONUS) > 0)
  2357.         {
  2358.             int add_dam = MIN(300, dam * pAttacker->GetLimitPoint(POINT_MALL_ATTBONUS) / 100);
  2359.             dam += add_dam;
  2360.         }
  2361.  
  2362.         if (pAttacker->IsPC())
  2363.         {
  2364.             /*
  2365.             int iEmpire = pAttacker->GetEmpire();
  2366.             long lMapIndex = pAttacker->GetMapIndex();
  2367.             int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex);
  2368.  
  2369.             // 10% reduce damage between Kingdoms
  2370.             if (iEmpire && iMapEmpire && iEmpire != iMapEmpire)
  2371.             {
  2372.                 dam = dam * 9 / 10;
  2373.             }
  2374.             */
  2375.  
  2376.             if (!IsPC() && GetMonsterDrainSPPoint())
  2377.             {
  2378.                 int iDrain = GetMonsterDrainSPPoint();
  2379.  
  2380.                 if (iDrain <= pAttacker->GetSP())
  2381.                     pAttacker->PointChange(POINT_SP, -iDrain);
  2382.                 else
  2383.                 {
  2384.                     int iSP = pAttacker->GetSP();
  2385.                     pAttacker->PointChange(POINT_SP, -iSP);
  2386.                 }
  2387.             }
  2388.  
  2389.         }
  2390.         else if (pAttacker->IsGuardNPC())
  2391.         {
  2392.             SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD);
  2393.             Stun();
  2394.             return true;
  2395.         }
  2396.  
  2397.         if (pAttacker->IsPC() && CMonarch::instance().IsPowerUp(pAttacker->GetEmpire()))
  2398.         {
  2399.            
  2400.             dam += dam / 10;
  2401.         }
  2402.  
  2403.         if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire()))
  2404.         {
  2405.            
  2406.             dam -= dam / 10;
  2407.         }
  2408.     }
  2409.     //puAttr.Pop();
  2410.  
  2411.     if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
  2412.         return false;
  2413.  
  2414.     if (!IsPC())
  2415.     {
  2416.         if (m_pkParty && m_pkParty->GetLeader())
  2417.             m_pkParty->GetLeader()->SetLastAttacked(get_dword_time());
  2418.         else
  2419.             SetLastAttacked(get_dword_time());
  2420.  
  2421.        
  2422.         MonsterChat(MONSTER_CHAT_ATTACKED);
  2423.     }
  2424.  
  2425. #ifdef GUILD_WAR_COUNTER
  2426.     if (type == DAMAGE_TYPE_MELEE
  2427.         || type == DAMAGE_TYPE_RANGE
  2428.         || type == DAMAGE_TYPE_FIRE
  2429.         || type == DAMAGE_TYPE_ICE
  2430.         || type == DAMAGE_TYPE_ELEC
  2431.         || type == DAMAGE_TYPE_MAGIC)
  2432.     {
  2433.         if (CWarMapManager::instance().IsWarMap(GetMapIndex()))
  2434.         {
  2435.             CWarMap* pMap = CWarMapManager::instance().Find(GetMapIndex());
  2436.             if (pMap)
  2437.             {
  2438.                 CGuild* attackerGuild = pAttacker->GetGuild();
  2439.                 CGuild* victimGuild = GetGuild();
  2440.                 if (attackerGuild && victimGuild)
  2441.                     if (attackerGuild->UnderWar(victimGuild->GetID()))
  2442.                         pMap->SendKillNotice(pAttacker, this, dam);
  2443.             }
  2444.         }
  2445.     }
  2446. #endif
  2447.  
  2448.     if (IsStun())
  2449.     {
  2450.         Dead(pAttacker);
  2451.         return true;
  2452.     }
  2453.  
  2454.     if (IsDead())
  2455.         return true;
  2456.  
  2457.    
  2458.     if (type == DAMAGE_TYPE_POISON)
  2459.     {
  2460.         if (GetHP() - dam <= 0)
  2461.         {
  2462.             dam = GetHP() - 1;
  2463.         }
  2464.     }
  2465. #ifdef ENABLE_WOLFMAN_CHARACTER
  2466.     else if (type == DAMAGE_TYPE_BLEEDING)
  2467.     {
  2468.         if (GetHP() - dam <= 0)
  2469.         {
  2470.             dam = GetHP();
  2471.         }
  2472.     }
  2473. #endif
  2474.     // ------------------------
  2475.    
  2476.     // -----------------------
  2477.     if (pAttacker && pAttacker->IsPC())
  2478.     {
  2479.         int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker);
  2480.         dam = dam * iDmgPct / 100;
  2481.     }
  2482.  
  2483.    
  2484.     if (IsMonster() && IsStoneSkinner())
  2485.     {
  2486.         if (GetHPPct() < GetMobTable().bStoneSkinPoint)
  2487.             dam /= 2;
  2488.     }
  2489.  
  2490. #if defined(__SHIP_DEFENSE__)
  2491.     CShipDefenseManager& rkShipDefenseMgr = CShipDefenseManager::Instance();
  2492.     if (rkShipDefenseMgr.IsDungeon(this->GetMapIndex()) && rkShipDefenseMgr.IsMast(this->GetRaceNum()))
  2493.     {
  2494.         const LPSECTREE_MAP c_lpSectreeMap = SECTREE_MANAGER::instance().GetMap(this->GetMapIndex());
  2495.         if (c_lpSectreeMap != nullptr)
  2496.             CShipDefenseManager::Instance().BroadcastAllianceHP(this, c_lpSectreeMap);
  2497.     }
  2498. #endif
  2499.  
  2500.     //PROF_UNIT puRest1("Rest1");
  2501.     if (pAttacker)
  2502.     {
  2503.        
  2504.         if (pAttacker->IsMonster() && pAttacker->IsDeathBlower())
  2505.         {
  2506.             if (pAttacker->IsDeathBlow())
  2507.             {
  2508.                 if (number(JOB_WARRIOR, JOB_MAX_NUM-1) == GetJob()) // @fixme192 (1, 4)
  2509.                 {
  2510.                     IsDeathBlow = true;
  2511.                     dam = dam * 4;
  2512.                 }
  2513.             }
  2514.         }
  2515.  
  2516.         dam = BlueDragon_Damage(this, pAttacker, dam);
  2517.  
  2518.         BYTE damageFlag = 0;
  2519.  
  2520.         if (type == DAMAGE_TYPE_POISON)
  2521.             damageFlag = DAMAGE_POISON;
  2522. #if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON)
  2523.         else if (type == DAMAGE_TYPE_BLEEDING)
  2524.             damageFlag = DAMAGE_BLEEDING;
  2525. #elif defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_MOB_BLEEDING_AS_POISON)
  2526.         else if (type == DAMAGE_TYPE_BLEEDING)
  2527.             damageFlag = DAMAGE_POISON;
  2528. #endif
  2529.         else
  2530.             damageFlag = DAMAGE_NORMAL;
  2531.  
  2532.         if (IsCritical == true)
  2533.             damageFlag |= DAMAGE_CRITICAL;
  2534.  
  2535.         if (IsPenetrate == true)
  2536.             damageFlag |= DAMAGE_PENETRATE;
  2537.  
  2538.  
  2539.        
  2540.         float damMul = this->GetDamMul();
  2541.         float tempDam = dam;
  2542.         dam = tempDam * damMul + 0.5f;
  2543.  
  2544.         if (pAttacker && pAttacker->IsPC() && IsNPC() && IsReflect())
  2545.             pAttacker->SendDamagePacket(this, dam/100*5, damageFlag);
  2546.  
  2547.         if (pAttacker)
  2548.             SendDamagePacket(pAttacker, dam, damageFlag);
  2549.  
  2550.         if (test_server)
  2551.         {
  2552.             int iTmpPercent = 0; // @fixme136
  2553.             if (GetMaxHP() >= 0)
  2554.                 iTmpPercent = (GetHP() * 100) / GetMaxHP();
  2555.  
  2556.             if(pAttacker)
  2557.             {
  2558.                 pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s",
  2559.                         GetName(),
  2560.                         dam,
  2561.                         GetHP(),
  2562.                         iTmpPercent,
  2563.                         IsCritical ? "crit " : "",
  2564.                         IsPenetrate ? "pene " : "",
  2565.                         IsDeathBlow ? "deathblow " : "");
  2566.             }
  2567.  
  2568.             ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s",
  2569.                     pAttacker ? pAttacker->GetName() : 0,
  2570.                     dam,
  2571.                     GetHP(),
  2572.                     iTmpPercent,
  2573.                     IsCritical ? "crit " : "",
  2574.                     IsPenetrate ? "pene " : "",
  2575.                     IsDeathBlow ? "deathblow " : "");
  2576.         }
  2577.  
  2578.         if (m_bDetailLog)
  2579.         {
  2580.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s[%d]가 공격 위치: %d %d"), pAttacker->GetName(), (DWORD) pAttacker->GetVID(), pAttacker->GetX(), pAttacker->GetY());
  2581.         }
  2582.     }
  2583.  
  2584. #ifdef ENABLE_WAR_BOARD
  2585.     if (pAttacker)
  2586.     {
  2587.         CWarMap * pWarMap = pAttacker->GetWarMap();
  2588.        
  2589.         if (pWarMap && pWarMap == GetWarMap())
  2590.         {
  2591.             CWarMap::TMemberStats * pStats = pWarMap->GetMemberStats(pAttacker);
  2592.             pStats->ullDamage += (unsigned long long) dam;
  2593.             pWarMap->SendStats(pStats);
  2594.         }
  2595.     }
  2596. #endif
  2597.  
  2598.     if (!cannot_dead)
  2599.     {
  2600.         if (GetHP() - dam <= 0) // @fixme137
  2601.             dam = GetHP();
  2602.         PointChange(POINT_HP, -dam, false);
  2603.     }
  2604.  
  2605.     //puRest1.Pop();
  2606.  
  2607.     //PROF_UNIT puRest2("Rest2");
  2608.     if (pAttacker && dam > 0 && IsNPC())
  2609.     {
  2610.         //PROF_UNIT puRest20("Rest20");
  2611.         TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID());
  2612.  
  2613.         if (it == m_map_kDamage.end())
  2614.         {
  2615.             m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0)));
  2616.             it = m_map_kDamage.find(pAttacker->GetVID());
  2617.         }
  2618.         else
  2619.         {
  2620.             it->second.iTotalDamage += dam;
  2621.         }
  2622.         //puRest20.Pop();
  2623.  
  2624.         //PROF_UNIT puRest21("Rest21");
  2625.         StartRecoveryEvent();
  2626.         //puRest21.Pop();
  2627.  
  2628.         //PROF_UNIT puRest22("Rest22");
  2629.         UpdateAggrPointEx(pAttacker, type, dam, it->second);
  2630.         //puRest22.Pop();
  2631.     }
  2632.     //puRest2.Pop();
  2633.  
  2634.     //PROF_UNIT puRest3("Rest3");
  2635.     /*
  2636.     if (GetHP() <= 0)
  2637.     {
  2638.         Stun();
  2639.  
  2640.         if (pAttacker && !pAttacker->IsNPC())
  2641.             m_dwKillerPID = pAttacker->GetPlayerID();
  2642.         else
  2643.             m_dwKillerPID = 0;
  2644.     }
  2645.     */
  2646.     if (GetHP() <= 0)
  2647.     {
  2648.         if (pAttacker && !pAttacker->IsNPC())
  2649.             m_dwKillerPID = pAttacker->GetPlayerID();
  2650.         else
  2651.             m_dwKillerPID = 0;
  2652.  
  2653.         Stun(true);
  2654.     }
  2655.  
  2656.     return false;
  2657. }
  2658.  
  2659. void CHARACTER::DistributeHP(LPCHARACTER pkKiller)
  2660. {
  2661.     if (pkKiller->GetDungeon())
  2662.         return;
  2663. }
  2664. #define ENABLE_NEWEXP_CALCULATION
  2665. #ifdef ENABLE_NEWEXP_CALCULATION
  2666. #define NEW_GET_LVDELTA(me, victim) aiPercentByDeltaLev[MINMAX(0, (victim + 15) - me, MAX_EXP_DELTA_OF_LEV - 1)]
  2667. typedef long double rate_t;
  2668. static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp)
  2669. {
  2670.     if (test_server && iExp < 0)
  2671.     {
  2672.         to->ChatPacket(CHAT_TYPE_INFO, "exp(%d) overflow", iExp);
  2673.         return;
  2674.     }
  2675.     // decrease/increase exp based on player<>mob level
  2676.     rate_t lvFactor = static_cast<rate_t>(NEW_GET_LVDELTA(to->GetLevel(), from->GetLevel())) / 100.0L;
  2677.     iExp *= lvFactor;
  2678.     // start calculating rate exp bonus
  2679.     int iBaseExp = iExp;
  2680.     rate_t rateFactor = 100;
  2681.     if (distribution_test_server)
  2682.         rateFactor *= 3;
  2683.  
  2684.     rateFactor += CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT);
  2685.     if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL))
  2686.         rateFactor += 20;
  2687.     if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000)
  2688.         rateFactor += 20;
  2689.     if (to->GetPoint(POINT_EXP_DOUBLE_BONUS))
  2690.         if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS))
  2691.             rateFactor += 30;
  2692.     if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP))
  2693.         rateFactor += 50;
  2694.  
  2695.     switch (to->GetMountVnum())
  2696.     {
  2697.         case 20110:
  2698.         case 20111:
  2699.         case 20112:
  2700.         case 20113:
  2701.             if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) ||
  2702.                     to->IsEquipUniqueItem(71121) )
  2703.             {
  2704.                 rateFactor += 10;
  2705.             }
  2706.             break;
  2707.  
  2708.         case 20114:
  2709.         case 20120:
  2710.         case 20121:
  2711.         case 20122:
  2712.         case 20123:
  2713.         case 20124:
  2714.         case 20125:
  2715.             rateFactor += 30;
  2716.             break;
  2717.     }
  2718.  
  2719.     if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
  2720.         rateFactor += 50;
  2721.     if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP))
  2722.         rateFactor += 50;
  2723.     if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
  2724.     {
  2725.         if (to->IsPCBang())
  2726.             rateFactor += to->GetPoint(POINT_PC_BANG_EXP_BONUS);
  2727.     }
  2728.     rateFactor += to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS);
  2729.     rateFactor += to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP);
  2730.     rateFactor += to->GetPoint(POINT_MALL_EXPBONUS);
  2731.     rateFactor += to->GetPoint(POINT_EXP);
  2732.     // useless (never used except for china intoxication) = always 100
  2733.     rateFactor = rateFactor * static_cast<rate_t>(CHARACTER_MANAGER::instance().GetMobExpRate(to))/100.0L;
  2734.     // fix underflow formula
  2735.     iExp = std::max<int>(0, iExp);
  2736.     rateFactor = std::max<rate_t>(100.0L, rateFactor);
  2737.     // apply calculated rate bonus
  2738.     iExp *= (rateFactor/100.0L);
  2739.     // if (test_server)
  2740.         // to->ChatPacket(CHAT_TYPE_INFO, "base_exp(%d) * rate(%Lf) = exp(%d)", iBaseExp, rateFactor/100.0L, iExp);
  2741.     auto iOldExp = iExp;
  2742.     // you can get at maximum only 10% of the total required exp at once (so, you need to kill at least 10 mobs to level up) (useless)
  2743.     iExp = MIN(to->GetNextExp() / 10, iExp);
  2744.     // it recalculate the given exp if the player level is greater than the exp_table size (useless)
  2745.     iExp = AdjustExpByLevel(to, iExp);
  2746.     if (test_server)
  2747.         // to->ChatPacket(CHAT_TYPE_INFO, "exp+minGNE+adjust(%d)", iExp);
  2748.         to->ChatPacket(CHAT_TYPE_INFO, "base_exp(%d) * rate(%Lf) = exp(%d) => exp+minGNE+adjust(%d)", iBaseExp, rateFactor/100.0L, iOldExp, iExp);
  2749.  
  2750.     // set
  2751.     to->PointChange(POINT_EXP, iExp, true);
  2752.     from->CreateFly(FLY_EXP, to);
  2753.     // marriage
  2754.     {
  2755.         LPCHARACTER you = to->GetMarryPartner();
  2756.         if (you)
  2757.         {
  2758.             // sometimes, this overflows
  2759.             DWORD dwUpdatePoint = (2000.0L/to->GetLevel()/to->GetLevel()/3)*iExp;
  2760.  
  2761.             if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 ||
  2762.                     you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0)
  2763.                 dwUpdatePoint *= 3;
  2764.  
  2765.             marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(to->GetPlayerID());
  2766.  
  2767.             // DIVORCE_NULL_BUG_FIX
  2768.             if (pMarriage && pMarriage->IsNear())
  2769.                 pMarriage->Update(dwUpdatePoint);
  2770.             // END_OF_DIVORCE_NULL_BUG_FIX
  2771.         }
  2772.     }
  2773. }
  2774. #else
  2775. static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp)
  2776. {
  2777.    
  2778.     iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp);
  2779.  
  2780.    
  2781.     if (distribution_test_server)
  2782.         iExp *= 3;
  2783.  
  2784.     int iBaseExp = iExp;
  2785.  
  2786.    
  2787.     iExp = iExp * (100 + CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT)) / 100;
  2788.  
  2789.    
  2790.     {
  2791.        
  2792.         if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL))
  2793.             iExp += iExp * 20 /100;
  2794.  
  2795.        
  2796.         if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000)
  2797.             iExp += iExp * 20 / 100; // 1.2배 (20%)
  2798.  
  2799.        
  2800.         if (to->GetPoint(POINT_EXP_DOUBLE_BONUS))
  2801.             if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS))
  2802.                 iExp += iExp * 30 / 100; // 1.3배 (30%)
  2803.  
  2804.        
  2805.         if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP))
  2806.             iExp += iExp * 50 / 100;
  2807.  
  2808.         switch (to->GetMountVnum())
  2809.         {
  2810.             case 20110:
  2811.             case 20111:
  2812.             case 20112:
  2813.             case 20113:
  2814.                 if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) ||
  2815.                         to->IsEquipUniqueItem(71121) )
  2816.                 {
  2817.                     iExp += iExp * 10 / 100;
  2818.                 }
  2819.                 break;
  2820.  
  2821.             case 20114:
  2822.             case 20120:
  2823.             case 20121:
  2824.             case 20122:
  2825.             case 20123:
  2826.             case 20124:
  2827.             case 20125:
  2828.                
  2829.                 iExp += iExp * 30 / 100;
  2830.                 break;
  2831.         }
  2832.     }
  2833.  
  2834.    
  2835.     {
  2836.        
  2837.         if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
  2838.         {
  2839.             iExp += (iExp * 50 / 100);
  2840.         }
  2841.  
  2842.         if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
  2843.         {
  2844.             iExp += (iExp * 50 / 100);
  2845.         }
  2846.  
  2847.        
  2848.         if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
  2849.         {
  2850.             if (to->IsPCBang() == true)
  2851.                 iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100);
  2852.         }
  2853.  
  2854.        
  2855.         iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100;
  2856.     }
  2857.  
  2858.     iExp += (iExp * to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP)/100);
  2859.     iExp += (iExp * to->GetPoint(POINT_MALL_EXPBONUS)/100);
  2860.     iExp += (iExp * to->GetPoint(POINT_EXP)/100);
  2861.  
  2862.     if (test_server)
  2863.     {
  2864.         sys_log(0, "Bonus Exp : Ramadan Candy: %d MallExp: %d PointExp: %d",
  2865.                 to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP),
  2866.                 to->GetPoint(POINT_MALL_EXPBONUS),
  2867.                 to->GetPoint(POINT_EXP)
  2868.                );
  2869.     }
  2870.  
  2871.    
  2872.     iExp = iExp * CHARACTER_MANAGER::instance().GetMobExpRate(to) / 100;
  2873.  
  2874.    
  2875.     iExp = MIN(to->GetNextExp() / 10, iExp);
  2876.  
  2877.     if (test_server)
  2878.     {
  2879.         if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && iBaseExp>0)
  2880.             to->ChatPacket(CHAT_TYPE_INFO, "exp bonus %d%%", (iExp-iBaseExp)*100/iBaseExp);
  2881.         to->ChatPacket(CHAT_TYPE_INFO, "exp(%d) base_exp(%d)", iExp, iBaseExp);
  2882.     }
  2883.  
  2884.     iExp = AdjustExpByLevel(to, iExp);
  2885.  
  2886.     to->PointChange(POINT_EXP, iExp, true);
  2887.     from->CreateFly(FLY_EXP, to);
  2888.  
  2889.     {
  2890.         LPCHARACTER you = to->GetMarryPartner();
  2891.        
  2892.         if (you)
  2893.         {
  2894.             // 1억이 100%
  2895.             DWORD dwUpdatePoint = 2000*iExp/to->GetLevel()/to->GetLevel()/3;
  2896.  
  2897.             if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 ||
  2898.                     you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0)
  2899.                 dwUpdatePoint = (DWORD)(dwUpdatePoint * 3);
  2900.  
  2901.             marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(to->GetPlayerID());
  2902.  
  2903.             // DIVORCE_NULL_BUG_FIX
  2904.             if (pMarriage && pMarriage->IsNear())
  2905.                 pMarriage->Update(dwUpdatePoint);
  2906.             // END_OF_DIVORCE_NULL_BUG_FIX
  2907.         }
  2908.     }
  2909. }
  2910. #endif
  2911.  
  2912. namespace NPartyExpDistribute
  2913. {
  2914.     struct FPartyTotaler
  2915.     {
  2916.         int     total;
  2917.         int     member_count;
  2918.         int     x, y;
  2919.  
  2920.         FPartyTotaler(LPCHARACTER center)
  2921.             : total(0), member_count(0), x(center->GetX()), y(center->GetY())
  2922.         {};
  2923.  
  2924.         void operator () (LPCHARACTER ch)
  2925.         {
  2926.             if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
  2927.             {
  2928.                 total += __GetPartyExpNP(ch->GetLevel());
  2929.  
  2930.                 ++member_count;
  2931.             }
  2932.         }
  2933.     };
  2934.  
  2935.     struct FPartyDistributor
  2936.     {
  2937.         int     total;
  2938.         LPCHARACTER c;
  2939.         int     x, y;
  2940.         DWORD       _iExp;
  2941.         int     m_iMode;
  2942.         int     m_iMemberCount;
  2943.  
  2944.         FPartyDistributor(LPCHARACTER center, int member_count, int total, DWORD iExp, int iMode)
  2945.             : total(total), c(center), x(center->GetX()), y(center->GetY()), _iExp(iExp), m_iMode(iMode), m_iMemberCount(member_count)
  2946.             {
  2947.                 if (m_iMemberCount == 0)
  2948.                     m_iMemberCount = 1;
  2949.             };
  2950.  
  2951.         void operator () (LPCHARACTER ch)
  2952.         {
  2953.             if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE)
  2954.             {
  2955.                 DWORD iExp2 = 0;
  2956.  
  2957.                 switch (m_iMode)
  2958.                 {
  2959.                     case PARTY_EXP_DISTRIBUTION_NON_PARITY:
  2960.                         iExp2 = (DWORD) (_iExp * (float) __GetPartyExpNP(ch->GetLevel()) / total);
  2961.                         break;
  2962.  
  2963.                     case PARTY_EXP_DISTRIBUTION_PARITY:
  2964.                         iExp2 = _iExp / m_iMemberCount;
  2965.                         break;
  2966.  
  2967.                     default:
  2968.                         sys_err("Unknown party exp distribution mode %d", m_iMode);
  2969.                         return;
  2970.                 }
  2971.  
  2972.                 GiveExp(c, ch, iExp2);
  2973.             }
  2974.         }
  2975.     };
  2976. }
  2977.  
  2978. typedef struct SDamageInfo
  2979. {
  2980.     int iDam;
  2981.     LPCHARACTER pAttacker;
  2982.     LPPARTY pParty;
  2983.  
  2984.     void Clear()
  2985.     {
  2986.         pAttacker = NULL;
  2987.         pParty = NULL;
  2988.     }
  2989.  
  2990.     inline void Distribute(LPCHARACTER ch, int iExp)
  2991.     {
  2992.         if (pAttacker)
  2993.             GiveExp(ch, pAttacker, iExp);
  2994.         else if (pParty)
  2995.         {
  2996.             NPartyExpDistribute::FPartyTotaler f(ch);
  2997.             pParty->ForEachOnlineMember(f);
  2998.  
  2999.             if (pParty->IsPositionNearLeader(ch))
  3000.                 iExp = iExp * (100 + pParty->GetExpBonusPercent()) / 100;
  3001.  
  3002.             if (test_server)
  3003.             {
  3004.                 if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && pParty->GetExpBonusPercent())
  3005.                     pParty->ChatPacketToAllMember(CHAT_TYPE_INFO, "exp party bonus %d%%", pParty->GetExpBonusPercent());
  3006.             }
  3007.  
  3008.            
  3009.             if (pParty->GetExpCentralizeCharacter())
  3010.             {
  3011.                 LPCHARACTER tch = pParty->GetExpCentralizeCharacter();
  3012.  
  3013.                 if (DISTANCE_APPROX(ch->GetX() - tch->GetX(), ch->GetY() - tch->GetY()) <= PARTY_DEFAULT_RANGE)
  3014.                 {
  3015.                     int iExpCenteralize = (int) (iExp * 0.05f);
  3016.                     iExp -= iExpCenteralize;
  3017.  
  3018.                     GiveExp(ch, pParty->GetExpCentralizeCharacter(), iExpCenteralize);
  3019.                 }
  3020.             }
  3021.  
  3022.             NPartyExpDistribute::FPartyDistributor fDist(ch, f.member_count, f.total, iExp, pParty->GetExpDistributionMode());
  3023.             pParty->ForEachOnlineMember(fDist);
  3024.         }
  3025.     }
  3026. } TDamageInfo;
  3027.  
  3028. #ifdef ENABLE_KILL_EVENT_FIX
  3029. LPCHARACTER CHARACTER::GetMostAttacked() {
  3030.     int iMostDam=-1;
  3031.     LPCHARACTER pkChrMostAttacked = NULL;
  3032.     auto it = m_map_kDamage.begin();
  3033.  
  3034.     while (it != m_map_kDamage.end()){
  3035.         //* getting information from the iterator
  3036.         const VID & c_VID = it->first;
  3037.         const int iDam    = it->second.iTotalDamage;
  3038.  
  3039.         //* increasing the iterator
  3040.         ++it;
  3041.  
  3042.         //* finding the character from his vid
  3043.         LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);
  3044.  
  3045.         //* if the attacked is now offline
  3046.         if (!pAttacker)
  3047.             continue;
  3048.  
  3049.         //* if the attacker is not a player
  3050.         if( pAttacker->IsNPC())
  3051.             continue;
  3052.  
  3053.         //* if the player is too far
  3054.         if(DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000)
  3055.             continue;
  3056.  
  3057.         if (iDam > iMostDam){
  3058.             pkChrMostAttacked = pAttacker;
  3059.             iMostDam = iDam;
  3060.         }
  3061.     }
  3062.  
  3063.     return pkChrMostAttacked;
  3064. }
  3065. #endif
  3066.  
  3067. LPCHARACTER CHARACTER::DistributeExp()
  3068. {
  3069.     int iExpToDistribute = GetExp();
  3070.  
  3071.     if (iExpToDistribute <= 0)
  3072.         return NULL;
  3073.  
  3074.     int iTotalDam = 0;
  3075.     LPCHARACTER pkChrMostAttacked = NULL;
  3076.     int iMostDam = 0;
  3077.  
  3078.     typedef std::vector<TDamageInfo> TDamageInfoTable;
  3079.     TDamageInfoTable damage_info_table;
  3080.     std::map<LPPARTY, TDamageInfo> map_party_damage;
  3081.  
  3082.     damage_info_table.reserve(m_map_kDamage.size());
  3083.  
  3084.     TDamageMap::iterator it = m_map_kDamage.begin();
  3085.  
  3086.    
  3087.     while (it != m_map_kDamage.end())
  3088.     {
  3089.         const VID & c_VID = it->first;
  3090.         int iDam = it->second.iTotalDamage;
  3091.  
  3092.         ++it;
  3093.  
  3094.         LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);
  3095.  
  3096.        
  3097.         if (!pAttacker || pAttacker->IsNPC() || DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000)
  3098.             continue;
  3099.  
  3100.         iTotalDam += iDam;
  3101.         if (!pkChrMostAttacked || iDam > iMostDam)
  3102.         {
  3103.             pkChrMostAttacked = pAttacker;
  3104.             iMostDam = iDam;
  3105.         }
  3106.  
  3107.         if (pAttacker->GetParty())
  3108.         {
  3109.             std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.find(pAttacker->GetParty());
  3110.             if (it == map_party_damage.end())
  3111.             {
  3112.                 TDamageInfo di;
  3113.                 di.iDam = iDam;
  3114.                 di.pAttacker = NULL;
  3115.                 di.pParty = pAttacker->GetParty();
  3116.                 map_party_damage.insert(std::make_pair(di.pParty, di));
  3117.             }
  3118.             else
  3119.             {
  3120.                 it->second.iDam += iDam;
  3121.             }
  3122.         }
  3123.         else
  3124.         {
  3125.             TDamageInfo di;
  3126.  
  3127.             di.iDam = iDam;
  3128.             di.pAttacker = pAttacker;
  3129.             di.pParty = NULL;
  3130.  
  3131.             //sys_log(0, "__ pq_damage %s %d", pAttacker->GetName(), iDam);
  3132.             //pq_damage.push(di);
  3133.             damage_info_table.push_back(di);
  3134.         }
  3135.     }
  3136.  
  3137.     for (std::map<LPPARTY, TDamageInfo>::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it)
  3138.     {
  3139.         damage_info_table.push_back(it->second);
  3140.         //sys_log(0, "__ pq_damage_party [%u] %d", it->second.pParty->GetLeaderPID(), it->second.iDam);
  3141.     }
  3142.  
  3143.     SetExp(0);
  3144.     //m_map_kDamage.clear();
  3145.  
  3146.     if (iTotalDam == 0)
  3147.         return NULL;
  3148.  
  3149.     if (m_pkChrStone)  
  3150.     {
  3151.         //sys_log(0, "__ Give half to Stone : %d", iExpToDistribute>>1);
  3152.         int iExp = iExpToDistribute >> 1;
  3153.         m_pkChrStone->SetExp(m_pkChrStone->GetExp() + iExp);
  3154.         iExpToDistribute -= iExp;
  3155.     }
  3156.  
  3157.     sys_log(1, "%s total exp: %d, damage_info_table.size() == %d, TotalDam %d",
  3158.             GetName(), iExpToDistribute, damage_info_table.size(), iTotalDam);
  3159.     //sys_log(1, "%s total exp: %d, pq_damage.size() == %d, TotalDam %d",
  3160.     //GetName(), iExpToDistribute, pq_damage.size(), iTotalDam);
  3161.  
  3162.     if (damage_info_table.empty())
  3163.         return NULL;
  3164.  
  3165.    
  3166.     DistributeHP(pkChrMostAttacked);   
  3167.  
  3168.     {
  3169.        
  3170.         TDamageInfoTable::iterator di = damage_info_table.begin();
  3171.         {
  3172.             TDamageInfoTable::iterator it;
  3173.  
  3174.             for (it = damage_info_table.begin(); it != damage_info_table.end();++it)
  3175.             {
  3176.                 if (it->iDam > di->iDam)
  3177.                     di = it;
  3178.             }
  3179.         }
  3180.  
  3181.         int iExp = iExpToDistribute / 5;
  3182.         iExpToDistribute -= iExp;
  3183.  
  3184.         float fPercent = (float) di->iDam / iTotalDam;
  3185.  
  3186.         if (fPercent > 1.0f)
  3187.         {
  3188.             sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di->pAttacker->GetName());
  3189.             fPercent = 1.0f;
  3190.         }
  3191.  
  3192.         iExp += (int) (iExpToDistribute * fPercent);
  3193.  
  3194.         //sys_log(0, "%s given exp percent %.1f + 20 dam %d", GetName(), fPercent * 100.0f, di.iDam);
  3195.  
  3196.         di->Distribute(this, iExp);
  3197.  
  3198.        
  3199.         if (fPercent == 1.0f)
  3200.             return pkChrMostAttacked;
  3201.  
  3202.         di->Clear();
  3203.     }
  3204.  
  3205.     {
  3206.        
  3207.         TDamageInfoTable::iterator it;
  3208.  
  3209.         for (it = damage_info_table.begin(); it != damage_info_table.end(); ++it)
  3210.         {
  3211.             TDamageInfo & di = *it;
  3212.  
  3213.             float fPercent = (float) di.iDam / iTotalDam;
  3214.  
  3215.             if (fPercent > 1.0f)
  3216.             {
  3217.                 sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di.pAttacker->GetName());
  3218.                 fPercent = 1.0f;
  3219.             }
  3220.  
  3221.             //sys_log(0, "%s given exp percent %.1f dam %d", GetName(), fPercent * 100.0f, di.iDam);
  3222.             di.Distribute(this, (int) (iExpToDistribute * fPercent));
  3223.         }
  3224.     }
  3225.  
  3226.     return pkChrMostAttacked;
  3227. }
  3228.  
  3229.  
  3230. int CHARACTER::GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount/* = 1 */)
  3231. {
  3232.     LPITEM pkBow;
  3233.  
  3234.     if (!(pkBow = GetWear(WEAR_WEAPON)) || pkBow->GetProto()->bSubType != WEAPON_BOW)
  3235.     {
  3236.         return 0;
  3237.     }
  3238.  
  3239.     LPITEM pkArrow;
  3240.  
  3241.     if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON ||
  3242.             pkArrow->GetProto()->bSubType != WEAPON_ARROW)
  3243.     {
  3244.         return 0;
  3245.     }
  3246.  
  3247.     iArrowCount = MIN(iArrowCount, pkArrow->GetCount());
  3248.  
  3249.     *ppkBow = pkBow;
  3250.     *ppkArrow = pkArrow;
  3251.  
  3252.     return iArrowCount;
  3253. }
  3254.  
  3255. void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount)
  3256. {
  3257.     if (pkArrow->GetVnum() >= 79501 && pkArrow->GetVnum() <= 79504)
  3258.     {
  3259.         return;
  3260.     }
  3261.            
  3262.     int iCount = pkArrow->GetCount();
  3263.     DWORD dwVnum = pkArrow->GetVnum();
  3264.     iCount = iCount - MIN(iCount, dwArrowCount);
  3265.     pkArrow->SetCount(iCount);
  3266.  
  3267.     if (iCount == 0)
  3268.     {
  3269.         LPITEM pkNewArrow = FindSpecifyItem(dwVnum);
  3270.  
  3271.         sys_log(0, "UseArrow : FindSpecifyItem %u %p", dwVnum, get_pointer(pkNewArrow));
  3272.  
  3273.         if (pkNewArrow)
  3274.             EquipItem(pkNewArrow);
  3275.     }
  3276. }
  3277.  
  3278. class CFuncShoot
  3279. {
  3280.     public:
  3281.         LPCHARACTER m_me;
  3282.         BYTE        m_bType;
  3283.         bool        m_bSucceed;
  3284.  
  3285.         CFuncShoot(LPCHARACTER ch, BYTE bType) : m_me(ch), m_bType(bType), m_bSucceed(FALSE)
  3286.         {
  3287.         }
  3288.  
  3289.         void operator () (DWORD dwTargetVID)
  3290.         {
  3291.             if (m_bType > 1)
  3292.             {
  3293.                 if (g_bSkillDisable)
  3294.                     return;
  3295.  
  3296.                 m_me->m_SkillUseInfo[m_bType].SetMainTargetVID(dwTargetVID);
  3297.                 /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK)
  3298.                   m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/
  3299.             }
  3300.  
  3301.             LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);
  3302.  
  3303.             if (!pkVictim)
  3304.                 return;
  3305.  
  3306.            
  3307.             if (!battle_is_attackable(m_me, pkVictim))
  3308.                 return;
  3309.  
  3310.             if (m_me->IsNPC())
  3311.             {
  3312.                 if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000)
  3313.                     return;
  3314.             }
  3315.  
  3316. #ifdef ENABLE_AFTERDEATH_SHIELD
  3317.             if(m_me->IsAfterdeathShieldProtected())
  3318.                 m_me->RemoveAfterdeathShield();
  3319. #endif
  3320.  
  3321.             #ifdef ENABLE_SKILL_COOLDOWN_CHECK
  3322.             if (m_me->IsPC() && m_bType > 0 && m_me->SkillIsOnCooldown(m_bType))
  3323.                 return;
  3324.             #endif
  3325.  
  3326.             LPITEM pkBow, pkArrow;
  3327.  
  3328.             switch (m_bType)
  3329.             {
  3330.                 case 0:
  3331.                     {
  3332.                         int iDam = 0;
  3333.  
  3334.                         if (m_me->IsPC())
  3335.                         {
  3336.                             if (m_me->GetJob() != JOB_ASSASSIN)
  3337.                                 return;
  3338.  
  3339.                             if (0 == m_me->GetArrowAndBow(&pkBow, &pkArrow))
  3340.                                 return;
  3341.  
  3342.                             if (m_me->GetSkillGroup() != 0)
  3343.                                 if (!m_me->IsNPC() && m_me->GetSkillGroup() != 2)
  3344.                                 {
  3345.                                     if (m_me->GetSP() < 5)
  3346.                                         return;
  3347.  
  3348.                                     m_me->PointChange(POINT_SP, -5);
  3349.                                 }
  3350.  
  3351.                             iDam = CalcArrowDamage(m_me, pkVictim, pkBow, pkArrow);
  3352.                             m_me->UseArrow(pkArrow, 1);
  3353.  
  3354.                             // check speed hack
  3355.                             DWORD   dwCurrentTime   = get_dword_time();
  3356.                             if (IS_SPEED_HACK(m_me, pkVictim, dwCurrentTime))
  3357.                                 iDam    = 0;
  3358.                         }
  3359.                         else
  3360.                             iDam = CalcMeleeDamage(m_me, pkVictim);
  3361.  
  3362.                         NormalAttackAffect(m_me, pkVictim);
  3363.  
  3364.                        
  3365.                         iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100;
  3366.  
  3367.                         //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam);
  3368.  
  3369.                         m_me->OnMove(true);
  3370.                         pkVictim->OnMove();
  3371.  
  3372.                         if (pkVictim->CanBeginFight())
  3373.                             pkVictim->BeginFight(m_me);
  3374.  
  3375.                         pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_NORMAL_RANGE);
  3376.                        
  3377.                     }
  3378.                     break;
  3379.  
  3380.                 case 1:
  3381.                     {
  3382.                         int iDam;
  3383.  
  3384.                         if (m_me->IsPC())
  3385.                             return;
  3386.  
  3387.                         iDam = CalcMagicDamage(m_me, pkVictim);
  3388.  
  3389.                         NormalAttackAffect(m_me, pkVictim);
  3390.  
  3391.                        
  3392. #ifdef ENABLE_MAGIC_REDUCTION_SYSTEM
  3393.                         const int resist_magic = MINMAX(0, pkVictim->GetPoint(POINT_RESIST_MAGIC), 100);
  3394.                         const int resist_magic_reduction = MINMAX(0, (m_me->GetJob()==JOB_SURA) ? m_me->GetPoint(POINT_RESIST_MAGIC_REDUCTION)/2 : m_me->GetPoint(POINT_RESIST_MAGIC_REDUCTION), 50);
  3395.                         const int total_res_magic = MINMAX(0, resist_magic - resist_magic_reduction, 100);
  3396.                         iDam = iDam * (100 - total_res_magic) / 100;
  3397. #else
  3398.                         iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100;
  3399. #endif
  3400.  
  3401.                         //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam);
  3402.  
  3403.                         m_me->OnMove(true);
  3404.                         pkVictim->OnMove();
  3405.  
  3406.                         if (pkVictim->CanBeginFight())
  3407.                             pkVictim->BeginFight(m_me);
  3408.  
  3409.                         pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_MAGIC);
  3410.                        
  3411.                     }
  3412.                     break;
  3413.  
  3414.                 case SKILL_YEONSA: 
  3415.                     {
  3416.                         //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100);
  3417.                         int iUseArrow = 1;
  3418.  
  3419.                        
  3420.                         {
  3421.                             if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
  3422.                             {
  3423.                                 m_me->OnMove(true);
  3424.                                 pkVictim->OnMove();
  3425.  
  3426.                                 if (pkVictim->CanBeginFight())
  3427.                                     pkVictim->BeginFight(m_me);
  3428.  
  3429.                                 m_me->ComputeSkill(m_bType, pkVictim);
  3430.                                 m_me->UseArrow(pkArrow, iUseArrow);
  3431.  
  3432.                                 if (pkVictim->IsDead())
  3433.                                     break;
  3434.  
  3435.                             }
  3436.                             else
  3437.                                 break;
  3438.                         }
  3439.                     }
  3440.                     break;
  3441.  
  3442.  
  3443.                 case SKILL_KWANKYEOK:
  3444.                     {
  3445.                         int iUseArrow = 1;
  3446.  
  3447.                         if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
  3448.                         {
  3449.                             m_me->OnMove(true);
  3450.                             pkVictim->OnMove();
  3451.  
  3452.                             if (pkVictim->CanBeginFight())
  3453.                                 pkVictim->BeginFight(m_me);
  3454.  
  3455.                             sys_log(0, "%s kwankeyok %s", m_me->GetName(), pkVictim->GetName());
  3456.                             m_me->ComputeSkill(m_bType, pkVictim);
  3457.                             m_me->UseArrow(pkArrow, iUseArrow);
  3458.                         }
  3459.                     }
  3460.                     break;
  3461.  
  3462.                 case SKILL_GIGUNG:
  3463.                     {
  3464.                         int iUseArrow = 1;
  3465.                         if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
  3466.                         {
  3467.                             m_me->OnMove(true);
  3468.                             pkVictim->OnMove();
  3469.  
  3470.                             if (pkVictim->CanBeginFight())
  3471.                                 pkVictim->BeginFight(m_me);
  3472.  
  3473.                             sys_log(0, "%s gigung %s", m_me->GetName(), pkVictim->GetName());
  3474.                             m_me->ComputeSkill(m_bType, pkVictim);
  3475.                             m_me->UseArrow(pkArrow, iUseArrow);
  3476.                         }
  3477.                     }
  3478.  
  3479.                     break;
  3480.                 case SKILL_HWAJO:
  3481.                     {
  3482.                         int iUseArrow = 1;
  3483.                         if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
  3484.                         {
  3485.                             m_me->OnMove(true);
  3486.                             pkVictim->OnMove();
  3487.  
  3488.                             if (pkVictim->CanBeginFight())
  3489.                                 pkVictim->BeginFight(m_me);
  3490.  
  3491.                             sys_log(0, "%s hwajo %s", m_me->GetName(), pkVictim->GetName());
  3492.                             m_me->ComputeSkill(m_bType, pkVictim);
  3493.                             m_me->UseArrow(pkArrow, iUseArrow);
  3494.                         }
  3495.                     }
  3496.  
  3497.                     break;
  3498.  
  3499.                 case SKILL_HORSE_WILDATTACK_RANGE:
  3500.                     {
  3501.                         int iUseArrow = 1;
  3502.                         if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow))
  3503.                         {
  3504.                             m_me->OnMove(true);
  3505.                             pkVictim->OnMove();
  3506.  
  3507.                             if (pkVictim->CanBeginFight())
  3508.                                 pkVictim->BeginFight(m_me);
  3509.  
  3510.                             sys_log(0, "%s horse_wildattack %s", m_me->GetName(), pkVictim->GetName());
  3511.                             m_me->ComputeSkill(m_bType, pkVictim);
  3512.                             m_me->UseArrow(pkArrow, iUseArrow);
  3513.                         }
  3514.                     }
  3515.  
  3516.                     break;
  3517.  
  3518.                 case SKILL_MARYUNG:
  3519.                 case SKILL_TUSOK:
  3520.                 case SKILL_BIPABU:
  3521.                 case SKILL_NOEJEON:
  3522.                 case SKILL_GEOMPUNG:
  3523.                 case SKILL_SANGONG:
  3524.                 case SKILL_MAHWAN:
  3525.                 case SKILL_PABEOB:
  3526.                     {
  3527.                         m_me->OnMove(true);
  3528.                         pkVictim->OnMove();
  3529.  
  3530.                         if (pkVictim->CanBeginFight())
  3531.                             pkVictim->BeginFight(m_me);
  3532.  
  3533.                         sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
  3534.                         m_me->ComputeSkill(m_bType, pkVictim);
  3535.                     }
  3536.                     break;
  3537.  
  3538.                 case SKILL_CHAIN:
  3539.                     {
  3540.                         m_me->OnMove(true);
  3541.                         pkVictim->OnMove();
  3542.  
  3543.                         if (pkVictim->CanBeginFight())
  3544.                             pkVictim->BeginFight(m_me);
  3545.  
  3546.                         sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
  3547.                         m_me->ComputeSkill(m_bType, pkVictim);
  3548.  
  3549.                        
  3550.                     }
  3551.                     break;
  3552.  
  3553.                 case SKILL_YONGBI:
  3554.                     {
  3555.                         m_me->OnMove(true);
  3556.                     }
  3557.                     break;
  3558.  
  3559.                 case SKILL_BYEURAK:
  3560.                     {
  3561.                         m_me->OnMove(true);
  3562.                         pkVictim->OnMove();
  3563.  
  3564.                         if (pkVictim->CanBeginFight())
  3565.                             pkVictim->BeginFight(m_me);
  3566.  
  3567.                         sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName());
  3568.                         m_me->ComputeSkill(m_bType, pkVictim);
  3569.                     }
  3570.                     break;
  3571.  
  3572.                     /*case SKILL_BUDONG:
  3573.                       {
  3574.                       m_me->OnMove(true);
  3575.                       pkVictim->OnMove();
  3576.  
  3577.                       DWORD * pdw;
  3578.                       DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw);
  3579.                       pdw[0] = m_me->GetVID();
  3580.                       pdw[1] = pkVictim->GetVID();
  3581.  
  3582.                       event_create(budong_event_func, dwEI, PASSES_PER_SEC(1));
  3583.                       }
  3584.                       break;*/
  3585.  
  3586.                 default:
  3587.                     sys_err("CFuncShoot: I don't know this type [%d] of range attack.", (int) m_bType);
  3588.                     break;
  3589.             }
  3590.  
  3591.             m_bSucceed = TRUE;
  3592.         }
  3593. };
  3594.  
  3595. bool CHARACTER::Shoot(BYTE bType)
  3596. {
  3597.     sys_log(1, "Shoot %s type %u flyTargets.size %zu", GetName(), bType, m_vec_dwFlyTargets.size());
  3598.  
  3599.     if (!CanMove())
  3600.     {
  3601.         return false;
  3602.     }
  3603.  
  3604.     CFuncShoot f(this, bType);
  3605.  
  3606.     if (m_dwFlyTargetID != 0)
  3607.     {
  3608.         f(m_dwFlyTargetID);
  3609.         m_dwFlyTargetID = 0;
  3610.     }
  3611.  
  3612.     f = std::for_each(m_vec_dwFlyTargets.begin(), m_vec_dwFlyTargets.end(), f);
  3613.     m_vec_dwFlyTargets.clear();
  3614.  
  3615.     return f.m_bSucceed;
  3616. }
  3617.  
  3618. void CHARACTER::FlyTarget(DWORD dwTargetVID, long x, long y, BYTE bHeader)
  3619. {
  3620.     LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);
  3621.     TPacketGCFlyTargeting pack;
  3622.  
  3623.     //pack.bHeader  = HEADER_GC_FLY_TARGETING;
  3624.     pack.bHeader    = (bHeader == HEADER_CG_FLY_TARGETING) ? HEADER_GC_FLY_TARGETING : HEADER_GC_ADD_FLY_TARGETING;
  3625.     pack.dwShooterVID   = GetVID();
  3626.  
  3627.     if (pkVictim)
  3628.     {
  3629.         pack.dwTargetVID = pkVictim->GetVID();
  3630.         pack.x = pkVictim->GetX();
  3631.         pack.y = pkVictim->GetY();
  3632.  
  3633.         if (bHeader == HEADER_CG_FLY_TARGETING)
  3634.             m_dwFlyTargetID = dwTargetVID;
  3635.         else
  3636.             m_vec_dwFlyTargets.push_back(dwTargetVID);
  3637.     }
  3638.     else
  3639.     {
  3640.         pack.dwTargetVID = 0;
  3641.         pack.x = x;
  3642.         pack.y = y;
  3643.     }
  3644.  
  3645.     sys_log(1, "FlyTarget %s vid %d x %d y %d", GetName(), pack.dwTargetVID, pack.x, pack.y);
  3646.     PacketAround(&pack, sizeof(pack), this);
  3647. }
  3648.  
  3649. LPCHARACTER CHARACTER::GetNearestVictim(LPCHARACTER pkChr)
  3650. {
  3651.     if (NULL == pkChr)
  3652.         pkChr = this;
  3653.  
  3654.     float fMinDist = 99999.0f;
  3655.     LPCHARACTER pkVictim = NULL;
  3656.  
  3657.     TDamageMap::iterator it = m_map_kDamage.begin();
  3658.  
  3659.    
  3660.     while (it != m_map_kDamage.end())
  3661.     {
  3662.         const VID & c_VID = it->first;
  3663.         ++it;
  3664.  
  3665.         LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);
  3666.  
  3667.         if (!pAttacker)
  3668.             continue;
  3669.  
  3670.         if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) ||
  3671.                 pAttacker->IsAffectFlag(AFF_INVISIBILITY) ||
  3672.                 pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE))
  3673.             continue;
  3674.  
  3675.         float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY());
  3676.  
  3677.         if (fDist < fMinDist)
  3678.         {
  3679.             pkVictim = pAttacker;
  3680.             fMinDist = fDist;
  3681.         }
  3682.     }
  3683.  
  3684.     return pkVictim;
  3685. }
  3686.  
  3687. void CHARACTER::SetVictim(LPCHARACTER pkVictim)
  3688. {
  3689.     if (!pkVictim)
  3690.     {
  3691.         if (0 != (DWORD)m_kVIDVictim)
  3692.             MonsterLog("공격 대상을 해제");
  3693.  
  3694.         m_kVIDVictim.Reset();
  3695.         battle_end(this);
  3696.     }
  3697.     else
  3698.     {
  3699.         if (m_kVIDVictim != pkVictim->GetVID())
  3700.             MonsterLog("공격 대상을 설정: %s", pkVictim->GetName());
  3701.  
  3702.         m_kVIDVictim = pkVictim->GetVID();
  3703.         m_dwLastVictimSetTime = get_dword_time();
  3704.     }
  3705. }
  3706.  
  3707. LPCHARACTER CHARACTER::GetVictim() const
  3708. {
  3709.     return CHARACTER_MANAGER::instance().Find(m_kVIDVictim);
  3710. }
  3711.  
  3712. LPCHARACTER CHARACTER::GetProtege() const
  3713. {
  3714.     if (m_pkChrStone)
  3715.         return m_pkChrStone;
  3716.  
  3717.     if (m_pkParty)
  3718.         return m_pkParty->GetLeader();
  3719.  
  3720.     return NULL;
  3721. }
  3722.  
  3723. #ifdef ENABLE_TITLE_SYSTEM
  3724. int CHARACTER::GetTitle() const
  3725. {
  3726.     return m_iPrestige;
  3727. }
  3728.  
  3729. int CHARACTER::GetRealTitle() const
  3730. {
  3731.     return m_iRealPrestige;
  3732. }
  3733.  
  3734. void CHARACTER::ShowTitle(bool bShow)
  3735. {
  3736.     if (bShow)
  3737.     {
  3738.         if (m_iPrestige != m_iRealPrestige)
  3739.         {
  3740.             m_iPrestige = m_iRealPrestige;
  3741.             UpdatePacket();
  3742.         }
  3743.     }
  3744.     else
  3745.     {
  3746.         if (m_iPrestige != 0)
  3747.         {
  3748.             m_iPrestige = 0;
  3749.             UpdatePacket();
  3750.         }
  3751.     }
  3752. }
  3753.  
  3754. void CHARACTER::UpdateTitle(int iAmount)
  3755. {
  3756.     bool bShow = false;
  3757.  
  3758.     if (m_iPrestige == m_iRealPrestige)
  3759.         bShow = true;
  3760.  
  3761.     int i = m_iPrestige;
  3762.     m_iRealPrestige = MINMAX(0, m_iRealPrestige + iAmount, 19);
  3763.     if (bShow)
  3764.     {
  3765.         m_iPrestige = m_iRealPrestige;
  3766.         if (i != m_iPrestige)
  3767.             UpdatePacket();
  3768.     }
  3769. }
  3770. #endif
  3771.  
  3772. int CHARACTER::GetAlignment() const
  3773. {
  3774.     return m_iAlignment;
  3775. }
  3776.  
  3777. int CHARACTER::GetRealAlignment() const
  3778. {
  3779.     return m_iRealAlignment;
  3780. }
  3781.  
  3782. void CHARACTER::ShowAlignment(bool bShow)
  3783. {
  3784.     if (bShow)
  3785.     {
  3786.         if (m_iAlignment != m_iRealAlignment)
  3787.         {
  3788.             m_iAlignment = m_iRealAlignment;
  3789.             UpdatePacket();
  3790.         }
  3791.     }
  3792.     else
  3793.     {
  3794.         if (m_iAlignment != 0)
  3795.         {
  3796.             m_iAlignment = 0;
  3797.             UpdatePacket();
  3798.         }
  3799.     }
  3800. }
  3801.  
  3802. void CHARACTER::UpdateAlignment(int iAmount)
  3803. {
  3804.     bool bShow = false;
  3805.  
  3806.     if (m_iAlignment == m_iRealAlignment)
  3807.         bShow = true;
  3808.  
  3809.     int i = m_iAlignment / 10;
  3810.  
  3811.     m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000);
  3812.  
  3813.     if (bShow)
  3814.     {
  3815.         m_iAlignment = m_iRealAlignment;
  3816.  
  3817.         if (i != m_iAlignment / 10)
  3818.             UpdatePacket();
  3819.     }
  3820. }
  3821.  
  3822. void CHARACTER::SetKillerMode(bool isOn)
  3823. {
  3824.     if ((isOn ? ADD_CHARACTER_STATE_KILLER : 0) == IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER))
  3825.         return;
  3826.  
  3827.     if (isOn)
  3828.         SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
  3829.     else
  3830.         REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
  3831.  
  3832.     m_iKillerModePulse = thecore_pulse();
  3833.     UpdatePacket();
  3834.     sys_log(0, "SetKillerMode Update %s[%d]", GetName(), GetPlayerID());
  3835. }
  3836.  
  3837. bool CHARACTER::IsKillerMode() const
  3838. {
  3839.     return IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER);
  3840. }
  3841.  
  3842. void CHARACTER::UpdateKillerMode()
  3843. {
  3844.     if (!IsKillerMode())
  3845.         return;
  3846.  
  3847.     if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(30))
  3848.         SetKillerMode(false);
  3849. }
  3850.  
  3851. void CHARACTER::SetPKMode(BYTE bPKMode)
  3852. {
  3853.     if (bPKMode >= PK_MODE_MAX_NUM)
  3854.         return;
  3855.  
  3856.     if (m_bPKMode == bPKMode)
  3857.         return;
  3858.  
  3859.     if (bPKMode == PK_MODE_GUILD && !GetGuild())
  3860.         bPKMode = PK_MODE_FREE;
  3861.  
  3862.     m_bPKMode = bPKMode;
  3863.     UpdatePacket();
  3864.  
  3865.     sys_log(0, "PK_MODE: %s %d", GetName(), m_bPKMode);
  3866. }
  3867.  
  3868. BYTE CHARACTER::GetPKMode() const
  3869. {
  3870.     return m_bPKMode;
  3871. }
  3872.  
  3873. struct FuncForgetMyAttacker
  3874. {
  3875.     LPCHARACTER m_ch;
  3876.     FuncForgetMyAttacker(LPCHARACTER ch)
  3877.     {
  3878.         m_ch = ch;
  3879.     }
  3880.     void operator()(LPENTITY ent)
  3881.     {
  3882.         if (ent->IsType(ENTITY_CHARACTER))
  3883.         {
  3884.             LPCHARACTER ch = (LPCHARACTER) ent;
  3885.             if (ch->IsPC())
  3886.                 return;
  3887.             if (ch->m_kVIDVictim == m_ch->GetVID())
  3888.                 ch->SetVictim(NULL);
  3889.         }
  3890.     }
  3891. };
  3892.  
  3893. struct FuncAggregateMonster
  3894. {
  3895.     LPCHARACTER m_ch;
  3896.     FuncAggregateMonster(LPCHARACTER ch)
  3897.     {
  3898.         m_ch = ch;
  3899.     }
  3900.     void operator()(LPENTITY ent)
  3901.     {
  3902.         if (ent->IsType(ENTITY_CHARACTER))
  3903.         {
  3904.             LPCHARACTER ch = (LPCHARACTER) ent;
  3905.             if (ch->IsPC())
  3906.                 return;
  3907.             if (!ch->IsMonster())
  3908.                 return;
  3909.             if (ch->GetVictim())
  3910.                 return;
  3911.  
  3912.             if (number(1, 100) <= 50)
  3913.                 if (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000)
  3914.                     if (ch->CanBeginFight())
  3915.                         ch->BeginFight(m_ch);
  3916.         }
  3917.     }
  3918. };
  3919.  
  3920. struct FuncAttractRanger
  3921. {
  3922.     LPCHARACTER m_ch;
  3923.     FuncAttractRanger(LPCHARACTER ch)
  3924.     {
  3925.         m_ch = ch;
  3926.     }
  3927.  
  3928.     void operator()(LPENTITY ent)
  3929.     {
  3930.         if (ent->IsType(ENTITY_CHARACTER))
  3931.         {
  3932.             LPCHARACTER ch = (LPCHARACTER) ent;
  3933.             if (ch->IsPC())
  3934.                 return;
  3935.             if (!ch->IsMonster())
  3936.                 return;
  3937.             if (ch->GetVictim() && ch->GetVictim() != m_ch)
  3938.                 return;
  3939.             if (ch->GetMobAttackRange() > 150)
  3940.             {
  3941.                 int iNewRange = 150;//(int)(ch->GetMobAttackRange() * 0.2);
  3942.                 if (iNewRange < 150)
  3943.                     iNewRange = 150;
  3944.  
  3945.                 ch->AddAffect(AFFECT_BOW_DISTANCE, POINT_BOW_DISTANCE, iNewRange - ch->GetMobAttackRange(), AFF_NONE, 3*60, 0, false);
  3946.             }
  3947.         }
  3948.     }
  3949. };
  3950.  
  3951. struct FuncPullMonster
  3952. {
  3953.     LPCHARACTER m_ch;
  3954.     int m_iLength;
  3955.     FuncPullMonster(LPCHARACTER ch, int iLength = 300)
  3956.     {
  3957.         m_ch = ch;
  3958.         m_iLength = iLength;
  3959.     }
  3960.  
  3961.     void operator()(LPENTITY ent)
  3962.     {
  3963.         if (ent->IsType(ENTITY_CHARACTER))
  3964.         {
  3965.             LPCHARACTER ch = (LPCHARACTER) ent;
  3966.             if (ch->IsPC())
  3967.                 return;
  3968.             if (!ch->IsMonster())
  3969.                 return;
  3970.             //if (ch->GetVictim() && ch->GetVictim() != m_ch)
  3971.             //return;
  3972.             float fDist = DISTANCE_APPROX(m_ch->GetX() - ch->GetX(), m_ch->GetY() - ch->GetY());
  3973.             if (fDist > 3000 || fDist < 100)
  3974.                 return;
  3975.  
  3976.             float fNewDist = fDist - m_iLength;
  3977.             if (fNewDist < 100)
  3978.                 fNewDist = 100;
  3979.  
  3980.             float degree = GetDegreeFromPositionXY(ch->GetX(), ch->GetY(), m_ch->GetX(), m_ch->GetY());
  3981.             float fx;
  3982.             float fy;
  3983.  
  3984.             GetDeltaByDegree(degree, fDist - fNewDist, &fx, &fy);
  3985.             long tx = (long)(ch->GetX() + fx);
  3986.             long ty = (long)(ch->GetY() + fy);
  3987.  
  3988.             ch->Sync(tx, ty);
  3989.             ch->Goto(tx, ty);
  3990.             ch->CalculateMoveDuration();
  3991.  
  3992.             ch->SyncPacket();
  3993.         }
  3994.     }
  3995. };
  3996.  
  3997. void CHARACTER::ForgetMyAttacker()
  3998. {
  3999.     LPSECTREE pSec = GetSectree();
  4000.     if (pSec)
  4001.     {
  4002.         FuncForgetMyAttacker f(this);
  4003.         pSec->ForEachAround(f);
  4004.     }
  4005.     ReviveInvisible(5);
  4006. }
  4007.  
  4008. void CHARACTER::AggregateMonster()
  4009. {
  4010.     LPSECTREE pSec = GetSectree();
  4011.     if (pSec)
  4012.     {
  4013.         FuncAggregateMonster f(this);
  4014.         pSec->ForEachAround(f);
  4015.     }
  4016. }
  4017.  
  4018. void CHARACTER::AttractRanger()
  4019. {
  4020.     LPSECTREE pSec = GetSectree();
  4021.     if (pSec)
  4022.     {
  4023.         FuncAttractRanger f(this);
  4024.         pSec->ForEachAround(f);
  4025.     }
  4026. }
  4027.  
  4028. void CHARACTER::PullMonster()
  4029. {
  4030.     LPSECTREE pSec = GetSectree();
  4031.     if (pSec)
  4032.     {
  4033.         FuncPullMonster f(this);
  4034.         pSec->ForEachAround(f);
  4035.     }
  4036. }
  4037.  
  4038. void CHARACTER::UpdateAggrPointEx(LPCHARACTER pAttacker, EDamageType type, int dam, CHARACTER::TBattleInfo & info)
  4039. {
  4040.    
  4041.     switch (type)
  4042.     {
  4043.         case DAMAGE_TYPE_NORMAL_RANGE:
  4044.             dam = (int) (dam*1.2f);
  4045.             break;
  4046.  
  4047.         case DAMAGE_TYPE_RANGE:
  4048.             dam = (int) (dam*1.5f);
  4049.             break;
  4050.  
  4051.         case DAMAGE_TYPE_MAGIC:
  4052.             dam = (int) (dam*1.2f);
  4053.             break;
  4054.  
  4055.         default:
  4056.             break;
  4057.     }
  4058.  
  4059.    
  4060.     if (pAttacker == GetVictim())
  4061.         dam = (int) (dam * 1.2f);
  4062.  
  4063.     info.iAggro += dam;
  4064.  
  4065.     if (info.iAggro < 0)
  4066.         info.iAggro = 0;
  4067.  
  4068.     //sys_log(0, "UpdateAggrPointEx for %s by %s dam %d total %d", GetName(), pAttacker->GetName(), dam, total);
  4069.     if (GetParty() && dam > 0 && type != DAMAGE_TYPE_SPECIAL)
  4070.     {
  4071.         LPPARTY pParty = GetParty();
  4072.  
  4073.        
  4074.         int iPartyAggroDist = dam;
  4075.  
  4076.         if (pParty->GetLeaderPID() == GetVID())
  4077.             iPartyAggroDist /= 2;
  4078.         else
  4079.             iPartyAggroDist /= 3;
  4080.  
  4081.         pParty->SendMessage(this, PM_AGGRO_INCREASE, iPartyAggroDist, pAttacker->GetVID());
  4082.     }
  4083.  
  4084.     ChangeVictimByAggro(info.iAggro, pAttacker);
  4085. }
  4086.  
  4087. void CHARACTER::UpdateAggrPoint(LPCHARACTER pAttacker, EDamageType type, int dam)
  4088. {
  4089.     if (IsDead() || IsStun())
  4090.         return;
  4091.  
  4092.     TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID());
  4093.  
  4094.     if (it == m_map_kDamage.end())
  4095.     {
  4096.         m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(0, dam)));
  4097.         it = m_map_kDamage.find(pAttacker->GetVID());
  4098.     }
  4099.  
  4100.     UpdateAggrPointEx(pAttacker, type, dam, it->second);
  4101. }
  4102.  
  4103. void CHARACTER::ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim)
  4104. {
  4105.     if (get_dword_time() - m_dwLastVictimSetTime < 3000)
  4106.         return;
  4107.  
  4108.     if (pNewVictim == GetVictim())
  4109.     {
  4110.         if (m_iMaxAggro < iNewAggro)
  4111.         {
  4112.             m_iMaxAggro = iNewAggro;
  4113.             return;
  4114.         }
  4115.  
  4116.        
  4117.         TDamageMap::iterator it;
  4118.         TDamageMap::iterator itFind = m_map_kDamage.end();
  4119.  
  4120.         for (it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it)
  4121.         {
  4122.             if (it->second.iAggro > iNewAggro)
  4123.             {
  4124.                 LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first);
  4125.  
  4126.                 if (ch && !ch->IsDead() && DISTANCE_APPROX(ch->GetX() - GetX(), ch->GetY() - GetY()) < 5000)
  4127.                 {
  4128.                     itFind = it;
  4129.                     iNewAggro = it->second.iAggro;
  4130.                 }
  4131.             }
  4132.         }
  4133.  
  4134.         if (itFind != m_map_kDamage.end())
  4135.         {
  4136.             m_iMaxAggro = iNewAggro;
  4137.             SetVictim(CHARACTER_MANAGER::instance().Find(itFind->first));
  4138.             m_dwStateDuration = 1;
  4139.         }
  4140.     }
  4141.     else
  4142.     {
  4143.         if (m_iMaxAggro < iNewAggro)
  4144.         {
  4145.             m_iMaxAggro = iNewAggro;
  4146.             SetVictim(pNewVictim);
  4147.             m_dwStateDuration = 1;
  4148.         }
  4149.     }
  4150. }
  4151.  
  4152.  
Advertisement
Add Comment
Please, Sign In to add comment