Advertisement
Hungary2

Untitled

May 9th, 2022
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 173.06 KB | None | 0 0
  1. #include "stdafx.h"
  2.  
  3. #include "../../common/teen_packet.h"
  4. #include "../../common/VnumHelper.h"
  5.  
  6. #include "char.h"
  7.  
  8. #include "config.h"
  9. #include "utils.h"
  10. #include "crc32.h"
  11. #include "char_manager.h"
  12. #include "desc_client.h"
  13. #include "desc_manager.h"
  14. #include "buffer_manager.h"
  15. #include "item_manager.h"
  16. #include "motion.h"
  17. #include "vector.h"
  18. #include "packet.h"
  19. #include "cmd.h"
  20. #include "fishing.h"
  21. #include "exchange.h"
  22. #include "battle.h"
  23. #include "affect.h"
  24. #include "shop.h"
  25. #include "shop_manager.h"
  26. #include "safebox.h"
  27. #include "regen.h"
  28. #include "pvp.h"
  29. #include "party.h"
  30. #include "start_position.h"
  31. #include "questmanager.h"
  32. #include "log.h"
  33. #include "p2p.h"
  34. #include "guild.h"
  35. #include "guild_manager.h"
  36. #include "dungeon.h"
  37. #include "messenger_manager.h"
  38. #include "unique_item.h"
  39. #include "priv_manager.h"
  40. #include "war_map.h"
  41. #include "xmas_event.h"
  42. #include "banword.h"
  43. #include "target.h"
  44. #include "wedding.h"
  45. #include "mob_manager.h"
  46. #include "mining.h"
  47. #include "monarch.h"
  48. #include "castle.h"
  49. #include "arena.h"
  50. #include "dev_log.h"
  51. #include "horsename_manager.h"
  52. #include "pcbang.h"
  53. #include "gm.h"
  54. #include "map_location.h"
  55. #include "BlueDragon_Binder.h"
  56. #include "HackShield.h"
  57. #include "skill_power.h"
  58. #include "XTrapManager.h"
  59. #include "buff_on_attributes.h"
  60.  
  61. #ifdef __PET_SYSTEM__
  62. #include "PetSystem.h"
  63. #endif
  64. #include "DragonSoul.h"
  65.  
  66. extern const BYTE g_aBuffOnAttrPoints;
  67. extern bool RaceToJob(unsigned race, unsigned *ret_job);
  68.  
  69. extern int g_nPortalLimitTime;
  70. extern int test_server;
  71.  
  72. extern bool IS_SUMMONABLE_ZONE(int map_index); // char_item.cpp
  73. bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index);
  74.  
  75. bool CAN_ENTER_ZONE(const LPCHARACTER& ch, int map_index)
  76. {
  77.     switch (map_index)
  78.     {
  79.     case 301:
  80.     case 302:
  81.     case 303:
  82.     case 304:
  83.         if (ch->GetLevel() < 90)
  84.             return false;
  85.     }
  86.     return true;
  87. }
  88.  
  89. // <Factor> DynamicCharacterPtr member function definitions
  90.  
  91. LPCHARACTER DynamicCharacterPtr::Get() const {
  92.     LPCHARACTER p = NULL;
  93.     if (is_pc) {
  94.         p = CHARACTER_MANAGER::instance().FindByPID(id);
  95.     } else {
  96.         p = CHARACTER_MANAGER::instance().Find(id);
  97.     }
  98.     return p;
  99. }
  100.  
  101. DynamicCharacterPtr& DynamicCharacterPtr::operator=(LPCHARACTER character) {
  102.     if (character == NULL) {
  103.         Reset();
  104.         return *this;
  105.     }
  106.     if (character->IsPC()) {
  107.         is_pc = true;
  108.         id = character->GetPlayerID();
  109.     } else {
  110.         is_pc = false;
  111.         id = character->GetVID();
  112.     }
  113.     return *this;
  114. }
  115.  
  116. CHARACTER::CHARACTER()
  117. {
  118.     m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty);
  119.     m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
  120.     m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty);
  121.  
  122.     Initialize();
  123. }
  124.  
  125. CHARACTER::~CHARACTER()
  126. {
  127.     Destroy();
  128. }
  129.  
  130. void CHARACTER::Initialize()
  131. {
  132.     CEntity::Initialize(ENTITY_CHARACTER);
  133.  
  134.     m_bNoOpenedShop = true;
  135.  
  136.     m_bOpeningSafebox = false;
  137.  
  138.     m_fSyncTime = get_float_time()-3;
  139.     m_dwPlayerID = 0;
  140.     m_dwKillerPID = 0;
  141.  
  142.     m_iMoveCount = 0;
  143.  
  144.     m_pkRegen = NULL;
  145.     regen_id_ = 0;
  146.     m_posRegen.x = m_posRegen.y = m_posRegen.z = 0;
  147.     m_posStart.x = m_posStart.y = 0;
  148.     m_posDest.x = m_posDest.y = 0;
  149.     m_fRegenAngle = 0.0f;
  150.  
  151.     m_pkMobData     = NULL;
  152.     m_pkMobInst     = NULL;
  153.  
  154.     m_pkShop        = NULL;
  155.     m_pkChrShopOwner    = NULL;
  156.     m_pkMyShop      = NULL;
  157.     m_pkExchange    = NULL;
  158.     m_pkParty       = NULL;
  159.     m_pkPartyRequestEvent = NULL;
  160.  
  161.     m_pGuild = NULL;
  162.  
  163.     m_pkChrTarget = NULL;
  164.  
  165.     m_pkMuyeongEvent = NULL;
  166.  
  167.     m_pkWarpNPCEvent = NULL;
  168.     m_pkDeadEvent = NULL;
  169.     m_pkStunEvent = NULL;
  170.     m_pkSaveEvent = NULL;
  171.     m_pkRecoveryEvent = NULL;
  172.     m_pkTimedEvent = NULL;
  173.     m_pkFishingEvent = NULL;
  174.     m_pkWarpEvent = NULL;
  175.  
  176.     // MINING
  177.     m_pkMiningEvent = NULL;
  178.     // END_OF_MINING
  179.  
  180.     m_pkPoisonEvent = NULL;
  181.     m_pkFireEvent = NULL;
  182.     m_pkCheckSpeedHackEvent = NULL;
  183.     m_speed_hack_count  = 0;
  184.  
  185.     m_pkAffectEvent = NULL;
  186.     m_afAffectFlag = TAffectFlag(0, 0);
  187.  
  188.     m_pkDestroyWhenIdleEvent = NULL;
  189.  
  190.     m_pkChrSyncOwner = NULL;
  191.  
  192.     memset(&m_points, 0, sizeof(m_points));
  193.     memset(&m_pointsInstant, 0, sizeof(m_pointsInstant));
  194.     memset(&m_quickslot, 0, sizeof(m_quickslot));
  195.  
  196.     m_bCharType = CHAR_TYPE_MONSTER;
  197.  
  198.     SetPosition(POS_STANDING);
  199.  
  200.     m_dwPlayStartTime = m_dwLastMoveTime = get_dword_time();
  201.  
  202.     GotoState(m_stateIdle);
  203.     m_dwStateDuration = 1;
  204.  
  205.     m_dwLastAttackTime = get_dword_time() - 20000;
  206.  
  207.     m_bAddChrState = 0;
  208.  
  209.     m_pkChrStone = NULL;
  210.  
  211.     m_pkSafebox = NULL;
  212.     m_iSafeboxSize = -1;
  213.     m_iSafeboxLoadTime = 0;
  214.  
  215.     m_pkMall = NULL;
  216.     m_iMallLoadTime = 0;
  217.  
  218.     m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
  219.     m_lWarpMapIndex = 0;
  220.  
  221.     m_posExit.x = m_posExit.y = m_posExit.z = 0;
  222.     m_lExitMapIndex = 0;
  223.  
  224.     m_pSkillLevels = NULL;
  225.  
  226.     m_dwMoveStartTime = 0;
  227.     m_dwMoveDuration = 0;
  228.  
  229.     m_dwFlyTargetID = 0;
  230.  
  231.     m_dwNextStatePulse = 0;
  232.  
  233.     m_dwLastDeadTime = get_dword_time()-180000;
  234.  
  235.     m_bSkipSave = false;
  236.  
  237.     m_bItemLoaded = false;
  238.  
  239.     m_bHasPoisoned = false;
  240.  
  241.     m_pkDungeon = NULL;
  242.     m_iEventAttr = 0;
  243.  
  244.     m_kAttackLog.dwVID = 0;
  245.     m_kAttackLog.dwTime = 0;
  246.  
  247.     m_bNowWalking = m_bWalking = false;
  248.     ResetChangeAttackPositionTime();
  249.  
  250.     m_bDetailLog = false;
  251.     m_bMonsterLog = false;
  252.  
  253.     m_bDisableCooltime = false;
  254.  
  255.     m_iAlignment = 0;
  256.     m_iRealAlignment = 0;
  257.  
  258.     m_iKillerModePulse = 0;
  259.     m_bPKMode = PK_MODE_PEACE;
  260.  
  261.     m_dwQuestNPCVID = 0;
  262.     m_dwQuestByVnum = 0;
  263.     m_pQuestItem = NULL;
  264.  
  265.     m_szMobileAuth[0] = '\0';
  266.  
  267.     m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000;
  268.  
  269.     m_bUnderRefine = false;
  270.  
  271.     // REFINE_NPC
  272.     m_dwRefineNPCVID = 0;
  273.     // END_OF_REFINE_NPC
  274.  
  275.     m_dwPolymorphRace = 0;
  276.  
  277.     m_bStaminaConsume = false;
  278.  
  279.     ResetChainLightningIndex();
  280.  
  281.     m_dwMountVnum = 0;
  282.     m_chHorse = NULL;
  283.     m_chRider = NULL;
  284.  
  285.     m_pWarMap = NULL;
  286.     m_pWeddingMap = NULL;
  287.     m_bChatCounter = 0;
  288.  
  289.     ResetStopTime();
  290.  
  291.     m_dwLastVictimSetTime = get_dword_time() - 3000;
  292.     m_iMaxAggro = -100;
  293.  
  294.     m_bSendHorseLevel = 0;
  295.     m_bSendHorseHealthGrade = 0;
  296.     m_bSendHorseStaminaGrade = 0;
  297.  
  298.     m_dwLoginPlayTime = 0;
  299.  
  300.     m_pkChrMarried = NULL;
  301.  
  302.     m_posSafeboxOpen.x = -1000;
  303.     m_posSafeboxOpen.y = -1000;
  304.  
  305.     // EQUIP_LAST_SKILL_DELAY
  306.     m_dwLastSkillTime = get_dword_time();
  307.     // END_OF_EQUIP_LAST_SKILL_DELAY
  308.  
  309.     // MOB_SKILL_COOLTIME
  310.     memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime));
  311.     // END_OF_MOB_SKILL_COOLTIME
  312.  
  313.     m_isinPCBang = false;
  314.  
  315.     // ARENA
  316.     m_pArena = NULL;
  317.     m_nPotionLimit = quest::CQuestManager::instance().GetEventFlag("arena_potion_limit_count");
  318.     // END_ARENA
  319.  
  320.     //PREVENT_TRADE_WINDOW
  321.     m_isOpenSafebox = 0;
  322.     //END_PREVENT_TRADE_WINDOW
  323.    
  324.     //PREVENT_REFINE_HACK
  325.     m_iRefineTime = 0;
  326.     //END_PREVENT_REFINE_HACK
  327.    
  328.     //RESTRICT_USE_SEED_OR_MOONBOTTLE
  329.     m_iSeedTime = 0;
  330.     //END_RESTRICT_USE_SEED_OR_MOONBOTTLE
  331.     //PREVENT_PORTAL_AFTER_EXCHANGE
  332.     m_iExchangeTime = 0;
  333.     //END_PREVENT_PORTAL_AFTER_EXCHANGE
  334.     //
  335.     m_iSafeboxLoadTime = 0;
  336.  
  337.     m_iMyShopTime = 0;
  338.  
  339.     InitMC();
  340.  
  341.     m_deposit_pulse = 0;
  342.  
  343.     SET_OVER_TIME(this, OT_NONE);
  344.  
  345.     m_strNewName = "";
  346.  
  347.     m_known_guild.clear();
  348.  
  349.     m_dwLogOffInterval = 0;
  350.  
  351.     m_bComboSequence = 0;
  352.     m_dwLastComboTime = 0;
  353.     m_bComboIndex = 0;
  354.     m_iComboHackCount = 0;
  355.     m_dwSkipComboAttackByTime = 0;
  356.  
  357.     m_dwMountTime = 0;
  358.  
  359.     m_dwLastGoldDropTime = 0;
  360.  
  361.     m_HackShieldCheckEvent = NULL;
  362.     m_HackShieldCheckMode = false;
  363.  
  364.     m_bIsLoadedAffect = false;
  365.     cannot_dead = false;
  366.  
  367. #ifdef __PET_SYSTEM__
  368.     m_petSystem = 0;
  369.     m_bIsPet = false;
  370. #endif
  371.  
  372.     m_fAttMul = 1.0f;
  373.     m_fDamMul = 1.0f;
  374.  
  375.     m_pointsInstant.iDragonSoulActiveDeck = -1;
  376.  
  377.     memset(&m_tvLastSyncTime, 0, sizeof(m_tvLastSyncTime));
  378.     m_iSyncHackCount = 0;
  379. }
  380.  
  381. void CHARACTER::Create(const char * c_pszName, DWORD vid, bool isPC)
  382. {
  383.     static int s_crc = 172814;
  384.  
  385.     char crc_string[128+1];
  386.     snprintf(crc_string, sizeof(crc_string), "%s%p%d", c_pszName, this, ++s_crc);
  387.     m_vid = VID(vid, GetCRC32(crc_string, strlen(crc_string)));
  388.  
  389.     if (isPC)
  390.         m_stName = c_pszName;
  391. }
  392.  
  393. void CHARACTER::Destroy()
  394. {
  395.     CloseMyShop();
  396.  
  397.     if (m_pkRegen)
  398.     {
  399.         if (m_pkDungeon) {
  400.             // Dungeon regen may not be valid at this point
  401.             if (m_pkDungeon->IsValidRegen(m_pkRegen, regen_id_)) {
  402.                 --m_pkRegen->count;
  403.             }
  404.         } else {
  405.             // Is this really safe?
  406.             --m_pkRegen->count;
  407.         }
  408.         m_pkRegen = NULL;
  409.     }
  410.  
  411.     if (m_pkDungeon)
  412.     {
  413.         SetDungeon(NULL);
  414.     }
  415.  
  416. #ifdef __PET_SYSTEM__
  417.     if (m_petSystem)
  418.     {
  419.         m_petSystem->Destroy();
  420.         delete m_petSystem;
  421.  
  422.         m_petSystem = 0;
  423.     }
  424. #endif
  425.  
  426.     HorseSummon(false);
  427.  
  428.     if (GetRider())
  429.         GetRider()->ClearHorseInfo();
  430.  
  431.     if( IsPC() )
  432.     {
  433.         if (isHackShieldEnable)
  434.         {
  435.             CHackShieldManager::instance().DeleteClientHandle(GetPlayerID());
  436.         }
  437.     }
  438.  
  439.     if (GetDesc())
  440.     {
  441.         GetDesc()->BindCharacter(NULL);
  442. //      BindDesc(NULL);
  443.     }
  444.  
  445.     if (m_pkExchange)
  446.         m_pkExchange->Cancel();
  447.  
  448.     SetVictim(NULL);
  449.  
  450.     if (GetShop())
  451.     {
  452.         GetShop()->RemoveGuest(this);
  453.         SetShop(NULL);
  454.     }
  455.  
  456.     ClearStone();
  457.     ClearSync();
  458.     ClearTarget();
  459.  
  460.     if (NULL == m_pkMobData)
  461.     {
  462.         DragonSoul_CleanUp();
  463.         ClearItem();
  464.     }
  465.  
  466.     // <Factor> m_pkParty becomes NULL after CParty destructor call!
  467.     LPPARTY party = m_pkParty;
  468.     if (party)
  469.     {
  470.         if (party->GetLeaderPID() == GetVID() && !IsPC())
  471.         {
  472.             M2_DELETE(party);
  473.         }
  474.         else
  475.         {
  476.             party->Unlink(this);
  477.  
  478.             if (!IsPC())
  479.                 party->Quit(GetVID());
  480.         }
  481.  
  482.         SetParty(NULL); // 안해도 되지만 안전하게.
  483.     }
  484.  
  485.     if (m_pkMobInst)
  486.     {
  487.         M2_DELETE(m_pkMobInst);
  488.         m_pkMobInst = NULL;
  489.     }
  490.  
  491.     m_pkMobData = NULL;
  492.  
  493.     if (m_pkSafebox)
  494.     {
  495.         M2_DELETE(m_pkSafebox);
  496.         m_pkSafebox = NULL;
  497.     }
  498.  
  499.     if (m_pkMall)
  500.     {
  501.         M2_DELETE(m_pkMall);
  502.         m_pkMall = NULL;
  503.     }
  504.  
  505.     m_set_pkChrSpawnedBy.clear();
  506.  
  507.     StopMuyeongEvent();
  508.     event_cancel(&m_pkWarpNPCEvent);
  509.     event_cancel(&m_pkRecoveryEvent);
  510.     event_cancel(&m_pkDeadEvent);
  511.     event_cancel(&m_pkSaveEvent);
  512.     event_cancel(&m_pkTimedEvent);
  513.     event_cancel(&m_pkStunEvent);
  514.     event_cancel(&m_pkFishingEvent);
  515.     event_cancel(&m_pkPoisonEvent);
  516.     event_cancel(&m_pkFireEvent);
  517.     event_cancel(&m_pkPartyRequestEvent);
  518.     //DELAYED_WARP
  519.     event_cancel(&m_pkWarpEvent);
  520.     event_cancel(&m_pkCheckSpeedHackEvent);
  521.     //END_DELAYED_WARP
  522.  
  523.     // RECALL_DELAY
  524.     //event_cancel(&m_pkRecallEvent);
  525.     // END_OF_RECALL_DELAY
  526.  
  527.     // MINING
  528.     event_cancel(&m_pkMiningEvent);
  529.     // END_OF_MINING
  530.  
  531.     StopHackShieldCheckCycle();
  532.  
  533.     for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it)
  534.     {
  535.         LPEVENT pkEvent = it->second;
  536.         event_cancel(&pkEvent);
  537.     }
  538.     m_mapMobSkillEvent.clear();
  539.  
  540.     //event_cancel(&m_pkAffectEvent);
  541.     ClearAffect();
  542.  
  543.     for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin();  it != m_map_buff_on_attrs.end(); it++)
  544.     {
  545.         if (NULL != it->second)
  546.         {
  547.             M2_DELETE(it->second);
  548.         }
  549.     }
  550.     m_map_buff_on_attrs.clear();
  551.  
  552.     event_cancel(&m_pkDestroyWhenIdleEvent);
  553.  
  554.     if (m_pSkillLevels)
  555.     {
  556.         M2_DELETE_ARRAY(m_pSkillLevels);
  557.         m_pSkillLevels = NULL;
  558.     }
  559.  
  560.     CEntity::Destroy();
  561.  
  562.     if (GetSectree())
  563.         GetSectree()->RemoveEntity(this);
  564.  
  565.     if (m_bMonsterLog)
  566.         CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
  567. }
  568.  
  569. const char * CHARACTER::GetName() const
  570. {
  571.     return m_stName.empty() ? (m_pkMobData ? m_pkMobData->m_table.szLocaleName : "") : m_stName.c_str();
  572. }
  573.  
  574. void CHARACTER::OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount)
  575. {
  576.     if (GetPart(PART_MAIN) > 2)
  577.     {
  578.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("갑옷을 벗어야 개인 상점을 열 수 있습니다."));
  579.         return;
  580.     }
  581.  
  582.     if (GetMyShop())    // 이미 샵이 열려 있으면 닫는다.
  583.     {
  584.         CloseMyShop();
  585.         return;
  586.     }
  587.  
  588.     // 진행중인 퀘스트가 있으면 상점을 열 수 없다.
  589.     quest::PC * pPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
  590.  
  591.     // GetPCForce는 NULL일 수 없으므로 따로 확인하지 않음
  592.     if (pPC->IsRunning())
  593.         return;
  594.  
  595.     if (bItemCount == 0)
  596.         return;
  597.  
  598.     int64_t nTotalMoney = 0;
  599.  
  600.     for (int n = 0; n < bItemCount; ++n)
  601.     {
  602.         nTotalMoney += static_cast<int64_t>((pTable+n)->price);
  603.     }
  604.  
  605.     nTotalMoney += static_cast<int64_t>(GetGold());
  606.  
  607.     if (GOLD_MAX <= nTotalMoney)
  608.     {
  609.         sys_err("[OVERFLOW_GOLD] Overflow (GOLD_MAX) id %u name %s", GetPlayerID(), GetName());
  610.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20억 냥을 초과하여 상점을 열수가 없습니다"));
  611.         return;
  612.     }
  613.  
  614.     char szSign[SHOP_SIGN_MAX_LEN+1];
  615.     strlcpy(szSign, c_pszSign, sizeof(szSign));
  616.  
  617.     m_stShopSign = szSign;
  618.  
  619.     if (m_stShopSign.length() == 0)
  620.         return;
  621.  
  622.     if (LC_IsCanada() == false)
  623.     {
  624.         if (CBanwordManager::instance().CheckString(m_stShopSign.c_str(), m_stShopSign.length()))
  625.         {
  626.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("비속어나 은어가 포함된 상점 이름으로 상점을 열 수 없습니다."));   
  627.             return;
  628.         }
  629.     }
  630.  
  631.     // MYSHOP_PRICE_LIST
  632.     std::map<DWORD, DWORD> itemkind;  // 아이템 종류별 가격, first: vnum, second: 단일 수량 가격
  633.     // END_OF_MYSHOP_PRICE_LIST
  634.  
  635.     std::set<TItemPos> cont;
  636.     for (BYTE i = 0; i < bItemCount; ++i)
  637.     {
  638.         if (cont.find((pTable + i)->pos) != cont.end())
  639.         {
  640.             sys_err("MYSHOP: duplicate shop item detected! (name: %s)", GetName());
  641.             return;
  642.         }
  643.  
  644.         // ANTI_GIVE, ANTI_MYSHOP check
  645.         LPITEM pkItem = GetItem((pTable + i)->pos);
  646.  
  647.         if (pkItem)
  648.         {
  649.             const TItemTable * item_table = pkItem->GetProto();
  650.  
  651.             if (item_table && (IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_MYSHOP)))
  652.             {
  653.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("유료화 아이템은 개인상점에서 판매할 수 없습니다."));
  654.                 return;
  655.             }
  656.  
  657.             if (pkItem->IsEquipped() == true)
  658.             {
  659.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("장비중인 아이템은 개인상점에서 판매할 수 없습니다."));
  660.                 return;
  661.             }
  662.  
  663.             if (true == pkItem->isLocked())
  664.             {
  665.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("사용중인 아이템은 개인상점에서 판매할 수 없습니다."));
  666.                 return;
  667.             }
  668.  
  669.             // MYSHOP_PRICE_LIST
  670.             itemkind[pkItem->GetVnum()] = (pTable + i)->price / pkItem->GetCount();
  671.             // END_OF_MYSHOP_PRICE_LIST
  672.         }
  673.  
  674.         cont.insert((pTable + i)->pos);
  675.     }
  676.  
  677.     // MYSHOP_PRICE_LIST
  678.     // 보따리 개수를 감소시킨다.
  679.     if (CountSpecifyItem(71049)) { // 비단 보따리는 없애지 않고 가격정보를 저장한다.
  680.  
  681.         //
  682.         // 아이템 가격정보를 저장하기 위해 아이템 가격정보 패킷을 만들어 DB 캐시에 보낸다.
  683.         //
  684.         TPacketMyshopPricelistHeader header;
  685.         TItemPriceInfo info;
  686.        
  687.         header.dwOwnerID = GetPlayerID();
  688.         header.byCount = itemkind.size();
  689.  
  690.         TEMP_BUFFER buf;
  691.         buf.write(&header, sizeof(header));
  692.  
  693.         for (itertype(itemkind) it = itemkind.begin(); it != itemkind.end(); ++it)
  694.         {
  695.             info.dwVnum = it->first;
  696.             info.dwPrice = it->second;
  697.  
  698.             buf.write(&info, sizeof(info));
  699.         }
  700.  
  701.         db_clientdesc->DBPacket(HEADER_GD_MYSHOP_PRICELIST_UPDATE, 0, buf.read_peek(), buf.size());
  702.     }
  703.     // END_OF_MYSHOP_PRICE_LIST
  704.     else if (CountSpecifyItem(50200))
  705.         RemoveSpecifyItem(50200, 1);
  706.     else
  707.         return; // 보따리가 없으면 중단.
  708.  
  709.     if (m_pkExchange)
  710.         m_pkExchange->Cancel();
  711.  
  712.     TPacketGCShopSign p;
  713.  
  714.     p.bHeader = HEADER_GC_SHOP_SIGN;
  715.     p.dwVID = GetVID();
  716.     strlcpy(p.szSign, c_pszSign, sizeof(p.szSign));
  717.  
  718.     PacketAround(&p, sizeof(TPacketGCShopSign));
  719.  
  720.     m_pkMyShop = CShopManager::instance().CreatePCShop(this, pTable, bItemCount);
  721.  
  722.     if (IsPolymorphed() == true)
  723.     {
  724.         RemoveAffect(AFFECT_POLYMORPH);
  725.     }
  726.  
  727.     if (GetHorse())
  728.     {
  729.         HorseSummon( false, true );
  730.     }
  731.     // new mount 이용 중에, 개인 상점 열면 자동 unmount
  732.     // StopRiding으로 뉴마운트까지 처리하면 좋은데 왜 그렇게 안해놨는지 알 수 없다.
  733.     else if (GetMountVnum())
  734.     {
  735.         RemoveAffect(AFFECT_MOUNT);
  736.         RemoveAffect(AFFECT_MOUNT_BONUS);
  737.     }
  738.     //if (!LC_IsNewCIBN())
  739.         SetPolymorph(30000, true);
  740.  
  741. }
  742.  
  743. void CHARACTER::CloseMyShop()
  744. {
  745.     if (GetMyShop())
  746.     {
  747.         m_stShopSign.clear();
  748.         CShopManager::instance().DestroyPCShop(this);
  749.         m_pkMyShop = NULL;
  750.  
  751.         TPacketGCShopSign p;
  752.  
  753.         p.bHeader = HEADER_GC_SHOP_SIGN;
  754.         p.dwVID = GetVID();
  755.         p.szSign[0] = '\0';
  756.  
  757.         PacketAround(&p, sizeof(p));
  758.        
  759.         //if (!LC_IsNewCIBN())
  760.             SetPolymorph(GetJob(), true);
  761.     }
  762. }
  763.  
  764. void EncodeMovePacket(TPacketGCMove & pack, DWORD dwVID, BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, BYTE bRot)
  765. {
  766.     pack.bHeader = HEADER_GC_MOVE;
  767.     pack.bFunc   = bFunc;
  768.     pack.bArg    = bArg;
  769.     pack.dwVID   = dwVID;
  770.     pack.dwTime  = dwTime ? dwTime : get_dword_time();
  771.     pack.bRot    = bRot;
  772.     pack.lX     = x;
  773.     pack.lY     = y;
  774.     pack.dwDuration = dwDuration;
  775. }
  776.  
  777. void CHARACTER::RestartAtSamePos()
  778. {
  779.     if (m_bIsObserver)
  780.         return;
  781.  
  782.     EncodeRemovePacket(this);
  783.     EncodeInsertPacket(this);
  784.  
  785.     ENTITY_MAP::iterator it = m_map_view.begin();
  786.  
  787.     while (it != m_map_view.end())
  788.     {
  789.         LPENTITY entity = (it++)->first;
  790.  
  791.         EncodeRemovePacket(entity);
  792.         if (!m_bIsObserver)
  793.             EncodeInsertPacket(entity);
  794.  
  795.         if( entity->IsType(ENTITY_CHARACTER) )
  796.         {
  797.             LPCHARACTER lpChar = (LPCHARACTER)entity;
  798.             if( lpChar->IsPC() || lpChar->IsNPC() || lpChar->IsMonster() )
  799.             {
  800.                 if (!entity->IsObserverMode())
  801.                     entity->EncodeInsertPacket(this);
  802.             }
  803.         }
  804.         else
  805.         {
  806.             if( !entity->IsObserverMode())
  807.             {
  808.                 entity->EncodeInsertPacket(this);
  809.             }
  810.         }
  811.     }
  812. }
  813.  
  814.  
  815. // Entity에 내가 나타났다고 패킷을 보낸다.
  816. void CHARACTER::EncodeInsertPacket(LPENTITY entity)
  817. {
  818.  
  819.     LPDESC d;
  820.  
  821.     if (!(d = entity->GetDesc()))
  822.         return;
  823.  
  824.     // 길드이름 버그 수정 코드
  825.     LPCHARACTER ch = (LPCHARACTER) entity;
  826.     ch->SendGuildName(GetGuild());
  827.     // 길드이름 버그 수정 코드
  828.  
  829.     TPacketGCCharacterAdd pack;
  830.  
  831.     pack.header     = HEADER_GC_CHARACTER_ADD;
  832.     pack.dwVID      = m_vid;
  833.     pack.bType      = GetCharType();
  834.     pack.angle      = GetRotation();
  835.     pack.x      = GetX();
  836.     pack.y      = GetY();
  837.     pack.z      = GetZ();
  838.     pack.wRaceNum   = GetRaceNum();
  839.     if (IsPet())
  840.     {
  841.         pack.bMovingSpeed   = 150;
  842.     }
  843.     else
  844.     {
  845.         pack.bMovingSpeed   = GetLimitPoint(POINT_MOV_SPEED);
  846.     }
  847.     pack.bAttackSpeed   = GetLimitPoint(POINT_ATT_SPEED);
  848.     pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
  849.     pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
  850.  
  851.     pack.bStateFlag = m_bAddChrState;
  852.  
  853.     int iDur = 0;
  854.  
  855.     if (m_posDest.x != pack.x || m_posDest.y != pack.y)
  856.     {
  857.         iDur = (m_dwMoveStartTime + m_dwMoveDuration) - get_dword_time();
  858.  
  859.         if (iDur <= 0)
  860.         {
  861.             pack.x = m_posDest.x;
  862.             pack.y = m_posDest.y;
  863.         }
  864.     }
  865.  
  866.     d->Packet(&pack, sizeof(pack));
  867.  
  868.     if (IsPC() == true || m_bCharType == CHAR_TYPE_NPC)
  869.     {
  870.         TPacketGCCharacterAdditionalInfo addPacket;
  871.         memset(&addPacket, 0, sizeof(TPacketGCCharacterAdditionalInfo));
  872.  
  873.         addPacket.header = HEADER_GC_CHAR_ADDITIONAL_INFO;
  874.         addPacket.dwVID = m_vid;
  875.  
  876.         addPacket.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
  877.         addPacket.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
  878.         addPacket.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
  879.         addPacket.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);
  880.  
  881.         addPacket.bPKMode = m_bPKMode;
  882.         addPacket.dwMountVnum = GetMountVnum();
  883.         addPacket.bEmpire = m_bEmpire;
  884.  
  885.         if (IsPC() == true && (LC_IsEurope() == true || LC_IsCanada() == true || LC_IsSingapore() == true))
  886.         {
  887.             addPacket.dwLevel = GetLevel();
  888.         }
  889.         else
  890.         {
  891.             addPacket.dwLevel = 0;
  892.         }
  893.  
  894.         if (false)
  895.         {
  896.             LPCHARACTER ch = (LPCHARACTER) entity;
  897.  
  898.             if (GetEmpire() == ch->GetEmpire() || ch->GetGMLevel() > GM_PLAYER || m_bCharType == CHAR_TYPE_NPC)
  899.             {
  900.                 goto show_all_info;
  901.             }
  902.             else
  903.             {
  904.                 memset(addPacket.name, 0, CHARACTER_NAME_MAX_LEN);
  905.                 addPacket.dwGuildID = 0;
  906.                 addPacket.sAlignment = 0;
  907.             }
  908.         }
  909.         else
  910.         {
  911.         show_all_info:
  912.             strlcpy(addPacket.name, GetName(), sizeof(addPacket.name));
  913.  
  914.             if (GetGuild() != NULL)
  915.             {  
  916.                 addPacket.dwGuildID = GetGuild()->GetID();
  917.             }
  918.             else
  919.             {
  920.                 addPacket.dwGuildID = 0;
  921.             }
  922.  
  923.             addPacket.sAlignment = m_iAlignment / 10;
  924.         }
  925.  
  926.         d->Packet(&addPacket, sizeof(TPacketGCCharacterAdditionalInfo));
  927.     }
  928.  
  929.     if (iDur)
  930.     {
  931.         TPacketGCMove pack;
  932.         EncodeMovePacket(pack, GetVID(), FUNC_MOVE, 0, m_posDest.x, m_posDest.y, iDur, 0, (BYTE) (GetRotation() / 5));
  933.         d->Packet(&pack, sizeof(pack));
  934.  
  935.         TPacketGCWalkMode p;
  936.         p.vid = GetVID();
  937.         p.header = HEADER_GC_WALK_MODE;
  938.         p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
  939.  
  940.         d->Packet(&p, sizeof(p));
  941.     }
  942.  
  943.     if (entity->IsType(ENTITY_CHARACTER) && GetDesc())
  944.     {
  945.         LPCHARACTER ch = (LPCHARACTER) entity;
  946.         if (ch->IsWalking())
  947.         {
  948.             TPacketGCWalkMode p;
  949.             p.vid = ch->GetVID();
  950.             p.header = HEADER_GC_WALK_MODE;
  951.             p.mode = ch->m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
  952.             GetDesc()->Packet(&p, sizeof(p));
  953.         }
  954.     }
  955.  
  956.     if (GetMyShop())
  957.     {
  958.         TPacketGCShopSign p;
  959.  
  960.         p.bHeader = HEADER_GC_SHOP_SIGN;
  961.         p.dwVID = GetVID();
  962.         strlcpy(p.szSign, m_stShopSign.c_str(), sizeof(p.szSign));
  963.  
  964.         d->Packet(&p, sizeof(TPacketGCShopSign));
  965.     }
  966.  
  967.     if (entity->IsType(ENTITY_CHARACTER))
  968.     {
  969.         sys_log(3, "EntityInsert %s (RaceNum %d) (%d %d) TO %s",
  970.                 GetName(), GetRaceNum(), GetX() / SECTREE_SIZE, GetY() / SECTREE_SIZE, ((LPCHARACTER)entity)->GetName());
  971.     }
  972. }
  973.  
  974. void CHARACTER::EncodeRemovePacket(LPENTITY entity)
  975. {
  976.     if (entity->GetType() != ENTITY_CHARACTER)
  977.         return;
  978.  
  979.     LPDESC d;
  980.  
  981.     if (!(d = entity->GetDesc()))
  982.         return;
  983.  
  984.     TPacketGCCharacterDelete pack;
  985.  
  986.     pack.header = HEADER_GC_CHARACTER_DEL;
  987.     pack.id = m_vid;
  988.  
  989.     d->Packet(&pack, sizeof(TPacketGCCharacterDelete));
  990.  
  991.     if (entity->IsType(ENTITY_CHARACTER))
  992.         sys_log(3, "EntityRemove %s(%d) FROM %s", GetName(), (DWORD) m_vid, ((LPCHARACTER) entity)->GetName());
  993. }
  994.  
  995. void CHARACTER::UpdatePacket()
  996. {
  997.     if (GetSectree() == NULL) return;
  998.  
  999.     TPacketGCCharacterUpdate pack;
  1000.     TPacketGCCharacterUpdate pack2;
  1001.  
  1002.     pack.header = HEADER_GC_CHARACTER_UPDATE;
  1003.     pack.dwVID = m_vid;
  1004.  
  1005.     pack.awPart[CHR_EQUIPPART_ARMOR] = GetPart(PART_MAIN);
  1006.     pack.awPart[CHR_EQUIPPART_WEAPON] = GetPart(PART_WEAPON);
  1007.     pack.awPart[CHR_EQUIPPART_HEAD] = GetPart(PART_HEAD);
  1008.     pack.awPart[CHR_EQUIPPART_HAIR] = GetPart(PART_HAIR);
  1009.  
  1010.     pack.bMovingSpeed   = GetLimitPoint(POINT_MOV_SPEED);
  1011.     pack.bAttackSpeed   = GetLimitPoint(POINT_ATT_SPEED);
  1012.     pack.bStateFlag = m_bAddChrState;
  1013.     pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
  1014.     pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
  1015.     pack.dwGuildID  = 0;
  1016.     pack.sAlignment = m_iAlignment / 10;
  1017.     pack.bPKMode    = m_bPKMode;
  1018.  
  1019.     if (GetGuild())
  1020.         pack.dwGuildID = GetGuild()->GetID();
  1021.  
  1022.     pack.dwMountVnum    = GetMountVnum();
  1023.  
  1024.     pack2 = pack;
  1025.     pack2.dwGuildID = 0;
  1026.     pack2.sAlignment = 0;
  1027.  
  1028.     if (false)
  1029.     {
  1030.         if (m_bIsObserver != true)
  1031.         {
  1032.             for (ENTITY_MAP::iterator iter = m_map_view.begin(); iter != m_map_view.end(); iter++)
  1033.             {
  1034.                 LPENTITY pEntity = iter->first;
  1035.  
  1036.                 if (pEntity != NULL)
  1037.                 {
  1038.                     if (pEntity->IsType(ENTITY_CHARACTER) == true)
  1039.                     {
  1040.                         if (pEntity->GetDesc() != NULL)
  1041.                         {
  1042.                             LPCHARACTER pChar = (LPCHARACTER)pEntity;
  1043.  
  1044.                             if (GetEmpire() == pChar->GetEmpire() || pChar->GetGMLevel() > GM_PLAYER)
  1045.                             {
  1046.                                 pEntity->GetDesc()->Packet(&pack, sizeof(pack));
  1047.                             }
  1048.                             else
  1049.                             {
  1050.                                 pEntity->GetDesc()->Packet(&pack2, sizeof(pack2));
  1051.                             }
  1052.                         }
  1053.                     }
  1054.                     else
  1055.                     {
  1056.                         if (pEntity->GetDesc() != NULL)
  1057.                         {
  1058.                             pEntity->GetDesc()->Packet(&pack, sizeof(pack));
  1059.                         }
  1060.                     }
  1061.                 }
  1062.             }
  1063.         }
  1064.  
  1065.         if (GetDesc() != NULL)
  1066.         {
  1067.             GetDesc()->Packet(&pack, sizeof(pack));
  1068.         }
  1069.     }
  1070.     else
  1071.     {
  1072.         PacketAround(&pack, sizeof(pack));
  1073.     }
  1074. }
  1075.  
  1076. LPCHARACTER CHARACTER::FindCharacterInView(const char * c_pszName, bool bFindPCOnly)
  1077. {
  1078.     ENTITY_MAP::iterator it = m_map_view.begin();
  1079.  
  1080.     for (; it != m_map_view.end(); ++it)
  1081.     {
  1082.         if (!it->first->IsType(ENTITY_CHARACTER))
  1083.             continue;
  1084.  
  1085.         LPCHARACTER tch = (LPCHARACTER) it->first;
  1086.  
  1087.         if (bFindPCOnly && tch->IsNPC())
  1088.             continue;
  1089.  
  1090.         if (!strcasecmp(tch->GetName(), c_pszName))
  1091.             return (tch);
  1092.     }
  1093.  
  1094.     return NULL;
  1095. }
  1096.  
  1097. void CHARACTER::SetPosition(int pos)
  1098. {
  1099.     if (pos == POS_STANDING)
  1100.     {
  1101.         REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
  1102.         REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN);
  1103.  
  1104.         event_cancel(&m_pkDeadEvent);
  1105.         event_cancel(&m_pkStunEvent);
  1106.     }
  1107.     else if (pos == POS_DEAD)
  1108.         SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_DEAD);
  1109.  
  1110.     if (!IsStone())
  1111.     {
  1112.         switch (pos)
  1113.         {
  1114.             case POS_FIGHTING:
  1115.                 if (!IsState(m_stateBattle))
  1116.                     MonsterLog("[BATTLE] 싸우는 상태");
  1117.  
  1118.                 GotoState(m_stateBattle);
  1119.                 break;
  1120.  
  1121.             default:
  1122.                 if (!IsState(m_stateIdle))
  1123.                     MonsterLog("[IDLE] 쉬는 상태");
  1124.  
  1125.                 GotoState(m_stateIdle);
  1126.                 break;
  1127.         }
  1128.     }
  1129.  
  1130.     m_pointsInstant.position = pos;
  1131. }
  1132.  
  1133. void CHARACTER::Save()
  1134. {
  1135.     if (!m_bSkipSave)
  1136.         CHARACTER_MANAGER::instance().DelayedSave(this);
  1137. }
  1138.  
  1139. void CHARACTER::CreatePlayerProto(TPlayerTable & tab)
  1140. {
  1141.     memset(&tab, 0, sizeof(TPlayerTable));
  1142.  
  1143.     if (GetNewName().empty())
  1144.     {
  1145.         strlcpy(tab.name, GetName(), sizeof(tab.name));
  1146.     }
  1147.     else
  1148.     {
  1149.         strlcpy(tab.name, GetNewName().c_str(), sizeof(tab.name));
  1150.     }
  1151.  
  1152.     strlcpy(tab.ip, GetDesc()->GetHostName(), sizeof(tab.ip));
  1153.  
  1154.     tab.id          = m_dwPlayerID;
  1155.     tab.voice       = GetPoint(POINT_VOICE);
  1156.     tab.level       = GetLevel();
  1157.     tab.level_step  = GetPoint(POINT_LEVEL_STEP);
  1158.     tab.exp         = GetExp();
  1159.     tab.gold        = GetGold();
  1160.     tab.job         = m_points.job;
  1161.     tab.part_base   = m_pointsInstant.bBasePart;
  1162.     tab.skill_group = m_points.skill_group;
  1163. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  1164.     tab.envanter    = Inven_Point();
  1165. #endif
  1166.    
  1167.     DWORD dwPlayedTime = (get_dword_time() - m_dwPlayStartTime);
  1168.  
  1169.     if (dwPlayedTime > 60000)
  1170.     {
  1171.         if (GetSectree() && !GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK))
  1172.         {
  1173.             if (GetRealAlignment() < 0)
  1174.             {
  1175.                 if (IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_TIME))
  1176.                     UpdateAlignment(120 * (dwPlayedTime / 60000));
  1177.                 else
  1178.                     UpdateAlignment(60 * (dwPlayedTime / 60000));
  1179.             }
  1180.             else
  1181.                 UpdateAlignment(5 * (dwPlayedTime / 60000));
  1182.         }
  1183.  
  1184.         SetRealPoint(POINT_PLAYTIME, GetRealPoint(POINT_PLAYTIME) + dwPlayedTime / 60000);
  1185.         ResetPlayTime(dwPlayedTime % 60000);
  1186.     }
  1187.  
  1188.     tab.playtime = GetRealPoint(POINT_PLAYTIME);
  1189.     tab.lAlignment = m_iRealAlignment;
  1190.  
  1191.     if (m_posWarp.x != 0 || m_posWarp.y != 0)
  1192.     {
  1193.         tab.x = m_posWarp.x;
  1194.         tab.y = m_posWarp.y;
  1195.         tab.z = 0;
  1196.         tab.lMapIndex = m_lWarpMapIndex;
  1197.     }
  1198.     else
  1199.     {
  1200.         tab.x = GetX();
  1201.         tab.y = GetY();
  1202.         tab.z = GetZ();
  1203.         tab.lMapIndex   = GetMapIndex();
  1204.     }
  1205.  
  1206.     if (m_lExitMapIndex == 0)
  1207.     {
  1208.         tab.lExitMapIndex   = tab.lMapIndex;
  1209.         tab.lExitX      = tab.x;
  1210.         tab.lExitY      = tab.y;
  1211.     }
  1212.     else
  1213.     {
  1214.         tab.lExitMapIndex   = m_lExitMapIndex;
  1215.         tab.lExitX      = m_posExit.x;
  1216.         tab.lExitY      = m_posExit.y;
  1217.     }
  1218.  
  1219.     sys_log(0, "SAVE: %s %dx%d", GetName(), tab.x, tab.y);
  1220.  
  1221.     tab.st = GetRealPoint(POINT_ST);
  1222.     tab.ht = GetRealPoint(POINT_HT);
  1223.     tab.dx = GetRealPoint(POINT_DX);
  1224.     tab.iq = GetRealPoint(POINT_IQ);
  1225.  
  1226.     tab.stat_point = GetPoint(POINT_STAT);
  1227.     tab.skill_point = GetPoint(POINT_SKILL);
  1228.     tab.sub_skill_point = GetPoint(POINT_SUB_SKILL);
  1229.     tab.horse_skill_point = GetPoint(POINT_HORSE_SKILL);
  1230.  
  1231.     tab.stat_reset_count = GetPoint(POINT_STAT_RESET_COUNT);
  1232.  
  1233.     tab.hp = GetHP();
  1234.     tab.sp = GetSP();
  1235.  
  1236.     tab.stamina = GetStamina();
  1237.  
  1238.     tab.sRandomHP = m_points.iRandomHP;
  1239.     tab.sRandomSP = m_points.iRandomSP;
  1240.  
  1241.     for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i)
  1242.         tab.quickslot[i] = m_quickslot[i];
  1243.  
  1244.     if (m_stMobile.length() && !*m_szMobileAuth)
  1245.         strlcpy(tab.szMobile, m_stMobile.c_str(), sizeof(tab.szMobile));
  1246.  
  1247.     thecore_memcpy(tab.parts, m_pointsInstant.parts, sizeof(tab.parts));
  1248.  
  1249.     // REMOVE_REAL_SKILL_LEVLES
  1250.     thecore_memcpy(tab.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
  1251.     // END_OF_REMOVE_REAL_SKILL_LEVLES
  1252.  
  1253.     tab.horse = GetHorseData();
  1254. }
  1255.  
  1256.  
  1257. void CHARACTER::SaveReal()
  1258. {
  1259.     if (m_bSkipSave)
  1260.         return;
  1261.  
  1262.     if (!GetDesc())
  1263.     {
  1264.         sys_err("Character::Save : no descriptor when saving (name: %s)", GetName());
  1265.         return;
  1266.     }
  1267.  
  1268.     TPlayerTable table;
  1269.     CreatePlayerProto(table);
  1270.  
  1271.     db_clientdesc->DBPacket(HEADER_GD_PLAYER_SAVE, GetDesc()->GetHandle(), &table, sizeof(TPlayerTable));
  1272.  
  1273.     quest::PC * pkQuestPC = quest::CQuestManager::instance().GetPCForce(GetPlayerID());
  1274.  
  1275.     if (!pkQuestPC)
  1276.         sys_err("CHARACTER::Save : null quest::PC pointer! (name %s)", GetName());
  1277.     else
  1278.     {
  1279.         pkQuestPC->Save();
  1280.     }
  1281.  
  1282.     marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
  1283.     if (pMarriage)
  1284.         pMarriage->Save();
  1285. }
  1286.  
  1287. void CHARACTER::FlushDelayedSaveItem()
  1288. {
  1289.     // 저장 안된 소지품을 전부 저장시킨다.
  1290.     LPITEM item;
  1291.  
  1292.     for (int i = 0; i < INVENTORY_AND_EQUIP_SLOT_MAX; ++i)
  1293.         if ((item = GetInventoryItem(i)))
  1294.             ITEM_MANAGER::instance().FlushDelayedSave(item);
  1295. }
  1296.  
  1297. void CHARACTER::Disconnect(const char * c_pszReason)
  1298. {
  1299.     assert(GetDesc() != NULL);
  1300.  
  1301.     sys_log(0, "DISCONNECT: %s (%s)", GetName(), c_pszReason ? c_pszReason : "unset" );
  1302.  
  1303.     if (GetShop())
  1304.     {
  1305.         GetShop()->RemoveGuest(this);
  1306.         SetShop(NULL);
  1307.     }
  1308.  
  1309.     if (GetArena() != NULL)
  1310.     {
  1311.         GetArena()->OnDisconnect(GetPlayerID());
  1312.     }
  1313.  
  1314.     if (GetParty() != NULL)
  1315.     {
  1316.         GetParty()->UpdateOfflineState(GetPlayerID());
  1317.     }
  1318.  
  1319.     marriage::CManager::instance().Logout(this);
  1320.  
  1321.     // P2P Logout
  1322.     TPacketGGLogout p;
  1323.     p.bHeader = HEADER_GG_LOGOUT;
  1324.     strlcpy(p.szName, GetName(), sizeof(p.szName));
  1325.     P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogout));
  1326.     char buf[51];
  1327.     snprintf(buf, sizeof(buf), "%s %d %d %ld %d",
  1328.         inet_ntoa(GetDesc()->GetAddr().sin_addr), GetGold(), g_bChannel, GetMapIndex(), GetAlignment());
  1329.  
  1330.     LogManager::instance().CharLog(this, 0, "LOGOUT", buf);
  1331.  
  1332.     if (LC_IsYMIR() || LC_IsKorea() || LC_IsBrazil())
  1333.     {
  1334.         long playTime = GetRealPoint(POINT_PLAYTIME) - m_dwLoginPlayTime;
  1335.         LogManager::instance().LoginLog(false, GetDesc()->GetAccountTable().id, GetPlayerID(), GetLevel(), GetJob(), playTime);
  1336.  
  1337.         if (LC_IsBrazil() != true)
  1338.             CPCBangManager::instance().Log(GetDesc()->GetHostName(), GetPlayerID(), playTime);
  1339.     }
  1340.  
  1341.     if (m_pWarMap)
  1342.         SetWarMap(NULL);
  1343.  
  1344.     if (m_pWeddingMap)
  1345.     {
  1346.         SetWeddingMap(NULL);
  1347.     }
  1348.  
  1349.     if (GetGuild())
  1350.         GetGuild()->LogoutMember(this);
  1351.  
  1352.     quest::CQuestManager::instance().LogoutPC(this);
  1353.  
  1354.     if (GetParty())
  1355.         GetParty()->Unlink(this);
  1356.  
  1357.     // 죽었을 때 접속끊으면 경험치 줄게 하기
  1358.     if (IsStun() || IsDead())
  1359.     {
  1360.         DeathPenalty(0);
  1361.         PointChange(POINT_HP, 50 - GetHP());
  1362.     }
  1363.  
  1364.  
  1365.     if (!CHARACTER_MANAGER::instance().FlushDelayedSave(this))
  1366.     {
  1367.         SaveReal();
  1368.     }
  1369.  
  1370.     FlushDelayedSaveItem();
  1371.  
  1372.     SaveAffect();
  1373.     m_bIsLoadedAffect = false;
  1374.  
  1375.     m_bSkipSave = true; // 이 이후에는 더이상 저장하면 안된다.
  1376.  
  1377.     quest::CQuestManager::instance().DisconnectPC(this);
  1378.  
  1379.     CloseSafebox();
  1380.  
  1381.     CloseMall();
  1382.  
  1383.     CPVPManager::instance().Disconnect(this);
  1384.  
  1385.     CTargetManager::instance().Logout(GetPlayerID());
  1386.  
  1387.     MessengerManager::instance().Logout(GetName());
  1388.  
  1389.     if (g_TeenDesc)
  1390.     {
  1391.         int     offset = 0;
  1392.         char    buf[245] = {0};
  1393.  
  1394.         buf[0] = HEADER_GT_LOGOUT;
  1395.         offset += 1;
  1396.  
  1397.         memset(buf+offset, 0x00, 2);
  1398.         offset += 2;
  1399.  
  1400.         TAccountTable   &acc_table = GetDesc()->GetAccountTable();
  1401.         memcpy(buf+offset, &acc_table.id, 4);
  1402.         offset += 4;
  1403.  
  1404.         g_TeenDesc->Packet(buf, offset);
  1405.     }
  1406.  
  1407.     if (GetDesc())
  1408.     {
  1409.         GetDesc()->BindCharacter(NULL);
  1410. //      BindDesc(NULL);
  1411.     }
  1412.  
  1413.     CXTrapManager::instance().DestroyClientSession(this);
  1414.  
  1415.     M2_DESTROY_CHARACTER(this);
  1416. }
  1417.  
  1418. bool CHARACTER::Show(long lMapIndex, long x, long y, long z, bool bShowSpawnMotion/* = false */)
  1419. {
  1420.     LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
  1421.  
  1422.     if (!sectree)
  1423.     {
  1424.         sys_log(0, "cannot find sectree by %dx%d mapindex %d", x, y, lMapIndex);
  1425.         return false;
  1426.     }
  1427.  
  1428.     SetMapIndex(lMapIndex);
  1429.  
  1430.     bool bChangeTree = false;
  1431.  
  1432.     if (!GetSectree() || GetSectree() != sectree)
  1433.         bChangeTree = true;
  1434.  
  1435.     if (bChangeTree)
  1436.     {
  1437.         if (GetSectree())
  1438.             GetSectree()->RemoveEntity(this);
  1439.  
  1440.         ViewCleanup();
  1441.     }
  1442.  
  1443.     if (!IsNPC())
  1444.     {
  1445.         sys_log(0, "SHOW: %s %dx%dx%d", GetName(), x, y, z);
  1446.         if (GetStamina() < GetMaxStamina())
  1447.             StartAffectEvent();
  1448.     }
  1449.     else if (m_pkMobData)
  1450.     {
  1451.         m_pkMobInst->m_posLastAttacked.x = x;
  1452.         m_pkMobInst->m_posLastAttacked.y = y;
  1453.         m_pkMobInst->m_posLastAttacked.z = z;
  1454.     }
  1455.  
  1456.     if (bShowSpawnMotion)
  1457.     {
  1458.         SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  1459.         m_afAffectFlag.Set(AFF_SPAWN);
  1460.     }
  1461.  
  1462.     SetXYZ(x, y, z);
  1463.  
  1464.     m_posDest.x = x;
  1465.     m_posDest.y = y;
  1466.     m_posDest.z = z;
  1467.  
  1468.     m_posStart.x = x;
  1469.     m_posStart.y = y;
  1470.     m_posStart.z = z;
  1471.  
  1472.     if (bChangeTree)
  1473.     {
  1474.         EncodeInsertPacket(this);
  1475.         sectree->InsertEntity(this);
  1476.  
  1477.         UpdateSectree();
  1478.     }
  1479.     else
  1480.     {
  1481.         ViewReencode();
  1482.         sys_log(0, "      in same sectree");
  1483.     }
  1484.  
  1485.     REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  1486.    
  1487.     SetValidComboInterval(0);
  1488.     return true;
  1489. }
  1490.  
  1491. // BGM_INFO
  1492. struct BGMInfo
  1493. {
  1494.     std::string name;
  1495.     float       vol;
  1496. };
  1497.  
  1498. typedef std::map<unsigned, BGMInfo> BGMInfoMap;
  1499.  
  1500. static BGMInfoMap   gs_bgmInfoMap;
  1501. static bool     gs_bgmVolEnable = false;
  1502.  
  1503. void CHARACTER_SetBGMVolumeEnable()
  1504. {
  1505.     gs_bgmVolEnable = true;
  1506.     sys_log(0, "bgm_info.set_bgm_volume_enable");
  1507. }
  1508.  
  1509. void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol)
  1510. {
  1511.     BGMInfo newInfo;
  1512.     newInfo.name = name;
  1513.     newInfo.vol = vol;
  1514.  
  1515.     gs_bgmInfoMap[mapIndex] = newInfo;
  1516.  
  1517.     sys_log(0, "bgm_info.add_info(%d, '%s', %f)", mapIndex, name, vol);
  1518. }
  1519.  
  1520. const BGMInfo& CHARACTER_GetBGMInfo(unsigned mapIndex)
  1521. {
  1522.     BGMInfoMap::iterator f = gs_bgmInfoMap.find(mapIndex);
  1523.     if (gs_bgmInfoMap.end() == f)
  1524.     {
  1525.         static BGMInfo s_empty = {"", 0.0f};
  1526.         return s_empty;
  1527.     }
  1528.     return f->second;
  1529. }
  1530.  
  1531. bool CHARACTER_IsBGMVolumeEnable()
  1532. {
  1533.     return gs_bgmVolEnable;
  1534. }
  1535. // END_OF_BGM_INFO
  1536.  
  1537. void CHARACTER::MainCharacterPacket()
  1538. {
  1539.     const unsigned mapIndex = GetMapIndex();
  1540.     const BGMInfo& bgmInfo = CHARACTER_GetBGMInfo(mapIndex);
  1541.  
  1542.     // SUPPORT_BGM
  1543.     if (!bgmInfo.name.empty())
  1544.     {
  1545.         if (CHARACTER_IsBGMVolumeEnable())
  1546.         {
  1547.             sys_log(1, "bgm_info.play_bgm_vol(%d, name='%s', vol=%f)", mapIndex, bgmInfo.name.c_str(), bgmInfo.vol);
  1548.             TPacketGCMainCharacter4_BGM_VOL mainChrPacket;
  1549.             mainChrPacket.header = HEADER_GC_MAIN_CHARACTER4_BGM_VOL;
  1550.             mainChrPacket.dwVID = m_vid;
  1551.             mainChrPacket.wRaceNum = GetRaceNum();
  1552.             mainChrPacket.lx = GetX();
  1553.             mainChrPacket.ly = GetY();
  1554.             mainChrPacket.lz = GetZ();
  1555.             mainChrPacket.empire = GetDesc()->GetEmpire();
  1556.             mainChrPacket.skill_group = GetSkillGroup();
  1557.             strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
  1558.  
  1559.             mainChrPacket.fBGMVol = bgmInfo.vol;
  1560.             strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
  1561.             GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter4_BGM_VOL));
  1562.         }
  1563.         else
  1564.         {
  1565.             sys_log(1, "bgm_info.play(%d, '%s')", mapIndex, bgmInfo.name.c_str());
  1566.             TPacketGCMainCharacter3_BGM mainChrPacket;
  1567.             mainChrPacket.header = HEADER_GC_MAIN_CHARACTER3_BGM;
  1568.             mainChrPacket.dwVID = m_vid;
  1569.             mainChrPacket.wRaceNum = GetRaceNum();
  1570.             mainChrPacket.lx = GetX();
  1571.             mainChrPacket.ly = GetY();
  1572.             mainChrPacket.lz = GetZ();
  1573.             mainChrPacket.empire = GetDesc()->GetEmpire();
  1574.             mainChrPacket.skill_group = GetSkillGroup();
  1575.             strlcpy(mainChrPacket.szChrName, GetName(), sizeof(mainChrPacket.szChrName));
  1576.             strlcpy(mainChrPacket.szBGMName, bgmInfo.name.c_str(), sizeof(mainChrPacket.szBGMName));
  1577.             GetDesc()->Packet(&mainChrPacket, sizeof(TPacketGCMainCharacter3_BGM));
  1578.         }
  1579.         //if (m_stMobile.length())
  1580.         //      ChatPacket(CHAT_TYPE_COMMAND, "sms");
  1581.     }
  1582.     // END_OF_SUPPORT_BGM
  1583.     else
  1584.     {
  1585.         sys_log(0, "bgm_info.play(%d, DEFAULT_BGM_NAME)", mapIndex);
  1586.  
  1587.         TPacketGCMainCharacter pack;
  1588.         pack.header = HEADER_GC_MAIN_CHARACTER;
  1589.         pack.dwVID = m_vid;
  1590.         pack.wRaceNum = GetRaceNum();
  1591.         pack.lx = GetX();
  1592.         pack.ly = GetY();
  1593.         pack.lz = GetZ();
  1594.         pack.empire = GetDesc()->GetEmpire();
  1595.         pack.skill_group = GetSkillGroup();
  1596.         strlcpy(pack.szName, GetName(), sizeof(pack.szName));
  1597.         GetDesc()->Packet(&pack, sizeof(TPacketGCMainCharacter));
  1598.  
  1599.         if (m_stMobile.length())
  1600.             ChatPacket(CHAT_TYPE_COMMAND, "sms");
  1601.     }
  1602. }
  1603.  
  1604. void CHARACTER::PointsPacket()
  1605. {
  1606.     if (!GetDesc())
  1607.         return;
  1608.  
  1609.     TPacketGCPoints pack;
  1610.  
  1611.     pack.header = HEADER_GC_CHARACTER_POINTS;
  1612.  
  1613.     pack.points[POINT_LEVEL]        = GetLevel();
  1614.     pack.points[POINT_EXP]      = GetExp();
  1615.     pack.points[POINT_NEXT_EXP]     = GetNextExp();
  1616.     pack.points[POINT_HP]       = GetHP();
  1617.     pack.points[POINT_MAX_HP]       = GetMaxHP();
  1618.     pack.points[POINT_SP]       = GetSP();
  1619.     pack.points[POINT_MAX_SP]       = GetMaxSP();
  1620.     pack.points[POINT_GOLD]     = GetGold();
  1621.     pack.points[POINT_STAMINA]      = GetStamina();
  1622.     pack.points[POINT_MAX_STAMINA]  = GetMaxStamina();
  1623. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  1624.     pack.points[POINT_INVEN]    = Inven_Point();
  1625. #endif
  1626.  
  1627.     for (int i = POINT_ST; i < POINT_MAX_NUM; ++i)
  1628.         pack.points[i] = GetPoint(i);
  1629.  
  1630.     GetDesc()->Packet(&pack, sizeof(TPacketGCPoints));
  1631. }
  1632.  
  1633. bool CHARACTER::ChangeSex()
  1634. {
  1635.     int src_race = GetRaceNum();
  1636.  
  1637.     switch (src_race)
  1638.     {
  1639.         case MAIN_RACE_WARRIOR_M:
  1640.             m_points.job = MAIN_RACE_WARRIOR_W;
  1641.             break;
  1642.  
  1643.         case MAIN_RACE_WARRIOR_W:
  1644.             m_points.job = MAIN_RACE_WARRIOR_M;
  1645.             break;
  1646.  
  1647.         case MAIN_RACE_ASSASSIN_M:
  1648.             m_points.job = MAIN_RACE_ASSASSIN_W;
  1649.             break;
  1650.  
  1651.         case MAIN_RACE_ASSASSIN_W:
  1652.             m_points.job = MAIN_RACE_ASSASSIN_M;
  1653.             break;
  1654.  
  1655.         case MAIN_RACE_SURA_M:
  1656.             m_points.job = MAIN_RACE_SURA_W;
  1657.             break;
  1658.  
  1659.         case MAIN_RACE_SURA_W:
  1660.             m_points.job = MAIN_RACE_SURA_M;
  1661.             break;
  1662.  
  1663.         case MAIN_RACE_SHAMAN_M:
  1664.             m_points.job = MAIN_RACE_SHAMAN_W;
  1665.             break;
  1666.  
  1667.         case MAIN_RACE_SHAMAN_W:
  1668.             m_points.job = MAIN_RACE_SHAMAN_M;
  1669.             break;
  1670.  
  1671.         default:
  1672.             sys_err("CHANGE_SEX: %s unknown race %d", GetName(), src_race);
  1673.             return false;
  1674.     }
  1675.  
  1676.     sys_log(0, "CHANGE_SEX: %s (%d -> %d)", GetName(), src_race, m_points.job);
  1677.     return true;
  1678. }
  1679.  
  1680. WORD CHARACTER::GetRaceNum() const
  1681. {
  1682.     if (m_dwPolymorphRace)
  1683.         return m_dwPolymorphRace;
  1684.  
  1685.     if (m_pkMobData)
  1686.         return m_pkMobData->m_table.dwVnum;
  1687.  
  1688.     return m_points.job;
  1689. }
  1690.  
  1691. void CHARACTER::SetRace(BYTE race)
  1692. {
  1693.     if (race >= MAIN_RACE_MAX_NUM)
  1694.     {
  1695.         sys_err("CHARACTER::SetRace(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
  1696.         return;
  1697.     }
  1698.  
  1699.     m_points.job = race;
  1700. }
  1701.  
  1702. BYTE CHARACTER::GetJob() const
  1703. {
  1704.     unsigned race = m_points.job;
  1705.     unsigned job;
  1706.  
  1707.     if (RaceToJob(race, &job))
  1708.         return job;
  1709.  
  1710.     sys_err("CHARACTER::GetJob(name=%s, race=%d).OUT_OF_RACE_RANGE", GetName(), race);
  1711.     return JOB_WARRIOR;
  1712. }
  1713.  
  1714. void CHARACTER::SetLevel(BYTE level)
  1715. {
  1716.     m_points.level = level;
  1717.  
  1718.     if (IsPC())
  1719.     {
  1720.         if (level < PK_PROTECT_LEVEL)
  1721.             SetPKMode(PK_MODE_PROTECT);
  1722.         else if (GetGMLevel() != GM_PLAYER)
  1723.             SetPKMode(PK_MODE_PROTECT);
  1724.         else if (m_bPKMode == PK_MODE_PROTECT)
  1725.             SetPKMode(PK_MODE_PEACE);
  1726.     }
  1727. }
  1728.  
  1729. void CHARACTER::SetEmpire(BYTE bEmpire)
  1730. {
  1731.     m_bEmpire = bEmpire;
  1732. }
  1733.  
  1734. void CHARACTER::SetPlayerProto(const TPlayerTable * t)
  1735. {
  1736.     if (!GetDesc() || !*GetDesc()->GetHostName())
  1737.         sys_err("cannot get desc or hostname");
  1738.     else
  1739.         SetGMLevel();
  1740.  
  1741.     m_bCharType = CHAR_TYPE_PC;
  1742.  
  1743.     m_dwPlayerID = t->id;
  1744.  
  1745.     m_iAlignment = t->lAlignment;
  1746.     m_iRealAlignment = t->lAlignment;
  1747.  
  1748.     m_points.voice = t->voice;
  1749.  
  1750.     m_points.skill_group = t->skill_group;
  1751.  
  1752.     m_pointsInstant.bBasePart = t->part_base;
  1753.     SetPart(PART_HAIR, t->parts[PART_HAIR]);
  1754.  
  1755.     m_points.iRandomHP = t->sRandomHP;
  1756.     m_points.iRandomSP = t->sRandomSP;
  1757.  
  1758.     // REMOVE_REAL_SKILL_LEVLES
  1759.     if (m_pSkillLevels)
  1760.         M2_DELETE_ARRAY(m_pSkillLevels);
  1761.  
  1762.     m_pSkillLevels = M2_NEW TPlayerSkill[SKILL_MAX_NUM];
  1763.     thecore_memcpy(m_pSkillLevels, t->skills, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
  1764.     // END_OF_REMOVE_REAL_SKILL_LEVLES
  1765.  
  1766.     if (t->lMapIndex >= 10000)
  1767.     {
  1768.         m_posWarp.x = t->lExitX;
  1769.         m_posWarp.y = t->lExitY;
  1770.         m_lWarpMapIndex = t->lExitMapIndex;
  1771.     }
  1772.  
  1773.     SetRealPoint(POINT_PLAYTIME, t->playtime);
  1774.     m_dwLoginPlayTime = t->playtime;
  1775.     SetRealPoint(POINT_ST, t->st);
  1776.     SetRealPoint(POINT_HT, t->ht);
  1777.     SetRealPoint(POINT_DX, t->dx);
  1778.     SetRealPoint(POINT_IQ, t->iq);
  1779.  
  1780.     SetPoint(POINT_ST, t->st);
  1781.     SetPoint(POINT_HT, t->ht);
  1782.     SetPoint(POINT_DX, t->dx);
  1783.     SetPoint(POINT_IQ, t->iq);
  1784.  
  1785.     SetPoint(POINT_STAT, t->stat_point);
  1786.     SetPoint(POINT_SKILL, t->skill_point);
  1787.     SetPoint(POINT_SUB_SKILL, t->sub_skill_point);
  1788.     SetPoint(POINT_HORSE_SKILL, t->horse_skill_point);
  1789.  
  1790.     SetPoint(POINT_STAT_RESET_COUNT, t->stat_reset_count);
  1791.  
  1792.     SetPoint(POINT_LEVEL_STEP, t->level_step);
  1793.     SetRealPoint(POINT_LEVEL_STEP, t->level_step);
  1794.  
  1795.     SetRace(t->job);
  1796.  
  1797.     SetLevel(t->level);
  1798.     SetExp(t->exp);
  1799.     SetGold(t->gold);
  1800. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  1801.     Set_Inventory_Point(t->envanter);
  1802. #endif
  1803.  
  1804.     SetMapIndex(t->lMapIndex);
  1805.     SetXYZ(t->x, t->y, t->z);
  1806.  
  1807.     ComputePoints();
  1808.  
  1809.     SetHP(t->hp);
  1810.     SetSP(t->sp);
  1811.     SetStamina(t->stamina);
  1812.  
  1813.     //GM일때 보호모드  
  1814.     if (!test_server)
  1815.     {
  1816.         if (GetGMLevel() > GM_LOW_WIZARD)
  1817.         {
  1818.             m_afAffectFlag.Set(AFF_YMIR);
  1819.             m_bPKMode = PK_MODE_PROTECT;
  1820.         }
  1821.     }
  1822.  
  1823.     if (GetLevel() < PK_PROTECT_LEVEL)
  1824.         m_bPKMode = PK_MODE_PROTECT;
  1825.  
  1826.     m_stMobile = t->szMobile;
  1827.  
  1828.     SetHorseData(t->horse);
  1829.  
  1830.     if (GetHorseLevel() > 0)
  1831.         UpdateHorseDataByLogoff(t->logoff_interval);
  1832.  
  1833.     thecore_memcpy(m_aiPremiumTimes, t->aiPremiumTimes, sizeof(t->aiPremiumTimes));
  1834.  
  1835.     m_dwLogOffInterval = t->logoff_interval;
  1836.  
  1837.     sys_log(0, "PLAYER_LOAD: %s PREMIUM %d %d, LOGGOFF_INTERVAL %u PTR: %p", t->name, m_aiPremiumTimes[0], m_aiPremiumTimes[1], t->logoff_interval, this);
  1838.  
  1839.     if (GetGMLevel() != GM_PLAYER)
  1840.     {
  1841.         LogManager::instance().CharLog(this, GetGMLevel(), "GM_LOGIN", "");
  1842.         sys_log(0, "GM_LOGIN(gmlevel=%d, name=%s(%d), pos=(%d, %d)", GetGMLevel(), GetName(), GetPlayerID(), GetX(), GetY());
  1843.     }
  1844.  
  1845. #ifdef __PET_SYSTEM__
  1846.     // NOTE: 일단 캐릭터가 PC인 경우에만 PetSystem을 갖도록 함. 유럽 머신당 메모리 사용률때문에 NPC까지 하긴 좀..
  1847.     if (m_petSystem)
  1848.     {
  1849.         m_petSystem->Destroy();
  1850.         delete m_petSystem;
  1851.     }
  1852.  
  1853.     m_petSystem = M2_NEW CPetSystem(this);
  1854. #endif
  1855. }
  1856.  
  1857. EVENTFUNC(kill_ore_load_event)
  1858. {
  1859.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  1860.     if ( info == NULL )
  1861.     {
  1862.         sys_err( "kill_ore_load_even> <Factor> Null pointer" );
  1863.         return 0;
  1864.     }
  1865.  
  1866.     LPCHARACTER ch = info->ch;
  1867.     if (ch == NULL) { // <Factor>
  1868.         return 0;
  1869.     }  
  1870.  
  1871.     ch->m_pkMiningEvent = NULL;
  1872.     M2_DESTROY_CHARACTER(ch);
  1873.     return 0;
  1874. }
  1875.  
  1876. void CHARACTER::SetProto(const CMob * pkMob)
  1877. {
  1878.     if (m_pkMobInst)
  1879.         M2_DELETE(m_pkMobInst);
  1880.  
  1881.     m_pkMobData = pkMob;
  1882.     m_pkMobInst = M2_NEW CMobInstance;
  1883.  
  1884.     m_bPKMode = PK_MODE_FREE;
  1885.  
  1886.     const TMobTable * t = &m_pkMobData->m_table;
  1887.  
  1888.     m_bCharType = t->bType;
  1889.  
  1890.     SetLevel(t->bLevel);
  1891.     SetEmpire(t->bEmpire);
  1892.  
  1893.     SetExp(t->dwExp);
  1894.     SetRealPoint(POINT_ST, t->bStr);
  1895.     SetRealPoint(POINT_DX, t->bDex);
  1896.     SetRealPoint(POINT_HT, t->bCon);
  1897.     SetRealPoint(POINT_IQ, t->bInt);
  1898.  
  1899.     ComputePoints();
  1900.  
  1901.     SetHP(GetMaxHP());
  1902.     SetSP(GetMaxSP());
  1903.  
  1904.     ////////////////////
  1905.     m_pointsInstant.dwAIFlag = t->dwAIFlag;
  1906.     SetImmuneFlag(t->dwImmuneFlag);
  1907.  
  1908.     AssignTriggers(t);
  1909.  
  1910.     ApplyMobAttribute(t);
  1911.  
  1912.     if (IsStone())
  1913.     {
  1914.         DetermineDropMetinStone();
  1915.     }
  1916.  
  1917.     if (IsWarp() || IsGoto())
  1918.     {
  1919.         StartWarpNPCEvent();
  1920.     }
  1921.  
  1922.     CHARACTER_MANAGER::instance().RegisterRaceNumMap(this);
  1923.  
  1924.     // XXX X-mas santa hardcoding
  1925.     if (GetRaceNum() == xmas::MOB_SANTA_VNUM)
  1926.     {
  1927.         SetPoint(POINT_ATT_GRADE_BONUS, 10);
  1928.         if (g_iUseLocale)
  1929.             SetPoint(POINT_DEF_GRADE_BONUS, 6);
  1930.         else
  1931.             SetPoint(POINT_DEF_GRADE_BONUS, 15);
  1932.  
  1933.         //산타용
  1934.         //m_dwPlayStartTime = get_dword_time() + 10 * 60 * 1000;
  1935.         //신선자 노해
  1936.         m_dwPlayStartTime = get_dword_time() + 30 * 1000;
  1937.         if (test_server)
  1938.             m_dwPlayStartTime = get_dword_time() + 30 * 1000;
  1939.     }
  1940.  
  1941.     // XXX CTF GuildWar hardcoding
  1942.     if (warmap::IsWarFlag(GetRaceNum()))
  1943.     {
  1944.         m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
  1945.         m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
  1946.         m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlag, &CHARACTER::EndStateEmpty);
  1947.     }
  1948.  
  1949.     if (warmap::IsWarFlagBase(GetRaceNum()))
  1950.     {
  1951.         m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
  1952.         m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
  1953.         m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateFlagBase, &CHARACTER::EndStateEmpty);
  1954.     }
  1955.  
  1956.     if (m_bCharType == CHAR_TYPE_HORSE ||
  1957.             GetRaceNum() == 20101 ||
  1958.             GetRaceNum() == 20102 ||
  1959.             GetRaceNum() == 20103 ||
  1960.             GetRaceNum() == 20104 ||
  1961.             GetRaceNum() == 20105 ||
  1962.             GetRaceNum() == 20106 ||
  1963.             GetRaceNum() == 20107 ||
  1964.             GetRaceNum() == 20108 ||
  1965.             GetRaceNum() == 20109
  1966.       )
  1967.     {
  1968.         m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
  1969.         m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty);
  1970.         m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateHorse, &CHARACTER::EndStateEmpty);
  1971.     }
  1972.  
  1973.     // MINING
  1974.     if (mining::IsVeinOfOre (GetRaceNum()))
  1975.     {
  1976.         char_event_info* info = AllocEventInfo<char_event_info>();
  1977.  
  1978.         info->ch = this;
  1979.  
  1980.         m_pkMiningEvent = event_create(kill_ore_load_event, info, PASSES_PER_SEC(number(7 * 60, 15 * 60)));
  1981.     }
  1982.     // END_OF_MINING
  1983. }
  1984.  
  1985. const TMobTable & CHARACTER::GetMobTable() const
  1986. {
  1987.     return m_pkMobData->m_table;
  1988. }
  1989.  
  1990. bool CHARACTER::IsRaceFlag(DWORD dwBit) const
  1991. {
  1992.     return m_pkMobData ? IS_SET(m_pkMobData->m_table.dwRaceFlag, dwBit) : 0;
  1993. }
  1994.  
  1995. DWORD CHARACTER::GetMobDamageMin() const
  1996. {
  1997.     return m_pkMobData->m_table.dwDamageRange[0];
  1998. }
  1999.  
  2000. DWORD CHARACTER::GetMobDamageMax() const
  2001. {
  2002.     return m_pkMobData->m_table.dwDamageRange[1];
  2003. }
  2004.  
  2005. float CHARACTER::GetMobDamageMultiply() const
  2006. {
  2007.     float fDamMultiply = GetMobTable().fDamMultiply;
  2008.  
  2009.     if (IsBerserk())
  2010.         fDamMultiply = fDamMultiply * 2.0f; // BALANCE: 광폭화 시 두배
  2011.  
  2012.     return fDamMultiply;
  2013. }
  2014.  
  2015. DWORD CHARACTER::GetMobDropItemVnum() const
  2016. {
  2017.     return m_pkMobData->m_table.dwDropItemVnum;
  2018. }
  2019.  
  2020. bool CHARACTER::IsSummonMonster() const
  2021. {
  2022.     return GetSummonVnum() != 0;
  2023. }
  2024.  
  2025. DWORD CHARACTER::GetSummonVnum() const
  2026. {
  2027.     return m_pkMobData ? m_pkMobData->m_table.dwSummonVnum : 0;
  2028. }
  2029.  
  2030. DWORD CHARACTER::GetPolymorphItemVnum() const
  2031. {
  2032.     return m_pkMobData ? m_pkMobData->m_table.dwPolymorphItemVnum : 0;
  2033. }
  2034.  
  2035. DWORD CHARACTER::GetMonsterDrainSPPoint() const
  2036. {
  2037.     return m_pkMobData ? m_pkMobData->m_table.dwDrainSP : 0;
  2038. }
  2039.  
  2040. BYTE CHARACTER::GetMobRank() const
  2041. {
  2042.     if (!m_pkMobData)
  2043.         return MOB_RANK_KNIGHT; // PC일 경우 KNIGHT급
  2044.  
  2045.     return m_pkMobData->m_table.bRank;
  2046. }
  2047.  
  2048. BYTE CHARACTER::GetMobSize() const
  2049. {
  2050.     if (!m_pkMobData)
  2051.         return MOBSIZE_MEDIUM;
  2052.  
  2053.     return m_pkMobData->m_table.bSize;
  2054. }
  2055.  
  2056. WORD CHARACTER::GetMobAttackRange() const
  2057. {
  2058.     switch (GetMobBattleType())
  2059.     {
  2060.         case BATTLE_TYPE_RANGE:
  2061.         case BATTLE_TYPE_MAGIC:
  2062.             return m_pkMobData->m_table.wAttackRange + GetPoint(POINT_BOW_DISTANCE);  
  2063.         default:
  2064.             return m_pkMobData->m_table.wAttackRange;
  2065.     }
  2066. }
  2067.  
  2068. BYTE CHARACTER::GetMobBattleType() const
  2069. {
  2070.     if (!m_pkMobData)
  2071.         return BATTLE_TYPE_MELEE;
  2072.  
  2073.     return (m_pkMobData->m_table.bBattleType);
  2074. }
  2075.  
  2076. void CHARACTER::ComputeBattlePoints()
  2077. {
  2078.     if (IsPolymorphed())
  2079.     {
  2080.         DWORD dwMobVnum = GetPolymorphVnum();
  2081.         const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
  2082.         int iAtt = 0;
  2083.         int iDef = 0;
  2084.  
  2085.         if (pMob)
  2086.         {
  2087.             iAtt = GetLevel() * 2 + GetPolymorphPoint(POINT_ST) * 2;
  2088.             // lev + con
  2089.             iDef = GetLevel() + GetPolymorphPoint(POINT_HT) + pMob->m_table.wDef;
  2090.         }
  2091.  
  2092.         SetPoint(POINT_ATT_GRADE, iAtt);
  2093.         SetPoint(POINT_DEF_GRADE, iDef);
  2094.         SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
  2095.         SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
  2096.     }
  2097.     else if (IsPC())
  2098.     {
  2099.         SetPoint(POINT_ATT_GRADE, 0);
  2100.         SetPoint(POINT_DEF_GRADE, 0);
  2101.         SetPoint(POINT_CLIENT_DEF_GRADE, 0);
  2102.         SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
  2103.         SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
  2104.  
  2105.         //
  2106.         // 기본 ATK = 2lev + 2str, 직업에 마다 2str은 바뀔 수 있음
  2107.         //
  2108.         int iAtk = GetLevel() * 2;
  2109.         int iStatAtk = 0;
  2110.  
  2111.         switch (GetJob())
  2112.         {
  2113.             case JOB_WARRIOR:
  2114.             case JOB_SURA:
  2115.                 iStatAtk = (2 * GetPoint(POINT_ST));
  2116.                 break;
  2117.  
  2118.             case JOB_ASSASSIN:
  2119.                 iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_DX)) / 3;
  2120.                 break;
  2121.  
  2122.             case JOB_SHAMAN:
  2123.                 iStatAtk = (4 * GetPoint(POINT_ST) + 2 * GetPoint(POINT_IQ)) / 3;
  2124.                 break;
  2125.  
  2126.             default:
  2127.                 sys_err("invalid job %d", GetJob());
  2128.                 iStatAtk = (2 * GetPoint(POINT_ST));
  2129.                 break;
  2130.         }
  2131.  
  2132.         // 말을 타고 있고, 스탯으로 인한 공격력이 ST*2 보다 낮으면 ST*2로 한다.
  2133.         // 스탯을 잘못 찍은 사람 공격력이 더 낮지 않게 하기 위해서다.
  2134.         if (GetMountVnum() && iStatAtk < 2 * GetPoint(POINT_ST))
  2135.             iStatAtk = (2 * GetPoint(POINT_ST));
  2136.  
  2137.         iAtk += iStatAtk;
  2138.  
  2139.         // 승마(말) : 검수라 데미지 감소  
  2140.         if (GetMountVnum())
  2141.         {
  2142.             if (GetJob() == JOB_SURA && GetSkillGroup() == 1)
  2143.             {
  2144.                 iAtk += (iAtk * GetHorseLevel()) / 60;
  2145.             }
  2146.             else
  2147.             {
  2148.                 iAtk += (iAtk * GetHorseLevel()) / 30;
  2149.             }
  2150.         }
  2151.        
  2152.         //
  2153.         // ATK Setting
  2154.         //
  2155.         iAtk += GetPoint(POINT_ATT_GRADE_BONUS);
  2156.  
  2157.         PointChange(POINT_ATT_GRADE, iAtk);
  2158.  
  2159.         // DEF = LEV + CON + ARMOR
  2160.         int iShowDef = GetLevel() + GetPoint(POINT_HT); // For Ymir(천마)
  2161.         int iDef = GetLevel() + (int) (GetPoint(POINT_HT) / 1.25); // For Other
  2162.         int iArmor = 0;
  2163.  
  2164.         LPITEM pkItem;
  2165.  
  2166.         for (int i = 0; i < WEAR_MAX_NUM; ++i)
  2167.             if ((pkItem = GetWear(i)) && pkItem->GetType() == ITEM_ARMOR)
  2168.             {
  2169.                 if (pkItem->GetSubType() == ARMOR_BODY || pkItem->GetSubType() == ARMOR_HEAD || pkItem->GetSubType() == ARMOR_FOOTS || pkItem->GetSubType() == ARMOR_SHIELD)
  2170.                 {
  2171.                     iArmor += pkItem->GetValue(1);
  2172.                     iArmor += (2 * pkItem->GetValue(5));
  2173.                 }
  2174.             }
  2175.  
  2176.         // 말 타고 있을 때 방어력이 말의 기준 방어력보다 낮으면 기준 방어력으로 설정
  2177.         if( true == IsHorseRiding() )
  2178.         {
  2179.             if (iArmor < GetHorseArmor())
  2180.                 iArmor = GetHorseArmor();
  2181.  
  2182.             const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID());
  2183.  
  2184.             if (pHorseName != NULL && strlen(pHorseName))
  2185.             {
  2186.                 iArmor += 20;
  2187.             }
  2188.         }
  2189.  
  2190.         iArmor += GetPoint(POINT_DEF_GRADE_BONUS);
  2191.         iArmor += GetPoint(POINT_PARTY_DEFENDER_BONUS);
  2192.  
  2193.         // INTERNATIONAL_VERSION
  2194.         if (LC_IsYMIR())
  2195.         {
  2196.             PointChange(POINT_DEF_GRADE, iShowDef + iArmor);
  2197.         }
  2198.         else
  2199.         {
  2200.             PointChange(POINT_DEF_GRADE, iDef + iArmor);
  2201.             PointChange(POINT_CLIENT_DEF_GRADE, (iShowDef + iArmor) - GetPoint(POINT_DEF_GRADE));
  2202.         }
  2203.         // END_OF_INTERNATIONAL_VERSION
  2204.  
  2205.         PointChange(POINT_MAGIC_ATT_GRADE, GetLevel() * 2 + GetPoint(POINT_IQ) * 2 + GetPoint(POINT_MAGIC_ATT_GRADE_BONUS));
  2206.         PointChange(POINT_MAGIC_DEF_GRADE, GetLevel() + (GetPoint(POINT_IQ) * 3 + GetPoint(POINT_HT)) / 3 + iArmor / 2 + GetPoint(POINT_MAGIC_DEF_GRADE_BONUS));
  2207.     }
  2208.     else
  2209.     {
  2210.         // 2lev + str * 2
  2211.         int iAtt = GetLevel() * 2 + GetPoint(POINT_ST) * 2;
  2212.         // lev + con
  2213.         int iDef = GetLevel() + GetPoint(POINT_HT) + GetMobTable().wDef;
  2214.  
  2215.         SetPoint(POINT_ATT_GRADE, iAtt);
  2216.         SetPoint(POINT_DEF_GRADE, iDef);
  2217.         SetPoint(POINT_MAGIC_ATT_GRADE, GetPoint(POINT_ATT_GRADE));
  2218.         SetPoint(POINT_MAGIC_DEF_GRADE, GetPoint(POINT_DEF_GRADE));
  2219.     }
  2220. }
  2221.  
  2222. void CHARACTER::ComputePoints()
  2223. {
  2224.     long lStat = GetPoint(POINT_STAT);
  2225.     long lStatResetCount = GetPoint(POINT_STAT_RESET_COUNT);
  2226.     long lSkillActive = GetPoint(POINT_SKILL);
  2227.     long lSkillSub = GetPoint(POINT_SUB_SKILL);
  2228.     long lSkillHorse = GetPoint(POINT_HORSE_SKILL);
  2229.     long lLevelStep = GetPoint(POINT_LEVEL_STEP);
  2230.  
  2231.     long lAttackerBonus = GetPoint(POINT_PARTY_ATTACKER_BONUS);
  2232.     long lTankerBonus = GetPoint(POINT_PARTY_TANKER_BONUS);
  2233.     long lBufferBonus = GetPoint(POINT_PARTY_BUFFER_BONUS);
  2234.     long lSkillMasterBonus = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
  2235.     long lHasteBonus = GetPoint(POINT_PARTY_HASTE_BONUS);
  2236.     long lDefenderBonus = GetPoint(POINT_PARTY_DEFENDER_BONUS);
  2237.  
  2238.     long lHPRecovery = GetPoint(POINT_HP_RECOVERY);
  2239.     long lSPRecovery = GetPoint(POINT_SP_RECOVERY);
  2240. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  2241.     long envanterim = Inven_Point();
  2242. #endif
  2243.  
  2244.     memset(m_pointsInstant.points, 0, sizeof(m_pointsInstant.points));
  2245.     BuffOnAttr_ClearAll();
  2246.     m_SkillDamageBonus.clear();
  2247.  
  2248.     SetPoint(POINT_STAT, lStat);
  2249.     SetPoint(POINT_SKILL, lSkillActive);
  2250.     SetPoint(POINT_SUB_SKILL, lSkillSub);
  2251.     SetPoint(POINT_HORSE_SKILL, lSkillHorse);
  2252.     SetPoint(POINT_LEVEL_STEP, lLevelStep);
  2253.     SetPoint(POINT_STAT_RESET_COUNT, lStatResetCount);
  2254.  
  2255.     SetPoint(POINT_ST, GetRealPoint(POINT_ST));
  2256.     SetPoint(POINT_HT, GetRealPoint(POINT_HT));
  2257.     SetPoint(POINT_DX, GetRealPoint(POINT_DX));
  2258.     SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));
  2259.  
  2260.     SetPart(PART_MAIN, GetOriginalPart(PART_MAIN));
  2261.     SetPart(PART_WEAPON, GetOriginalPart(PART_WEAPON));
  2262.     SetPart(PART_HEAD, GetOriginalPart(PART_HEAD));
  2263.     SetPart(PART_HAIR, GetOriginalPart(PART_HAIR));
  2264.  
  2265.     SetPoint(POINT_PARTY_ATTACKER_BONUS, lAttackerBonus);
  2266.     SetPoint(POINT_PARTY_TANKER_BONUS, lTankerBonus);
  2267.     SetPoint(POINT_PARTY_BUFFER_BONUS, lBufferBonus);
  2268.     SetPoint(POINT_PARTY_SKILL_MASTER_BONUS, lSkillMasterBonus);
  2269.     SetPoint(POINT_PARTY_HASTE_BONUS, lHasteBonus);
  2270.     SetPoint(POINT_PARTY_DEFENDER_BONUS, lDefenderBonus);
  2271.  
  2272.     SetPoint(POINT_HP_RECOVERY, lHPRecovery);
  2273.     SetPoint(POINT_SP_RECOVERY, lSPRecovery);
  2274.  
  2275.     // PC_BANG_ITEM_ADD
  2276.     SetPoint(POINT_PC_BANG_EXP_BONUS, 0);
  2277.     SetPoint(POINT_PC_BANG_DROP_BONUS, 0);
  2278. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  2279.     SetPoint(POINT_INVEN, envanterim);
  2280. #endif
  2281.  
  2282.     // END_PC_BANG_ITEM_ADD
  2283.  
  2284.     int iMaxHP, iMaxSP;
  2285.     int iMaxStamina;
  2286.  
  2287.     if (IsPC())
  2288.     {
  2289.         // 최대 생명력/정신력
  2290.         iMaxHP = JobInitialPoints[GetJob()].max_hp + m_points.iRandomHP + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].hp_per_ht;
  2291.         iMaxSP = JobInitialPoints[GetJob()].max_sp + m_points.iRandomSP + GetPoint(POINT_IQ) * JobInitialPoints[GetJob()].sp_per_iq;
  2292.         iMaxStamina = JobInitialPoints[GetJob()].max_stamina + GetPoint(POINT_HT) * JobInitialPoints[GetJob()].stamina_per_con;
  2293.  
  2294.         {
  2295.             CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_ADD_HP);
  2296.  
  2297.             if (NULL != pkSk)
  2298.             {
  2299.                 pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_ADD_HP) / 100.0f);
  2300.  
  2301.                 iMaxHP += static_cast<int>(pkSk->kPointPoly.Eval());
  2302.             }
  2303.         }
  2304.  
  2305.         // 기본 값들
  2306.         SetPoint(POINT_MOV_SPEED,   100);
  2307.         SetPoint(POINT_ATT_SPEED,   100);
  2308.         PointChange(POINT_ATT_SPEED, GetPoint(POINT_PARTY_HASTE_BONUS));
  2309.         SetPoint(POINT_CASTING_SPEED,   100);
  2310.     }
  2311.     else
  2312.     {
  2313.         iMaxHP = m_pkMobData->m_table.dwMaxHP;
  2314.         iMaxSP = 0;
  2315.         iMaxStamina = 0;
  2316.  
  2317.         SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
  2318.         SetPoint(POINT_MOV_SPEED, m_pkMobData->m_table.sMovingSpeed);
  2319.         SetPoint(POINT_CASTING_SPEED, m_pkMobData->m_table.sAttackSpeed);
  2320.     }
  2321.  
  2322.     if (IsPC())
  2323.     {
  2324.         // 말 타고 있을 때는 기본 스탯이 말의 기준 스탯보다 낮으면 높게 만든다.
  2325.         // 따라서 말의 기준 스탯이 무사 기준이므로, 수라/무당은 전체 스탯 합이
  2326.         // 대채적으로 더 올라가게 될 것이다.
  2327.         if (GetMountVnum())
  2328.         {
  2329.             if (GetHorseST() > GetPoint(POINT_ST))
  2330.                 PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
  2331.  
  2332.             if (GetHorseDX() > GetPoint(POINT_DX))
  2333.                 PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
  2334.  
  2335.             if (GetHorseHT() > GetPoint(POINT_HT))
  2336.                 PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
  2337.  
  2338.             if (GetHorseIQ() > GetPoint(POINT_IQ))
  2339.                 PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
  2340.         }
  2341.  
  2342.     }
  2343.  
  2344.     ComputeBattlePoints();
  2345.  
  2346.     // 기본 HP/SP 설정
  2347.     if (iMaxHP != GetMaxHP())
  2348.     {
  2349.         SetRealPoint(POINT_MAX_HP, iMaxHP); // 기본HP를 RealPoint에 저장해 놓는다.
  2350.     }
  2351.  
  2352.     PointChange(POINT_MAX_HP, 0);
  2353.  
  2354.     if (iMaxSP != GetMaxSP())
  2355.     {
  2356.         SetRealPoint(POINT_MAX_SP, iMaxSP); // 기본SP를 RealPoint에 저장해 놓는다.
  2357.     }
  2358.  
  2359.     PointChange(POINT_MAX_SP, 0);
  2360.  
  2361.     SetMaxStamina(iMaxStamina);
  2362.  
  2363.     m_pointsInstant.dwImmuneFlag = 0;
  2364.  
  2365.     for (int i = 0 ; i < WEAR_MAX_NUM; i++)
  2366.     {
  2367.         LPITEM pItem = GetWear(i);
  2368.         if (pItem)
  2369.         {
  2370.             pItem->ModifyPoints(true);
  2371.             SET_BIT(m_pointsInstant.dwImmuneFlag, GetWear(i)->GetImmuneFlag());
  2372.         }
  2373.     }
  2374.  
  2375.     // 용혼석 시스템
  2376.     // ComputePoints에서는 케릭터의 모든 속성값을 초기화하고,
  2377.     // 아이템, 버프 등에 관련된 모든 속성값을 재계산하기 때문에,
  2378.     // 용혼석 시스템도 ActiveDeck에 있는 모든 용혼석의 속성값을 다시 적용시켜야 한다.
  2379.     if (DragonSoul_IsDeckActivated())
  2380.     {
  2381.         for (int i = WEAR_MAX_NUM + DS_SLOT_MAX * DragonSoul_GetActiveDeck();
  2382.             i < WEAR_MAX_NUM + DS_SLOT_MAX * (DragonSoul_GetActiveDeck() + 1); i++)
  2383.         {
  2384.             LPITEM pItem = GetWear(i);
  2385.             if (pItem)
  2386.             {
  2387.                 if (DSManager::instance().IsTimeLeftDragonSoul(pItem))
  2388.                     pItem->ModifyPoints(true);
  2389.             }
  2390.         }
  2391.     }
  2392.  
  2393.     if (GetHP() > GetMaxHP())
  2394.         PointChange(POINT_HP, GetMaxHP() - GetHP());
  2395.  
  2396.     if (GetSP() > GetMaxSP())
  2397.         PointChange(POINT_SP, GetMaxSP() - GetSP());
  2398.  
  2399.     ComputeSkillPoints();
  2400.  
  2401.     RefreshAffect();
  2402.     CPetSystem* pPetSystem = GetPetSystem();
  2403.     if (NULL != pPetSystem)
  2404.     {
  2405.         pPetSystem->RefreshBuff();
  2406.     }
  2407.  
  2408.     for (TMapBuffOnAttrs::iterator it = m_map_buff_on_attrs.begin(); it != m_map_buff_on_attrs.end(); it++)
  2409.     {
  2410.         it->second->GiveAllAttributes();
  2411.     }
  2412.  
  2413.     UpdatePacket();
  2414. }
  2415.  
  2416. // m_dwPlayStartTime의 단위는 milisecond다. 데이터베이스에는 분단위로 기록하기
  2417. // 때문에 플레이시간을 계산할 때 / 60000 으로 나눠서 하는데, 그 나머지 값이 남았
  2418. // 을 때 여기에 dwTimeRemain으로 넣어서 제대로 계산되도록 해주어야 한다.
  2419. void CHARACTER::ResetPlayTime(DWORD dwTimeRemain)
  2420. {
  2421.     m_dwPlayStartTime = get_dword_time() - dwTimeRemain;
  2422. }
  2423.  
  2424. const int aiRecoveryPercents[10] = { 1, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
  2425.  
  2426. EVENTFUNC(recovery_event)
  2427. {
  2428.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  2429.     if ( info == NULL )
  2430.     {
  2431.         sys_err( "recovery_event> <Factor> Null pointer" );
  2432.         return 0;
  2433.     }
  2434.  
  2435.     LPCHARACTER ch = info->ch;
  2436.  
  2437.     if (ch == NULL) { // <Factor>
  2438.         return 0;
  2439.     }  
  2440.  
  2441.     if (!ch->IsPC())
  2442.     {
  2443.         //
  2444.         // 몬스터 회복
  2445.         //
  2446.         if (ch->IsAffectFlag(AFF_POISON))
  2447.             return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
  2448.  
  2449.         if (2493 == ch->GetMobTable().dwVnum)
  2450.         {
  2451.             int regenPct = BlueDragon_GetRangeFactor("hp_regen", ch->GetHPPct());
  2452.             regenPct += ch->GetMobTable().bRegenPercent;
  2453.  
  2454.             for (int i=1 ; i <= 4 ; ++i)
  2455.             {
  2456.                 if (REGEN_PECT_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
  2457.                 {
  2458.                     DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
  2459.                     size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
  2460.                     size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );
  2461.  
  2462.                     regenPct += (val*cnt);
  2463.  
  2464.                     break;
  2465.                 }
  2466.             }
  2467.  
  2468.             ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * regenPct) / 100));
  2469.         }
  2470.         else if (!ch->IsDoor())
  2471.         {
  2472.             ch->MonsterLog("HP_REGEN +%d", MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
  2473.             ch->PointChange(POINT_HP, MAX(1, (ch->GetMaxHP() * ch->GetMobTable().bRegenPercent) / 100));
  2474.         }
  2475.  
  2476.         if (ch->GetHP() >= ch->GetMaxHP())
  2477.         {
  2478.             ch->m_pkRecoveryEvent = NULL;
  2479.             return 0;
  2480.         }
  2481.  
  2482.         if (2493 == ch->GetMobTable().dwVnum)
  2483.         {
  2484.             for (int i=1 ; i <= 4 ; ++i)
  2485.             {
  2486.                 if (REGEN_TIME_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
  2487.                 {
  2488.                     DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
  2489.                     size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
  2490.                     size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( ch->GetMapIndex(), dwDragonStoneID );
  2491.  
  2492.                     return PASSES_PER_SEC(MAX(1, (ch->GetMobTable().bRegenCycle - (val*cnt))));
  2493.                 }
  2494.             }
  2495.         }
  2496.  
  2497.         return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
  2498.     }
  2499.     else
  2500.     {
  2501.         //
  2502.         // PC 회복
  2503.         //
  2504.         ch->CheckTarget();
  2505.         //ch->UpdateSectree(); // 여기서 이걸 왜하지?
  2506.         ch->UpdateKillerMode();
  2507.  
  2508.         if (ch->IsAffectFlag(AFF_POISON) == true)
  2509.         {
  2510.             // 중독인 경우 자동회복 금지
  2511.             // 파법술인 경우 자동회복 금지
  2512.             return 3;
  2513.         }
  2514.  
  2515.         int iSec = (get_dword_time() - ch->GetLastMoveTime()) / 3000;
  2516.  
  2517.         // SP 회복 루틴.
  2518.         // 왜 이걸로 해서 함수로 빼놨는가 ?!
  2519.         ch->DistributeSP(ch);
  2520.  
  2521.         if (ch->GetMaxHP() <= ch->GetHP())
  2522.             return PASSES_PER_SEC(3);
  2523.  
  2524.         int iPercent = 0;
  2525.         int iAmount = 0;
  2526.        
  2527.         {
  2528.             iPercent = aiRecoveryPercents[MIN(9, iSec)];
  2529.             iAmount = 15 + (ch->GetMaxHP() * iPercent) / 100;
  2530.         }
  2531.        
  2532.         iAmount += (iAmount * ch->GetPoint(POINT_HP_REGEN)) / 100;
  2533.  
  2534.         sys_log(1, "RECOVERY_EVENT: %s %d HP_REGEN %d HP +%d", ch->GetName(), iPercent, ch->GetPoint(POINT_HP_REGEN), iAmount);
  2535.  
  2536.         ch->PointChange(POINT_HP, iAmount, false);
  2537.         return PASSES_PER_SEC(3);
  2538.     }
  2539. }
  2540.  
  2541. void CHARACTER::StartRecoveryEvent()
  2542. {
  2543.     if (m_pkRecoveryEvent)
  2544.         return;
  2545.  
  2546.     if (IsDead() || IsStun())
  2547.         return;
  2548.  
  2549.     if (IsNPC() && GetHP() >= GetMaxHP()) // 몬스터는 체력이 다 차있으면 시작 안한다.
  2550.         return;
  2551.  
  2552.     char_event_info* info = AllocEventInfo<char_event_info>();
  2553.  
  2554.     info->ch = this;
  2555.  
  2556.     int iSec = IsPC() ? 3 : (MAX(1, GetMobTable().bRegenCycle));
  2557.     m_pkRecoveryEvent = event_create(recovery_event, info, PASSES_PER_SEC(iSec));
  2558. }
  2559.  
  2560. void CHARACTER::Standup()
  2561. {
  2562.     struct packet_position pack_position;
  2563.  
  2564.     if (!IsPosition(POS_SITTING))
  2565.         return;
  2566.  
  2567.     SetPosition(POS_STANDING);
  2568.  
  2569.     sys_log(1, "STANDUP: %s", GetName());
  2570.  
  2571.     pack_position.header    = HEADER_GC_CHARACTER_POSITION;
  2572.     pack_position.vid       = GetVID();
  2573.     pack_position.position  = POSITION_GENERAL;
  2574.  
  2575.     PacketAround(&pack_position, sizeof(pack_position));
  2576. }
  2577.  
  2578. void CHARACTER::Sitdown(int is_ground)
  2579. {
  2580.     struct packet_position pack_position;
  2581.  
  2582.     if (IsPosition(POS_SITTING))
  2583.         return;
  2584.  
  2585.     SetPosition(POS_SITTING);
  2586.     sys_log(1, "SITDOWN: %s", GetName());
  2587.  
  2588.     pack_position.header    = HEADER_GC_CHARACTER_POSITION;
  2589.     pack_position.vid       = GetVID();
  2590.     pack_position.position  = POSITION_SITTING_GROUND;
  2591.     PacketAround(&pack_position, sizeof(pack_position));
  2592. }
  2593.  
  2594. void CHARACTER::SetRotation(float fRot)
  2595. {
  2596.     m_pointsInstant.fRot = fRot;
  2597. }
  2598.  
  2599. // x, y 방향으로 보고 선다.
  2600. void CHARACTER::SetRotationToXY(long x, long y)
  2601. {
  2602.     SetRotation(GetDegreeFromPositionXY(GetX(), GetY(), x, y));
  2603. }
  2604.  
  2605. bool CHARACTER::CannotMoveByAffect() const
  2606. {
  2607.     return (IsAffectFlag(AFF_STUN));
  2608. }
  2609.  
  2610. bool CHARACTER::CanMove() const
  2611. {
  2612.     if (CannotMoveByAffect())
  2613.         return false;
  2614.  
  2615.     if (GetMyShop())    // 상점 연 상태에서는 움직일 수 없음
  2616.         return false;
  2617.  
  2618.     // 0.2초 전이라면 움직일 수 없다.
  2619.     /*
  2620.        if (get_float_time() - m_fSyncTime < 0.2f)
  2621.        return false;
  2622.      */
  2623.     return true;
  2624. }
  2625.  
  2626. // 무조건 x, y 위치로 이동 시킨다.
  2627. bool CHARACTER::Sync(long x, long y)
  2628. {
  2629.     if (!GetSectree())
  2630.         return false;
  2631.  
  2632.     LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y);
  2633.  
  2634.     if (!new_tree)
  2635.     {
  2636.         if (GetDesc())
  2637.         {
  2638.             sys_err("cannot find tree at %d %d (name: %s)", x, y, GetName());
  2639.             GetDesc()->SetPhase(PHASE_CLOSE);
  2640.         }
  2641.         else
  2642.         {
  2643.             sys_err("no tree: %s %d %d %d", GetName(), x, y, GetMapIndex());
  2644.             Dead();
  2645.         }
  2646.  
  2647.         return false;
  2648.     }
  2649.  
  2650.     SetRotationToXY(x, y);
  2651.     SetXYZ(x, y, 0);
  2652.  
  2653.     if (GetDungeon())
  2654.     {
  2655.         // 던젼용 이벤트 속성 변화
  2656.         int iLastEventAttr = m_iEventAttr;
  2657.         m_iEventAttr = new_tree->GetEventAttribute(x, y);
  2658.  
  2659.         if (m_iEventAttr != iLastEventAttr)
  2660.         {
  2661.             if (GetParty())
  2662.             {
  2663.                 quest::CQuestManager::instance().AttrOut(GetParty()->GetLeaderPID(), this, iLastEventAttr);
  2664.                 quest::CQuestManager::instance().AttrIn(GetParty()->GetLeaderPID(), this, m_iEventAttr);
  2665.             }
  2666.             else
  2667.             {
  2668.                 quest::CQuestManager::instance().AttrOut(GetPlayerID(), this, iLastEventAttr);
  2669.                 quest::CQuestManager::instance().AttrIn(GetPlayerID(), this, m_iEventAttr);
  2670.             }
  2671.         }
  2672.     }
  2673.  
  2674.     if (GetSectree() != new_tree)
  2675.     {
  2676.         if (!IsNPC())
  2677.         {
  2678.             SECTREEID id = new_tree->GetID();
  2679.             SECTREEID old_id = GetSectree()->GetID();
  2680.  
  2681.             sys_log(0, "SECTREE DIFFER: %s %dx%d was %dx%d",
  2682.                     GetName(),
  2683.                     id.coord.x,
  2684.                     id.coord.y,
  2685.                     old_id.coord.x,
  2686.                     old_id.coord.y);
  2687.         }
  2688.  
  2689.         new_tree->InsertEntity(this);
  2690.     }
  2691.  
  2692.     return true;
  2693. }
  2694.  
  2695. void CHARACTER::Stop()
  2696. {
  2697.     if (!IsState(m_stateIdle))
  2698.         MonsterLog("[IDLE] 정지");
  2699.  
  2700.     GotoState(m_stateIdle);
  2701.  
  2702.     m_posDest.x = m_posStart.x = GetX();
  2703.     m_posDest.y = m_posStart.y = GetY();
  2704. }
  2705.  
  2706. bool CHARACTER::Goto(long x, long y)
  2707. {
  2708.     // TODO 거리체크 필요
  2709.     // 같은 위치면 이동할 필요 없음 (자동 성공)
  2710.     if (GetX() == x && GetY() == y)
  2711.         return false;
  2712.  
  2713.     if (m_posDest.x == x && m_posDest.y == y)
  2714.     {
  2715.         if (!IsState(m_stateMove))
  2716.         {
  2717.             m_dwStateDuration = 4;
  2718.             GotoState(m_stateMove);
  2719.         }
  2720.         return false;
  2721.     }
  2722.  
  2723.     m_posDest.x = x;
  2724.     m_posDest.y = y;
  2725.  
  2726.     CalculateMoveDuration();
  2727.  
  2728.     m_dwStateDuration = 4;
  2729.  
  2730.    
  2731.     if (!IsState(m_stateMove))
  2732.     {
  2733.         MonsterLog("[MOVE] %s", GetVictim() ? "대상추적" : "그냥이동");
  2734.  
  2735.         if (GetVictim())
  2736.         {
  2737.             //MonsterChat(MONSTER_CHAT_CHASE);
  2738.             MonsterChat(MONSTER_CHAT_ATTACK);
  2739.         }
  2740.     }
  2741.  
  2742.     GotoState(m_stateMove);
  2743.  
  2744.     return true;
  2745. }
  2746.  
  2747.  
  2748. DWORD CHARACTER::GetMotionMode() const
  2749. {
  2750.     DWORD dwMode = MOTION_MODE_GENERAL;
  2751.  
  2752.     if (IsPolymorphed())
  2753.         return dwMode;
  2754.  
  2755.     LPITEM pkItem;
  2756.  
  2757.     if ((pkItem = GetWear(WEAR_WEAPON)))
  2758.     {
  2759.         switch (pkItem->GetProto()->bSubType)
  2760.         {
  2761.             case WEAPON_SWORD:
  2762.                 dwMode = MOTION_MODE_ONEHAND_SWORD;
  2763.                 break;
  2764.  
  2765.             case WEAPON_TWO_HANDED:
  2766.                 dwMode = MOTION_MODE_TWOHAND_SWORD;
  2767.                 break;
  2768.  
  2769.             case WEAPON_DAGGER:
  2770.                 dwMode = MOTION_MODE_DUALHAND_SWORD;
  2771.                 break;
  2772.  
  2773.             case WEAPON_BOW:
  2774.                 dwMode = MOTION_MODE_BOW;
  2775.                 break;
  2776.  
  2777.             case WEAPON_BELL:
  2778.                 dwMode = MOTION_MODE_BELL;
  2779.                 break;
  2780.  
  2781.             case WEAPON_FAN:
  2782.                 dwMode = MOTION_MODE_FAN;
  2783.                 break;
  2784.         }
  2785.     }
  2786.     return dwMode;
  2787. }
  2788.  
  2789. float CHARACTER::GetMoveMotionSpeed() const
  2790. {
  2791.     DWORD dwMode = GetMotionMode();
  2792.  
  2793.     const CMotion * pkMotion = NULL;
  2794.  
  2795.     if (!GetMountVnum())
  2796.         pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(dwMode, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
  2797.     else
  2798.     {
  2799.         pkMotion = CMotionManager::instance().GetMotion(GetMountVnum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
  2800.  
  2801.         if (!pkMotion)
  2802.             pkMotion = CMotionManager::instance().GetMotion(GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_HORSE, (IsWalking() && IsPC()) ? MOTION_WALK : MOTION_RUN));
  2803.     }
  2804.  
  2805.     if (pkMotion)
  2806.         return -pkMotion->GetAccumVector().y / pkMotion->GetDuration();
  2807.     else
  2808.     {
  2809.         sys_err("cannot find motion (name %s race %d mode %d)", GetName(), GetRaceNum(), dwMode);
  2810.         return 300.0f;
  2811.     }
  2812. }
  2813.  
  2814. float CHARACTER::GetMoveSpeed() const
  2815. {
  2816.     return GetMoveMotionSpeed() * 10000 / CalculateDuration(GetLimitPoint(POINT_MOV_SPEED), 10000);
  2817. }
  2818.  
  2819. void CHARACTER::CalculateMoveDuration()
  2820. {
  2821.     m_posStart.x = GetX();
  2822.     m_posStart.y = GetY();
  2823.  
  2824.     float fDist = DISTANCE_SQRT(m_posStart.x - m_posDest.x, m_posStart.y - m_posDest.y);
  2825.  
  2826.     float motionSpeed = GetMoveMotionSpeed();
  2827.  
  2828.     m_dwMoveDuration = CalculateDuration(GetLimitPoint(POINT_MOV_SPEED),
  2829.             (int) ((fDist / motionSpeed) * 1000.0f));
  2830.  
  2831.     if (IsNPC())
  2832.         sys_log(1, "%s: GOTO: distance %f, spd %u, duration %u, motion speed %f pos %d %d -> %d %d",
  2833.                 GetName(), fDist, GetLimitPoint(POINT_MOV_SPEED), m_dwMoveDuration, motionSpeed,
  2834.                 m_posStart.x, m_posStart.y, m_posDest.x, m_posDest.y);
  2835.  
  2836.     m_dwMoveStartTime = get_dword_time();
  2837. }
  2838.  
  2839. // x y 위치로 이동 한다. (이동할 수 있는 가 없는 가를 확인 하고 Sync 메소드로 실제 이동 한다)
  2840. // 서버는 char의 x, y 값을 바로 바꾸지만,
  2841. // 클라에서는 이전 위치에서 바꾼 x, y까지 interpolation한다.
  2842. // 걷거나 뛰는 것은 char의 m_bNowWalking에 달려있다.
  2843. // Warp를 의도한 것이라면 Show를 사용할 것.
  2844. bool CHARACTER::Move(long x, long y)
  2845. {
  2846.     // 같은 위치면 이동할 필요 없음 (자동 성공)
  2847.     if (GetX() == x && GetY() == y)
  2848.         return true;
  2849.  
  2850.     if (test_server)
  2851.         if (m_bDetailLog)
  2852.             sys_log(0, "%s position %u %u", GetName(), x, y);
  2853.  
  2854.     OnMove();
  2855.     return Sync(x, y);
  2856. }
  2857.  
  2858. void CHARACTER::SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, int iRot)
  2859. {
  2860.     TPacketGCMove pack;
  2861.  
  2862.     if (bFunc == FUNC_WAIT)
  2863.     {
  2864.         x = m_posDest.x;
  2865.         y = m_posDest.y;
  2866.         dwDuration = m_dwMoveDuration;
  2867.     }
  2868.  
  2869.     EncodeMovePacket(pack, GetVID(), bFunc, bArg, x, y, dwDuration, dwTime, iRot == -1 ? (int) GetRotation() / 5 : iRot);
  2870.     PacketView(&pack, sizeof(TPacketGCMove), this);
  2871. }
  2872.  
  2873. int CHARACTER::GetRealPoint(BYTE type) const
  2874. {
  2875.     return m_points.points[type];
  2876. }
  2877.  
  2878. void CHARACTER::SetRealPoint(BYTE type, int val)
  2879. {
  2880.     m_points.points[type] = val;
  2881. }
  2882.  
  2883. int CHARACTER::GetPolymorphPoint(BYTE type) const
  2884. {
  2885.     if (IsPolymorphed() && !IsPolyMaintainStat())
  2886.     {
  2887.         DWORD dwMobVnum = GetPolymorphVnum();
  2888.         const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
  2889.         int iPower = GetPolymorphPower();
  2890.  
  2891.         if (pMob)
  2892.         {
  2893.             switch (type)
  2894.             {
  2895.                 case POINT_ST:
  2896.                     if (GetJob() == JOB_SHAMAN || GetJob() == JOB_SURA && GetSkillGroup() == 2)
  2897.                         return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_IQ);
  2898.                     return pMob->m_table.bStr * iPower / 100 + GetPoint(POINT_ST);
  2899.  
  2900.                 case POINT_HT:
  2901.                     return pMob->m_table.bCon * iPower / 100 + GetPoint(POINT_HT);
  2902.  
  2903.                 case POINT_IQ:
  2904.                     return pMob->m_table.bInt * iPower / 100 + GetPoint(POINT_IQ);
  2905.  
  2906.                 case POINT_DX:
  2907.                     return pMob->m_table.bDex * iPower / 100 + GetPoint(POINT_DX);
  2908.             }
  2909.         }
  2910.     }
  2911.  
  2912.     return GetPoint(type);
  2913. }
  2914.  
  2915. int CHARACTER::GetPoint(BYTE type) const
  2916. {
  2917.     if (type >= POINT_MAX_NUM)
  2918.     {
  2919.         sys_err("Point type overflow (type %u)", type);
  2920.         return 0;
  2921.     }
  2922.  
  2923.     int val = m_pointsInstant.points[type];
  2924.     int max_val = INT_MAX;
  2925.  
  2926.     switch (type)
  2927.     {
  2928.         case POINT_STEAL_HP:
  2929.         case POINT_STEAL_SP:
  2930.             max_val = 50;
  2931.             break;
  2932.     }
  2933.  
  2934.     if (val > max_val)
  2935.         sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);
  2936.  
  2937.     return (val);
  2938. }
  2939.  
  2940. int CHARACTER::GetLimitPoint(BYTE type) const
  2941. {
  2942.     if (type >= POINT_MAX_NUM)
  2943.     {
  2944.         sys_err("Point type overflow (type %u)", type);
  2945.         return 0;
  2946.     }
  2947.  
  2948.     int val = m_pointsInstant.points[type];
  2949.     int max_val = INT_MAX;
  2950.     int limit = INT_MAX;
  2951.     int min_limit = -INT_MAX;
  2952.  
  2953.     switch (type)
  2954.     {
  2955.         case POINT_ATT_SPEED:
  2956.             min_limit = 0;
  2957.  
  2958.             if (IsPC())
  2959.                 limit = 170;
  2960.             else
  2961.                 limit = 250;
  2962.             break;
  2963.  
  2964.         case POINT_MOV_SPEED:
  2965.             min_limit = 0;
  2966.  
  2967.             if (IsPC())
  2968.                 limit = 200;
  2969.             else
  2970.                 limit = 250;
  2971.             break;
  2972.  
  2973.         case POINT_STEAL_HP:
  2974.         case POINT_STEAL_SP:
  2975.             limit = 50;
  2976.             max_val = 50;
  2977.             break;
  2978.  
  2979.         case POINT_MALL_ATTBONUS:
  2980.         case POINT_MALL_DEFBONUS:
  2981.             limit = 20;
  2982.             max_val = 50;
  2983.             break;
  2984.     }
  2985.  
  2986.     if (val > max_val)
  2987.         sys_err("POINT_ERROR: %s type %d val %d (max: %d)", GetName(), val, max_val);
  2988.  
  2989.     if (val > limit)
  2990.         val = limit;
  2991.  
  2992.     if (val < min_limit)
  2993.         val = min_limit;
  2994.  
  2995.     return (val);
  2996. }
  2997.  
  2998. void CHARACTER::SetPoint(BYTE type, int val)
  2999. {
  3000.     if (type >= POINT_MAX_NUM)
  3001.     {
  3002.         sys_err("Point type overflow (type %u)", type);
  3003.         return;
  3004.     }
  3005.  
  3006.     m_pointsInstant.points[type] = val;
  3007.  
  3008.     // 아직 이동이 다 안끝났다면 이동 시간 계산을 다시 해야 한다.
  3009.     if (type == POINT_MOV_SPEED && get_dword_time() < m_dwMoveStartTime + m_dwMoveDuration)
  3010.     {
  3011.         CalculateMoveDuration();
  3012.     }
  3013. }
  3014.  
  3015. INT CHARACTER::GetAllowedGold() const
  3016. {
  3017.     if (GetLevel() <= 10)
  3018.         return 100000;
  3019.     else if (GetLevel() <= 20)
  3020.         return 500000;
  3021.     else
  3022.         return 50000000;
  3023. }
  3024.  
  3025. void CHARACTER::CheckMaximumPoints()
  3026. {
  3027.     if (GetMaxHP() < GetHP())
  3028.         PointChange(POINT_HP, GetMaxHP() - GetHP());
  3029.  
  3030.     if (GetMaxSP() < GetSP())
  3031.         PointChange(POINT_SP, GetMaxSP() - GetSP());
  3032. }
  3033.  
  3034. void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast)
  3035. {
  3036.     int val = 0;
  3037.  
  3038.     //sys_log(0, "PointChange %d %d | %d -> %d cHP %d mHP %d", type, amount, GetPoint(type), GetPoint(type)+amount, GetHP(), GetMaxHP());
  3039.  
  3040.     switch (type)
  3041.     {
  3042.         case POINT_NONE:
  3043.             return;
  3044.  
  3045.         case POINT_LEVEL:
  3046.             if ((GetLevel() + amount) > gPlayerMaxLevel)
  3047.                 return;
  3048.  
  3049.             SetLevel(GetLevel() + amount);
  3050.             val = GetLevel();
  3051.  
  3052.             sys_log(0, "LEVELUP: %s %d NEXT EXP %d", GetName(), GetLevel(), GetNextExp());
  3053.  
  3054.             PointChange(POINT_NEXT_EXP, GetNextExp(), false);
  3055.  
  3056.             if (amount)
  3057.             {
  3058.                 quest::CQuestManager::instance().LevelUp(GetPlayerID());
  3059.  
  3060.                 LogManager::instance().LevelLog(this, val, GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000);
  3061.  
  3062.                 if (GetGuild())
  3063.                 {
  3064.                     GetGuild()->LevelChange(GetPlayerID(), GetLevel());
  3065.                 }
  3066.  
  3067.                 if (GetParty())
  3068.                 {
  3069.                     GetParty()->RequestSetMemberLevel(GetPlayerID(), GetLevel());
  3070.                 }
  3071.             }
  3072.             break;
  3073.  
  3074.         case POINT_NEXT_EXP:
  3075.             val = GetNextExp();
  3076.             bAmount = false;    // 무조건 bAmount는 false 여야 한다.
  3077.             break;
  3078.  
  3079.         case POINT_EXP:
  3080.             {
  3081.                 DWORD exp = GetExp();
  3082.                 DWORD next_exp = GetNextExp();
  3083.  
  3084.                 // 청소년보호
  3085.                 if (LC_IsNewCIBN())
  3086.                 {
  3087.                     if (IsOverTime(OT_NONE))
  3088.                     {
  3089.                         dev_log(LOG_DEB0, "<EXP_LOG> %s = NONE", GetName());
  3090.                     }
  3091.                     else if (IsOverTime(OT_3HOUR))
  3092.                     {
  3093.                         amount = (amount / 2);
  3094.                         dev_log(LOG_DEB0, "<EXP_LOG> %s = 3HOUR", GetName());
  3095.                     }
  3096.                     else if (IsOverTime(OT_5HOUR))
  3097.                     {
  3098.                         amount = 0;
  3099.                         dev_log(LOG_DEB0, "<EXP_LOG> %s = 5HOUR", GetName());
  3100.                     }
  3101.                 }
  3102.  
  3103.                 // exp가 0 이하로 가지 않도록 한다
  3104.                 if (amount < 0 && exp < -amount)
  3105.                 {
  3106.                     sys_log(1, "%s AMOUNT < 0 %d, CUR EXP: %d", GetName(), -amount, exp);
  3107.                     amount = -exp;
  3108.  
  3109.                     SetExp(exp + amount);
  3110.                     val = GetExp();
  3111.                 }
  3112.                 else
  3113.                 {
  3114.                     if (gPlayerMaxLevel <= GetLevel())
  3115.                         return;
  3116.  
  3117.                     if (test_server)
  3118.                         ChatPacket(CHAT_TYPE_INFO, "You have gained %d exp.", amount);
  3119.  
  3120.                     DWORD iExpBalance = 0;
  3121.  
  3122.                     // 레벨 업!
  3123.                     if (exp + amount >= next_exp)
  3124.                     {
  3125.                         iExpBalance = (exp + amount) - next_exp;
  3126.                         amount = next_exp - exp;
  3127.  
  3128.                         SetExp(0);
  3129.                         exp = next_exp;
  3130.                     }
  3131.                     else
  3132.                     {
  3133.                         SetExp(exp + amount);
  3134.                         exp = GetExp();
  3135.                     }
  3136.  
  3137.                     DWORD q = DWORD(next_exp / 4.0f);
  3138.                     int iLevStep = GetRealPoint(POINT_LEVEL_STEP);
  3139.  
  3140.                     // iLevStep이 4 이상이면 레벨이 올랐어야 하므로 여기에 올 수 없는 값이다.
  3141.                     if (iLevStep >= 4)
  3142.                     {
  3143.                         sys_err("%s LEVEL_STEP bigger than 4! (%d)", GetName(), iLevStep);
  3144.                         iLevStep = 4;
  3145.                     }
  3146.  
  3147.                     if (exp >= next_exp && iLevStep < 4)
  3148.                     {
  3149.                         for (int i = 0; i < 4 - iLevStep; ++i)
  3150.                             PointChange(POINT_LEVEL_STEP, 1, false, true);
  3151.                     }
  3152.                     else if (exp >= q * 3 && iLevStep < 3)
  3153.                     {
  3154.                         for (int i = 0; i < 3 - iLevStep; ++i)
  3155.                             PointChange(POINT_LEVEL_STEP, 1, false, true);
  3156.                     }
  3157.                     else if (exp >= q * 2 && iLevStep < 2)
  3158.                     {
  3159.                         for (int i = 0; i < 2 - iLevStep; ++i)
  3160.                             PointChange(POINT_LEVEL_STEP, 1, false, true);
  3161.                     }
  3162.                     else if (exp >= q && iLevStep < 1)
  3163.                         PointChange(POINT_LEVEL_STEP, 1);
  3164.  
  3165.                     if (iExpBalance)
  3166.                     {
  3167.                         PointChange(POINT_EXP, iExpBalance);
  3168.                     }
  3169.  
  3170.                     val = GetExp();
  3171.                 }
  3172.             }
  3173.             break;
  3174.  
  3175.         case POINT_LEVEL_STEP:
  3176.             if (amount > 0)
  3177.             {
  3178.                 val = GetPoint(POINT_LEVEL_STEP) + amount;
  3179.  
  3180.                 switch (val)
  3181.                 {
  3182.                     case 1:
  3183.                     case 2:
  3184.                     case 3:
  3185.                         //if (GetLevel() < 100) PointChange(POINT_STAT, 1);
  3186.                         if (GetLevel() < 91) PointChange(POINT_STAT, 1);
  3187.                         break;
  3188.  
  3189.                     case 4:
  3190.                         {
  3191.                             int iHP = number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end);
  3192.                             int iSP = number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end);
  3193.  
  3194.                             m_points.iRandomHP += iHP;
  3195.                             m_points.iRandomSP += iSP;
  3196.  
  3197.                             if (GetSkillGroup())
  3198.                             {
  3199.                                 if (GetLevel() >= 5)
  3200.                                     PointChange(POINT_SKILL, 1);
  3201.  
  3202.                                 if (GetLevel() >= 9)
  3203.                                     PointChange(POINT_SUB_SKILL, 1);
  3204.                             }
  3205.  
  3206.                             PointChange(POINT_MAX_HP, iHP);
  3207.                             PointChange(POINT_MAX_SP, iSP);
  3208.                             PointChange(POINT_LEVEL, 1, false, true);
  3209.  
  3210.                             val = 0;
  3211.                         }
  3212.                         break;
  3213.                 }
  3214.  
  3215.                 if (GetLevel() <= 10)
  3216.                     AutoGiveItem(27001, 2);
  3217.                 else if (GetLevel() <= 30)
  3218.                     AutoGiveItem(27002, 2);
  3219.                 else
  3220.                 {
  3221.                     AutoGiveItem(27002, 2);
  3222. //                  AutoGiveItem(27003, 2);
  3223.                 }
  3224.  
  3225.                 PointChange(POINT_HP, GetMaxHP() - GetHP());
  3226.                 PointChange(POINT_SP, GetMaxSP() - GetSP());
  3227.                 PointChange(POINT_STAMINA, GetMaxStamina() - GetStamina());
  3228.  
  3229.                 SetPoint(POINT_LEVEL_STEP, val);
  3230.                 SetRealPoint(POINT_LEVEL_STEP, val);
  3231.  
  3232.                 Save();
  3233.             }
  3234.             else
  3235.                 val = GetPoint(POINT_LEVEL_STEP);
  3236.  
  3237.             break;
  3238.  
  3239.         case POINT_HP:
  3240.             {
  3241.                 if (IsDead() || IsStun())
  3242.                     return;
  3243.  
  3244.                 int prev_hp = GetHP();
  3245.  
  3246.                 amount = MIN(GetMaxHP() - GetHP(), amount);
  3247.                 SetHP(GetHP() + amount);
  3248.                 val = GetHP();
  3249.  
  3250.                 BroadcastTargetPacket();
  3251.  
  3252.                 if (GetParty() && IsPC() && val != prev_hp)
  3253.                     GetParty()->SendPartyInfoOneToAll(this);
  3254.             }
  3255.             break;
  3256.  
  3257.         case POINT_SP:
  3258.             {
  3259.                 if (IsDead() || IsStun())
  3260.                     return;
  3261.  
  3262.                 amount = MIN(GetMaxSP() - GetSP(), amount);
  3263.                 SetSP(GetSP() + amount);
  3264.                 val = GetSP();
  3265.             }
  3266.             break;
  3267.  
  3268.         case POINT_STAMINA:
  3269.             {
  3270.                 if (IsDead() || IsStun())
  3271.                     return;
  3272.  
  3273.                 int prev_val = GetStamina();
  3274.                 amount = MIN(GetMaxStamina() - GetStamina(), amount);
  3275.                 SetStamina(GetStamina() + amount);
  3276.                 val = GetStamina();
  3277.                
  3278.                 if (val == 0)
  3279.                 {
  3280.                     // Stamina가 없으니 걷자!
  3281.                     SetNowWalking(true);
  3282.                 }
  3283.                 else if (prev_val == 0)
  3284.                 {
  3285.                     // 없던 스테미나가 생겼으니 이전 모드 복귀
  3286.                     ResetWalking();
  3287.                 }
  3288.  
  3289.                 if (amount < 0 && val != 0) // 감소는 보내지않는다.
  3290.                     return;
  3291.             }
  3292.             break;
  3293.  
  3294.         case POINT_MAX_HP:
  3295.             {
  3296.                 SetPoint(type, GetPoint(type) + amount);
  3297.  
  3298.                 //SetMaxHP(GetMaxHP() + amount);
  3299.                 // 최대 생명력 = (기본 최대 생명력 + 추가) * 최대생명력%
  3300.                 int hp = GetRealPoint(POINT_MAX_HP);
  3301.                 int add_hp = MIN(3500, hp * GetPoint(POINT_MAX_HP_PCT) / 100);
  3302.                 add_hp += GetPoint(POINT_MAX_HP);
  3303.                 add_hp += GetPoint(POINT_PARTY_TANKER_BONUS);
  3304.  
  3305.                 SetMaxHP(hp + add_hp);
  3306.  
  3307.                 val = GetMaxHP();
  3308.             }
  3309.             break;
  3310.  
  3311.         case POINT_MAX_SP:
  3312.             {
  3313.                 SetPoint(type, GetPoint(type) + amount);
  3314.  
  3315.                 //SetMaxSP(GetMaxSP() + amount);
  3316.                 // 최대 정신력 = (기본 최대 정신력 + 추가) * 최대정신력%
  3317.                 int sp = GetRealPoint(POINT_MAX_SP);
  3318.                 int add_sp = MIN(800, sp * GetPoint(POINT_MAX_SP_PCT) / 100);
  3319.                 add_sp += GetPoint(POINT_MAX_SP);
  3320.                 add_sp += GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
  3321.  
  3322.                 SetMaxSP(sp + add_sp);
  3323.  
  3324.                 val = GetMaxSP();
  3325.             }
  3326.             break;
  3327.  
  3328.         case POINT_MAX_HP_PCT:
  3329.             SetPoint(type, GetPoint(type) + amount);
  3330.             val = GetPoint(type);
  3331.  
  3332.             PointChange(POINT_MAX_HP, 0);
  3333.             break;
  3334.  
  3335.         case POINT_MAX_SP_PCT:
  3336.             SetPoint(type, GetPoint(type) + amount);
  3337.             val = GetPoint(type);
  3338.  
  3339.             PointChange(POINT_MAX_SP, 0);
  3340.             break;
  3341.  
  3342.         case POINT_MAX_STAMINA:
  3343.             SetMaxStamina(GetMaxStamina() + amount);
  3344.             val = GetMaxStamina();
  3345.             break;
  3346.            
  3347. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  3348.         case POINT_INVEN:
  3349.             {
  3350.                 const int64_t Envantertoplami = static_cast<int64_t>(Inven_Point()) + static_cast<int64_t>(amount);
  3351.                 if (Envantertoplami > INVENTORY_LOCK_COVER_COUNT)
  3352.                 {
  3353.                     sys_err("[POINT_INVEN ERROR]");
  3354.                     return;
  3355.                 }
  3356.                 Set_Inventory_Point(Inven_Point() + amount);
  3357.                 val = Inven_Point();
  3358.             }
  3359.             break;
  3360. #endif
  3361.            
  3362.         case POINT_GOLD:
  3363.             {
  3364.                 const int64_t nTotalMoney = static_cast<int64_t>(GetGold()) + static_cast<int64_t>(amount);
  3365.  
  3366.                 if (GOLD_MAX <= nTotalMoney)
  3367.                 {
  3368.                     sys_err("[OVERFLOW_GOLD] OriGold %d AddedGold %d id %u Name %s ", GetGold(), amount, GetPlayerID(), GetName());
  3369.                     LogManager::instance().CharLog(this, GetGold() + amount, "OVERFLOW_GOLD", "");
  3370.                     return;
  3371.                 }
  3372.  
  3373.                 // 청소년보호
  3374.                 if (LC_IsNewCIBN() && amount > 0)
  3375.                 {
  3376.                     if (IsOverTime(OT_NONE))
  3377.                     {
  3378.                         dev_log(LOG_DEB0, "<GOLD_LOG> %s = NONE", GetName());
  3379.                     }
  3380.                     else if (IsOverTime(OT_3HOUR))
  3381.                     {
  3382.                         amount = (amount / 2);
  3383.                         dev_log(LOG_DEB0, "<GOLD_LOG> %s = 3HOUR", GetName());
  3384.                     }
  3385.                     else if (IsOverTime(OT_5HOUR))
  3386.                     {
  3387.                         amount = 0;
  3388.                         dev_log(LOG_DEB0, "<GOLD_LOG> %s = 5HOUR", GetName());
  3389.                     }
  3390.                 }
  3391.  
  3392.                 SetGold(GetGold() + amount);
  3393.                 val = GetGold();
  3394.             }
  3395.             break;
  3396.  
  3397.         case POINT_SKILL:
  3398.         case POINT_STAT:
  3399.         case POINT_SUB_SKILL:
  3400.         case POINT_STAT_RESET_COUNT:
  3401.         case POINT_HORSE_SKILL:
  3402.             SetPoint(type, GetPoint(type) + amount);
  3403.             val = GetPoint(type);
  3404.  
  3405.             SetRealPoint(type, val);
  3406.             break;
  3407.  
  3408.         case POINT_DEF_GRADE:
  3409.             SetPoint(type, GetPoint(type) + amount);
  3410.             val = GetPoint(type);
  3411.  
  3412.             PointChange(POINT_CLIENT_DEF_GRADE, amount);
  3413.             break;
  3414.  
  3415.         case POINT_CLIENT_DEF_GRADE:
  3416.             SetPoint(type, GetPoint(type) + amount);
  3417.             val = GetPoint(type);
  3418.             break;
  3419.  
  3420.         case POINT_ST:
  3421.         case POINT_HT:
  3422.         case POINT_DX:
  3423.         case POINT_IQ:
  3424.         case POINT_HP_REGEN:
  3425.         case POINT_SP_REGEN:
  3426.         case POINT_ATT_SPEED:
  3427.         case POINT_ATT_GRADE:
  3428.         case POINT_MOV_SPEED:
  3429.         case POINT_CASTING_SPEED:
  3430.         case POINT_MAGIC_ATT_GRADE:
  3431.         case POINT_MAGIC_DEF_GRADE:
  3432.         case POINT_BOW_DISTANCE:
  3433.         case POINT_HP_RECOVERY:
  3434.         case POINT_SP_RECOVERY:
  3435.  
  3436.         case POINT_ATTBONUS_HUMAN:  // 42 인간에게 강함
  3437.         case POINT_ATTBONUS_ANIMAL: // 43 동물에게 데미지 % 증가
  3438.         case POINT_ATTBONUS_ORC:        // 44 웅귀에게 데미지 % 증가
  3439.         case POINT_ATTBONUS_MILGYO: // 45 밀교에게 데미지 % 증가
  3440.         case POINT_ATTBONUS_UNDEAD: // 46 시체에게 데미지 % 증가
  3441.         case POINT_ATTBONUS_DEVIL:  // 47 마귀(악마)에게 데미지 % 증가
  3442.  
  3443.         case POINT_ATTBONUS_MONSTER:
  3444.         case POINT_ATTBONUS_SURA:
  3445.         case POINT_ATTBONUS_ASSASSIN:
  3446.         case POINT_ATTBONUS_WARRIOR:
  3447.         case POINT_ATTBONUS_SHAMAN:
  3448.  
  3449.         case POINT_POISON_PCT:
  3450.         case POINT_STUN_PCT:
  3451.         case POINT_SLOW_PCT:
  3452.  
  3453.         case POINT_BLOCK:
  3454.         case POINT_DODGE:
  3455.  
  3456.         case POINT_CRITICAL_PCT:
  3457.         case POINT_RESIST_CRITICAL:
  3458.         case POINT_PENETRATE_PCT:
  3459.         case POINT_RESIST_PENETRATE:
  3460.         case POINT_CURSE_PCT:
  3461.  
  3462.         case POINT_STEAL_HP:        // 48 생명력 흡수
  3463.         case POINT_STEAL_SP:        // 49 정신력 흡수
  3464.  
  3465.         case POINT_MANA_BURN_PCT:   // 50 마나 번
  3466.         case POINT_DAMAGE_SP_RECOVER:   // 51 공격당할 시 정신력 회복 확률
  3467.         case POINT_RESIST_NORMAL_DAMAGE:
  3468.         case POINT_RESIST_SWORD:
  3469.         case POINT_RESIST_TWOHAND:
  3470.         case POINT_RESIST_DAGGER:
  3471.         case POINT_RESIST_BELL:
  3472.         case POINT_RESIST_FAN:
  3473.         case POINT_RESIST_BOW:
  3474.         case POINT_RESIST_FIRE:
  3475.         case POINT_RESIST_ELEC:
  3476.         case POINT_RESIST_MAGIC:
  3477.         case POINT_RESIST_WIND:
  3478.         case POINT_RESIST_ICE:
  3479.         case POINT_RESIST_EARTH:
  3480.         case POINT_RESIST_DARK:
  3481.         case POINT_REFLECT_MELEE:   // 67 공격 반사
  3482.         case POINT_REFLECT_CURSE:   // 68 저주 반사
  3483.         case POINT_POISON_REDUCE:   // 69 독데미지 감소
  3484.         case POINT_KILL_SP_RECOVER: // 70 적 소멸시 MP 회복
  3485.         case POINT_KILL_HP_RECOVERY:    // 75  
  3486.         case POINT_HIT_HP_RECOVERY:
  3487.         case POINT_HIT_SP_RECOVERY:
  3488.         case POINT_MANASHIELD:
  3489.         case POINT_ATT_BONUS:
  3490.         case POINT_DEF_BONUS:
  3491.         case POINT_SKILL_DAMAGE_BONUS:
  3492.         case POINT_NORMAL_HIT_DAMAGE_BONUS:
  3493.  
  3494.             // DEPEND_BONUS_ATTRIBUTES
  3495.         case POINT_SKILL_DEFEND_BONUS:
  3496.         case POINT_NORMAL_HIT_DEFEND_BONUS:
  3497.             SetPoint(type, GetPoint(type) + amount);
  3498.             val = GetPoint(type);
  3499.             break;
  3500.             // END_OF_DEPEND_BONUS_ATTRIBUTES
  3501.  
  3502.         case POINT_PARTY_ATTACKER_BONUS:
  3503.         case POINT_PARTY_TANKER_BONUS:
  3504.         case POINT_PARTY_BUFFER_BONUS:
  3505.         case POINT_PARTY_SKILL_MASTER_BONUS:
  3506.         case POINT_PARTY_HASTE_BONUS:
  3507.         case POINT_PARTY_DEFENDER_BONUS:
  3508.  
  3509.         case POINT_RESIST_WARRIOR :
  3510.         case POINT_RESIST_ASSASSIN :
  3511.         case POINT_RESIST_SURA :
  3512.         case POINT_RESIST_SHAMAN :
  3513.  
  3514.             SetPoint(type, GetPoint(type) + amount);
  3515.             val = GetPoint(type);
  3516.             break;
  3517.  
  3518.         case POINT_MALL_ATTBONUS:
  3519.         case POINT_MALL_DEFBONUS:
  3520.         case POINT_MALL_EXPBONUS:
  3521.         case POINT_MALL_ITEMBONUS:
  3522.         case POINT_MALL_GOLDBONUS:
  3523.         case POINT_MELEE_MAGIC_ATT_BONUS_PER:
  3524.             if (GetPoint(type) + amount > 100)
  3525.             {
  3526.                 sys_err("MALL_BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
  3527.                 amount = 100 - GetPoint(type);
  3528.             }
  3529.  
  3530.             SetPoint(type, GetPoint(type) + amount);
  3531.             val = GetPoint(type);
  3532.             break;
  3533.  
  3534.             // PC_BANG_ITEM_ADD    
  3535.         case POINT_PC_BANG_EXP_BONUS :
  3536.         case POINT_PC_BANG_DROP_BONUS :
  3537.         case POINT_RAMADAN_CANDY_BONUS_EXP:
  3538.             SetPoint(type, amount);
  3539.             val = GetPoint(type);
  3540.             break;
  3541.             // END_PC_BANG_ITEM_ADD    
  3542.  
  3543.         case POINT_EXP_DOUBLE_BONUS:    // 71  
  3544.         case POINT_GOLD_DOUBLE_BONUS:   // 72  
  3545.         case POINT_ITEM_DROP_BONUS: // 73  
  3546.         case POINT_POTION_BONUS:    // 74
  3547.             if (GetPoint(type) + amount > 100)
  3548.             {
  3549.                 sys_err("BONUS exceeded over 100!! point type: %d name: %s amount %d", type, GetName(), amount);
  3550.                 amount = 100 - GetPoint(type);
  3551.             }
  3552.  
  3553.             SetPoint(type, GetPoint(type) + amount);
  3554.             val = GetPoint(type);
  3555.             break;
  3556.  
  3557.         case POINT_IMMUNE_STUN:     // 76
  3558.             SetPoint(type, GetPoint(type) + amount);
  3559.             val = GetPoint(type);
  3560.             if (val)
  3561.             {
  3562.                 SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
  3563.             }
  3564.             else
  3565.             {
  3566.                 REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_STUN);
  3567.             }
  3568.             break;
  3569.  
  3570.         case POINT_IMMUNE_SLOW:     // 77  
  3571.             SetPoint(type, GetPoint(type) + amount);
  3572.             val = GetPoint(type);
  3573.             if (val)
  3574.             {
  3575.                 SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
  3576.             }
  3577.             else
  3578.             {
  3579.                 REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_SLOW);
  3580.             }
  3581.             break;
  3582.  
  3583.         case POINT_IMMUNE_FALL: // 78  
  3584.             SetPoint(type, GetPoint(type) + amount);
  3585.             val = GetPoint(type);
  3586.             if (val)
  3587.             {
  3588.                 SET_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
  3589.             }
  3590.             else
  3591.             {
  3592.                 REMOVE_BIT(m_pointsInstant.dwImmuneFlag, IMMUNE_FALL);
  3593.             }
  3594.             break;
  3595.  
  3596.         case POINT_ATT_GRADE_BONUS:
  3597.             SetPoint(type, GetPoint(type) + amount);
  3598.             PointChange(POINT_ATT_GRADE, amount);
  3599.             val = GetPoint(type);
  3600.             break;
  3601.  
  3602.         case POINT_DEF_GRADE_BONUS:
  3603.             SetPoint(type, GetPoint(type) + amount);
  3604.             PointChange(POINT_DEF_GRADE, amount);
  3605.             val = GetPoint(type);
  3606.             break;
  3607.  
  3608.         case POINT_MAGIC_ATT_GRADE_BONUS:
  3609.             SetPoint(type, GetPoint(type) + amount);
  3610.             PointChange(POINT_MAGIC_ATT_GRADE, amount);
  3611.             val = GetPoint(type);
  3612.             break;
  3613.  
  3614.         case POINT_MAGIC_DEF_GRADE_BONUS:
  3615.             SetPoint(type, GetPoint(type) + amount);
  3616.             PointChange(POINT_MAGIC_DEF_GRADE, amount);
  3617.             val = GetPoint(type);
  3618.             break;
  3619.  
  3620.         case POINT_VOICE:
  3621.         case POINT_EMPIRE_POINT:
  3622.             //sys_err("CHARACTER::PointChange: %s: point cannot be changed. use SetPoint instead (type: %d)", GetName(), type);
  3623.             val = GetRealPoint(type);
  3624.             break;
  3625.  
  3626.         case POINT_POLYMORPH:
  3627.             SetPoint(type, GetPoint(type) + amount);
  3628.             val = GetPoint(type);
  3629.             SetPolymorph(val);
  3630.             break;
  3631.  
  3632.         case POINT_MOUNT:
  3633.             SetPoint(type, GetPoint(type) + amount);
  3634.             val = GetPoint(type);
  3635.             MountVnum(val);
  3636.             break;
  3637.  
  3638.         case POINT_ENERGY:
  3639.         case POINT_COSTUME_ATTR_BONUS:
  3640.             {
  3641.                 int old_val = GetPoint(type);
  3642.                 SetPoint(type, old_val + amount);
  3643.                 val = GetPoint(type);
  3644.                 BuffOnAttr_ValueChange(type, old_val, val);
  3645.             }
  3646.             break;
  3647.  
  3648.         default:
  3649.             sys_err("CHARACTER::PointChange: %s: unknown point change type %d", GetName(), type);
  3650.             return;
  3651.     }
  3652.  
  3653.     switch (type)
  3654.     {
  3655.         case POINT_LEVEL:
  3656.         case POINT_ST:
  3657.         case POINT_DX:
  3658.         case POINT_IQ:
  3659.         case POINT_HT:
  3660.             ComputeBattlePoints();
  3661.             break;
  3662.         case POINT_MAX_HP:
  3663.         case POINT_MAX_SP:
  3664.         case POINT_MAX_STAMINA:
  3665.             break;
  3666.     }
  3667.  
  3668.     if (type == POINT_HP && amount == 0)
  3669.         return;
  3670.  
  3671.     if (GetDesc())
  3672.     {
  3673.         struct packet_point_change pack;
  3674.  
  3675.         pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
  3676.         pack.dwVID = m_vid;
  3677.         pack.type = type;
  3678.         pack.value = val;
  3679.  
  3680.         if (bAmount)
  3681.             pack.amount = amount;
  3682.         else
  3683.             pack.amount = 0;
  3684.  
  3685.         if (!bBroadcast)
  3686.             GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
  3687.         else
  3688.             PacketAround(&pack, sizeof(pack));
  3689.     }
  3690. }
  3691.  
  3692. void CHARACTER::ApplyPoint(BYTE bApplyType, int iVal)
  3693. {
  3694.     switch (bApplyType)
  3695.     {
  3696.         case APPLY_NONE:            // 0
  3697.             break;;
  3698.  
  3699.         case APPLY_CON:
  3700.             PointChange(POINT_HT, iVal);
  3701.             PointChange(POINT_MAX_HP, (iVal * JobInitialPoints[GetJob()].hp_per_ht));
  3702.             PointChange(POINT_MAX_STAMINA, (iVal * JobInitialPoints[GetJob()].stamina_per_con));
  3703.             break;
  3704.  
  3705.         case APPLY_INT:
  3706.             PointChange(POINT_IQ, iVal);
  3707.             PointChange(POINT_MAX_SP, (iVal * JobInitialPoints[GetJob()].sp_per_iq));
  3708.             break;
  3709.  
  3710.         case APPLY_SKILL:
  3711.             // SKILL_DAMAGE_BONUS
  3712.             {
  3713.                 // 최상위 비트 기준으로 8비트 vnum, 9비트 add, 15비트 change
  3714.                 // 00000000 00000000 00000000 00000000
  3715.                 // ^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^
  3716.                 // vnum     ^ add       change
  3717.                 BYTE bSkillVnum = (BYTE) (((DWORD)iVal) >> 24);
  3718.                 int iAdd = iVal & 0x00800000;
  3719.                 int iChange = iVal & 0x007fffff;
  3720.  
  3721.                 sys_log(1, "APPLY_SKILL skill %d add? %d change %d", bSkillVnum, iAdd ? 1 : 0, iChange);
  3722.  
  3723.                 if (0 == iAdd)
  3724.                     iChange = -iChange;
  3725.  
  3726.                 boost::unordered_map<BYTE, int>::iterator iter = m_SkillDamageBonus.find(bSkillVnum);
  3727.  
  3728.                 if (iter == m_SkillDamageBonus.end())
  3729.                     m_SkillDamageBonus.insert(std::make_pair(bSkillVnum, iChange));
  3730.                 else
  3731.                     iter->second += iChange;
  3732.             }
  3733.             // END_OF_SKILL_DAMAGE_BONUS
  3734.             break;
  3735.  
  3736.         case APPLY_STR:
  3737.         case APPLY_DEX:
  3738.         case APPLY_MAX_HP:
  3739.         case APPLY_MAX_SP:
  3740.         case APPLY_MAX_HP_PCT:
  3741.         case APPLY_MAX_SP_PCT:
  3742.         case APPLY_ATT_SPEED:
  3743.         case APPLY_MOV_SPEED:
  3744.         case APPLY_CAST_SPEED:
  3745.         case APPLY_HP_REGEN:
  3746.         case APPLY_SP_REGEN:
  3747.         case APPLY_POISON_PCT:
  3748.         case APPLY_STUN_PCT:
  3749.         case APPLY_SLOW_PCT:
  3750.         case APPLY_CRITICAL_PCT:
  3751.         case APPLY_PENETRATE_PCT:
  3752.         case APPLY_ATTBONUS_HUMAN:
  3753.         case APPLY_ATTBONUS_ANIMAL:
  3754.         case APPLY_ATTBONUS_ORC:
  3755.         case APPLY_ATTBONUS_MILGYO:
  3756.         case APPLY_ATTBONUS_UNDEAD:
  3757.         case APPLY_ATTBONUS_DEVIL:
  3758.         case APPLY_ATTBONUS_WARRIOR:    // 59
  3759.         case APPLY_ATTBONUS_ASSASSIN:   // 60
  3760.         case APPLY_ATTBONUS_SURA:   // 61
  3761.         case APPLY_ATTBONUS_SHAMAN: // 62
  3762.         case APPLY_ATTBONUS_MONSTER:    // 63
  3763.         case APPLY_STEAL_HP:
  3764.         case APPLY_STEAL_SP:
  3765.         case APPLY_MANA_BURN_PCT:
  3766.         case APPLY_DAMAGE_SP_RECOVER:
  3767.         case APPLY_BLOCK:
  3768.         case APPLY_DODGE:
  3769.         case APPLY_RESIST_SWORD:
  3770.         case APPLY_RESIST_TWOHAND:
  3771.         case APPLY_RESIST_DAGGER:
  3772.         case APPLY_RESIST_BELL:
  3773.         case APPLY_RESIST_FAN:
  3774.         case APPLY_RESIST_BOW:
  3775.         case APPLY_RESIST_FIRE:
  3776.         case APPLY_RESIST_ELEC:
  3777.         case APPLY_RESIST_MAGIC:
  3778.         case APPLY_RESIST_WIND:
  3779.         case APPLY_RESIST_ICE:
  3780.         case APPLY_RESIST_EARTH:
  3781.         case APPLY_RESIST_DARK:
  3782.         case APPLY_REFLECT_MELEE:
  3783.         case APPLY_REFLECT_CURSE:
  3784.         case APPLY_ANTI_CRITICAL_PCT:
  3785.         case APPLY_ANTI_PENETRATE_PCT:
  3786.         case APPLY_POISON_REDUCE:
  3787.         case APPLY_KILL_SP_RECOVER:
  3788.         case APPLY_EXP_DOUBLE_BONUS:
  3789.         case APPLY_GOLD_DOUBLE_BONUS:
  3790.         case APPLY_ITEM_DROP_BONUS:
  3791.         case APPLY_POTION_BONUS:
  3792.         case APPLY_KILL_HP_RECOVER:
  3793.         case APPLY_IMMUNE_STUN:
  3794.         case APPLY_IMMUNE_SLOW:
  3795.         case APPLY_IMMUNE_FALL:
  3796.         case APPLY_BOW_DISTANCE:
  3797.         case APPLY_ATT_GRADE_BONUS:
  3798.         case APPLY_DEF_GRADE_BONUS:
  3799.         case APPLY_MAGIC_ATT_GRADE:
  3800.         case APPLY_MAGIC_DEF_GRADE:
  3801.         case APPLY_CURSE_PCT:
  3802.         case APPLY_MAX_STAMINA:
  3803.         case APPLY_MALL_ATTBONUS:
  3804.         case APPLY_MALL_DEFBONUS:
  3805.         case APPLY_MALL_EXPBONUS:
  3806.         case APPLY_MALL_ITEMBONUS:
  3807.         case APPLY_MALL_GOLDBONUS:
  3808.         case APPLY_SKILL_DAMAGE_BONUS:
  3809.         case APPLY_NORMAL_HIT_DAMAGE_BONUS:
  3810.  
  3811.             // DEPEND_BONUS_ATTRIBUTES
  3812.         case APPLY_SKILL_DEFEND_BONUS:
  3813.         case APPLY_NORMAL_HIT_DEFEND_BONUS:
  3814.             // END_OF_DEPEND_BONUS_ATTRIBUTES
  3815.  
  3816.         case APPLY_PC_BANG_EXP_BONUS :
  3817.         case APPLY_PC_BANG_DROP_BONUS :
  3818.  
  3819.         case APPLY_RESIST_WARRIOR :
  3820.         case APPLY_RESIST_ASSASSIN :
  3821.         case APPLY_RESIST_SURA :
  3822.         case APPLY_RESIST_SHAMAN : 
  3823.         case APPLY_ENERGY:                  // 82 기력
  3824.         case APPLY_DEF_GRADE:               // 83 방어력. DEF_GRADE_BONUS는 클라에서 두배로 보여지는 의도된 버그(...)가 있다.
  3825.         case APPLY_COSTUME_ATTR_BONUS:      // 84 코스튬 아이템에 붙은 속성치 보너스
  3826.         case APPLY_MAGIC_ATTBONUS_PER:      // 85 마법 공격력 +x%
  3827.         case APPLY_MELEE_MAGIC_ATTBONUS_PER:            // 86 마법 + 밀리 공격력 +x%
  3828.             PointChange(aApplyInfo[bApplyType].bPointType, iVal);
  3829.             break;
  3830.  
  3831.         default:
  3832.             sys_err("Unknown apply type %d name %s", bApplyType, GetName());
  3833.             break;
  3834.     }
  3835. }
  3836.  
  3837. void CHARACTER::MotionPacketEncode(BYTE motion, LPCHARACTER victim, struct packet_motion * packet)
  3838. {
  3839.     packet->header  = HEADER_GC_MOTION;
  3840.     packet->vid     = m_vid;
  3841.     packet->motion  = motion;
  3842.  
  3843.     if (victim)
  3844.         packet->victim_vid = victim->GetVID();
  3845.     else
  3846.         packet->victim_vid = 0;
  3847. }
  3848.  
  3849. void CHARACTER::Motion(BYTE motion, LPCHARACTER victim)
  3850. {
  3851.     struct packet_motion pack_motion;
  3852.     MotionPacketEncode(motion, victim, &pack_motion);
  3853.     PacketAround(&pack_motion, sizeof(struct packet_motion));
  3854. }
  3855.  
  3856. EVENTFUNC(save_event)
  3857. {
  3858.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  3859.     if ( info == NULL )
  3860.     {
  3861.         sys_err( "save_event> <Factor> Null pointer" );
  3862.         return 0;
  3863.     }
  3864.  
  3865.     LPCHARACTER ch = info->ch;
  3866.  
  3867.     if (ch == NULL) { // <Factor>
  3868.         return 0;
  3869.     }  
  3870.     sys_log(1, "SAVE_EVENT: %s", ch->GetName());
  3871.     ch->Save();
  3872.     ch->FlushDelayedSaveItem();
  3873.     return (save_event_second_cycle);
  3874. }
  3875.  
  3876. void CHARACTER::StartSaveEvent()
  3877. {
  3878.     if (m_pkSaveEvent)
  3879.         return;
  3880.  
  3881.     char_event_info* info = AllocEventInfo<char_event_info>();
  3882.  
  3883.     info->ch = this;
  3884.     m_pkSaveEvent = event_create(save_event, info, save_event_second_cycle);
  3885. }
  3886.  
  3887. void CHARACTER::MonsterLog(const char* format, ...)
  3888. {
  3889.     if (!test_server)
  3890.         return;
  3891.  
  3892.     if (IsPC())
  3893.         return;
  3894.  
  3895.     char chatbuf[CHAT_MAX_LEN + 1];
  3896.     int len = snprintf(chatbuf, sizeof(chatbuf), "%u)", (DWORD)GetVID());
  3897.  
  3898.     if (len < 0 || len >= (int) sizeof(chatbuf))
  3899.         len = sizeof(chatbuf) - 1;
  3900.  
  3901.     va_list args;
  3902.  
  3903.     va_start(args, format);
  3904.  
  3905.     int len2 = vsnprintf(chatbuf + len, sizeof(chatbuf) - len, format, args);
  3906.  
  3907.     if (len2 < 0 || len2 >= (int) sizeof(chatbuf) - len)
  3908.         len += (sizeof(chatbuf) - len) - 1;
  3909.     else
  3910.         len += len2;
  3911.  
  3912.     // \0 문자 포함
  3913.     ++len;
  3914.  
  3915.     va_end(args);
  3916.  
  3917.     TPacketGCChat pack_chat;
  3918.  
  3919.     pack_chat.header    = HEADER_GC_CHAT;
  3920.     pack_chat.size      = sizeof(TPacketGCChat) + len;
  3921.     pack_chat.type      = CHAT_TYPE_TALKING;
  3922.     pack_chat.id        = (DWORD)GetVID();
  3923.     pack_chat.bEmpire   = 0;
  3924.  
  3925.     TEMP_BUFFER buf;
  3926.     buf.write(&pack_chat, sizeof(TPacketGCChat));
  3927.     buf.write(chatbuf, len);
  3928.  
  3929.     CHARACTER_MANAGER::instance().PacketMonsterLog(this, buf.read_peek(), buf.size());
  3930. }
  3931.  
  3932. void CHARACTER::ChatPacket(BYTE type, const char * format, ...)
  3933. {
  3934.     LPDESC d = GetDesc();
  3935.  
  3936.     if (!d || !format)
  3937.         return;
  3938.  
  3939.     char chatbuf[CHAT_MAX_LEN + 1];
  3940.     va_list args;
  3941.  
  3942.     va_start(args, format);
  3943.     int len = vsnprintf(chatbuf, sizeof(chatbuf), format, args);
  3944.     va_end(args);
  3945.  
  3946.     struct packet_chat pack_chat;
  3947.  
  3948.     pack_chat.header    = HEADER_GC_CHAT;
  3949.     pack_chat.size      = sizeof(struct packet_chat) + len;
  3950.     pack_chat.type      = type;
  3951.     pack_chat.id        = 0;
  3952.     pack_chat.bEmpire   = d->GetEmpire();
  3953.  
  3954.     TEMP_BUFFER buf;
  3955.     buf.write(&pack_chat, sizeof(struct packet_chat));
  3956.     buf.write(chatbuf, len);
  3957.  
  3958.     d->Packet(buf.read_peek(), buf.size());
  3959.  
  3960.     if (type == CHAT_TYPE_COMMAND && test_server)
  3961.         sys_log(0, "SEND_COMMAND %s %s", GetName(), chatbuf);
  3962. }
  3963.  
  3964. // MINING
  3965. void CHARACTER::mining_take()
  3966. {
  3967.     m_pkMiningEvent = NULL;
  3968. }
  3969.  
  3970. void CHARACTER::mining_cancel()
  3971. {
  3972.     if (m_pkMiningEvent)
  3973.     {
  3974.         sys_log(0, "XXX MINING CANCEL");
  3975.         event_cancel(&m_pkMiningEvent);
  3976.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채광을 중단하였습니다."));
  3977.     }
  3978. }
  3979.  
  3980. void CHARACTER::mining(LPCHARACTER chLoad)
  3981. {
  3982.     if (m_pkMiningEvent)
  3983.     {
  3984.         mining_cancel();
  3985.         return;
  3986.     }
  3987.  
  3988.     if (!chLoad)
  3989.         return;
  3990.  
  3991.     if (mining::GetRawOreFromLoad(chLoad->GetRaceNum()) == 0)
  3992.         return;
  3993.  
  3994.     LPITEM pick = GetWear(WEAR_WEAPON);
  3995.  
  3996.     if (!pick || pick->GetType() != ITEM_PICK)
  3997.     {
  3998.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("곡괭이를 장착하세요."));
  3999.         return;
  4000.     }
  4001.  
  4002.     int count = number(5, 15); // 동작 횟수, 한 동작당 2초
  4003.  
  4004.     // 채광 동작을 보여줌
  4005.     TPacketGCDigMotion p;
  4006.     p.header = HEADER_GC_DIG_MOTION;
  4007.     p.vid = GetVID();
  4008.     p.target_vid = chLoad->GetVID();
  4009.     p.count = count;
  4010.  
  4011.     PacketAround(&p, sizeof(p));
  4012.  
  4013.     m_pkMiningEvent = mining::CreateMiningEvent(this, chLoad, count);
  4014. }
  4015. // END_OF_MINING
  4016.  
  4017. void CHARACTER::fishing()
  4018. {
  4019.     if (m_pkFishingEvent)
  4020.     {
  4021.         fishing_take();
  4022.         return;
  4023.     }
  4024.  
  4025.     // 못감 속성에서 낚시를 시도한다?
  4026.     {
  4027.         LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(GetMapIndex());
  4028.  
  4029.         int x = GetX();
  4030.         int y = GetY();
  4031.  
  4032.         LPSECTREE tree = pkSectreeMap->Find(x, y);
  4033.         DWORD dwAttr = tree->GetAttribute(x, y);
  4034.  
  4035.         if (IS_SET(dwAttr, ATTR_BLOCK))
  4036.         {
  4037.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시를 할 수 있는 곳이 아닙니다"));
  4038.             return;
  4039.         }
  4040.     }
  4041.  
  4042.     LPITEM rod = GetWear(WEAR_WEAPON);
  4043.  
  4044.     // 낚시대 장착
  4045.     if (!rod || rod->GetType() != ITEM_ROD)
  4046.     {
  4047.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대를 장착 하세요."));
  4048.         return;
  4049.     }
  4050.  
  4051.     if (0 == rod->GetSocket(2))
  4052.     {
  4053.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("미끼를 끼고 던져 주세요."));
  4054.         return;
  4055.     }
  4056.  
  4057.     float fx, fy;
  4058.     GetDeltaByDegree(GetRotation(), 400.0f, &fx, &fy);
  4059.  
  4060.     m_pkFishingEvent = fishing::CreateFishingEvent(this);
  4061. }
  4062.  
  4063. void CHARACTER::fishing_take()
  4064. {
  4065.     LPITEM rod = GetWear(WEAR_WEAPON);
  4066.     if (rod && rod->GetType() == ITEM_ROD)
  4067.     {
  4068.         using fishing::fishing_event_info;
  4069.         if (m_pkFishingEvent)
  4070.         {
  4071.             struct fishing_event_info* info = dynamic_cast<struct fishing_event_info*>(m_pkFishingEvent->info);
  4072.  
  4073.             if (info)
  4074.                 fishing::Take(info, this);
  4075.         }
  4076.     }
  4077.     else
  4078.     {
  4079.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("낚시대가 아닌 물건으로 낚시를 할 수 없습니다!"));
  4080.     }
  4081.  
  4082.     event_cancel(&m_pkFishingEvent);
  4083. }
  4084.  
  4085. bool CHARACTER::StartStateMachine(int iNextPulse)
  4086. {
  4087.     if (CHARACTER_MANAGER::instance().AddToStateList(this))
  4088.     {
  4089.         m_dwNextStatePulse = thecore_heart->pulse + iNextPulse;
  4090.         return true;
  4091.     }
  4092.  
  4093.     return false;
  4094. }
  4095.  
  4096. void CHARACTER::StopStateMachine()
  4097. {
  4098.     CHARACTER_MANAGER::instance().RemoveFromStateList(this);
  4099. }
  4100.  
  4101. void CHARACTER::UpdateStateMachine(DWORD dwPulse)
  4102. {
  4103.     if (dwPulse < m_dwNextStatePulse)
  4104.         return;
  4105.  
  4106.     if (IsDead())
  4107.         return;
  4108.  
  4109.     Update();
  4110.     m_dwNextStatePulse = dwPulse + m_dwStateDuration;
  4111. }
  4112.  
  4113. void CHARACTER::SetNextStatePulse(int iNextPulse)
  4114. {
  4115.     CHARACTER_MANAGER::instance().AddToStateList(this);
  4116.     m_dwNextStatePulse = iNextPulse;
  4117.  
  4118.     if (iNextPulse < 10)
  4119.         MonsterLog("다음상태로어서가자");
  4120. }
  4121.  
  4122.  
  4123. // 캐릭터 인스턴스 업데이트 함수.
  4124. void CHARACTER::UpdateCharacter(DWORD dwPulse)
  4125. {
  4126.     CFSM::Update();
  4127. }
  4128.  
  4129. void CHARACTER::SetShop(LPSHOP pkShop)
  4130. {
  4131.     if ((m_pkShop = pkShop))
  4132.         SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
  4133.     else
  4134.     {
  4135.         REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_SHOP);
  4136.         SetShopOwner(NULL);
  4137.     }
  4138. }
  4139.  
  4140. void CHARACTER::SetExchange(CExchange * pkExchange)
  4141. {
  4142.     m_pkExchange = pkExchange;
  4143. }
  4144.  
  4145. void CHARACTER::SetPart(BYTE bPartPos, WORD wVal)
  4146. {
  4147.     assert(bPartPos < PART_MAX_NUM);
  4148.     m_pointsInstant.parts[bPartPos] = wVal;
  4149. }
  4150.  
  4151. WORD CHARACTER::GetPart(BYTE bPartPos) const
  4152. {
  4153.     assert(bPartPos < PART_MAX_NUM);
  4154.     return m_pointsInstant.parts[bPartPos];
  4155. }
  4156.  
  4157. WORD CHARACTER::GetOriginalPart(BYTE bPartPos) const
  4158. {
  4159.     switch (bPartPos)
  4160.     {
  4161.         case PART_MAIN:
  4162.             if (!IsPC()) // PC가 아닌 경우 현재 파트를 그대로 리턴
  4163.                 return GetPart(PART_MAIN);
  4164.             else
  4165.                 return m_pointsInstant.bBasePart;
  4166.  
  4167.         case PART_HAIR:
  4168.             return GetPart(PART_HAIR);
  4169.  
  4170.         default:
  4171.             return 0;
  4172.     }
  4173. }
  4174.  
  4175. BYTE CHARACTER::GetCharType() const
  4176. {
  4177.     return m_bCharType;
  4178. }
  4179.  
  4180. bool CHARACTER::SetSyncOwner(LPCHARACTER ch, bool bRemoveFromList)
  4181. {
  4182.     // TRENT_MONSTER
  4183.     if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
  4184.         return false;
  4185.     // END_OF_TRENT_MONSTER
  4186.  
  4187.     if (ch == this)
  4188.     {
  4189.         sys_err("SetSyncOwner owner == this (%p)", this);
  4190.         return false;
  4191.     }
  4192.  
  4193.     if (!ch)
  4194.     {
  4195.         if (bRemoveFromList && m_pkChrSyncOwner)
  4196.         {
  4197.             m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
  4198.         }
  4199.  
  4200.         if (m_pkChrSyncOwner)
  4201.             sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
  4202.  
  4203.         // 리스트에서 제거하지 않더라도 포인터는 NULL로 셋팅되어야 한다.
  4204.         m_pkChrSyncOwner = NULL;
  4205.     }
  4206.     else
  4207.     {
  4208.         if (!IsSyncOwner(ch))
  4209.             return false;
  4210.  
  4211.         // 거리가 200 이상이면 SyncOwner가 될 수 없다.
  4212.         if (DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY()) > 250)
  4213.         {
  4214.             sys_log(1, "SetSyncOwner distance over than 250 %s %s", GetName(), ch->GetName());
  4215.  
  4216.             // SyncOwner일 경우 Owner로 표시한다.
  4217.             if (m_pkChrSyncOwner == ch)
  4218.                 return true;
  4219.  
  4220.             return false;
  4221.         }
  4222.  
  4223.         if (m_pkChrSyncOwner != ch)
  4224.         {
  4225.             if (m_pkChrSyncOwner)
  4226.             {
  4227.                 sys_log(1, "SyncRelease %s %p from %s", GetName(), this, m_pkChrSyncOwner->GetName());
  4228.                 m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.remove(this);
  4229.             }
  4230.  
  4231.             m_pkChrSyncOwner = ch;
  4232.             m_pkChrSyncOwner->m_kLst_pkChrSyncOwned.push_back(this);
  4233.  
  4234.             // SyncOwner가 바뀌면 LastSyncTime을 초기화한다.
  4235.             static const timeval zero_tv = {0, 0};
  4236.             SetLastSyncTime(zero_tv);
  4237.  
  4238.             sys_log(1, "SetSyncOwner set %s %p to %s", GetName(), this, ch->GetName());
  4239.         }
  4240.  
  4241.         m_fSyncTime = get_float_time();
  4242.     }
  4243.  
  4244.     // TODO: Sync Owner가 같더라도 계속 패킷을 보내고 있으므로,
  4245.     //       동기화 된 시간이 3초 이상 지났을 때 풀어주는 패킷을
  4246.     //       보내는 방식으로 하면 패킷을 줄일 수 있다.
  4247.     TPacketGCOwnership pack;
  4248.  
  4249.     pack.bHeader    = HEADER_GC_OWNERSHIP;
  4250.     pack.dwOwnerVID = ch ? ch->GetVID() : 0;
  4251.     pack.dwVictimVID    = GetVID();
  4252.  
  4253.     PacketAround(&pack, sizeof(TPacketGCOwnership));
  4254.     return true;
  4255. }
  4256.  
  4257. struct FuncClearSync
  4258. {
  4259.     void operator () (LPCHARACTER ch)
  4260.     {
  4261.         assert(ch != NULL);
  4262.         ch->SetSyncOwner(NULL, false);  // false 플래그로 해야 for_each 가 제대로 돈다.
  4263.     }
  4264. };
  4265.  
  4266. void CHARACTER::ClearSync()
  4267. {
  4268.     SetSyncOwner(NULL);
  4269.  
  4270.     // 아래 for_each에서 나를 m_pkChrSyncOwner로 가진 자들의 포인터를 NULL로 한다.
  4271.     std::for_each(m_kLst_pkChrSyncOwned.begin(), m_kLst_pkChrSyncOwned.end(), FuncClearSync());
  4272.     m_kLst_pkChrSyncOwned.clear();
  4273. }
  4274.  
  4275. bool CHARACTER::IsSyncOwner(LPCHARACTER ch) const
  4276. {
  4277.     if (m_pkChrSyncOwner == ch)
  4278.         return true;
  4279.  
  4280.     // 마지막으로 동기화 된 시간이 3초 이상 지났다면 소유권이 아무에게도
  4281.     // 없다. 따라서 아무나 SyncOwner이므로 true 리턴
  4282.     if (get_float_time() - m_fSyncTime >= 3.0f)
  4283.         return true;
  4284.  
  4285.     return false;
  4286. }
  4287.  
  4288. void CHARACTER::SetParty(LPPARTY pkParty)
  4289. {
  4290.     if (pkParty == m_pkParty)
  4291.         return;
  4292.  
  4293.     if (pkParty && m_pkParty)
  4294.         sys_err("%s is trying to reassigning party (current %p, new party %p)", GetName(), get_pointer(m_pkParty), get_pointer(pkParty));
  4295.  
  4296.     sys_log(1, "PARTY set to %p", get_pointer(pkParty));
  4297.  
  4298.     //if (m_pkDungeon && IsPC())
  4299.     //SetDungeon(NULL);
  4300.     m_pkParty = pkParty;
  4301.  
  4302.     if (IsPC())
  4303.     {
  4304.         if (m_pkParty)
  4305.             SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
  4306.         else
  4307.             REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_PARTY);
  4308.  
  4309.         UpdatePacket();
  4310.     }
  4311. }
  4312.  
  4313. // PARTY_JOIN_BUG_FIX
  4314. /// 파티 가입 event 정보
  4315. EVENTINFO(TPartyJoinEventInfo)
  4316. {
  4317.     DWORD   dwGuestPID;     ///< 파티에 참여할 캐릭터의 PID
  4318.     DWORD   dwLeaderPID;        ///< 파티 리더의 PID
  4319.  
  4320.     TPartyJoinEventInfo()
  4321.     : dwGuestPID( 0 )
  4322.     , dwLeaderPID( 0 )
  4323.     {
  4324.     }
  4325. } ;
  4326.  
  4327. EVENTFUNC(party_request_event)
  4328. {
  4329.     TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(  event->info );
  4330.  
  4331.     if ( info == NULL )
  4332.     {
  4333.         sys_err( "party_request_event> <Factor> Null pointer" );
  4334.         return 0;
  4335.     }
  4336.  
  4337.     LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->dwGuestPID);
  4338.  
  4339.     if (ch)
  4340.     {
  4341.         sys_log(0, "PartyRequestEvent %s", ch->GetName());
  4342.         ch->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
  4343.         ch->SetPartyRequestEvent(NULL);
  4344.     }
  4345.  
  4346.     return 0;
  4347. }
  4348.  
  4349. bool CHARACTER::RequestToParty(LPCHARACTER leader)
  4350. {
  4351.     if (leader->GetParty())
  4352.         leader = leader->GetParty()->GetLeaderCharacter();
  4353.  
  4354.     if (!leader)
  4355.     {
  4356.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("파티장이 접속 상태가 아니라서 요청을 할 수 없습니다."));
  4357.         return false;
  4358.     }
  4359.  
  4360.     if (m_pkPartyRequestEvent)
  4361.         return false;
  4362.  
  4363.     if (!IsPC() || !leader->IsPC())
  4364.         return false;
  4365.  
  4366.     if (leader->IsBlockMode(BLOCK_PARTY_REQUEST))
  4367.         return false;
  4368.  
  4369.     PartyJoinErrCode errcode = IsPartyJoinableCondition(leader, this);
  4370.  
  4371.     switch (errcode)
  4372.     {
  4373.         case PERR_NONE:
  4374.             break;
  4375.  
  4376.         case PERR_SERVER:
  4377.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  4378.             return false;
  4379.  
  4380.         case PERR_DIFFEMPIRE:
  4381.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 제국과 파티를 이룰 수 없습니다."));
  4382.             return false;
  4383.  
  4384.         case PERR_DUNGEON:
  4385.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다."));
  4386.             return false;
  4387.  
  4388.         case PERR_OBSERVER:
  4389.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
  4390.             return false;
  4391.  
  4392.         case PERR_LVBOUNDARY:
  4393.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
  4394.             return false;
  4395.  
  4396.         case PERR_LOWLEVEL:
  4397.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
  4398.             return false;
  4399.  
  4400.         case PERR_HILEVEL:
  4401.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
  4402.             return false;
  4403.  
  4404.         case PERR_ALREADYJOIN:  
  4405.             return false;
  4406.  
  4407.         case PERR_PARTYISFULL:
  4408.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4409.             return false;
  4410.  
  4411.         default:
  4412.             sys_err("Do not process party join error(%d)", errcode);
  4413.             return false;
  4414.     }
  4415.  
  4416.     TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();
  4417.  
  4418.     info->dwGuestPID = GetPlayerID();
  4419.     info->dwLeaderPID = leader->GetPlayerID();
  4420.  
  4421.     SetPartyRequestEvent(event_create(party_request_event, info, PASSES_PER_SEC(10)));
  4422.  
  4423.     leader->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequest %u", (DWORD) GetVID());
  4424.     ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s 님에게 파티가입 신청을 했습니다."), leader->GetName());
  4425.     return true;
  4426. }
  4427.  
  4428. void CHARACTER::DenyToParty(LPCHARACTER member)
  4429. {
  4430.     sys_log(1, "DenyToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));
  4431.  
  4432.     if (!member->m_pkPartyRequestEvent)
  4433.         return;
  4434.  
  4435.     TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);
  4436.  
  4437.     if (!info)
  4438.     {
  4439.         sys_err( "CHARACTER::DenyToParty> <Factor> Null pointer" );
  4440.         return;
  4441.     }
  4442.  
  4443.     if (info->dwGuestPID != member->GetPlayerID())
  4444.         return;
  4445.  
  4446.     if (info->dwLeaderPID != GetPlayerID())
  4447.         return;
  4448.  
  4449.     event_cancel(&member->m_pkPartyRequestEvent);
  4450.  
  4451.     member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
  4452. }
  4453.  
  4454. void CHARACTER::AcceptToParty(LPCHARACTER member)
  4455. {
  4456.     sys_log(1, "AcceptToParty %s member %s %p", GetName(), member->GetName(), get_pointer(member->m_pkPartyRequestEvent));
  4457.  
  4458.     if (!member->m_pkPartyRequestEvent)
  4459.         return;
  4460.  
  4461.     TPartyJoinEventInfo * info = dynamic_cast<TPartyJoinEventInfo *>(member->m_pkPartyRequestEvent->info);
  4462.  
  4463.     if (!info)
  4464.     {
  4465.         sys_err( "CHARACTER::AcceptToParty> <Factor> Null pointer" );
  4466.         return;
  4467.     }
  4468.  
  4469.     if (info->dwGuestPID != member->GetPlayerID())
  4470.         return;
  4471.  
  4472.     if (info->dwLeaderPID != GetPlayerID())
  4473.         return;
  4474.  
  4475.     event_cancel(&member->m_pkPartyRequestEvent);
  4476.  
  4477.     if (!GetParty())
  4478.         member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 파티에 속해있지 않습니다."));
  4479.     else
  4480.     {
  4481.         if (GetPlayerID() != GetParty()->GetLeaderPID())
  4482.             return;
  4483.  
  4484.         PartyJoinErrCode errcode = IsPartyJoinableCondition(this, member);
  4485.         switch (errcode)
  4486.         {
  4487.             case PERR_NONE:         member->PartyJoin(this); return;
  4488.             case PERR_SERVER:       member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다.")); break;
  4489.             case PERR_DUNGEON:      member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다.")); break;
  4490.             case PERR_OBSERVER:     member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다.")); break;
  4491.             case PERR_LVBOUNDARY:   member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다.")); break;
  4492.             case PERR_LOWLEVEL:     member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다.")); break;
  4493.             case PERR_HILEVEL:      member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다.")); break;
  4494.             case PERR_ALREADYJOIN:  break;
  4495.             case PERR_PARTYISFULL: {
  4496.                                        ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4497.                                        member->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티의 인원제한이 초과하여 파티에 참가할 수 없습니다."));
  4498.                                        break;
  4499.                                    }
  4500.             default: sys_err("Do not process party join error(%d)", errcode);
  4501.         }
  4502.     }
  4503.  
  4504.     member->ChatPacket(CHAT_TYPE_COMMAND, "PartyRequestDenied");
  4505. }
  4506.  
  4507. /**
  4508.  * 파티 초대 event callback 함수.
  4509.  * event 가 발동하면 초대 거절로 처리한다.
  4510.  */
  4511. EVENTFUNC(party_invite_event)
  4512. {
  4513.     TPartyJoinEventInfo * pInfo = dynamic_cast<TPartyJoinEventInfo *>(  event->info );
  4514.  
  4515.     if ( pInfo == NULL )
  4516.     {
  4517.         sys_err( "party_invite_event> <Factor> Null pointer" );
  4518.         return 0;
  4519.     }
  4520.  
  4521.     LPCHARACTER pchInviter = CHARACTER_MANAGER::instance().FindByPID(pInfo->dwLeaderPID);
  4522.  
  4523.     if (pchInviter)
  4524.     {
  4525.         sys_log(1, "PartyInviteEvent %s", pchInviter->GetName());
  4526.         pchInviter->PartyInviteDeny(pInfo->dwGuestPID);
  4527.     }
  4528.  
  4529.     return 0;
  4530. }
  4531.  
  4532. void CHARACTER::PartyInvite(LPCHARACTER pchInvitee)
  4533. {
  4534.     if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
  4535.     {
  4536.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티원을 초대할 수 있는 권한이 없습니다."));
  4537.         return;
  4538.     }
  4539.     else if (pchInvitee->IsBlockMode(BLOCK_PARTY_INVITE))
  4540.     {
  4541.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s 님이 파티 거부 상태입니다."), pchInvitee->GetName());
  4542.         return;
  4543.     }
  4544.  
  4545.     PartyJoinErrCode errcode = IsPartyJoinableCondition(this, pchInvitee);
  4546.  
  4547.     switch (errcode)
  4548.     {
  4549.         case PERR_NONE:
  4550.             break;
  4551.  
  4552.         case PERR_SERVER:
  4553.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  4554.             return;
  4555.  
  4556.         case PERR_DIFFEMPIRE:
  4557.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 다른 제국과 파티를 이룰 수 없습니다."));
  4558.             return;
  4559.  
  4560.         case PERR_DUNGEON:
  4561.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대를 할 수 없습니다."));
  4562.             return;
  4563.  
  4564.         case PERR_OBSERVER:
  4565.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
  4566.             return;
  4567.  
  4568.         case PERR_LVBOUNDARY:
  4569.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
  4570.             return;
  4571.  
  4572.         case PERR_LOWLEVEL:
  4573.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
  4574.             return;
  4575.  
  4576.         case PERR_HILEVEL:
  4577.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
  4578.             return;
  4579.  
  4580.         case PERR_ALREADYJOIN:
  4581.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 이미 %s님은 파티에 속해 있습니다."), pchInvitee->GetName());
  4582.             return;
  4583.  
  4584.         case PERR_PARTYISFULL:
  4585.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4586.             return;
  4587.  
  4588.         default:
  4589.             sys_err("Do not process party join error(%d)", errcode);
  4590.             return;
  4591.     }
  4592.  
  4593.     if (m_PartyInviteEventMap.end() != m_PartyInviteEventMap.find(pchInvitee->GetPlayerID()))
  4594.         return;
  4595.  
  4596.     //
  4597.     // EventMap 에 이벤트 추가
  4598.     //
  4599.     TPartyJoinEventInfo* info = AllocEventInfo<TPartyJoinEventInfo>();
  4600.  
  4601.     info->dwGuestPID = pchInvitee->GetPlayerID();
  4602.     info->dwLeaderPID = GetPlayerID();
  4603.  
  4604.     m_PartyInviteEventMap.insert(EventMap::value_type(pchInvitee->GetPlayerID(), event_create(party_invite_event, info, PASSES_PER_SEC(10))));
  4605.  
  4606.     //
  4607.     // 초대 받는 character 에게 초대 패킷 전송
  4608.     //
  4609.  
  4610.     TPacketGCPartyInvite p;
  4611.     p.header = HEADER_GC_PARTY_INVITE;
  4612.     p.leader_vid = GetVID();
  4613.     pchInvitee->GetDesc()->Packet(&p, sizeof(p));
  4614. }
  4615.  
  4616. void CHARACTER::PartyInviteAccept(LPCHARACTER pchInvitee)
  4617. {
  4618.     EventMap::iterator itFind = m_PartyInviteEventMap.find(pchInvitee->GetPlayerID());
  4619.  
  4620.     if (itFind == m_PartyInviteEventMap.end())
  4621.     {
  4622.         sys_log(1, "PartyInviteAccept from not invited character(%s)", pchInvitee->GetName());
  4623.         return;
  4624.     }
  4625.  
  4626.     event_cancel(&itFind->second);
  4627.     m_PartyInviteEventMap.erase(itFind);
  4628.  
  4629.     if (GetParty() && GetParty()->GetLeaderPID() != GetPlayerID())
  4630.     {
  4631.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티원을 초대할 수 있는 권한이 없습니다."));
  4632.         return;
  4633.     }
  4634.  
  4635.     PartyJoinErrCode errcode = IsPartyJoinableMutableCondition(this, pchInvitee);
  4636.  
  4637.     switch (errcode)
  4638.     {
  4639.         case PERR_NONE:
  4640.             break;
  4641.  
  4642.         case PERR_SERVER:
  4643.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 서버 문제로 파티 관련 처리를 할 수 없습니다."));
  4644.             return;
  4645.  
  4646.         case PERR_DUNGEON:
  4647.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 던전 안에서는 파티 초대에 응할 수 없습니다."));
  4648.             return;
  4649.  
  4650.         case PERR_OBSERVER:
  4651.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 관전 모드에선 파티 초대를 할 수 없습니다."));
  4652.             return;
  4653.  
  4654.         case PERR_LVBOUNDARY:
  4655.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> -30 ~ +30 레벨 이내의 상대방만 초대할 수 있습니다."));
  4656.             return;
  4657.  
  4658.         case PERR_LOWLEVEL:
  4659.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최고 레벨 보다 30레벨이 낮아 초대할 수 없습니다."));
  4660.             return;
  4661.  
  4662.         case PERR_HILEVEL:
  4663.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티내 최저 레벨 보다 30레벨이 높아 초대할 수 없습니다."));
  4664.             return;
  4665.  
  4666.         case PERR_ALREADYJOIN:
  4667.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티 초대에 응할 수 없습니다."));
  4668.             return;
  4669.  
  4670.         case PERR_PARTYISFULL:
  4671.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 더 이상 파티원을 초대할 수 없습니다."));
  4672.             pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> 파티의 인원제한이 초과하여 파티에 참가할 수 없습니다."));
  4673.             return;
  4674.  
  4675.         default:
  4676.             sys_err("ignore party join error(%d)", errcode);
  4677.             return;
  4678.     }
  4679.  
  4680.     //
  4681.     // 파티 가입 처리
  4682.     //
  4683.  
  4684.     if (GetParty())
  4685.         pchInvitee->PartyJoin(this);
  4686.     else
  4687.     {
  4688.         LPPARTY pParty = CPartyManager::instance().CreateParty(this);
  4689.  
  4690.         pParty->Join(pchInvitee->GetPlayerID());
  4691.         pParty->Link(pchInvitee);
  4692.         pParty->SendPartyInfoAllToOne(this);
  4693.     }
  4694. }
  4695.  
  4696. void CHARACTER::PartyInviteDeny(DWORD dwPID)
  4697. {
  4698.     EventMap::iterator itFind = m_PartyInviteEventMap.find(dwPID);
  4699.  
  4700.     if (itFind == m_PartyInviteEventMap.end())
  4701.     {
  4702.         sys_log(1, "PartyInviteDeny to not exist event(inviter PID: %d, invitee PID: %d)", GetPlayerID(), dwPID);
  4703.         return;
  4704.     }
  4705.  
  4706.     event_cancel(&itFind->second);
  4707.     m_PartyInviteEventMap.erase(itFind);
  4708.  
  4709.     LPCHARACTER pchInvitee = CHARACTER_MANAGER::instance().FindByPID(dwPID);
  4710.     if (pchInvitee)
  4711.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님이 파티 초대를 거절하셨습니다."), pchInvitee->GetName());
  4712. }
  4713.  
  4714. void CHARACTER::PartyJoin(LPCHARACTER pLeader)
  4715. {
  4716.     pLeader->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님이 파티에 참가하셨습니다."), GetName());
  4717.     ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<파티> %s님의 파티에 참가하셨습니다."), pLeader->GetName());
  4718.  
  4719.     pLeader->GetParty()->Join(GetPlayerID());
  4720.     pLeader->GetParty()->Link(this);
  4721. }
  4722.  
  4723. CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
  4724. {
  4725.     if (pchLeader->GetEmpire() != pchGuest->GetEmpire())
  4726.         return PERR_DIFFEMPIRE;
  4727.  
  4728.     return IsPartyJoinableMutableCondition(pchLeader, pchGuest);
  4729. }
  4730.  
  4731. static bool __party_can_join_by_level(LPCHARACTER leader, LPCHARACTER quest)
  4732. {
  4733.     int level_limit = 30;
  4734.  
  4735.     if (LC_IsCanada())
  4736.         level_limit = 15;
  4737.     else if (LC_IsBrazil() == true)
  4738.     {
  4739.         level_limit = 10;
  4740.     }
  4741.     else
  4742.         level_limit = 30;
  4743.  
  4744.     return (abs(leader->GetLevel() - quest->GetLevel()) <= level_limit);
  4745. }
  4746.  
  4747. CHARACTER::PartyJoinErrCode CHARACTER::IsPartyJoinableMutableCondition(const LPCHARACTER pchLeader, const LPCHARACTER pchGuest)
  4748. {
  4749.     if (!CPartyManager::instance().IsEnablePCParty())
  4750.         return PERR_SERVER;
  4751.     else if (pchLeader->GetDungeon())
  4752.         return PERR_DUNGEON;
  4753.     else if (pchGuest->IsObserverMode())
  4754.         return PERR_OBSERVER;
  4755.     else if (false == __party_can_join_by_level(pchLeader, pchGuest))
  4756.         return PERR_LVBOUNDARY;
  4757.     else if (pchGuest->GetParty())
  4758.         return PERR_ALREADYJOIN;
  4759.     else if (pchLeader->GetParty())
  4760.     {
  4761.         if (pchLeader->GetParty()->GetMemberCount() == PARTY_MAX_MEMBER)
  4762.             return PERR_PARTYISFULL;
  4763.     }
  4764.  
  4765.     return PERR_NONE;
  4766. }
  4767. // END_OF_PARTY_JOIN_BUG_FIX
  4768.  
  4769. void CHARACTER::SetDungeon(LPDUNGEON pkDungeon)
  4770. {
  4771.     if (pkDungeon && m_pkDungeon)
  4772.         sys_err("%s is trying to reassigning dungeon (current %p, new party %p)", GetName(), get_pointer(m_pkDungeon), get_pointer(pkDungeon));
  4773.  
  4774.     if (m_pkDungeon == pkDungeon) {
  4775.         return;
  4776.     }
  4777.  
  4778.     if (m_pkDungeon)
  4779.     {
  4780.         if (IsPC())
  4781.         {
  4782.             if (GetParty())
  4783.                 m_pkDungeon->DecPartyMember(GetParty(), this);
  4784.             else
  4785.                 m_pkDungeon->DecMember(this);
  4786.         }
  4787.         else if (IsMonster() || IsStone())
  4788.         {
  4789.             m_pkDungeon->DecMonster();
  4790.         }
  4791.     }
  4792.  
  4793.     m_pkDungeon = pkDungeon;
  4794.  
  4795.     if (pkDungeon)
  4796.     {
  4797.         sys_log(0, "%s DUNGEON set to %p, PARTY is %p", GetName(), get_pointer(pkDungeon), get_pointer(m_pkParty));
  4798.  
  4799.         if (IsPC())
  4800.         {
  4801.             if (GetParty())
  4802.                 m_pkDungeon->IncPartyMember(GetParty(), this);
  4803.             else
  4804.                 m_pkDungeon->IncMember(this);
  4805.         }
  4806.         else if (IsMonster() || IsStone())
  4807.         {
  4808.             m_pkDungeon->IncMonster();
  4809.         }
  4810.     }
  4811. }
  4812.  
  4813. void CHARACTER::SetWarMap(CWarMap * pWarMap)
  4814. {
  4815.     if (m_pWarMap)
  4816.         m_pWarMap->DecMember(this);
  4817.  
  4818.     m_pWarMap = pWarMap;
  4819.  
  4820.     if (m_pWarMap)
  4821.         m_pWarMap->IncMember(this);
  4822. }
  4823.  
  4824. void CHARACTER::SetWeddingMap(marriage::WeddingMap* pMap)
  4825. {
  4826.     if (m_pWeddingMap)
  4827.         m_pWeddingMap->DecMember(this);
  4828.  
  4829.     m_pWeddingMap = pMap;
  4830.  
  4831.     if (m_pWeddingMap)
  4832.         m_pWeddingMap->IncMember(this);
  4833. }
  4834.  
  4835. void CHARACTER::SetRegen(LPREGEN pkRegen)
  4836. {
  4837.     m_pkRegen = pkRegen;
  4838.     if (pkRegen != NULL) {
  4839.         regen_id_ = pkRegen->id;
  4840.     }
  4841.     m_fRegenAngle = GetRotation();
  4842.     m_posRegen = GetXYZ();
  4843. }
  4844.  
  4845. bool CHARACTER::OnIdle()
  4846. {
  4847.     return false;
  4848. }
  4849.  
  4850. void CHARACTER::OnMove(bool bIsAttack)
  4851. {
  4852.     m_dwLastMoveTime = get_dword_time();
  4853.  
  4854.     if (bIsAttack)
  4855.     {
  4856.         m_dwLastAttackTime = m_dwLastMoveTime;
  4857.  
  4858.         if (IsAffectFlag(AFF_REVIVE_INVISIBLE))
  4859.             RemoveAffect(AFFECT_REVIVE_INVISIBLE);
  4860.  
  4861.         if (IsAffectFlag(AFF_EUNHYUNG))
  4862.         {
  4863.             RemoveAffect(SKILL_EUNHYUNG);
  4864.             SetAffectedEunhyung();
  4865.         }
  4866.         else
  4867.         {
  4868.             ClearAffectedEunhyung();
  4869.         }
  4870.  
  4871.         /*if (IsAffectFlag(AFF_JEONSIN))
  4872.           RemoveAffect(SKILL_JEONSINBANGEO);*/
  4873.     }
  4874.  
  4875.     /*if (IsAffectFlag(AFF_GUNGON))
  4876.       RemoveAffect(SKILL_GUNGON);*/
  4877.  
  4878.     // MINING
  4879.     mining_cancel();
  4880.     // END_OF_MINING
  4881. }
  4882.  
  4883. void CHARACTER::OnClick(LPCHARACTER pkChrCauser)
  4884. {
  4885.     if (!pkChrCauser)
  4886.     {
  4887.         sys_err("OnClick %s by NULL", GetName());
  4888.         return;
  4889.     }
  4890.  
  4891.     DWORD vid = GetVID();
  4892.     sys_log(0, "OnClick %s[vnum %d ServerUniqueID %d, pid %d] by %s", GetName(), GetRaceNum(), vid, GetPlayerID(), pkChrCauser->GetName());
  4893.  
  4894.     // 상점을 연상태로 퀘스트를 진행할 수 없다.
  4895.     {
  4896.         // 단, 자신은 자신의 상점을 클릭할 수 있다.
  4897.         if (pkChrCauser->GetMyShop() && pkChrCauser != this)
  4898.         {
  4899.             sys_err("OnClick Fail (%s->%s) - pc has shop", pkChrCauser->GetName(), GetName());
  4900.             return;
  4901.         }
  4902.     }
  4903.  
  4904.     // 교환중일때 퀘스트를 진행할 수 없다.
  4905.     {
  4906.         if (pkChrCauser->GetExchange())
  4907.         {
  4908.             sys_err("OnClick Fail (%s->%s) - pc is exchanging", pkChrCauser->GetName(), GetName());
  4909.             return;
  4910.         }
  4911.     }
  4912.  
  4913.     if (IsPC())
  4914.     {
  4915.         // 타겟으로 설정된 경우는 PC에 의한 클릭도 퀘스트로 처리하도록 합니다.
  4916.         if (!CTargetManager::instance().GetTargetInfo(pkChrCauser->GetPlayerID(), TARGET_TYPE_VID, GetVID()))
  4917.         {
  4918.             // 2005.03.17.myevan.타겟이 아닌 경우는 개인 상점 처리 기능을 작동시킨다.
  4919.             if (GetMyShop())
  4920.             {
  4921.                 if (pkChrCauser->IsDead() == true) return;
  4922.  
  4923.                 //PREVENT_TRADE_WINDOW
  4924.                 if (pkChrCauser == this) // 자기는 가능
  4925.                 {
  4926.                     if ((GetExchange() || IsOpenSafebox() || GetShopOwner()) || IsCubeOpen())
  4927.                     {
  4928.                         pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중(창고,교환,상점)에는 개인상점을 사용할 수 없습니다."));
  4929.                         return;
  4930.                     }
  4931.                 }
  4932.                 else // 다른 사람이 클릭했을때
  4933.                 {
  4934.                     // 클릭한 사람이 교환/창고/개인상점/상점이용중이라면 불가
  4935.                     if ((pkChrCauser->GetExchange() || pkChrCauser->IsOpenSafebox() || pkChrCauser->GetMyShop() || pkChrCauser->GetShopOwner()) || pkChrCauser->IsCubeOpen() )
  4936.                     {
  4937.                         pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래중(창고,교환,상점)에는 개인상점을 사용할 수 없습니다."));
  4938.                         return;
  4939.                     }
  4940.  
  4941.                     // 클릭한 대상이 교환/창고/상점이용중이라면 불가
  4942.                     //if ((GetExchange() || IsOpenSafebox() || GetShopOwner()))
  4943.                     if ((GetExchange() || IsOpenSafebox() || IsCubeOpen()))
  4944.                     {
  4945.                         pkChrCauser->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 다른 거래를 하고 있는 중입니다."));
  4946.                         return;
  4947.                     }
  4948.                 }
  4949.                 //END_PREVENT_TRADE_WINDOW
  4950.  
  4951.                 if (pkChrCauser->GetShop())
  4952.                 {
  4953.                     pkChrCauser->GetShop()->RemoveGuest(pkChrCauser);
  4954.                     pkChrCauser->SetShop(NULL);
  4955.                 }
  4956.  
  4957.                 GetMyShop()->AddGuest(pkChrCauser, GetVID(), false);
  4958.                 pkChrCauser->SetShopOwner(this);
  4959.                 return;
  4960.             }
  4961.  
  4962.             if (test_server)
  4963.                 sys_err("%s.OnClickFailure(%s) - target is PC", pkChrCauser->GetName(), GetName());
  4964.  
  4965.             return;
  4966.         }
  4967.     }
  4968.  
  4969.     // 청소년은 퀘스트 못함
  4970.     if (LC_IsNewCIBN())
  4971.     {
  4972.         if (pkChrCauser->IsOverTime(OT_3HOUR))
  4973.         {
  4974.             sys_log(0, "Teen OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 3);
  4975.             return;
  4976.         }
  4977.         else if (pkChrCauser->IsOverTime(OT_5HOUR))
  4978.         {
  4979.             sys_log(0, "Teen OverTime : name = %s, hour = %d)", pkChrCauser->GetName(), 5);
  4980.             return;
  4981.         }
  4982.     }
  4983.  
  4984.  
  4985.     pkChrCauser->SetQuestNPCID(GetVID());
  4986.  
  4987.     if (quest::CQuestManager::instance().Click(pkChrCauser->GetPlayerID(), this))
  4988.     {
  4989.         return;
  4990.     }
  4991.  
  4992.  
  4993.     // NPC 전용 기능 수행 : 상점 열기 등
  4994.     if (!IsPC())
  4995.     {
  4996.         if (!m_triggerOnClick.pFunc)
  4997.         {
  4998.             // NPC 트리거 시스템 로그 보기
  4999.             //sys_err("%s.OnClickFailure(%s) : triggerOnClick.pFunc is EMPTY(pid=%d)",
  5000.             //          pkChrCauser->GetName(),
  5001.             //          GetName(),
  5002.             //          pkChrCauser->GetPlayerID());
  5003.             return;
  5004.         }
  5005.  
  5006.         m_triggerOnClick.pFunc(this, pkChrCauser);
  5007.     }
  5008.  
  5009. }
  5010.  
  5011. BYTE CHARACTER::GetGMLevel() const
  5012. {
  5013.     if (test_server)
  5014.         return GM_IMPLEMENTOR;
  5015.     return m_pointsInstant.gm_level;
  5016. }
  5017.  
  5018. void CHARACTER::SetGMLevel()
  5019. {
  5020.     if (GetDesc())
  5021.     {
  5022.         m_pointsInstant.gm_level =  gm_get_level(GetName(), GetDesc()->GetHostName(), GetDesc()->GetAccountTable().login);
  5023.     }
  5024.     else
  5025.     {
  5026.         m_pointsInstant.gm_level = GM_PLAYER;
  5027.     }
  5028. }
  5029.  
  5030. BOOL CHARACTER::IsGM() const
  5031. {
  5032.     if (m_pointsInstant.gm_level != GM_PLAYER)
  5033.         return true;
  5034.     if (test_server)
  5035.         return true;
  5036.     return false;
  5037. }
  5038.  
  5039. void CHARACTER::SetStone(LPCHARACTER pkChrStone)
  5040. {
  5041.     m_pkChrStone = pkChrStone;
  5042.  
  5043.     if (m_pkChrStone)
  5044.     {
  5045.         if (pkChrStone->m_set_pkChrSpawnedBy.find(this) == pkChrStone->m_set_pkChrSpawnedBy.end())
  5046.             pkChrStone->m_set_pkChrSpawnedBy.insert(this);
  5047.     }
  5048. }
  5049.  
  5050. struct FuncDeadSpawnedByStone
  5051. {
  5052.     void operator () (LPCHARACTER ch)
  5053.     {
  5054.         ch->Dead(NULL);
  5055.         ch->SetStone(NULL);
  5056.     }
  5057. };
  5058.  
  5059. void CHARACTER::ClearStone()
  5060. {
  5061.     if (!m_set_pkChrSpawnedBy.empty())
  5062.     {
  5063.         // 내가 스폰시킨 몬스터들을 모두 죽인다.
  5064.         FuncDeadSpawnedByStone f;
  5065.         std::for_each(m_set_pkChrSpawnedBy.begin(), m_set_pkChrSpawnedBy.end(), f);
  5066.         m_set_pkChrSpawnedBy.clear();
  5067.     }
  5068.  
  5069.     if (!m_pkChrStone)
  5070.         return;
  5071.  
  5072.     m_pkChrStone->m_set_pkChrSpawnedBy.erase(this);
  5073.     m_pkChrStone = NULL;
  5074. }
  5075.  
  5076. void CHARACTER::ClearTarget()
  5077. {
  5078.     if (m_pkChrTarget)
  5079.     {
  5080.         m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
  5081.         m_pkChrTarget = NULL;
  5082.     }
  5083.  
  5084.     TPacketGCTarget p;
  5085.  
  5086.     p.header = HEADER_GC_TARGET;
  5087.     p.dwVID = 0;
  5088.     p.bHPPercent = 0;
  5089.  
  5090.     CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();
  5091.  
  5092.     while (it != m_set_pkChrTargetedBy.end())
  5093.     {
  5094.         LPCHARACTER pkChr = *(it++);
  5095.         pkChr->m_pkChrTarget = NULL;
  5096.  
  5097.         if (!pkChr->GetDesc())
  5098.         {
  5099.             sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
  5100.             abort();
  5101.         }
  5102.  
  5103.         pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
  5104.     }
  5105.  
  5106.     m_set_pkChrTargetedBy.clear();
  5107. }
  5108.  
  5109. void CHARACTER::SetTarget(LPCHARACTER pkChrTarget)
  5110. {
  5111.     if (m_pkChrTarget == pkChrTarget)
  5112.         return;
  5113.  
  5114.     // CASTLE
  5115.     if (IS_CASTLE_MAP(GetMapIndex()) && !IsGM())
  5116.         return;
  5117.     // CASTLE
  5118.  
  5119.     if (m_pkChrTarget)
  5120.         m_pkChrTarget->m_set_pkChrTargetedBy.erase(this);
  5121.  
  5122.     m_pkChrTarget = pkChrTarget;
  5123.  
  5124.     TPacketGCTarget p;
  5125.  
  5126.     p.header = HEADER_GC_TARGET;
  5127.  
  5128.     if (m_pkChrTarget)
  5129.     {
  5130.         m_pkChrTarget->m_set_pkChrTargetedBy.insert(this);
  5131.  
  5132.         p.dwVID = m_pkChrTarget->GetVID();
  5133.  
  5134.         if (m_pkChrTarget->IsPC() && !m_pkChrTarget->IsPolymorphed() || m_pkChrTarget->GetMaxHP() <= 0)
  5135.             p.bHPPercent = 0;
  5136.         else
  5137.         {
  5138.             if (m_pkChrTarget->GetRaceNum() == 20101 ||
  5139.                     m_pkChrTarget->GetRaceNum() == 20102 ||
  5140.                     m_pkChrTarget->GetRaceNum() == 20103 ||
  5141.                     m_pkChrTarget->GetRaceNum() == 20104 ||
  5142.                     m_pkChrTarget->GetRaceNum() == 20105 ||
  5143.                     m_pkChrTarget->GetRaceNum() == 20106 ||
  5144.                     m_pkChrTarget->GetRaceNum() == 20107 ||
  5145.                     m_pkChrTarget->GetRaceNum() == 20108 ||
  5146.                     m_pkChrTarget->GetRaceNum() == 20109)
  5147.             {
  5148.                 LPCHARACTER owner = m_pkChrTarget->GetVictim();
  5149.  
  5150.                 if (owner)
  5151.                 {
  5152.                     int iHorseHealth = owner->GetHorseHealth();
  5153.                     int iHorseMaxHealth = owner->GetHorseMaxHealth();
  5154.  
  5155.                     if (iHorseMaxHealth)
  5156.                         p.bHPPercent = MINMAX(0,  iHorseHealth * 100 / iHorseMaxHealth, 100);
  5157.                     else
  5158.                         p.bHPPercent = 100;
  5159.                 }
  5160.                 else
  5161.                     p.bHPPercent = 100;
  5162.             }
  5163.             else
  5164.                 p.bHPPercent = MINMAX(0, (m_pkChrTarget->GetHP() * 100) / m_pkChrTarget->GetMaxHP(), 100);
  5165.         }
  5166.     }
  5167.     else
  5168.     {
  5169.         p.dwVID = 0;
  5170.         p.bHPPercent = 0;
  5171.     }
  5172.  
  5173.     GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
  5174. }
  5175.  
  5176. void CHARACTER::BroadcastTargetPacket()
  5177. {
  5178.     if (m_set_pkChrTargetedBy.empty())
  5179.         return;
  5180.  
  5181.     TPacketGCTarget p;
  5182.  
  5183.     p.header = HEADER_GC_TARGET;
  5184.     p.dwVID = GetVID();
  5185.  
  5186.     if (IsPC())
  5187.         p.bHPPercent = 0;
  5188.     else
  5189.         p.bHPPercent = MINMAX(0, (GetHP() * 100) / GetMaxHP(), 100);
  5190.  
  5191.     CHARACTER_SET::iterator it = m_set_pkChrTargetedBy.begin();
  5192.  
  5193.     while (it != m_set_pkChrTargetedBy.end())
  5194.     {
  5195.         LPCHARACTER pkChr = *it++;
  5196.  
  5197.         if (!pkChr->GetDesc())
  5198.         {
  5199.             sys_err("%s %p does not have desc", pkChr->GetName(), get_pointer(pkChr));
  5200.             abort();
  5201.         }
  5202.  
  5203.         pkChr->GetDesc()->Packet(&p, sizeof(TPacketGCTarget));
  5204.     }
  5205. }
  5206.  
  5207. void CHARACTER::CheckTarget()
  5208. {
  5209.     if (!m_pkChrTarget)
  5210.         return;
  5211.  
  5212.     if (DISTANCE_APPROX(GetX() - m_pkChrTarget->GetX(), GetY() - m_pkChrTarget->GetY()) >= 4800)
  5213.         SetTarget(NULL);
  5214. }
  5215.  
  5216. void CHARACTER::SetWarpLocation(long lMapIndex, long x, long y)
  5217. {
  5218.     m_posWarp.x = x * 100;
  5219.     m_posWarp.y = y * 100;
  5220.     m_lWarpMapIndex = lMapIndex;
  5221. }
  5222.  
  5223. void CHARACTER::SaveExitLocation()
  5224. {
  5225.     m_posExit = GetXYZ();
  5226.     m_lExitMapIndex = GetMapIndex();
  5227. }
  5228.  
  5229. void CHARACTER::ExitToSavedLocation()
  5230. {
  5231.     sys_log (0, "ExitToSavedLocation");
  5232.     WarpSet(m_posWarp.x, m_posWarp.y, m_lWarpMapIndex);
  5233.  
  5234.     m_posExit.x = m_posExit.y = m_posExit.z = 0;
  5235.     m_lExitMapIndex = 0;
  5236. }
  5237.  
  5238. // fixme
  5239. // 지금까진 privateMapIndex 가 현재 맵 인덱스와 같은지 체크 하는 것을 외부에서 하고,
  5240. // 다르면 warpset을 불렀는데
  5241. // 이를 warpset 안으로 넣자.
  5242. bool CHARACTER::WarpSet(long x, long y, long lPrivateMapIndex)
  5243. {
  5244.     if (!IsPC())
  5245.         return false;
  5246.  
  5247.     long lAddr;
  5248.     long lMapIndex;
  5249.     WORD wPort;
  5250.  
  5251.     if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort))
  5252.     {
  5253.         sys_err("cannot find map location index %d x %d y %d name %s", lMapIndex, x, y, GetName());
  5254.         return false;
  5255.     }
  5256.  
  5257.     //Send Supplementary Data Block if new map requires security packages in loading this map
  5258.     {
  5259.         long lCurAddr;
  5260.         long lCurMapIndex = 0;
  5261.         WORD wCurPort;
  5262.  
  5263.         CMapLocation::instance().Get(GetX(), GetY(), lCurMapIndex, lCurAddr, wCurPort);
  5264.  
  5265.         //do not send SDB files if char is in the same map
  5266.         if( lCurMapIndex != lMapIndex )
  5267.         {
  5268.             const TMapRegion * rMapRgn = SECTREE_MANAGER::instance().GetMapRegion(lMapIndex);
  5269.             {
  5270.                 DESC_MANAGER::instance().SendClientPackageSDBToLoadMap( GetDesc(), rMapRgn->strMapName.c_str() );  
  5271.             }
  5272.         }
  5273.     }
  5274.  
  5275.     if (lPrivateMapIndex >= 10000)
  5276.     {
  5277.         if (lPrivateMapIndex / 10000 != lMapIndex)
  5278.         {
  5279.             sys_err("Invalid map inedx %d, must be child of %d", lPrivateMapIndex, lMapIndex);
  5280.             return false;
  5281.         }
  5282.  
  5283.         lMapIndex = lPrivateMapIndex;
  5284.     }
  5285.  
  5286.     Stop();
  5287.     Save();
  5288.  
  5289.     if (GetSectree())
  5290.     {
  5291.         GetSectree()->RemoveEntity(this);
  5292.         ViewCleanup();
  5293.  
  5294.         EncodeRemovePacket(this);
  5295.     }
  5296.  
  5297.     m_lWarpMapIndex = lMapIndex;
  5298.     m_posWarp.x = x;
  5299.     m_posWarp.y = y;
  5300.  
  5301.     sys_log(0, "WarpSet %s %d %d current map %d target map %d", GetName(), x, y, GetMapIndex(), lMapIndex);
  5302.  
  5303.     TPacketGCWarp p;
  5304.  
  5305.     p.bHeader   = HEADER_GC_WARP;
  5306.     p.lX    = x;
  5307.     p.lY    = y;
  5308.     p.lAddr = lAddr;
  5309.     p.wPort = wPort;
  5310.  
  5311.     GetDesc()->Packet(&p, sizeof(TPacketGCWarp));
  5312.  
  5313.     //if (!LC_IsNewCIBN())
  5314.     {
  5315.         char buf[256];
  5316.         snprintf(buf, sizeof(buf), "%s MapIdx %ld DestMapIdx%ld DestX%ld DestY%ld Empire%d", GetName(), GetMapIndex(), lPrivateMapIndex, x, y, GetEmpire());
  5317.         LogManager::instance().CharLog(this, 0, "WARP", buf);
  5318.     }
  5319.  
  5320.     return true;
  5321. }
  5322.  
  5323. void CHARACTER::WarpEnd()
  5324. {
  5325.     if (test_server)
  5326.         sys_log(0, "WarpEnd %s", GetName());
  5327.  
  5328.     if (m_posWarp.x == 0 && m_posWarp.y == 0)
  5329.         return;
  5330.  
  5331.     int index = m_lWarpMapIndex;
  5332.  
  5333.     if (index > 10000)
  5334.         index /= 10000;
  5335.  
  5336.     if (!map_allow_find(index))
  5337.     {
  5338.         // 이 곳으로 워프할 수 없으므로 워프하기 전 좌표로 되돌리자.
  5339.         sys_err("location %d %d not allowed to login this server", m_posWarp.x, m_posWarp.y);
  5340.         GetDesc()->SetPhase(PHASE_CLOSE);
  5341.         return;
  5342.     }
  5343.  
  5344.     sys_log(0, "WarpEnd %s %d %u %u", GetName(), m_lWarpMapIndex, m_posWarp.x, m_posWarp.y);
  5345.  
  5346.     Show(m_lWarpMapIndex, m_posWarp.x, m_posWarp.y, 0);
  5347.     Stop();
  5348.  
  5349.     m_lWarpMapIndex = 0;
  5350.     m_posWarp.x = m_posWarp.y = m_posWarp.z = 0;
  5351.  
  5352.     {
  5353.         // P2P Login
  5354.         TPacketGGLogin p;
  5355.  
  5356.         p.bHeader = HEADER_GG_LOGIN;
  5357.         strlcpy(p.szName, GetName(), sizeof(p.szName));
  5358.         p.dwPID = GetPlayerID();
  5359.         p.bEmpire = GetEmpire();
  5360.         p.lMapIndex = SECTREE_MANAGER::instance().GetMapIndex(GetX(), GetY());
  5361.         p.bChannel = g_bChannel;
  5362.  
  5363.         P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGLogin));
  5364.     }
  5365. }
  5366.  
  5367. bool CHARACTER::Return()
  5368. {
  5369.     if (!IsNPC())
  5370.         return false;
  5371.  
  5372.     int x, y;
  5373.     /*
  5374.        float fDist = DISTANCE_SQRT(m_pkMobData->m_posLastAttacked.x - GetX(), m_pkMobData->m_posLastAttacked.y - GetY());
  5375.        float fx, fy;
  5376.        GetDeltaByDegree(GetRotation(), fDist, &fx, &fy);
  5377.        x = GetX() + (int) fx;
  5378.        y = GetY() + (int) fy;
  5379.      */
  5380.     SetVictim(NULL);
  5381.  
  5382.     x = m_pkMobInst->m_posLastAttacked.x;
  5383.     y = m_pkMobInst->m_posLastAttacked.y;
  5384.  
  5385.     SetRotationToXY(x, y);
  5386.  
  5387.     if (!Goto(x, y))
  5388.         return false;
  5389.  
  5390.     SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
  5391.  
  5392.     if (test_server)
  5393.         sys_log(0, "%s %p 포기하고 돌아가자! %d %d", GetName(), this, x, y);
  5394.  
  5395.     if (GetParty())
  5396.         GetParty()->SendMessage(this, PM_RETURN, x, y);
  5397.  
  5398.     return true;
  5399. }
  5400.  
  5401. bool CHARACTER::Follow(LPCHARACTER pkChr, float fMinDistance)
  5402. {
  5403.     if (IsPC())
  5404.     {
  5405.         sys_err("CHARACTER::Follow : PC cannot use this method", GetName());
  5406.         return false;
  5407.     }
  5408.  
  5409.     // TRENT_MONSTER
  5410.     if (IS_SET(m_pointsInstant.dwAIFlag, AIFLAG_NOMOVE))
  5411.     {
  5412.         if (pkChr->IsPC()) // 쫓아가는 상대가 PC일 때
  5413.         {
  5414.             // If i'm in a party. I must obey party leader's AI.
  5415.             if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
  5416.             {
  5417.                 if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
  5418.                 {
  5419.                     // 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
  5420.                     if (m_pkMobData->m_table.wAttackRange < DISTANCE_APPROX(pkChr->GetX() - GetX(), pkChr->GetY() - GetY()))
  5421.                         if (Return())
  5422.                             return true;
  5423.                 }
  5424.             }
  5425.         }
  5426.         return false;
  5427.     }
  5428.     // END_OF_TRENT_MONSTER
  5429.  
  5430.     long x = pkChr->GetX();
  5431.     long y = pkChr->GetY();
  5432.  
  5433.     if (pkChr->IsPC()) // 쫓아가는 상대가 PC일 때
  5434.     {
  5435.         // If i'm in a party. I must obey party leader's AI.
  5436.         if (!GetParty() || !GetParty()->GetLeader() || GetParty()->GetLeader() == this)
  5437.         {
  5438.             if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
  5439.             {
  5440.                 // 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
  5441.                 if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
  5442.                     if (Return())
  5443.                         return true;
  5444.             }
  5445.         }
  5446.     }
  5447.  
  5448.     if (IsGuardNPC())
  5449.     {
  5450.         if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
  5451.             if (Return())
  5452.                 return true;
  5453.     }
  5454.  
  5455.     if (pkChr->IsState(pkChr->m_stateMove) &&
  5456.         GetMobBattleType() != BATTLE_TYPE_RANGE &&
  5457.         GetMobBattleType() != BATTLE_TYPE_MAGIC &&
  5458.         false == IsPet())
  5459.     {
  5460.         // 대상이 이동중이면 예측 이동을 한다
  5461.         // 나와 상대방의 속도차와 거리로부터 만날 시간을 예상한 후
  5462.         // 상대방이 그 시간까지 직선으로 이동한다고 가정하여 거기로 이동한다.
  5463.         float rot = pkChr->GetRotation();
  5464.         float rot_delta = GetDegreeDelta(rot, GetDegreeFromPositionXY(GetX(), GetY(), pkChr->GetX(), pkChr->GetY()));
  5465.  
  5466.         float yourSpeed = pkChr->GetMoveSpeed();
  5467.         float mySpeed = GetMoveSpeed();
  5468.  
  5469.         float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
  5470.         float fFollowSpeed = mySpeed - yourSpeed * cos(rot_delta * M_PI / 180);
  5471.  
  5472.         if (fFollowSpeed >= 0.1f)
  5473.         {
  5474.             float fMeetTime = fDist / fFollowSpeed;
  5475.             float fYourMoveEstimateX, fYourMoveEstimateY;
  5476.  
  5477.             if( fMeetTime * yourSpeed <= 100000.0f )
  5478.             {
  5479.                 GetDeltaByDegree(pkChr->GetRotation(), fMeetTime * yourSpeed, &fYourMoveEstimateX, &fYourMoveEstimateY);
  5480.  
  5481.                 x += (long) fYourMoveEstimateX;
  5482.                 y += (long) fYourMoveEstimateY;
  5483.  
  5484.                 float fDistNew = sqrt(((double)x - GetX())*(x-GetX())+((double)y - GetY())*(y-GetY()));
  5485.                 if (fDist < fDistNew)
  5486.                 {
  5487.                     x = (long)(GetX() + (x - GetX()) * fDist / fDistNew);
  5488.                     y = (long)(GetY() + (y - GetY()) * fDist / fDistNew);
  5489.                 }
  5490.             }
  5491.         }
  5492.     }
  5493.  
  5494.     // 가려는 위치를 바라봐야 한다.
  5495.     SetRotationToXY(x, y);
  5496.  
  5497.     float fDist = DISTANCE_SQRT(x - GetX(), y - GetY());
  5498.  
  5499.     if (fDist <= fMinDistance)
  5500.         return false;
  5501.  
  5502.     float fx, fy;
  5503.  
  5504.     if (IsChangeAttackPosition(pkChr) && GetMobRank() < MOB_RANK_BOSS)
  5505.     {
  5506.         // 상대방 주변 랜덤한 곳으로 이동
  5507.         SetChangeAttackPositionTime();
  5508.  
  5509.         int retry = 16;
  5510.         int dx, dy;
  5511.         int rot = (int) GetDegreeFromPositionXY(x, y, GetX(), GetY());
  5512.  
  5513.         while (--retry)
  5514.         {
  5515.             if (fDist < 500.0f)
  5516.                 GetDeltaByDegree((rot + number(-90, 90) + number(-90, 90)) % 360, fMinDistance, &fx, &fy);
  5517.             else
  5518.                 GetDeltaByDegree(number(0, 359), fMinDistance, &fx, &fy);
  5519.  
  5520.             dx = x + (int) fx;
  5521.             dy = y + (int) fy;
  5522.  
  5523.             LPSECTREE tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), dx, dy);
  5524.  
  5525.             if (NULL == tree)
  5526.                 break;
  5527.  
  5528.             if (0 == (tree->GetAttribute(dx, dy) & (ATTR_BLOCK | ATTR_OBJECT)))
  5529.                 break;
  5530.         }
  5531.  
  5532.         //sys_log(0, "근처 어딘가로 이동 %s retry %d", GetName(), retry);
  5533.         if (!Goto(dx, dy))
  5534.             return false;
  5535.     }
  5536.     else
  5537.     {
  5538.         // 직선 따라가기
  5539.         float fDistToGo = fDist - fMinDistance;
  5540.         GetDeltaByDegree(GetRotation(), fDistToGo, &fx, &fy);
  5541.  
  5542.         //sys_log(0, "직선으로 이동 %s", GetName());
  5543.         if (!Goto(GetX() + (int) fx, GetY() + (int) fy))
  5544.             return false;
  5545.     }
  5546.  
  5547.     SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
  5548.     //MonsterLog("쫓아가기; %s", pkChr->GetName());
  5549.     return true;
  5550. }
  5551.  
  5552. float CHARACTER::GetDistanceFromSafeboxOpen() const
  5553. {
  5554.     return DISTANCE_APPROX(GetX() - m_posSafeboxOpen.x, GetY() - m_posSafeboxOpen.y);
  5555. }
  5556.  
  5557. void CHARACTER::SetSafeboxOpenPosition()
  5558. {
  5559.     m_posSafeboxOpen = GetXYZ();
  5560. }
  5561.  
  5562. CSafebox * CHARACTER::GetSafebox() const
  5563. {
  5564.     return m_pkSafebox;
  5565. }
  5566.  
  5567. void CHARACTER::ReqSafeboxLoad(const char* pszPassword)
  5568. {
  5569.     if (!*pszPassword || strlen(pszPassword) > SAFEBOX_PASSWORD_MAX_LEN)
  5570.     {
  5571.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 잘못된 암호를 입력하셨습니다."));
  5572.         return;
  5573.     }
  5574.     else if (m_pkSafebox)
  5575.     {
  5576.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고가 이미 열려있습니다."));
  5577.         return;
  5578.     }
  5579.  
  5580.     int iPulse = thecore_pulse();
  5581.  
  5582.     if (iPulse - GetSafeboxLoadTime()  < PASSES_PER_SEC(10))
  5583.     {
  5584.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 창고를 닫은지 10초 안에는 열 수 없습니다."));
  5585.         return;
  5586.     }
  5587.     else if (GetDistanceFromSafeboxOpen() > 1000)
  5588.     {
  5589.         ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<창고> 거리가 멀어서 창고를 열 수 없습니다."));
  5590.         return;
  5591.     }
  5592.     else if (m_bOpeningSafebox)
  5593.     {
  5594.         sys_log(0, "Overlapped safebox load request from %s", GetName());
  5595.         return;
  5596.     }
  5597.  
  5598.     SetSafeboxLoadTime();
  5599.     m_bOpeningSafebox = true;
  5600.  
  5601.     TSafeboxLoadPacket p;
  5602.     p.dwID = GetDesc()->GetAccountTable().id;
  5603.     strlcpy(p.szLogin, GetDesc()->GetAccountTable().login, sizeof(p.szLogin));
  5604.     strlcpy(p.szPassword, pszPassword, sizeof(p.szPassword));
  5605.  
  5606.     db_clientdesc->DBPacket(HEADER_GD_SAFEBOX_LOAD, GetDesc()->GetHandle(), &p, sizeof(p));
  5607. }
  5608.  
  5609. void CHARACTER::LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems)
  5610. {
  5611.     bool bLoaded = false;
  5612.  
  5613.     //PREVENT_TRADE_WINDOW
  5614.     SetOpenSafebox(true);
  5615.     //END_PREVENT_TRADE_WINDOW
  5616.  
  5617.     if (m_pkSafebox)
  5618.         bLoaded = true;
  5619.  
  5620.     if (!m_pkSafebox)
  5621.         m_pkSafebox = M2_NEW CSafebox(this, iSize, dwGold);
  5622.     else
  5623.         m_pkSafebox->ChangeSize(iSize);
  5624.  
  5625.     m_iSafeboxSize = iSize;
  5626.  
  5627.     TPacketCGSafeboxSize p;
  5628.  
  5629.     p.bHeader = HEADER_GC_SAFEBOX_SIZE;
  5630.     p.bSize = iSize;
  5631.  
  5632.     GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
  5633.  
  5634.     if (!bLoaded)
  5635.     {
  5636.         for (int i = 0; i < iItemCount; ++i, ++pItems)
  5637.         {
  5638.             if (!m_pkSafebox->IsValidPosition(pItems->pos))
  5639.                 continue;
  5640.  
  5641.             LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);
  5642.  
  5643.             if (!item)
  5644.             {
  5645.                 sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
  5646.                 continue;
  5647.             }
  5648.  
  5649.             item->SetSkipSave(true);
  5650.             item->SetSockets(pItems->alSockets);
  5651.             item->SetAttributes(pItems->aAttr);
  5652.  
  5653.             if (!m_pkSafebox->Add(pItems->pos, item))
  5654.             {
  5655.                 M2_DESTROY_ITEM(item);
  5656.             }
  5657.             else
  5658.                 item->SetSkipSave(false);
  5659.         }
  5660.     }
  5661. }
  5662.  
  5663. void CHARACTER::ChangeSafeboxSize(BYTE bSize)
  5664. {
  5665.     //if (!m_pkSafebox)
  5666.     //return;
  5667.  
  5668.     TPacketCGSafeboxSize p;
  5669.  
  5670.     p.bHeader = HEADER_GC_SAFEBOX_SIZE;
  5671.     p.bSize = bSize;
  5672.  
  5673.     GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
  5674.  
  5675.     if (m_pkSafebox)
  5676.         m_pkSafebox->ChangeSize(bSize);
  5677.  
  5678.     m_iSafeboxSize = bSize;
  5679. }
  5680.  
  5681. void CHARACTER::CloseSafebox()
  5682. {
  5683.     if (!m_pkSafebox)
  5684.         return;
  5685.  
  5686.     //PREVENT_TRADE_WINDOW
  5687.     SetOpenSafebox(false);
  5688.     //END_PREVENT_TRADE_WINDOW
  5689.  
  5690.     m_pkSafebox->Save();
  5691.  
  5692.     M2_DELETE(m_pkSafebox);
  5693.     m_pkSafebox = NULL;
  5694.  
  5695.     ChatPacket(CHAT_TYPE_COMMAND, "CloseSafebox");
  5696.  
  5697.     SetSafeboxLoadTime();
  5698.     m_bOpeningSafebox = false;
  5699.  
  5700.     Save();
  5701. }
  5702.  
  5703. CSafebox * CHARACTER::GetMall() const
  5704. {
  5705.     return m_pkMall;
  5706. }
  5707.  
  5708. void CHARACTER::LoadMall(int iItemCount, TPlayerItem * pItems)
  5709. {
  5710.     bool bLoaded = false;
  5711.  
  5712.     if (m_pkMall)
  5713.         bLoaded = true;
  5714.  
  5715.     if (!m_pkMall)
  5716.         m_pkMall = M2_NEW CSafebox(this, 3 * SAFEBOX_PAGE_SIZE, 0);
  5717.     else
  5718.         m_pkMall->ChangeSize(3 * SAFEBOX_PAGE_SIZE);
  5719.  
  5720.     m_pkMall->SetWindowMode(MALL);
  5721.  
  5722.     TPacketCGSafeboxSize p;
  5723.  
  5724.     p.bHeader = HEADER_GC_MALL_OPEN;
  5725.     p.bSize = 3 * SAFEBOX_PAGE_SIZE;
  5726.  
  5727.     GetDesc()->Packet(&p, sizeof(TPacketCGSafeboxSize));
  5728.  
  5729.     if (!bLoaded)
  5730.     {
  5731.         for (int i = 0; i < iItemCount; ++i, ++pItems)
  5732.         {
  5733.             if (!m_pkMall->IsValidPosition(pItems->pos))
  5734.                 continue;
  5735.  
  5736.             LPITEM item = ITEM_MANAGER::instance().CreateItem(pItems->vnum, pItems->count, pItems->id);
  5737.  
  5738.             if (!item)
  5739.             {
  5740.                 sys_err("cannot create item vnum %d id %u (name: %s)", pItems->vnum, pItems->id, GetName());
  5741.                 continue;
  5742.             }
  5743.  
  5744.             item->SetSkipSave(true);
  5745.             item->SetSockets(pItems->alSockets);
  5746.             item->SetAttributes(pItems->aAttr);
  5747.  
  5748.             if (!m_pkMall->Add(pItems->pos, item))
  5749.                 M2_DESTROY_ITEM(item);
  5750.             else
  5751.                 item->SetSkipSave(false);
  5752.         }
  5753.     }
  5754. }
  5755.  
  5756. void CHARACTER::CloseMall()
  5757. {
  5758.     if (!m_pkMall)
  5759.         return;
  5760.  
  5761.     m_pkMall->Save();
  5762.  
  5763.     M2_DELETE(m_pkMall);
  5764.     m_pkMall = NULL;
  5765.  
  5766.     ChatPacket(CHAT_TYPE_COMMAND, "CloseMall");
  5767. }
  5768.  
  5769. bool CHARACTER::BuildUpdatePartyPacket(TPacketGCPartyUpdate & out)
  5770. {
  5771.     if (!GetParty())
  5772.         return false;
  5773.  
  5774.     memset(&out, 0, sizeof(out));
  5775.  
  5776.     out.header      = HEADER_GC_PARTY_UPDATE;
  5777.     out.pid     = GetPlayerID();
  5778.     out.percent_hp  = MINMAX(0, GetHP() * 100 / GetMaxHP(), 100);
  5779.     out.role        = GetParty()->GetRole(GetPlayerID());
  5780.  
  5781.     sys_log(1, "PARTY %s role is %d", GetName(), out.role);
  5782.  
  5783.     LPCHARACTER l = GetParty()->GetLeaderCharacter();
  5784.  
  5785.     if (l && DISTANCE_APPROX(GetX() - l->GetX(), GetY() - l->GetY()) < PARTY_DEFAULT_RANGE)
  5786.     {
  5787.         if (g_iUseLocale)
  5788.             out.affects[0] = GetParty()->GetPartyBonusExpPercent();
  5789.         else
  5790.             out.affects[0] = GetParty()->GetExpBonusPercent();
  5791.         out.affects[1] = GetPoint(POINT_PARTY_ATTACKER_BONUS);
  5792.         out.affects[2] = GetPoint(POINT_PARTY_TANKER_BONUS);
  5793.         out.affects[3] = GetPoint(POINT_PARTY_BUFFER_BONUS);
  5794.         out.affects[4] = GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
  5795.         out.affects[5] = GetPoint(POINT_PARTY_HASTE_BONUS);
  5796.         out.affects[6] = GetPoint(POINT_PARTY_DEFENDER_BONUS);
  5797.     }
  5798.  
  5799.     return true;
  5800. }
  5801.  
  5802. int CHARACTER::GetLeadershipSkillLevel() const
  5803. {
  5804.     return GetSkillLevel(SKILL_LEADERSHIP);
  5805. }
  5806.  
  5807. void CHARACTER::QuerySafeboxSize()
  5808. {
  5809.     if (m_iSafeboxSize == -1)
  5810.     {
  5811.         DBManager::instance().ReturnQuery(QID_SAFEBOX_SIZE,
  5812.                 GetPlayerID(),
  5813.                 NULL,
  5814.                 "SELECT size FROM safebox%s WHERE account_id = %u",
  5815.                 get_table_postfix(),
  5816.                 GetDesc()->GetAccountTable().id);
  5817.     }
  5818. }
  5819.  
  5820. void CHARACTER::SetSafeboxSize(int iSize)
  5821. {
  5822.     sys_log(1, "SetSafeboxSize: %s %d", GetName(), iSize);
  5823.     m_iSafeboxSize = iSize;
  5824.     DBManager::instance().Query("UPDATE safebox%s SET size = %d WHERE account_id = %u", get_table_postfix(), iSize / SAFEBOX_PAGE_SIZE, GetDesc()->GetAccountTable().id);
  5825. }
  5826.  
  5827. int CHARACTER::GetSafeboxSize() const
  5828. {
  5829.     return m_iSafeboxSize;
  5830. }
  5831.  
  5832. void CHARACTER::SetNowWalking(bool bWalkFlag)
  5833. {
  5834.     //if (m_bNowWalking != bWalkFlag || IsNPC())
  5835.     if (m_bNowWalking != bWalkFlag)
  5836.     {
  5837.         if (bWalkFlag)
  5838.         {
  5839.             m_bNowWalking = true;
  5840.             m_dwWalkStartTime = get_dword_time();
  5841.         }
  5842.         else
  5843.         {
  5844.             m_bNowWalking = false;
  5845.         }
  5846.  
  5847.         //if (m_bNowWalking)
  5848.         {
  5849.             TPacketGCWalkMode p;
  5850.             p.vid = GetVID();
  5851.             p.header = HEADER_GC_WALK_MODE;
  5852.             p.mode = m_bNowWalking ? WALKMODE_WALK : WALKMODE_RUN;
  5853.  
  5854.             PacketView(&p, sizeof(p));
  5855.         }
  5856.  
  5857.         if (IsNPC())
  5858.         {
  5859.             if (m_bNowWalking)
  5860.                 MonsterLog("걷는다");
  5861.             else
  5862.                 MonsterLog("뛴다");
  5863.         }
  5864.  
  5865.         //sys_log(0, "%s is now %s", GetName(), m_bNowWalking?"walking.":"running.");
  5866.     }
  5867. }
  5868.  
  5869. void CHARACTER::StartStaminaConsume()
  5870. {
  5871.     if (m_bStaminaConsume)
  5872.         return;
  5873.     PointChange(POINT_STAMINA, 0);
  5874.     m_bStaminaConsume = true;
  5875.     //ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
  5876.     if (IsStaminaHalfConsume())
  5877.         ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec / 2, GetStamina());
  5878.     else
  5879.         ChatPacket(CHAT_TYPE_COMMAND, "StartStaminaConsume %d %d", STAMINA_PER_STEP * passes_per_sec, GetStamina());
  5880. }
  5881.  
  5882. void CHARACTER::StopStaminaConsume()
  5883. {
  5884.     if (!m_bStaminaConsume)
  5885.         return;
  5886.     PointChange(POINT_STAMINA, 0);
  5887.     m_bStaminaConsume = false;
  5888.     ChatPacket(CHAT_TYPE_COMMAND, "StopStaminaConsume %d", GetStamina());
  5889. }
  5890.  
  5891. bool CHARACTER::IsStaminaConsume() const
  5892. {
  5893.     return m_bStaminaConsume;
  5894. }
  5895.  
  5896. bool CHARACTER::IsStaminaHalfConsume() const
  5897. {
  5898.     return IsEquipUniqueItem(UNIQUE_ITEM_HALF_STAMINA);
  5899. }
  5900.  
  5901. void CHARACTER::ResetStopTime()
  5902. {
  5903.     m_dwStopTime = get_dword_time();
  5904. }
  5905.  
  5906. DWORD CHARACTER::GetStopTime() const
  5907. {
  5908.     return m_dwStopTime;
  5909. }
  5910.  
  5911. void CHARACTER::ResetPoint(int iLv)
  5912. {
  5913.     BYTE bJob = GetJob();
  5914.  
  5915.     PointChange(POINT_LEVEL, iLv - GetLevel());
  5916.  
  5917.     SetRealPoint(POINT_ST, JobInitialPoints[bJob].st);
  5918.     SetPoint(POINT_ST, GetRealPoint(POINT_ST));
  5919.  
  5920.     SetRealPoint(POINT_HT, JobInitialPoints[bJob].ht);
  5921.     SetPoint(POINT_HT, GetRealPoint(POINT_HT));
  5922.  
  5923.     SetRealPoint(POINT_DX, JobInitialPoints[bJob].dx);
  5924.     SetPoint(POINT_DX, GetRealPoint(POINT_DX));
  5925.  
  5926.     SetRealPoint(POINT_IQ, JobInitialPoints[bJob].iq);
  5927.     SetPoint(POINT_IQ, GetRealPoint(POINT_IQ));
  5928.  
  5929.     SetRandomHP((iLv - 1) * number(JobInitialPoints[GetJob()].hp_per_lv_begin, JobInitialPoints[GetJob()].hp_per_lv_end));
  5930.     SetRandomSP((iLv - 1) * number(JobInitialPoints[GetJob()].sp_per_lv_begin, JobInitialPoints[GetJob()].sp_per_lv_end));
  5931.  
  5932.     //PointChange(POINT_STAT, ((MINMAX(1, iLv, 90) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
  5933.    
  5934.     if(iLv <= 90)
  5935.         PointChange(POINT_STAT, ((MINMAX(1, iLv, 90) - 1) * 3) + GetPoint(POINT_LEVEL_STEP) - GetPoint(POINT_STAT));
  5936.     else
  5937.         PointChange(POINT_STAT, 270 - GetPoint(POINT_STAT));
  5938.    
  5939.     ComputePoints();
  5940.  
  5941.     // 회복
  5942.     PointChange(POINT_HP, GetMaxHP() - GetHP());
  5943.     PointChange(POINT_SP, GetMaxSP() - GetSP());
  5944.  
  5945.     PointsPacket();
  5946.  
  5947.     LogManager::instance().CharLog(this, 0, "RESET_POINT", "");
  5948. }
  5949.  
  5950. bool CHARACTER::IsChangeAttackPosition(LPCHARACTER target) const
  5951. {
  5952.     if (!IsNPC())
  5953.         return true;
  5954.  
  5955.     DWORD dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_NEAR;
  5956.  
  5957.     if (DISTANCE_APPROX(GetX() - target->GetX(), GetY() - target->GetY()) >
  5958.         AI_CHANGE_ATTACK_POISITION_DISTANCE + GetMobAttackRange())
  5959.         dwChangeTime = AI_CHANGE_ATTACK_POISITION_TIME_FAR;
  5960.  
  5961.     return get_dword_time() - m_dwLastChangeAttackPositionTime > dwChangeTime;
  5962. }
  5963.  
  5964. void CHARACTER::GiveRandomSkillBook()
  5965. {
  5966.     LPITEM item = AutoGiveItem(50300);
  5967.  
  5968.     if (NULL != item)
  5969.     {
  5970.         BYTE bJob = 0;
  5971.  
  5972.         if (!number(0, 1))
  5973.             bJob = GetJob() + 1;
  5974.  
  5975.         DWORD dwSkillVnum = 0;
  5976.  
  5977.         do
  5978.         {
  5979.             dwSkillVnum = number(1, 111);
  5980.             const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum);
  5981.  
  5982.             if (NULL == pkSk)
  5983.                 continue;
  5984.  
  5985.             if (bJob && bJob != pkSk->dwType)
  5986.                 continue;
  5987.  
  5988.             break;
  5989.         } while (true);
  5990.  
  5991.         item->SetSocket(0, dwSkillVnum);
  5992.     }
  5993. }
  5994.  
  5995. void CHARACTER::ReviveInvisible(int iDur)
  5996. {
  5997.     AddAffect(AFFECT_REVIVE_INVISIBLE, POINT_NONE, 0, AFF_REVIVE_INVISIBLE, iDur, 0, true);
  5998. }
  5999.  
  6000. void CHARACTER::ToggleMonsterLog()
  6001. {
  6002.     m_bMonsterLog = !m_bMonsterLog;
  6003.  
  6004.     if (m_bMonsterLog)
  6005.     {
  6006.         CHARACTER_MANAGER::instance().RegisterForMonsterLog(this);
  6007.     }
  6008.     else
  6009.     {
  6010.         CHARACTER_MANAGER::instance().UnregisterForMonsterLog(this);
  6011.     }
  6012. }
  6013.  
  6014. void CHARACTER::SetGuild(CGuild* pGuild)
  6015. {
  6016.     if (m_pGuild != pGuild)
  6017.     {
  6018.         m_pGuild = pGuild;
  6019.         UpdatePacket();
  6020.     }
  6021. }
  6022.  
  6023. void CHARACTER::SendGreetMessage()
  6024. {
  6025.     typeof(DBManager::instance().GetGreetMessage()) v = DBManager::instance().GetGreetMessage();
  6026.  
  6027.     for (itertype(v) it = v.begin(); it != v.end(); ++it)
  6028.     {
  6029.         ChatPacket(CHAT_TYPE_NOTICE, it->c_str());
  6030.     }
  6031. }
  6032.  
  6033. void CHARACTER::BeginStateEmpty()
  6034. {
  6035.     MonsterLog("!");
  6036. }
  6037.  
  6038. void CHARACTER::EffectPacket(int enumEffectType)
  6039. {
  6040.     TPacketGCSpecialEffect p;
  6041.  
  6042.     p.header = HEADER_GC_SEPCIAL_EFFECT;
  6043.     p.type = enumEffectType;
  6044.     p.vid = GetVID();
  6045.  
  6046.     PacketAround(&p, sizeof(TPacketGCSpecialEffect));
  6047. }
  6048.  
  6049. void CHARACTER::SpecificEffectPacket(const char filename[MAX_EFFECT_FILE_NAME])
  6050. {
  6051.     TPacketGCSpecificEffect p;
  6052.  
  6053.     p.header = HEADER_GC_SPECIFIC_EFFECT;
  6054.     p.vid = GetVID();
  6055.     memcpy (p.effect_file, filename, MAX_EFFECT_FILE_NAME);
  6056.  
  6057.     PacketAround(&p, sizeof(TPacketGCSpecificEffect));
  6058. }
  6059.  
  6060. void CHARACTER::MonsterChat(BYTE bMonsterChatType)
  6061. {
  6062.     if (IsPC())
  6063.         return;
  6064.  
  6065.     char sbuf[256+1];
  6066.  
  6067.     if (IsMonster())
  6068.     {
  6069.         if (number(0, 60))
  6070.             return;
  6071.  
  6072.         snprintf(sbuf, sizeof(sbuf),
  6073.                 "(locale.monster_chat[%i] and locale.monster_chat[%i][%d] or '')",
  6074.                 GetRaceNum(), GetRaceNum(), bMonsterChatType*3 + number(1, 3));
  6075.     }
  6076.     else
  6077.     {
  6078.         if (bMonsterChatType != MONSTER_CHAT_WAIT)
  6079.             return;
  6080.  
  6081.         if (IsGuardNPC())
  6082.         {
  6083.             if (number(0, 6))
  6084.                 return;
  6085.         }
  6086.         else
  6087.         {
  6088.             if (number(0, 30))
  6089.                 return;
  6090.         }
  6091.  
  6092.         snprintf(sbuf, sizeof(sbuf), "(locale.monster_chat[%i] and locale.monster_chat[%i][number(1, table.getn(locale.monster_chat[%i]))] or '')", GetRaceNum(), GetRaceNum(), GetRaceNum());
  6093.     }
  6094.  
  6095.     std::string text = quest::ScriptToString(sbuf);
  6096.  
  6097.     if (text.empty())
  6098.         return;
  6099.  
  6100.     struct packet_chat pack_chat;
  6101.  
  6102.     pack_chat.header    = HEADER_GC_CHAT;
  6103.     pack_chat.size  = sizeof(struct packet_chat) + text.size() + 1;
  6104.     pack_chat.type      = CHAT_TYPE_TALKING;
  6105.     pack_chat.id        = GetVID();
  6106.     pack_chat.bEmpire   = 0;
  6107.  
  6108.     TEMP_BUFFER buf;
  6109.     buf.write(&pack_chat, sizeof(struct packet_chat));
  6110.     buf.write(text.c_str(), text.size() + 1);
  6111.  
  6112.     PacketAround(buf.read_peek(), buf.size());
  6113. }
  6114.  
  6115. void CHARACTER::SetQuestNPCID(DWORD vid)
  6116. {
  6117.     m_dwQuestNPCVID = vid;
  6118. }
  6119.  
  6120. LPCHARACTER CHARACTER::GetQuestNPC() const
  6121. {
  6122.     return CHARACTER_MANAGER::instance().Find(m_dwQuestNPCVID);
  6123. }
  6124.  
  6125. void CHARACTER::SetQuestItemPtr(LPITEM item)
  6126. {
  6127.     m_pQuestItem = item;
  6128. }
  6129.  
  6130. void CHARACTER::ClearQuestItemPtr()
  6131. {
  6132.     m_pQuestItem = NULL;
  6133. }
  6134.  
  6135. LPITEM CHARACTER::GetQuestItemPtr() const
  6136. {
  6137.     return m_pQuestItem;
  6138. }
  6139.  
  6140. LPDUNGEON CHARACTER::GetDungeonForce() const
  6141. {
  6142.     if (m_lWarpMapIndex > 10000)
  6143.         return CDungeonManager::instance().FindByMapIndex(m_lWarpMapIndex);
  6144.  
  6145.     return m_pkDungeon;
  6146. }
  6147.  
  6148. void CHARACTER::SetBlockMode(BYTE bFlag)
  6149. {
  6150.     m_pointsInstant.bBlockMode = bFlag;
  6151.  
  6152.     ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
  6153.  
  6154.     SetQuestFlag("game_option.block_exchange", bFlag & BLOCK_EXCHANGE ? 1 : 0);
  6155.     SetQuestFlag("game_option.block_party_invite", bFlag & BLOCK_PARTY_INVITE ? 1 : 0);
  6156.     SetQuestFlag("game_option.block_guild_invite", bFlag & BLOCK_GUILD_INVITE ? 1 : 0);
  6157.     SetQuestFlag("game_option.block_whisper", bFlag & BLOCK_WHISPER ? 1 : 0);
  6158.     SetQuestFlag("game_option.block_messenger_invite", bFlag & BLOCK_MESSENGER_INVITE ? 1 : 0);
  6159.     SetQuestFlag("game_option.block_party_request", bFlag & BLOCK_PARTY_REQUEST ? 1 : 0);
  6160. }
  6161.  
  6162. void CHARACTER::SetBlockModeForce(BYTE bFlag)
  6163. {
  6164.     m_pointsInstant.bBlockMode = bFlag;
  6165.     ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
  6166. }
  6167.  
  6168. bool CHARACTER::IsGuardNPC() const
  6169. {
  6170.     return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
  6171. }
  6172.  
  6173. int CHARACTER::GetPolymorphPower() const
  6174. {
  6175.     if (test_server)
  6176.     {
  6177.         int value = quest::CQuestManager::instance().GetEventFlag("poly");
  6178.         if (value)
  6179.             return value;
  6180.     }
  6181.     return aiPolymorphPowerByLevel[MINMAX(0, GetSkillLevel(SKILL_POLYMORPH), 40)];
  6182. }
  6183.  
  6184. void CHARACTER::SetPolymorph(DWORD dwRaceNum, bool bMaintainStat)
  6185. {
  6186.     if (dwRaceNum < JOB_MAX_NUM)
  6187.     {
  6188.         dwRaceNum = 0;
  6189.         bMaintainStat = false;
  6190.     }
  6191.  
  6192.     if (m_dwPolymorphRace == dwRaceNum)
  6193.         return;
  6194.  
  6195.     m_bPolyMaintainStat = bMaintainStat;
  6196.     m_dwPolymorphRace = dwRaceNum;
  6197.  
  6198.     sys_log(0, "POLYMORPH: %s race %u ", GetName(), dwRaceNum);
  6199.  
  6200.     if (dwRaceNum != 0)
  6201.         StopRiding();
  6202.  
  6203.     SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  6204.     m_afAffectFlag.Set(AFF_SPAWN);
  6205.  
  6206.     ViewReencode();
  6207.  
  6208.     REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
  6209.  
  6210.     if (!bMaintainStat)
  6211.     {
  6212.         PointChange(POINT_ST, 0);
  6213.         PointChange(POINT_DX, 0);
  6214.         PointChange(POINT_IQ, 0);
  6215.         PointChange(POINT_HT, 0);
  6216.     }
  6217.  
  6218.     // 폴리모프 상태에서 죽는 경우, 폴리모프가 풀리게 되는데
  6219.     // 폴리 모프 전후로 valid combo interval이 다르기 때문에
  6220.     // Combo 핵 또는 Hacker로 인식하는 경우가 있다.
  6221.     // 따라서 폴리모프를 풀거나 폴리모프 하게 되면,
  6222.     // valid combo interval을 reset한다.
  6223.     SetValidComboInterval(0);
  6224.     SetComboSequence(0);
  6225.  
  6226.     ComputeBattlePoints();
  6227. }
  6228.  
  6229. int CHARACTER::GetQuestFlag(const std::string& flag) const
  6230. {
  6231.     quest::CQuestManager& q = quest::CQuestManager::instance();
  6232.     quest::PC* pPC = q.GetPC(GetPlayerID());
  6233.     return pPC->GetFlag(flag);
  6234. }
  6235.  
  6236. void CHARACTER::SetQuestFlag(const std::string& flag, int value)
  6237. {
  6238.     quest::CQuestManager& q = quest::CQuestManager::instance();
  6239.     quest::PC* pPC = q.GetPC(GetPlayerID());
  6240.     pPC->SetFlag(flag, value);
  6241. }
  6242.  
  6243. void CHARACTER::DetermineDropMetinStone()
  6244. {
  6245.     const int METIN_STONE_NUM = 14;
  6246.     static DWORD c_adwMetin[METIN_STONE_NUM] =
  6247.     {
  6248.         28030,
  6249.         28031,
  6250.         28032,
  6251.         28033,
  6252.         28034,
  6253.         28035,
  6254.         28036,
  6255.         28037,
  6256.         28038,
  6257.         28039,
  6258.         28040,
  6259.         28041,
  6260.         28042,
  6261.         28043,
  6262.     };
  6263.     DWORD stone_num = GetRaceNum();
  6264.     int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop;
  6265.     if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num)
  6266.     {
  6267.         m_dwDropMetinStone = 0;
  6268.     }
  6269.     else
  6270.     {
  6271.         const SStoneDropInfo & info = aStoneDrop[idx];
  6272.         m_bDropMetinStonePct = info.iDropPct;
  6273.         {
  6274.             m_dwDropMetinStone = c_adwMetin[number(0, METIN_STONE_NUM - 1)];
  6275.             int iGradePct = number(1, 100);
  6276.             for (int iStoneLevel = 0; iStoneLevel < STONE_LEVEL_MAX_NUM; iStoneLevel ++)
  6277.             {
  6278.                 int iLevelGradePortion = info.iLevelPct[iStoneLevel];
  6279.                 if (iGradePct <= iLevelGradePortion)
  6280.                 {
  6281.                     break;
  6282.                 }
  6283.                 else
  6284.                 {
  6285.                     iGradePct -= iLevelGradePortion;
  6286.                     m_dwDropMetinStone += 100; // 돌 +a -> +(a+1)이 될때마다 100씩 증가
  6287.                 }
  6288.             }
  6289.         }
  6290.     }
  6291. }
  6292.  
  6293. void CHARACTER::SendEquipment(LPCHARACTER ch)
  6294. {
  6295.     TPacketViewEquip p;
  6296.     p.header = HEADER_GC_VIEW_EQUIP;
  6297.     p.vid    = GetVID();
  6298.     for (int i = 0; i<WEAR_MAX_NUM; i++)
  6299.     {
  6300.         LPITEM item = GetWear(i);
  6301.         if (item)
  6302.         {
  6303.             p.equips[i].vnum = item->GetVnum();
  6304.             p.equips[i].count = item->GetCount();
  6305.  
  6306.             thecore_memcpy(p.equips[i].alSockets, item->GetSockets(), sizeof(p.equips[i].alSockets));
  6307.             thecore_memcpy(p.equips[i].aAttr, item->GetAttributes(), sizeof(p.equips[i].aAttr));
  6308.         }
  6309.         else
  6310.         {
  6311.             p.equips[i].vnum = 0;
  6312.         }
  6313.     }
  6314.     ch->GetDesc()->Packet(&p, sizeof(p));
  6315. }
  6316.  
  6317. bool CHARACTER::CanSummon(int iLeaderShip)
  6318. {
  6319.     return (iLeaderShip >= 20 || iLeaderShip >= 12 && m_dwLastDeadTime + 180 > get_dword_time());
  6320. }
  6321.  
  6322.  
  6323. void CHARACTER::MountVnum(DWORD vnum)
  6324. {
  6325.     if (m_dwMountVnum == vnum)
  6326.         return;
  6327.  
  6328.     m_dwMountVnum = vnum;
  6329.     m_dwMountTime = get_dword_time();
  6330.  
  6331.     if (m_bIsObserver)
  6332.         return;
  6333.  
  6334.     //NOTE : Mount한다고 해서 Client Side의 객체를 삭제하진 않는다.
  6335.     //그리고 서버Side에서 탔을때 위치 이동은 하지 않는다. 왜냐하면 Client Side에서 Coliision Adjust를 할수 있는데
  6336.     //객체를 소멸시켰다가 서버위치로 이동시키면 이때 collision check를 하지는 않으므로 배경에 끼거나 뚫고 나가는 문제가 존재한다.
  6337.     m_posDest.x = m_posStart.x = GetX();
  6338.     m_posDest.y = m_posStart.y = GetY();
  6339.     //EncodeRemovePacket(this);
  6340.     EncodeInsertPacket(this);
  6341.  
  6342.     ENTITY_MAP::iterator it = m_map_view.begin();
  6343.  
  6344.     while (it != m_map_view.end())
  6345.     {
  6346.         LPENTITY entity = (it++)->first;
  6347.  
  6348.         //Mount한다고 해서 Client Side의 객체를 삭제하진 않는다.
  6349.         //EncodeRemovePacket(entity);
  6350.         //if (!m_bIsObserver)
  6351.         EncodeInsertPacket(entity);
  6352.  
  6353.         //if (!entity->IsObserverMode())
  6354.         //  entity->EncodeInsertPacket(this);
  6355.     }
  6356.  
  6357.     SetValidComboInterval(0);
  6358.     SetComboSequence(0);
  6359.  
  6360.     ComputePoints();
  6361. }
  6362.  
  6363. namespace {
  6364.     class FuncCheckWarp
  6365.     {
  6366.         public:
  6367.             FuncCheckWarp(LPCHARACTER pkWarp)
  6368.             {
  6369.                 m_lTargetY = 0;
  6370.                 m_lTargetX = 0;
  6371.  
  6372.                 m_lX = pkWarp->GetX();
  6373.                 m_lY = pkWarp->GetY();
  6374.  
  6375.                 m_bInvalid = false;
  6376.                 m_bEmpire = pkWarp->GetEmpire();
  6377.  
  6378.                 char szTmp[64];
  6379.  
  6380.                 if (3 != sscanf(pkWarp->GetName(), " %s %ld %ld ", szTmp, &m_lTargetX, &m_lTargetY))
  6381.                 {
  6382.                     if (number(1, 100) < 5)
  6383.                         sys_err("Warp NPC name wrong : vnum(%d) name(%s)", pkWarp->GetRaceNum(), pkWarp->GetName());
  6384.  
  6385.                     m_bInvalid = true;
  6386.  
  6387.                     return;
  6388.                 }
  6389.  
  6390.                 m_lTargetX *= 100;
  6391.                 m_lTargetY *= 100;
  6392.  
  6393.                 m_bUseWarp = true;
  6394.  
  6395.                 if (pkWarp->IsGoto())
  6396.                 {
  6397.                     LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(pkWarp->GetMapIndex());
  6398.                     m_lTargetX += pkSectreeMap->m_setting.iBaseX;
  6399.                     m_lTargetY += pkSectreeMap->m_setting.iBaseY;
  6400.                     m_bUseWarp = false;
  6401.                 }
  6402.             }
  6403.  
  6404.             bool Valid()
  6405.             {
  6406.                 return !m_bInvalid;
  6407.             }
  6408.  
  6409.             void operator () (LPENTITY ent)
  6410.             {
  6411.                 if (!Valid())
  6412.                     return;
  6413.  
  6414.                 if (!ent->IsType(ENTITY_CHARACTER))
  6415.                     return;
  6416.  
  6417.                 LPCHARACTER pkChr = (LPCHARACTER) ent;
  6418.  
  6419.                 if (!pkChr->IsPC())
  6420.                     return;
  6421.  
  6422.                 int iDist = DISTANCE_APPROX(pkChr->GetX() - m_lX, pkChr->GetY() - m_lY);
  6423.  
  6424.                 if (iDist > 300)
  6425.                     return;
  6426.  
  6427.                 if (m_bEmpire && pkChr->GetEmpire() && m_bEmpire != pkChr->GetEmpire())
  6428.                     return;
  6429.  
  6430.                 if (pkChr->IsHack())
  6431.                     return;
  6432.  
  6433.                 if (!pkChr->CanHandleItem(false, true))
  6434.                     return;
  6435.                
  6436.                 if (m_bUseWarp)
  6437.                     pkChr->WarpSet(m_lTargetX, m_lTargetY);
  6438.                 else
  6439.                 {
  6440.                     pkChr->Show(pkChr->GetMapIndex(), m_lTargetX, m_lTargetY);
  6441.                     pkChr->Stop();
  6442.                 }
  6443.             }
  6444.  
  6445.             bool m_bInvalid;
  6446.             bool m_bUseWarp;
  6447.  
  6448.             long m_lX;
  6449.             long m_lY;
  6450.             long m_lTargetX;
  6451.             long m_lTargetY;
  6452.  
  6453.             BYTE m_bEmpire;
  6454.     };
  6455. }
  6456.  
  6457. EVENTFUNC(warp_npc_event)
  6458. {
  6459.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  6460.     if ( info == NULL )
  6461.     {
  6462.         sys_err( "warp_npc_event> <Factor> Null pointer" );
  6463.         return 0;
  6464.     }
  6465.  
  6466.     LPCHARACTER ch = info->ch;
  6467.  
  6468.     if (ch == NULL) { // <Factor>
  6469.         return 0;
  6470.     }  
  6471.  
  6472.     if (!ch->GetSectree())
  6473.     {
  6474.         ch->m_pkWarpNPCEvent = NULL;
  6475.         return 0;
  6476.     }
  6477.  
  6478.     FuncCheckWarp f(ch);
  6479.     if (f.Valid())
  6480.         ch->GetSectree()->ForEachAround(f);
  6481.  
  6482.     return passes_per_sec / 2;
  6483. }
  6484.  
  6485.  
  6486. void CHARACTER::StartWarpNPCEvent()
  6487. {
  6488.     if (m_pkWarpNPCEvent)
  6489.         return;
  6490.  
  6491.     if (!IsWarp() && !IsGoto())
  6492.         return;
  6493.  
  6494.     char_event_info* info = AllocEventInfo<char_event_info>();
  6495.  
  6496.     info->ch = this;
  6497.  
  6498.     m_pkWarpNPCEvent = event_create(warp_npc_event, info, passes_per_sec / 2);
  6499. }
  6500.  
  6501. void CHARACTER::SyncPacket()
  6502. {
  6503.     TEMP_BUFFER buf;
  6504.  
  6505.     TPacketCGSyncPositionElement elem;
  6506.  
  6507.     elem.dwVID = GetVID();
  6508.     elem.lX = GetX();
  6509.     elem.lY = GetY();
  6510.  
  6511.     TPacketGCSyncPosition pack;
  6512.  
  6513.     pack.bHeader = HEADER_GC_SYNC_POSITION;
  6514.     pack.wSize = sizeof(TPacketGCSyncPosition) + sizeof(elem);
  6515.  
  6516.     buf.write(&pack, sizeof(pack));
  6517.     buf.write(&elem, sizeof(elem));
  6518.  
  6519.     PacketAround(buf.read_peek(), buf.size());
  6520. }
  6521.  
  6522. LPCHARACTER CHARACTER::GetMarryPartner() const
  6523. {
  6524.     return m_pkChrMarried;
  6525. }
  6526.  
  6527. void CHARACTER::SetMarryPartner(LPCHARACTER ch)
  6528. {
  6529.     m_pkChrMarried = ch;
  6530. }
  6531.  
  6532. int CHARACTER::GetMarriageBonus(DWORD dwItemVnum, bool bSum)
  6533. {
  6534.     if (IsNPC())
  6535.         return 0;
  6536.  
  6537.     marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(GetPlayerID());
  6538.  
  6539.     if (!pMarriage)
  6540.         return 0;
  6541.  
  6542.     return pMarriage->GetBonus(dwItemVnum, bSum, this);
  6543. }
  6544.  
  6545. void CHARACTER::ConfirmWithMsg(const char* szMsg, int iTimeout, DWORD dwRequestPID)
  6546. {
  6547.     if (!IsPC())
  6548.         return;
  6549.  
  6550.     TPacketGCQuestConfirm p;
  6551.  
  6552.     p.header = HEADER_GC_QUEST_CONFIRM;
  6553.     p.requestPID = dwRequestPID;
  6554.     p.timeout = iTimeout;
  6555.     strlcpy(p.msg, szMsg, sizeof(p.msg));
  6556.  
  6557.     GetDesc()->Packet(&p, sizeof(p));
  6558. }
  6559.  
  6560. int CHARACTER::GetPremiumRemainSeconds(BYTE bType) const
  6561. {
  6562.     if (bType >= PREMIUM_MAX_NUM)
  6563.         return 0;
  6564.  
  6565.     return m_aiPremiumTimes[bType] - get_global_time();
  6566. }
  6567.  
  6568. bool CHARACTER::WarpToPID(DWORD dwPID)
  6569. {
  6570.     LPCHARACTER victim;
  6571.     if ((victim = (CHARACTER_MANAGER::instance().FindByPID(dwPID))))
  6572.     {
  6573.         int mapIdx = victim->GetMapIndex();
  6574.         if (IS_SUMMONABLE_ZONE(mapIdx))
  6575.         {
  6576.             if (CAN_ENTER_ZONE(this, mapIdx))
  6577.             {
  6578.                 WarpSet(victim->GetX(), victim->GetY());
  6579.             }
  6580.             else
  6581.             {
  6582.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  6583.                 return false;
  6584.             }
  6585.         }
  6586.         else
  6587.         {
  6588.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  6589.             return false;
  6590.         }
  6591.     }
  6592.     else
  6593.     {
  6594.         // 다른 서버에 로그인된 사람이 있음 -> 메시지 보내 좌표를 받아오자
  6595.         // 1. A.pid, B.pid 를 뿌림
  6596.         // 2. B.pid를 가진 서버가 뿌린서버에게 A.pid, 좌표 를 보냄
  6597.         // 3. 워프
  6598.         CCI * pcci = P2P_MANAGER::instance().FindByPID(dwPID);
  6599.  
  6600.         if (!pcci)
  6601.         {
  6602.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 온라인 상태가 아닙니다."));
  6603.             return false;
  6604.         }
  6605.  
  6606.         if (pcci->bChannel != g_bChannel)
  6607.         {
  6608.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 %d 채널에 있습니다. (현재 채널 %d)"), pcci->bChannel, g_bChannel);
  6609.             return false;
  6610.         }
  6611.         else if (false == IS_SUMMONABLE_ZONE(pcci->lMapIndex))
  6612.         {
  6613.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  6614.             return false;
  6615.         }
  6616.         else
  6617.         {
  6618.             if (!CAN_ENTER_ZONE(this, pcci->lMapIndex))
  6619.             {
  6620.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상대방이 있는 곳으로 워프할 수 없습니다."));
  6621.                 return false;
  6622.             }
  6623.  
  6624.             TPacketGGFindPosition p;
  6625.             p.header = HEADER_GG_FIND_POSITION;
  6626.             p.dwFromPID = GetPlayerID();
  6627.             p.dwTargetPID = dwPID;
  6628.             pcci->pkDesc->Packet(&p, sizeof(TPacketGGFindPosition));
  6629.  
  6630.             if (test_server)
  6631.                 ChatPacket(CHAT_TYPE_PARTY, "sent find position packet for teleport");
  6632.         }
  6633.     }
  6634.     return true;
  6635. }
  6636.  
  6637. // ADD_REFINE_BUILDING
  6638. CGuild* CHARACTER::GetRefineGuild() const
  6639. {
  6640.     LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
  6641.  
  6642.     return (chRefineNPC ? chRefineNPC->GetGuild() : NULL);
  6643. }
  6644.  
  6645. bool CHARACTER::IsRefineThroughGuild() const
  6646. {
  6647.     return GetRefineGuild() != NULL;
  6648. }
  6649.  
  6650. int CHARACTER::ComputeRefineFee(int iCost, int iMultiply) const
  6651. {
  6652.     CGuild* pGuild = GetRefineGuild();
  6653.     if (pGuild)
  6654.     {
  6655.         if (pGuild == GetGuild())
  6656.             return iCost * iMultiply * 9 / 10;
  6657.  
  6658.         // 다른 제국 사람이 시도하는 경우 추가로 3배 더
  6659.         LPCHARACTER chRefineNPC = CHARACTER_MANAGER::instance().Find(m_dwRefineNPCVID);
  6660.         if (chRefineNPC && chRefineNPC->GetEmpire() != GetEmpire())
  6661.             return iCost * iMultiply * 3;
  6662.  
  6663.         return iCost * iMultiply;
  6664.     }
  6665.     else
  6666.         return iCost;
  6667. }
  6668.  
  6669. void CHARACTER::PayRefineFee(int iTotalMoney)
  6670. {
  6671.     int iFee = iTotalMoney / 10;
  6672.     CGuild* pGuild = GetRefineGuild();
  6673.  
  6674.     int iRemain = iTotalMoney;
  6675.  
  6676.     if (pGuild)
  6677.     {
  6678.         // 자기 길드이면 iTotalMoney에 이미 10%가 제외되어있다
  6679.         if (pGuild != GetGuild())
  6680.         {
  6681.             pGuild->RequestDepositMoney(this, iFee);
  6682.             iRemain -= iFee;
  6683.         }
  6684.     }
  6685.  
  6686.     PointChange(POINT_GOLD, -iRemain);
  6687. }
  6688. // END_OF_ADD_REFINE_BUILDING
  6689.  
  6690. //Hack 방지를 위한 체크.
  6691. bool CHARACTER::IsHack(bool bSendMsg, bool bCheckShopOwner, int limittime)
  6692. {
  6693.     const int iPulse = thecore_pulse();
  6694.  
  6695.     if (test_server)
  6696.         bSendMsg = true;
  6697.  
  6698.     //창고 연후 체크
  6699.     if (iPulse - GetSafeboxLoadTime() < PASSES_PER_SEC(limittime))
  6700.     {
  6701.         if (bSendMsg)
  6702.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("창고를 연후 %d초 이내에는 다른곳으로 이동할수 없습니다."), limittime);
  6703.  
  6704.         if (test_server)
  6705.             ChatPacket(CHAT_TYPE_INFO, "[TestOnly]Pulse %d LoadTime %d PASS %d", iPulse, GetSafeboxLoadTime(), PASSES_PER_SEC(limittime));
  6706.         return true;
  6707.     }
  6708.  
  6709.     //거래관련 창 체크
  6710.     if (bCheckShopOwner)
  6711.     {
  6712.         if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
  6713.         {
  6714.             if (bSendMsg)
  6715.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 다른곳으로 이동,종료 할수 없습니다"));
  6716.  
  6717.             return true;
  6718.         }
  6719.     }
  6720.     else
  6721.     {
  6722.         if (GetExchange() || GetMyShop() || IsOpenSafebox() || IsCubeOpen())
  6723.         {
  6724.             if (bSendMsg)
  6725.                 ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래창,창고 등을 연 상태에서는 다른곳으로 이동,종료 할수 없습니다"));
  6726.  
  6727.             return true;
  6728.         }
  6729.     }
  6730.  
  6731.     //PREVENT_PORTAL_AFTER_EXCHANGE
  6732.     //교환 후 시간체크
  6733.     if (iPulse - GetExchangeTime()  < PASSES_PER_SEC(limittime))
  6734.     {
  6735.         if (bSendMsg)
  6736.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 다른지역으로 이동 할 수 없습니다."), limittime );
  6737.         return true;
  6738.     }
  6739.     //END_PREVENT_PORTAL_AFTER_EXCHANGE
  6740.  
  6741.     //PREVENT_ITEM_COPY
  6742.     if (iPulse - GetMyShopTime() < PASSES_PER_SEC(limittime))
  6743.     {
  6744.         if (bSendMsg)
  6745.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("거래 후 %d초 이내에는 다른지역으로 이동 할 수 없습니다."), limittime);
  6746.         return true;
  6747.     }
  6748.  
  6749.     if (iPulse - GetRefineTime() < PASSES_PER_SEC(limittime))
  6750.     {
  6751.         if (bSendMsg)
  6752.             ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 개량후 %d초 이내에는 귀환부,귀환기억부를 사용할 수 없습니다."), limittime);
  6753.         return true;
  6754.     }
  6755.     //END_PREVENT_ITEM_COPY
  6756.  
  6757.     return false;
  6758. }
  6759.  
  6760. BOOL CHARACTER::IsMonarch() const
  6761. {
  6762.     //MONARCH_LIMIT
  6763.     if (CMonarch::instance().IsMonarch(GetPlayerID(), GetEmpire()))
  6764.         return true;
  6765.  
  6766.     return false;
  6767.  
  6768.     //END_MONARCH_LIMIT
  6769. }
  6770. void CHARACTER::Say(const std::string & s)
  6771. {
  6772.     struct ::packet_script packet_script;
  6773.  
  6774.     packet_script.header = HEADER_GC_SCRIPT;
  6775.     packet_script.skin = 1;
  6776.     packet_script.src_size = s.size();
  6777.     packet_script.size = packet_script.src_size + sizeof(struct packet_script);
  6778.    
  6779.     TEMP_BUFFER buf;
  6780.  
  6781.     buf.write(&packet_script, sizeof(struct packet_script));
  6782.     buf.write(&s[0], s.size());
  6783.  
  6784.     if (IsPC())
  6785.     {
  6786.         GetDesc()->Packet(buf.read_peek(), buf.size());
  6787.     }
  6788. }
  6789.  
  6790. //
  6791. // Monarch
  6792. //
  6793. void CHARACTER::InitMC()
  6794. {
  6795.     for (int n = 0; n < MI_MAX; ++n)
  6796.     {
  6797.         m_dwMonarchCooltime[n] = thecore_pulse();
  6798.     }
  6799.  
  6800.     m_dwMonarchCooltimelimit[MI_HEAL] = PASSES_PER_SEC(MC_HEAL);
  6801.     m_dwMonarchCooltimelimit[MI_WARP] = PASSES_PER_SEC(MC_WARP);
  6802.     m_dwMonarchCooltimelimit[MI_TRANSFER] = PASSES_PER_SEC(MC_TRANSFER);
  6803.     m_dwMonarchCooltimelimit[MI_TAX] = PASSES_PER_SEC(MC_TAX);
  6804.     m_dwMonarchCooltimelimit[MI_SUMMON] = PASSES_PER_SEC(MC_SUMMON);
  6805.  
  6806.     m_dwMonarchCooltime[MI_HEAL] -= PASSES_PER_SEC(GetMCL(MI_HEAL));
  6807.     m_dwMonarchCooltime[MI_WARP] -= PASSES_PER_SEC(GetMCL(MI_WARP));
  6808.     m_dwMonarchCooltime[MI_TRANSFER] -= PASSES_PER_SEC(GetMCL(MI_TRANSFER));
  6809.     m_dwMonarchCooltime[MI_TAX] -= PASSES_PER_SEC(GetMCL(MI_TAX));
  6810.     m_dwMonarchCooltime[MI_SUMMON] -= PASSES_PER_SEC(GetMCL(MI_SUMMON));
  6811. }
  6812.  
  6813. DWORD CHARACTER::GetMC(enum MONARCH_INDEX e) const
  6814. {
  6815.     return m_dwMonarchCooltime[e];
  6816. }
  6817.  
  6818. void CHARACTER::SetMC(enum MONARCH_INDEX e)
  6819. {
  6820.     m_dwMonarchCooltime[e] = thecore_pulse();
  6821. }
  6822.  
  6823. bool CHARACTER::IsMCOK(enum MONARCH_INDEX e) const
  6824. {
  6825.     int iPulse = thecore_pulse();
  6826.  
  6827.     if ((iPulse -  GetMC(e)) <  GetMCL(e))
  6828.     {
  6829.         if (test_server)
  6830.             sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
  6831.        
  6832.         return false;
  6833.     }
  6834.    
  6835.     if (test_server)
  6836.         sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
  6837.  
  6838.     return true;
  6839. }
  6840.  
  6841. DWORD CHARACTER::GetMCL(enum MONARCH_INDEX e) const
  6842. {
  6843.     return m_dwMonarchCooltimelimit[e];
  6844. }
  6845.  
  6846. DWORD CHARACTER::GetMCLTime(enum MONARCH_INDEX e) const
  6847. {
  6848.     int iPulse = thecore_pulse();
  6849.  
  6850.     if (test_server)
  6851.         sys_log(0, " Pulse %d cooltime %d, limit %d", iPulse, GetMC(e), GetMCL(e));
  6852.  
  6853.     return  (GetMCL(e)) / passes_per_sec   -  (iPulse - GetMC(e)) / passes_per_sec;
  6854. }
  6855.  
  6856. bool CHARACTER::IsSiegeNPC() const
  6857. {
  6858.     return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
  6859. }
  6860.  
  6861. //------------------------------------------------
  6862. void CHARACTER::UpdateDepositPulse()
  6863. {
  6864.     m_deposit_pulse = thecore_pulse() + PASSES_PER_SEC(60*5);   // 5분
  6865. }
  6866.  
  6867. bool CHARACTER::CanDeposit() const
  6868. {
  6869.     return (m_deposit_pulse == 0 || (m_deposit_pulse < thecore_pulse()));
  6870. }
  6871. //------------------------------------------------
  6872.  
  6873. ESex GET_SEX(LPCHARACTER ch)
  6874. {
  6875.     switch (ch->GetRaceNum())
  6876.     {
  6877.         case MAIN_RACE_WARRIOR_M:
  6878.         case MAIN_RACE_SURA_M:
  6879.         case MAIN_RACE_ASSASSIN_M:
  6880.         case MAIN_RACE_SHAMAN_M:
  6881.             return SEX_MALE;
  6882.  
  6883.         case MAIN_RACE_ASSASSIN_W:
  6884.         case MAIN_RACE_SHAMAN_W:
  6885.         case MAIN_RACE_WARRIOR_W:
  6886.         case MAIN_RACE_SURA_W:
  6887.             return SEX_FEMALE;
  6888.     }
  6889.  
  6890.     /* default sex = male */
  6891.     return SEX_MALE;
  6892. }
  6893.  
  6894. int CHARACTER::GetHPPct() const
  6895. {
  6896.     return (GetHP() * 100) / GetMaxHP();
  6897. }
  6898.  
  6899. bool CHARACTER::IsBerserk() const
  6900. {
  6901.     if (m_pkMobInst != NULL)
  6902.         return m_pkMobInst->m_IsBerserk;
  6903.     else
  6904.         return false;
  6905. }
  6906.  
  6907. void CHARACTER::SetBerserk(bool mode)
  6908. {
  6909.     if (m_pkMobInst != NULL)
  6910.         m_pkMobInst->m_IsBerserk = mode;
  6911. }
  6912.  
  6913. bool CHARACTER::IsGodSpeed() const
  6914. {
  6915.     if (m_pkMobInst != NULL)
  6916.     {
  6917.         return m_pkMobInst->m_IsGodSpeed;
  6918.     }
  6919.     else
  6920.     {
  6921.         return false;
  6922.     }
  6923. }
  6924.  
  6925. void CHARACTER::SetGodSpeed(bool mode)
  6926. {
  6927.     if (m_pkMobInst != NULL)
  6928.     {
  6929.         m_pkMobInst->m_IsGodSpeed = mode;
  6930.  
  6931.         if (mode == true)
  6932.         {
  6933.             SetPoint(POINT_ATT_SPEED, 250);
  6934.         }
  6935.         else
  6936.         {
  6937.             SetPoint(POINT_ATT_SPEED, m_pkMobData->m_table.sAttackSpeed);
  6938.         }
  6939.     }
  6940. }
  6941.  
  6942. bool CHARACTER::IsDeathBlow() const
  6943. {
  6944.     if (number(1, 100) <= m_pkMobData->m_table.bDeathBlowPoint)
  6945.     {
  6946.         return true;
  6947.     }
  6948.     else
  6949.     {
  6950.         return false;
  6951.     }
  6952. }
  6953.  
  6954. struct FFindReviver
  6955. {
  6956.     FFindReviver()
  6957.     {
  6958.         pChar = NULL;
  6959.         HasReviver = false;
  6960.     }
  6961.    
  6962.     void operator() (LPCHARACTER ch)
  6963.     {
  6964.         if (ch->IsMonster() != true)
  6965.         {
  6966.             return;
  6967.         }
  6968.  
  6969.         if (ch->IsReviver() == true && pChar != ch && ch->IsDead() != true)
  6970.         {
  6971.             if (number(1, 100) <= ch->GetMobTable().bRevivePoint)
  6972.             {
  6973.                 HasReviver = true;
  6974.                 pChar = ch;
  6975.             }
  6976.         }
  6977.     }
  6978.  
  6979.     LPCHARACTER pChar;
  6980.     bool HasReviver;
  6981. };
  6982.  
  6983. bool CHARACTER::HasReviverInParty() const
  6984. {
  6985.     LPPARTY party = GetParty();
  6986.  
  6987.     if (party != NULL)
  6988.     {
  6989.         if (party->GetMemberCount() == 1) return false;
  6990.  
  6991.         FFindReviver f;
  6992.         party->ForEachMemberPtr(f);
  6993.         return f.HasReviver;
  6994.     }
  6995.  
  6996.     return false;
  6997. }
  6998.  
  6999. bool CHARACTER::IsRevive() const
  7000. {
  7001.     if (m_pkMobInst != NULL)
  7002.     {
  7003.         return m_pkMobInst->m_IsRevive;
  7004.     }
  7005.  
  7006.     return false;
  7007. }
  7008.  
  7009. void CHARACTER::SetRevive(bool mode)
  7010. {
  7011.     if (m_pkMobInst != NULL)
  7012.     {
  7013.         m_pkMobInst->m_IsRevive = mode;
  7014.     }
  7015. }
  7016.  
  7017. #define IS_SPEED_HACK_PLAYER(ch) (ch->m_speed_hack_count > SPEEDHACK_LIMIT_COUNT)
  7018.  
  7019. EVENTFUNC(check_speedhack_event)
  7020. {
  7021.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  7022.     if ( info == NULL )
  7023.     {
  7024.         sys_err( "check_speedhack_event> <Factor> Null pointer" );
  7025.         return 0;
  7026.     }
  7027.  
  7028.     LPCHARACTER ch = info->ch;
  7029.  
  7030.     if (NULL == ch || ch->IsNPC())
  7031.         return 0;
  7032.  
  7033.     if (IS_SPEED_HACK_PLAYER(ch))
  7034.     {
  7035.         // write hack log
  7036.         LogManager::instance().SpeedHackLog(ch->GetPlayerID(), ch->GetX(), ch->GetY(), ch->m_speed_hack_count);
  7037.  
  7038.         if (false == LC_IsEurope())
  7039.         {
  7040.             // close connection
  7041.             LPDESC desc = ch->GetDesc();
  7042.  
  7043.             if (desc)
  7044.             {
  7045.                 DESC_MANAGER::instance().DestroyDesc(desc);
  7046.                 return 0;
  7047.             }
  7048.         }
  7049.     }
  7050.  
  7051.     ch->m_speed_hack_count = 0;
  7052.  
  7053.     ch->ResetComboHackCount();
  7054.     return PASSES_PER_SEC(60);
  7055. }
  7056.  
  7057. void CHARACTER::StartCheckSpeedHackEvent()
  7058. {
  7059.     if (m_pkCheckSpeedHackEvent)
  7060.         return;
  7061.  
  7062.     char_event_info* info = AllocEventInfo<char_event_info>();
  7063.  
  7064.     info->ch = this;
  7065.  
  7066.     m_pkCheckSpeedHackEvent = event_create(check_speedhack_event, info, PASSES_PER_SEC(60));    // 1분
  7067. }
  7068.  
  7069. void CHARACTER::GoHome()
  7070. {
  7071.     WarpSet(EMPIRE_START_X(GetEmpire()), EMPIRE_START_Y(GetEmpire()));
  7072. }
  7073.  
  7074. void CHARACTER::SendGuildName(CGuild* pGuild)
  7075. {
  7076.     if (NULL == pGuild) return;
  7077.  
  7078.     DESC    *desc = GetDesc();
  7079.  
  7080.     if (NULL == desc) return;
  7081.     if (m_known_guild.find(pGuild->GetID()) != m_known_guild.end()) return;
  7082.  
  7083.     m_known_guild.insert(pGuild->GetID());
  7084.  
  7085.     TPacketGCGuildName  pack;
  7086.     memset(&pack, 0x00, sizeof(pack));
  7087.  
  7088.     pack.header     = HEADER_GC_GUILD;
  7089.     pack.subheader  = GUILD_SUBHEADER_GC_GUILD_NAME;
  7090.     pack.size       = sizeof(TPacketGCGuildName);
  7091.     pack.guildID    = pGuild->GetID();
  7092.     memcpy(pack.guildName, pGuild->GetName(), GUILD_NAME_MAX_LEN);
  7093.  
  7094.     desc->Packet(&pack, sizeof(pack));
  7095. }
  7096.  
  7097. void CHARACTER::SendGuildName(DWORD dwGuildID)
  7098. {
  7099.     SendGuildName(CGuildManager::instance().FindGuild(dwGuildID));
  7100. }
  7101.  
  7102. EVENTFUNC(destroy_when_idle_event)
  7103. {
  7104.     char_event_info* info = dynamic_cast<char_event_info*>( event->info );
  7105.     if ( info == NULL )
  7106.     {
  7107.         sys_err( "destroy_when_idle_event> <Factor> Null pointer" );
  7108.         return 0;
  7109.     }
  7110.  
  7111.     LPCHARACTER ch = info->ch;
  7112.     if (ch == NULL) { // <Factor>
  7113.         return 0;
  7114.     }  
  7115.  
  7116.     if (ch->GetVictim())
  7117.     {
  7118.         return PASSES_PER_SEC(300);
  7119.     }
  7120.  
  7121.     sys_log(1, "DESTROY_WHEN_IDLE: %s", ch->GetName());
  7122.  
  7123.     ch->m_pkDestroyWhenIdleEvent = NULL;
  7124.     M2_DESTROY_CHARACTER(ch);
  7125.     return 0;
  7126. }
  7127.  
  7128. void CHARACTER::StartDestroyWhenIdleEvent()
  7129. {
  7130.     if (m_pkDestroyWhenIdleEvent)
  7131.         return;
  7132.  
  7133.     char_event_info* info = AllocEventInfo<char_event_info>();
  7134.  
  7135.     info->ch = this;
  7136.  
  7137.     m_pkDestroyWhenIdleEvent = event_create(destroy_when_idle_event, info, PASSES_PER_SEC(300));
  7138. }
  7139.  
  7140. void CHARACTER::SetComboSequence(BYTE seq)
  7141. {
  7142.     m_bComboSequence = seq;
  7143. }
  7144.  
  7145. BYTE CHARACTER::GetComboSequence() const
  7146. {
  7147.     return m_bComboSequence;
  7148. }
  7149.  
  7150. void CHARACTER::SetLastComboTime(DWORD time)
  7151. {
  7152.     m_dwLastComboTime = time;
  7153. }
  7154.  
  7155. DWORD CHARACTER::GetLastComboTime() const
  7156. {
  7157.     return m_dwLastComboTime;
  7158. }
  7159.  
  7160. void CHARACTER::SetValidComboInterval(int interval)
  7161. {
  7162.     m_iValidComboInterval = interval;
  7163. }
  7164.  
  7165. int CHARACTER::GetValidComboInterval() const
  7166. {
  7167.     return m_iValidComboInterval;
  7168. }
  7169.  
  7170. BYTE CHARACTER::GetComboIndex() const
  7171. {
  7172.     return m_bComboIndex;
  7173. }
  7174.  
  7175. void CHARACTER::IncreaseComboHackCount(int k)
  7176. {
  7177.     m_iComboHackCount += k;
  7178.  
  7179.     if (m_iComboHackCount >= 10)
  7180.     {
  7181.         if (GetDesc())
  7182.             if (GetDesc()->DelayedDisconnect(number(2, 7)))
  7183.             {
  7184.                 sys_log(0, "COMBO_HACK_DISCONNECT: %s count: %d", GetName(), m_iComboHackCount);
  7185.                 LogManager::instance().HackLog("Combo", this);
  7186.             }
  7187.     }
  7188. }
  7189.  
  7190. void CHARACTER::ResetComboHackCount()
  7191. {
  7192.     m_iComboHackCount = 0;
  7193. }
  7194.  
  7195. void CHARACTER::SkipComboAttackByTime(int interval)
  7196. {
  7197.     m_dwSkipComboAttackByTime = get_dword_time() + interval;
  7198. }
  7199.  
  7200. DWORD CHARACTER::GetSkipComboAttackByTime() const
  7201. {
  7202.     return m_dwSkipComboAttackByTime;
  7203. }
  7204.  
  7205. void CHARACTER::ResetChatCounter()
  7206. {
  7207.     m_bChatCounter = 0;
  7208. }
  7209.  
  7210. BYTE CHARACTER::IncreaseChatCounter()
  7211. {
  7212.     return ++m_bChatCounter;
  7213. }
  7214.  
  7215. BYTE CHARACTER::GetChatCounter() const
  7216. {
  7217.     return m_bChatCounter;
  7218. }
  7219.  
  7220. // 말이나 다른것을 타고 있나?
  7221. bool CHARACTER::IsRiding() const
  7222. {
  7223.     return IsHorseRiding() || GetMountVnum();
  7224. }
  7225.  
  7226. bool CHARACTER::CanWarp() const
  7227. {
  7228.     const int iPulse = thecore_pulse();
  7229.     const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime);
  7230.  
  7231.     if ((iPulse - GetSafeboxLoadTime()) < limit_time)
  7232.         return false;
  7233.  
  7234.     if ((iPulse - GetExchangeTime()) < limit_time)
  7235.         return false;
  7236.  
  7237.     if ((iPulse - GetMyShopTime()) < limit_time)
  7238.         return false;
  7239.  
  7240.     if ((iPulse - GetRefineTime()) < limit_time)
  7241.         return false;
  7242.  
  7243.     if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen())
  7244.         return false;
  7245.  
  7246.     return true;
  7247. }
  7248.  
  7249. DWORD CHARACTER::GetNextExp() const
  7250. {
  7251.     if (PLAYER_EXP_TABLE_MAX < GetLevel())
  7252.         return 2500000000;
  7253.     else
  7254.         return exp_table[GetLevel()];
  7255. }
  7256.  
  7257. int CHARACTER::GetSkillPowerByLevel(int level, bool bMob) const
  7258. {
  7259.     return CTableBySkill::instance().GetSkillPowerByLevelFromType(GetJob(), GetSkillGroup(), MINMAX(0, level, SKILL_MAX_LEVEL), bMob);
  7260. }
  7261.  
  7262. #ifdef ENABLE_EXTEND_INVEN_SYSTEM
  7263. bool CHARACTER::Update_Inven()
  7264. {
  7265.     if (Inven_Point() >= INVENTORY_LOCK_COVER_COUNT) return false;
  7266.     std::vector<int> needkeys;
  7267.     for (int i = INVENTORY_NEED_KEY_START; i <= (INVENTORY_LOCKED_PAGE_COUNT*INVENTORY_NEED_KEY_INCREASE)+1; i++)
  7268.         for (int j = 0; j < 3; j++)
  7269.             needkeys.push_back(i);
  7270.     int needkey = needkeys[Inven_Point()];
  7271.     int keycount = CountSpecifyItem(INVENTORY_OPEN_KEY_VNUM) + CountSpecifyItem(INVENTORY_OPEN_KEY_VNUM2);
  7272.     if (keycount >= needkey) {
  7273.         int willdelete = INVENTORY_START_DELETE_VNUM;
  7274.         while (needkey) {
  7275.             if (!CountSpecifyItem(willdelete))
  7276.                 willdelete = INVENTORY_START_DELETE_VNUM == INVENTORY_OPEN_KEY_VNUM ? INVENTORY_OPEN_KEY_VNUM2 : INVENTORY_OPEN_KEY_VNUM;
  7277.             RemoveSpecifyItem(willdelete);
  7278.             needkey--;
  7279.         }
  7280.     }  
  7281.     else {
  7282.         ChatPacket(CHAT_TYPE_COMMAND, "ExInvenItemUseMsg %d", needkey - keycount);
  7283.         return false;
  7284.     }
  7285.     PointChange(POINT_INVEN, 1, false);
  7286.     return true;
  7287. }
  7288. #endif
  7289.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement