Advertisement
Guest User

char.cpp

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