Guest User

Untitled

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