#include "stdafx.h" #include "utils.h" #include "config.h" #include "desc.h" #include "desc_manager.h" #include "char_manager.h" #include "item.h" #include "item_manager.h" #include "mob_manager.h" #include "battle.h" #include "pvp.h" #include "skill.h" #include "start_position.h" #include "profiler.h" #include "cmd.h" #include "dungeon.h" #include "log.h" #include "unique_item.h" #include "priv_manager.h" #include "db.h" #include "vector.h" #include "marriage.h" #include "arena.h" #include "regen.h" #include "monarch.h" #include "exchange.h" #include "shop_manager.h" #include "castle.h" #include "dev_log.h" #include "ani.h" #include "BattleArena.h" #include "packet.h" #include "party.h" #include "affect.h" #include "guild.h" #include "guild_manager.h" #include "questmanager.h" #include "questlua.h" #include "threeway_war.h" #include "BlueDragon.h" #include "DragonLair.h" DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp) { if (PLAYER_EXP_TABLE_MAX < ch->GetLevel()) { double ret = 0.95; double factor = 0.1; for (ssize_t i=0 ; i < ch->GetLevel()-100 ; ++i) { if ( (i%10) == 0) factor /= 2.0; ret *= 1.0 - factor; } ret = ret * static_cast(exp); if (ret < 1.0) return 1; return static_cast(ret); } return exp; } bool CHARACTER::CanBeginFight() const { if (!CanMove()) return false; return m_pointsInstant.position == POS_STANDING && !IsDead() && !IsStun(); } void CHARACTER::BeginFight(LPCHARACTER pkVictim) { SetVictim(pkVictim); SetPosition(POS_FIGHTING); SetNextStatePulse(1); } bool CHARACTER::CanFight() const { return m_pointsInstant.position >= POS_FIGHTING ? true : false; } void CHARACTER::CreateFly(BYTE bType, LPCHARACTER pkVictim) { TPacketGCCreateFly packFly; packFly.bHeader = HEADER_GC_CREATE_FLY; packFly.bType = bType; packFly.dwStartVID = GetVID(); packFly.dwEndVID = pkVictim->GetVID(); PacketAround(&packFly, sizeof(TPacketGCCreateFly)); } void CHARACTER::DistributeSP(LPCHARACTER pkKiller, int iMethod) { if (pkKiller->GetSP() >= pkKiller->GetMaxSP()) return; bool bAttacking = (get_dword_time() - GetLastAttackTime()) < 3000; bool bMoving = (get_dword_time() - GetLastMoveTime()) < 3000; if (iMethod == 1) { int num = number(0, 3); if (!num) { int iLvDelta = GetLevel() - pkKiller->GetLevel(); int iAmount = 0; if (iLvDelta >= 5) iAmount = 10; else if (iLvDelta >= 0) iAmount = 6; else if (iLvDelta >= -3) iAmount = 2; if (iAmount != 0) { iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; if (iAmount >= 11) CreateFly(FLY_SP_BIG, pkKiller); else if (iAmount >= 7) CreateFly(FLY_SP_MEDIUM, pkKiller); else CreateFly(FLY_SP_SMALL, pkKiller); pkKiller->PointChange(POINT_SP, iAmount); } } } else { if (pkKiller->GetJob() == JOB_SHAMAN || (pkKiller->GetJob() == JOB_SURA && pkKiller->GetSkillGroup() == 2)) { int iAmount; if (bAttacking) iAmount = 2 + GetMaxSP() / 100; else if (bMoving) iAmount = 3 + GetMaxSP() * 2 / 100; else iAmount = 10 + GetMaxSP() * 3 / 100; // Æò»ó½Ã iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; pkKiller->PointChange(POINT_SP, iAmount); } else { int iAmount; if (bAttacking) iAmount = 2 + pkKiller->GetMaxSP() / 200; else if (bMoving) iAmount = 2 + pkKiller->GetMaxSP() / 100; else { // Æò»ó½Ã if (pkKiller->GetHP() < pkKiller->GetMaxHP()) iAmount = 2 + (pkKiller->GetMaxSP() / 100); // ÇÇ ´Ù ¾ÈáÀ»¶§ else iAmount = 9 + (pkKiller->GetMaxSP() / 100); // ±âº» } iAmount += (iAmount * pkKiller->GetPoint(POINT_SP_REGEN)) / 100; pkKiller->PointChange(POINT_SP, iAmount); } } } bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType) { if (test_server) sys_log(0, "[TEST_SERVER] Attack : %s type %d, MobBattleType %d", GetName(), bType, !GetMobBattleType() ? 0 : GetMobAttackRange()); //PROF_UNIT puAttack("Attack"); if (!CanMove()) return false; // CASTLE if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim)) return false; // CASTLE DWORD dwCurrentTime = get_dword_time(); if (IsPC()) { if (IS_SPEED_HACK(this, pkVictim, dwCurrentTime)) return false; if (bType == 0 && dwCurrentTime < GetSkipComboAttackByTime()) return false; } else { MonsterChat(MONSTER_CHAT_ATTACK); } pkVictim->SetSyncOwner(this); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(this); int iRet; if (bType == 0) { // // ÀÏ¹Ý °ø°Ý // switch (GetMobBattleType()) { case BATTLE_TYPE_MELEE: case BATTLE_TYPE_POWER: case BATTLE_TYPE_TANKER: case BATTLE_TYPE_SUPER_POWER: case BATTLE_TYPE_SUPER_TANKER: iRet = battle_melee_attack(this, pkVictim); break; case BATTLE_TYPE_RANGE: FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING); iRet = Shoot(0) ? BATTLE_DAMAGE : BATTLE_NONE; break; case BATTLE_TYPE_MAGIC: FlyTarget(pkVictim->GetVID(), pkVictim->GetX(), pkVictim->GetY(), HEADER_CG_FLY_TARGETING); iRet = Shoot(1) ? BATTLE_DAMAGE : BATTLE_NONE; break; default: sys_err("Unhandled battle type %d", GetMobBattleType()); iRet = BATTLE_NONE; break; } } else { if (IsPC() == true) { if (dwCurrentTime - m_dwLastSkillTime > 1500) { sys_log(1, "HACK: Too long skill using term. Name(%s) PID(%u) delta(%u)", GetName(), GetPlayerID(), (dwCurrentTime - m_dwLastSkillTime)); return false; } } sys_log(1, "Attack call ComputeSkill %d %s", bType, pkVictim?pkVictim->GetName():""); iRet = ComputeSkill(bType, pkVictim); } //if (test_server && IsPC()) // sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet); if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD) { OnMove(true); pkVictim->OnMove(); // only pc sets victim null. For npc, state machine will reset this. if (BATTLE_DEAD == iRet && IsPC()) SetVictim(NULL); return true; } return false; } void CHARACTER::DeathPenalty(BYTE bTown) { sys_log(1, "DEATH_PERNALY_CHECK(%s) town(%d)", GetName(), bTown); Cube_close(this); if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) { return; } if (GetLevel() < 10) { sys_log(0, "NO_DEATH_PENALTY_LESS_LV10(%s)", GetName()); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¿ë½ÅÀÇ °¡È£·Î °æÇèÄ¡°¡ ¶³¾îÁöÁö ¾Ê¾Ò½À´Ï´Ù.")); return; } if (number(0, 2)) { sys_log(0, "NO_DEATH_PENALTY_LUCK(%s)", GetName()); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¿ë½ÅÀÇ °¡È£·Î °æÇèÄ¡°¡ ¶³¾îÁöÁö ¾Ê¾Ò½À´Ï´Ù.")); return; } if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY)) { REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); // NO_DEATH_PENALTY_BUG_FIX if (LC_IsYMIR()) // õ¸¶ ¹öÀü¿¡¼­´Â ¾ðÁ¦³ª ¿ë½ÅÀÇ °¡È£ ¾ÆÀÌÅÛÀ» üũÇÑ´Ù. { if (FindAffect(AFFECT_NO_DEATH_PENALTY)) { sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName()); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¿ë½ÅÀÇ °¡È£·Î °æÇèÄ¡°¡ ¶³¾îÁöÁö ¾Ê¾Ò½À´Ï´Ù.")); RemoveAffect(AFFECT_NO_DEATH_PENALTY); return; } } else if (!bTown) // ±¹Á¦ ¹öÀü¿¡¼­´Â Á¦ÀÚ¸® ºÎÈ°½Ã¸¸ ¿ë½ÅÀÇ °¡È£¸¦ »ç¿ëÇÑ´Ù. (¸¶À» º¹±Í½Ã´Â °æÇèÄ¡ ÆгÎƼ ¾øÀ½) { if (FindAffect(AFFECT_NO_DEATH_PENALTY)) { sys_log(0, "NO_DEATH_PENALTY_AFFECT(%s)", GetName()); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¿ë½ÅÀÇ °¡È£·Î °æÇèÄ¡°¡ ¶³¾îÁöÁö ¾Ê¾Ò½À´Ï´Ù.")); RemoveAffect(AFFECT_NO_DEATH_PENALTY); return; } } // END_OF_NO_DEATH_PENALTY_BUG_FIX int iLoss = ((GetNextExp() * aiExpLossPercents[MINMAX(1, GetLevel(), PLAYER_EXP_TABLE_MAX)]) / 100); if (true == LC_IsYMIR()) { if (PLAYER_EXP_TABLE_MAX < GetLevel()) { iLoss = MIN(500000, iLoss); } else { iLoss = MIN(200000, iLoss); } } else if (true == LC_IsEurope()) { iLoss = MIN(800000, iLoss); } if (bTown) { if (g_iUseLocale) { iLoss = 0; } else { iLoss -= iLoss / 3; } } if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS)) iLoss /= 2; sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, aiExpLossPercents[MIN(gPlayerMaxLevel, GetLevel())]); PointChange(POINT_EXP, -iLoss, true); } } bool CHARACTER::IsStun() const { if (IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN)) return true; return false; } EVENTFUNC(StunEvent) { char_event_info* info = dynamic_cast( event->info ); if ( info == NULL ) { sys_err( "StunEvent> Null pointer" ); return 0; } LPCHARACTER ch = info->ch; if (ch == NULL) { // return 0; } ch->m_pkStunEvent = NULL; ch->Dead(); return 0; } void CHARACTER::Stun() { if (IsStun()) return; if (IsDead()) return; if (!IsPC() && m_pkParty) { m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0); } sys_log(1, "%s: Stun %p", GetName(), this); PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY)); PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY)); CloseMyShop(); event_cancel(&m_pkRecoveryEvent); // ȸº¹ À̺¥Æ®¸¦ Á×ÀδÙ. TPacketGCStun pack; pack.header = HEADER_GC_STUN; pack.vid = m_vid; PacketAround(&pack, sizeof(pack)); SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); if (m_pkStunEvent) return; char_event_info* info = AllocEventInfo(); info->ch = this; m_pkStunEvent = event_create(StunEvent, info, PASSES_PER_SEC(3)); } EVENTINFO(SCharDeadEventInfo) { bool isPC; uint32_t dwID; SCharDeadEventInfo() : isPC(0) , dwID(0) { } }; EVENTFUNC(dead_event) { const SCharDeadEventInfo* info = dynamic_cast(event->info); if ( info == NULL ) { sys_err( "dead_event> Null pointer" ); return 0; } LPCHARACTER ch = NULL; if (true == info->isPC) { ch = CHARACTER_MANAGER::instance().FindByPID( info->dwID ); } else { ch = CHARACTER_MANAGER::instance().Find( info->dwID ); } if (NULL == ch) { sys_err("DEAD_EVENT: cannot find char pointer with %s id(%d)", info->isPC ? "PC" : "MOB", info->dwID ); return 0; } ch->m_pkDeadEvent = NULL; if (ch->GetDesc()) { ch->GetDesc()->SetPhase(PHASE_GAME); ch->SetPosition(POS_STANDING); PIXEL_POSITION pos; if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos)) ch->WarpSet(pos.x, pos.y); else { sys_err("cannot find spawn position (name %s)", ch->GetName()); ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire())); } ch->PointChange(POINT_HP, (ch->GetMaxHP() / 2) - ch->GetHP(), true); ch->DeathPenalty(0); ch->StartRecoveryEvent(); ch->ChatPacket(CHAT_TYPE_COMMAND, "CloseRestartWindow"); } else { if (ch->IsMonster() == true) { if (ch->IsRevive() == false && ch->HasReviverInParty() == true) { ch->SetPosition(POS_STANDING); ch->SetHP(ch->GetMaxHP()); ch->ViewReencode(); ch->SetAggressive(); ch->SetRevive(true); return 0; } } M2_DESTROY_CHARACTER(ch); } return 0; } bool CHARACTER::IsDead() const { if (m_pointsInstant.position == POS_DEAD) return true; return false; } #define GetGoldMultipler() (distribution_test_server ? 3 : 1) void CHARACTER::RewardGold(LPCHARACTER pkAttacker) { // ADD_PREMIUM bool isAutoLoot = (pkAttacker->GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 || pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT)) ? true : false; // Á¦3ÀÇ ¼Õ // END_OF_ADD_PREMIUM PIXEL_POSITION pos; if (!isAutoLoot) if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) return; int iTotalGold = 0; // // --------- µ· µå·Ó È®·ü °è»ê ---------- // int iGoldPercent = MobRankStats[GetMobRank()].iGoldPercent; if (pkAttacker->IsPC()) iGoldPercent = iGoldPercent * (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD_DROP)) / 100; if (pkAttacker->GetPoint(POINT_MALL_GOLDBONUS)) iGoldPercent += (iGoldPercent * pkAttacker->GetPoint(POINT_MALL_GOLDBONUS) / 100); iGoldPercent = iGoldPercent * CHARACTER_MANAGER::instance().GetMobGoldDropRate(pkAttacker) / 100; // ADD_PREMIUM if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 || pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD)) iGoldPercent += iGoldPercent; // END_OF_ADD_PREMIUM if (iGoldPercent > 100) iGoldPercent = 100; int iPercent; if (GetMobRank() >= MOB_RANK_BOSS) iPercent = ((iGoldPercent * PERCENT_LVDELTA_BOSS(pkAttacker->GetLevel(), GetLevel())) / 100); else iPercent = ((iGoldPercent * PERCENT_LVDELTA(pkAttacker->GetLevel(), GetLevel())) / 100); //int iPercent = CALCULATE_VALUE_LVDELTA(pkAttacker->GetLevel(), GetLevel(), iGoldPercent); if (number(1, 100) > iPercent) return; int iGoldMultipler = GetGoldMultipler(); if (1 == number(1, 50000)) // 1/50000 È®·ü·Î µ·ÀÌ 10¹è iGoldMultipler *= 10; else if (1 == number(1, 10000)) // 1/10000 È®·ü·Î µ·ÀÌ 5¹è iGoldMultipler *= 5; // °³ÀÎ Àû¿ë if (pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS)) if (number(1, 100) <= pkAttacker->GetPoint(POINT_GOLD_DOUBLE_BONUS)) iGoldMultipler *= 2; // // --------- µ· µå·Ó ¹è¼ö °áÁ¤ ---------- // if (test_server) pkAttacker->ChatPacket(CHAT_TYPE_PARTY, "gold_mul %d rate %d", iGoldMultipler, CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker)); // // --------- ½ÇÁ¦ µå·Ó ó¸® ------------- // LPITEM item; int iGold10DropPct = 100; iGold10DropPct = (iGold10DropPct * 100) / (100 + CPrivManager::instance().GetPriv(pkAttacker, PRIV_GOLD10_DROP)); // MOB_RANK°¡ BOSSº¸´Ù ³ôÀ¸¸é ¹«Á¶°Ç µ·Æøź if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0) { if (1 == number(1, iGold10DropPct)) iGoldMultipler *= 10; // 1% È®·ü·Î µ· 10¹è int iSplitCount = number(25, 35); for (int i = 0; i < iSplitCount; ++i) { int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax) / iSplitCount; if (test_server) sys_log(0, "iGold %d", iGold); iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; iGold *= iGoldMultipler; if (iGold == 0) { continue ; } if (test_server) { sys_log(0, "Drop Moeny MobGoldAmountRate %d %d", CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker), iGoldMultipler); sys_log(0, "Drop Money gold %d GoldMin %d GoldMax %d", iGold, GetMobTable().dwGoldMax, GetMobTable().dwGoldMax); } // NOTE: µ· ÆøźÀº Á¦ 3ÀÇ ¼Õ 󸮸¦ ÇÏÁö ¾ÊÀ½ if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold))) { pos.x = GetX() + ((number(-14, 14) + number(-14, 14)) * 23); pos.y = GetY() + ((number(-14, 14) + number(-14, 14)) * 23); item->AddToGround(GetMapIndex(), pos); item->StartDestroyEvent(); iTotalGold += iGold; // Total gold } } } // 1% È®·ü·Î µ·À» 10°³ ¶³¾î ¶ß¸°´Ù. (10¹è µå·ÓÀÓ) else if (1 == number(1, iGold10DropPct)) { // // µ· Æøź½Ä µå·Ó // for (int i = 0; i < 10; ++i) { int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; iGold *= iGoldMultipler; if (iGold == 0) { continue; } // NOTE: µ· ÆøźÀº Á¦ 3ÀÇ ¼Õ 󸮸¦ ÇÏÁö ¾ÊÀ½ if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold))) { pos.x = GetX() + (number(-7, 7) * 20); pos.y = GetY() + (number(-7, 7) * 20); item->AddToGround(GetMapIndex(), pos); item->StartDestroyEvent(); iTotalGold += iGold; // Total gold } } } else { // // ÀϹÝÀûÀÎ ¹æ½ÄÀÇ µ· µå·Ó // int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100; iGold *= iGoldMultipler; int iSplitCount; if (iGold >= 3 && !LC_IsYMIR()) iSplitCount = number(1, 3); else if (GetMobRank() >= MOB_RANK_BOSS) { iSplitCount = number(3, 10); if ((iGold / iSplitCount) == 0) iSplitCount = 1; } else iSplitCount = 1; if (iGold != 0) { iTotalGold += iGold; // Total gold for (int i = 0; i < iSplitCount; ++i) { if (isAutoLoot) { pkAttacker->GiveGold(iGold / iSplitCount); } else if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount))) { pos.x = GetX() + (number(-7, 7) * 20); pos.y = GetY() + (number(-7, 7) * 20); item->AddToGround(GetMapIndex(), pos); item->StartDestroyEvent(); } } } } DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold); } void CHARACTER::Reward(bool bItemDrop) { if (GetRaceNum() == 5001) // ¿Ö±¸´Â µ·À» ¹«Á¶°Ç µå·Ó { PIXEL_POSITION pos; if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), GetX(), GetY(), pos)) return; LPITEM item; int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax); iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(NULL) / 100; iGold *= GetGoldMultipler(); int iSplitCount = number(25, 35); sys_log(0, "WAEGU Dead gold %d split %d", iGold, iSplitCount); for (int i = 1; i <= iSplitCount; ++i) { if ((item = ITEM_MANAGER::instance().CreateItem(1, iGold / iSplitCount))) { if (i != 0) { pos.x = number(-7, 7) * 20; pos.y = number(-7, 7) * 20; pos.x += GetX(); pos.y += GetY(); } item->AddToGround(GetMapIndex(), pos); item->StartDestroyEvent(); } } return; } //PROF_UNIT puReward("Reward"); LPCHARACTER pkAttacker = DistributeExp(); if (!pkAttacker) return; //PROF_UNIT pu1("r1"); if (pkAttacker->IsPC()) { if (GetLevel() - pkAttacker->GetLevel() >= -10) if (pkAttacker->GetRealAlignment() < 0) { if (pkAttacker->IsEquipUniqueItem(UNIQUE_ITEM_FASTER_ALIGNMENT_UP_BY_KILL)) pkAttacker->UpdateAlignment(14); else pkAttacker->UpdateAlignment(7); } else pkAttacker->UpdateAlignment(2); pkAttacker->SetQuestNPCID(GetVID()); quest::CQuestManager::instance().Kill(pkAttacker->GetPlayerID(), GetRaceNum()); CHARACTER_MANAGER::instance().KillLog(GetRaceNum()); if (!number(0, 9)) { if (pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY)) { int iHP = pkAttacker->GetMaxHP() * pkAttacker->GetPoint(POINT_KILL_HP_RECOVERY) / 100; pkAttacker->PointChange(POINT_HP, iHP); CreateFly(FLY_HP_SMALL, pkAttacker); } if (pkAttacker->GetPoint(POINT_KILL_SP_RECOVER)) { int iSP = pkAttacker->GetMaxSP() * pkAttacker->GetPoint(POINT_KILL_SP_RECOVER) / 100; pkAttacker->PointChange(POINT_SP, iSP); CreateFly(FLY_SP_SMALL, pkAttacker); } } } //pu1.Pop(); if (!bItemDrop) return; PIXEL_POSITION pos = GetXYZ(); if (!SECTREE_MANAGER::instance().GetMovablePosition(GetMapIndex(), pos.x, pos.y, pos)) return; // // µ· µå·Ó // //PROF_UNIT pu2("r2"); if (test_server) sys_log(0, "Drop money : Attacker %s", pkAttacker->GetName()); RewardGold(pkAttacker); //pu2.Pop(); // // ¾ÆÀÌÅÛ µå·Ó // //PROF_UNIT pu3("r3"); LPITEM item; static std::vector s_vec_item; s_vec_item.clear(); if (ITEM_MANAGER::instance().CreateDropItem(this, pkAttacker, s_vec_item)) { if (s_vec_item.size() == 0); else if (s_vec_item.size() == 1) { item = s_vec_item[0]; item->AddToGround(GetMapIndex(), pos); if (CBattleArena::instance().IsBattleArenaMap(pkAttacker->GetMapIndex()) == false) { item->SetOwnership(pkAttacker); } item->StartDestroyEvent(); pos.x = number(-7, 7) * 20; pos.y = number(-7, 7) * 20; pos.x += GetX(); pos.y += GetY(); sys_log(0, "DROP_ITEM: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName()); } else { int iItemIdx = s_vec_item.size() - 1; std::priority_queue > pq; int total_dam = 0; for (TDamageMap::iterator it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it) { int iDamage = it->second.iTotalDamage; if (iDamage > 0) { LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first); if (ch) { pq.push(std::make_pair(iDamage, ch)); total_dam += iDamage; } } } std::vector v; while (!pq.empty() && pq.top().first * 10 >= total_dam) { v.push_back(pq.top().second); pq.pop(); } if (v.empty()) { // µ¥¹ÌÁö¸¦ Ưº°È÷ ¸¹ÀÌ ÁØ »ç¶÷ÀÌ ¾øÀ¸´Ï ¼ÒÀ¯±Ç ¾øÀ½ while (iItemIdx >= 0) { item = s_vec_item[iItemIdx--]; if (!item) { sys_err("item null in vector idx %d", iItemIdx + 1); continue; } item->AddToGround(GetMapIndex(), pos); // 10% ÀÌÇÏ µ¥¹ÌÁö ÁØ »ç¶÷³¢¸®´Â ¼ÒÀ¯±Ç¾øÀ½ //item->SetOwnership(pkAttacker); item->StartDestroyEvent(); pos.x = number(-7, 7) * 20; pos.y = number(-7, 7) * 20; pos.x += GetX(); pos.y += GetY(); sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName()); } } else { // µ¥¹ÌÁö ¸¹ÀÌ ÁØ »ç¶÷µé ³¢¸®¸¸ ¼ÒÀ¯±Ç ³ª´²°¡Áü std::vector::iterator it = v.begin(); while (iItemIdx >= 0) { item = s_vec_item[iItemIdx--]; if (!item) { sys_err("item null in vector idx %d", iItemIdx + 1); continue; } item->AddToGround(GetMapIndex(), pos); LPCHARACTER ch = *it; if (ch->GetParty()) ch = ch->GetParty()->GetNextOwnership(ch, GetX(), GetY()); ++it; if (it == v.end()) it = v.begin(); if (CBattleArena::instance().IsBattleArenaMap(ch->GetMapIndex()) == false) { item->SetOwnership(ch); } item->StartDestroyEvent(); pos.x = number(-7, 7) * 20; pos.y = number(-7, 7) * 20; pos.x += GetX(); pos.y += GetY(); sys_log(0, "DROP_ITEM: %s %d %d by %s", item->GetName(), pos.x, pos.y, GetName()); } } } } m_map_kDamage.clear(); } struct TItemDropPenalty { int iInventoryPct; // Range: 1 ~ 1000 int iInventoryQty; // Range: -- int iEquipmentPct; // Range: 1 ~ 100 int iEquipmentQty; // Range: -- }; TItemDropPenalty aItemDropPenalty_kor[9] = { { 0, 0, 0, 0 }, // ¼±¿Õ { 0, 0, 0, 0 }, // ¿µ¿õ { 0, 0, 0, 0 }, // ¼ºÀÚ { 0, 0, 0, 0 }, // ÁöÀÎ { 0, 0, 0, 0 }, // ¾ç¹Î { 25, 1, 5, 1 }, // ³¶ÀÎ { 50, 2, 10, 1 }, // ¾ÇÀÎ { 75, 4, 15, 1 }, // ¸¶µÎ { 100, 8, 20, 1 }, // ÆÐ¿Õ }; void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller) { // °³ÀλóÁ¡À» ¿¬ »óÅ¿¡¼­´Â ¾ÆÀÌÅÛÀ» µå·ÓÇÏÁö ¾Ê´Â´Ù. if (GetMyShop()) return; if (false == LC_IsYMIR()) { if (GetLevel() < 50) return; } if (CBattleArena::instance().IsBattleArenaMap(GetMapIndex()) == true) { return; } struct TItemDropPenalty * table = &aItemDropPenalty_kor[0]; if (GetLevel() < 10) return; int iAlignIndex; if (GetRealAlignment() >= 120000) iAlignIndex = 0; else if (GetRealAlignment() >= 80000) iAlignIndex = 1; else if (GetRealAlignment() >= 40000) iAlignIndex = 2; else if (GetRealAlignment() >= 10000) iAlignIndex = 3; else if (GetRealAlignment() >= 0) iAlignIndex = 4; else if (GetRealAlignment() > -40000) iAlignIndex = 5; else if (GetRealAlignment() > -80000) iAlignIndex = 6; else if (GetRealAlignment() > -120000) iAlignIndex = 7; else iAlignIndex = 8; std::vector > vec_item; LPITEM pkItem; int i; bool isDropAllEquipments = false; TItemDropPenalty & r = table[iAlignIndex]; sys_log(0, "%s align %d inven_pct %d equip_pct %d", GetName(), iAlignIndex, r.iInventoryPct, r.iEquipmentPct); bool bDropInventory = r.iInventoryPct >= number(1, 1000); bool bDropEquipment = r.iEquipmentPct >= number(1, 100); bool bDropAntiDropUniqueItem = false; if ((bDropInventory || bDropEquipment) && IsEquipUniqueItem(UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY)) { bDropInventory = false; bDropEquipment = false; bDropAntiDropUniqueItem = true; } if (bDropInventory) // Drop Inventory { std::vector vec_bSlots; for (i = 0; i < INVENTORY_MAX_NUM; ++i) if (GetInventoryItem(i)) vec_bSlots.push_back(i); if (!vec_bSlots.empty()) { random_shuffle(vec_bSlots.begin(), vec_bSlots.end()); int iQty = MIN(vec_bSlots.size(), r.iInventoryQty); if (iQty) iQty = number(1, iQty); for (i = 0; i < iQty; ++i) { pkItem = GetInventoryItem(vec_bSlots[i]); if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) continue; SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255); vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), INVENTORY)); } } else if (iAlignIndex == 8) isDropAllEquipments = true; } if (bDropEquipment) // Drop Equipment { std::vector vec_bSlots; for (i = 0; i < WEAR_MAX_NUM; ++i) if (GetWear(i)) vec_bSlots.push_back(i); if (!vec_bSlots.empty()) { random_shuffle(vec_bSlots.begin(), vec_bSlots.end()); int iQty; if (isDropAllEquipments) iQty = vec_bSlots.size(); else iQty = MIN(vec_bSlots.size(), number(1, r.iEquipmentQty)); if (iQty) iQty = number(1, iQty); for (i = 0; i < iQty; ++i) { pkItem = GetWear(vec_bSlots[i]); if (IS_SET(pkItem->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_PKDROP)) continue; SyncQuickslot(QUICKSLOT_TYPE_ITEM, vec_bSlots[i], 255); vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT)); } } } if (bDropAntiDropUniqueItem) { LPITEM pkItem; pkItem = GetWear(WEAR_UNIQUE1); if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY) { SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE1, 255); vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT)); } pkItem = GetWear(WEAR_UNIQUE2); if (pkItem && pkItem->GetVnum() == UNIQUE_ITEM_SKIP_ITEM_DROP_PENALTY) { SyncQuickslot(QUICKSLOT_TYPE_ITEM, WEAR_UNIQUE2, 255); vec_item.push_back(std::make_pair(pkItem->RemoveFromCharacter(), EQUIPMENT)); } } { PIXEL_POSITION pos; pos.x = GetX(); pos.y = GetY(); unsigned int i; for (i = 0; i < vec_item.size(); ++i) { LPITEM item = vec_item[i].first; int window = vec_item[i].second; item->AddToGround(GetMapIndex(), pos); item->StartDestroyEvent(); sys_log(0, "DROP_ITEM_PK: %s %d %d from %s", item->GetName(), pos.x, pos.y, GetName()); LogManager::instance().ItemLog(this, item, "DEAD_DROP", (window == INVENTORY) ? "INVENTORY" : ((window == EQUIPMENT) ? "EQUIPMENT" : "")); pos.x = GetX() + number(-7, 7) * 20; pos.y = GetY() + number(-7, 7) * 20; } } } class FPartyAlignmentCompute { public: FPartyAlignmentCompute(int iAmount, int x, int y) { m_iAmount = iAmount; m_iCount = 0; m_iStep = 0; m_iKillerX = x; m_iKillerY = y; } void operator () (LPCHARACTER pkChr) { if (DISTANCE_APPROX(pkChr->GetX() - m_iKillerX, pkChr->GetY() - m_iKillerY) < PARTY_DEFAULT_RANGE) { if (m_iStep == 0) { ++m_iCount; } else { pkChr->UpdateAlignment(m_iAmount / m_iCount); } } } int m_iAmount; int m_iCount; int m_iStep; int m_iKillerX; int m_iKillerY; }; void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead) { if (IsDead()) return; { if (IsHorseRiding()) { StopRiding(); } else if (GetMountVnum()) { RemoveAffect(AFFECT_MOUNT_BONUS); m_dwMountVnum = 0; UnEquipSpecialRideUniqueItem(); UpdatePacket(); } } if (!pkKiller && m_dwKillerPID) pkKiller = CHARACTER_MANAGER::instance().FindByPID(m_dwKillerPID); m_dwKillerPID = 0; // ¹Ýµå½Ã ÃʱâÈ­ ÇؾßÇÔ DO NOT DELETE THIS LINE UNLESS YOU ARE 1000000% SURE bool isAgreedPVP = false; bool isUnderGuildWar = false; bool isDuel = false; bool isForked = false; if (pkKiller && pkKiller->IsPC()) { if (pkKiller->m_pkChrTarget == this) pkKiller->SetTarget(NULL); if (!IsPC() && pkKiller->GetDungeon()) pkKiller->GetDungeon()->IncKillCount(pkKiller, this); isAgreedPVP = CPVPManager::instance().Dead(this, pkKiller->GetPlayerID()); isDuel = CArenaManager::instance().OnDead(pkKiller, this); if (IsPC()) { CGuild * g1 = GetGuild(); CGuild * g2 = pkKiller->GetGuild(); if (g1 && g2) if (g1->UnderWar(g2->GetID())) isUnderGuildWar = true; pkKiller->SetQuestNPCID(GetVID()); quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC); CGuildManager::instance().Kill(pkKiller, this); } } //CHECK_FORKEDROAD_WAR if (IsPC()) { if (CThreeWayWar::instance().IsThreeWayWarMapIndex(GetMapIndex())) isForked = true; } //END_CHECK_FORKEDROAD_WAR if (pkKiller && !isAgreedPVP && !isUnderGuildWar && IsPC() && !isDuel && !isForked && !IS_CASTLE_MAP(GetMapIndex())) { if (GetGMLevel() == GM_PLAYER || test_server) { ItemDropPenalty(pkKiller); } } // CASTLE_SIEGE if (IS_CASTLE_MAP(GetMapIndex())) { if (CASTLE_FROG_VNUM == GetRaceNum()) castle_frog_die(this, pkKiller); else if (castle_is_guard_vnum(GetRaceNum())) castle_guard_die(this, pkKiller); else if (castle_is_tower_vnum(GetRaceNum())) castle_tower_die(this, pkKiller); } // CASTLE_SIEGE if (true == isForked) { CThreeWayWar::instance().onDead( this, pkKiller ); } SetPosition(POS_DEAD); ClearAffect(true); if (pkKiller && IsPC()) { if (!pkKiller->IsPC()) { if (!isForked) { sys_log(1, "DEAD: %s %p WITH PENALTY", GetName(), this); SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); LogManager::instance().CharLog(this, pkKiller->GetRaceNum(), "DEAD_BY_NPC", pkKiller->GetName()); } } else { sys_log(1, "DEAD_BY_PC: %s %p KILLER %s %p", GetName(), this, pkKiller->GetName(), get_pointer(pkKiller)); REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); if (GetEmpire() != pkKiller->GetEmpire()) { int iEP = MIN(GetPoint(POINT_EMPIRE_POINT), pkKiller->GetPoint(POINT_EMPIRE_POINT)); PointChange(POINT_EMPIRE_POINT, -(iEP / 10)); pkKiller->PointChange(POINT_EMPIRE_POINT, iEP / 5); if (GetPoint(POINT_EMPIRE_POINT) < 10) { // TODO : ÀÔ±¸·Î ³¯¸®´Â Äڵ带 ³Ö¾î¾ß ÇÑ´Ù. } char buf[256]; snprintf(buf, sizeof(buf), "%d %d %d %s %d %d %d %s", GetEmpire(), GetAlignment(), GetPKMode(), GetName(), pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName()); LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf); } else { if (!isAgreedPVP && !isUnderGuildWar && !IsKillerMode() && GetAlignment() >= 0 && !isDuel && !isForked) { int iNoPenaltyProb = 0; if (g_iUseLocale) { if (pkKiller->GetAlignment() >= 0) // 1/3 percent down iNoPenaltyProb = 33; else // 4/5 percent down iNoPenaltyProb = 20; } if (number(1, 100) < iNoPenaltyProb) pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¿ë½ÅÀÇ º¸È£·Î ¾ÆÀÌÅÛÀÌ ¶³¾îÁöÁö ¾Ê¾Ò½À´Ï´Ù.")); else { if (g_iUseLocale && pkKiller->GetParty()) { FPartyAlignmentCompute f(-20000, pkKiller->GetX(), pkKiller->GetY()); pkKiller->GetParty()->ForEachOnlineMember(f); if (f.m_iCount == 0) pkKiller->UpdateAlignment(-20000); else { sys_log(0, "ALIGNMENT PARTY count %d amount %d", f.m_iCount, f.m_iAmount); f.m_iStep = 1; pkKiller->GetParty()->ForEachOnlineMember(f); } } else pkKiller->UpdateAlignment(-20000); } } char buf[256]; snprintf(buf, sizeof(buf), "%d %d %d %s %d %d %d %s", GetEmpire(), GetAlignment(), GetPKMode(), GetName(), pkKiller->GetEmpire(), pkKiller->GetAlignment(), pkKiller->GetPKMode(), pkKiller->GetName()); LogManager::instance().CharLog(this, pkKiller->GetPlayerID(), "DEAD_BY_PC", buf); } } } else { sys_log(1, "DEAD: %s %p", GetName(), this); REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_DEATH_PENALTY); } ClearSync(); //sys_log(1, "stun cancel %s[%d]", GetName(), (DWORD)GetVID()); event_cancel(&m_pkStunEvent); // ±âÀý À̺¥Æ®´Â Á×ÀδÙ. if (IsPC()) { m_dwLastDeadTime = get_dword_time(); SetKillerMode(false); GetDesc()->SetPhase(PHASE_DEAD); } else { // °¡µå¿¡°Ô °ø°Ý¹ÞÀº ¸ó½ºÅÍ´Â º¸»óÀÌ ¾ø¾î¾ß ÇÑ´Ù. if (!IS_SET(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD)) { if (!(pkKiller && pkKiller->IsPC() && pkKiller->GetGuild() && pkKiller->GetGuild()->UnderAnyWar(GUILD_WAR_TYPE_FIELD))) { // ºÎÈ°ÇÏ´Â ¸ó½ºÅÍ´Â º¸»óÀ» ÁÖÁö ¾Ê´Â´Ù. if (GetMobTable().dwResurrectionVnum) { // DUNGEON_MONSTER_REBIRTH_BUG_FIX LPCHARACTER chResurrect = CHARACTER_MANAGER::instance().SpawnMob(GetMobTable().dwResurrectionVnum, GetMapIndex(), GetX(), GetY(), GetZ(), true, (int) GetRotation()); if (GetDungeon() && chResurrect) { chResurrect->SetDungeon(GetDungeon()); } // END_OF_DUNGEON_MONSTER_REBIRTH_BUG_FIX Reward(false); } else if (IsRevive() == true) { Reward(false); } else { Reward(true); // Drops gold, item, etc.. } } else { if (pkKiller->m_dwUnderGuildWarInfoMessageTime < get_dword_time()) { pkKiller->m_dwUnderGuildWarInfoMessageTime = get_dword_time() + 60000; pkKiller->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<±æµå> ±æµåÀüÁß¿¡´Â »ç³É¿¡ µû¸¥ ÀÌÀÍÀÌ ¾ø½À´Ï´Ù.")); } } } } // BOSS_KILL_LOG if (GetMobRank() >= MOB_RANK_BOSS && pkKiller && pkKiller->IsPC()) { char buf[51]; snprintf(buf, sizeof(buf), "%d %d", g_bChannel, pkKiller->GetMapIndex()); if (IsStone()) LogManager::instance().CharLog(pkKiller, GetRaceNum(), "STONE_KILL", buf); else LogManager::instance().CharLog(pkKiller, GetRaceNum(), "BOSS_KILL", buf); } // END_OF_BOSS_KILL_LOG TPacketGCDead pack; pack.header = HEADER_GC_DEAD; pack.vid = m_vid; PacketAround(&pack, sizeof(pack)); REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); // Ç÷¹À̾î ij¸¯ÅÍÀ̸é if (GetDesc() != NULL) { // // Ŭ¶óÀ̾ðÆ®¿¡ ¿¡ÆåÆ® ÆÐŶÀ» ´Ù½Ã º¸³½´Ù. // itertype(m_list_pkAffect) it = m_list_pkAffect.begin(); while (it != m_list_pkAffect.end()) SendAffectAddPacket(GetDesc(), *it++); } // // Dead À̺¥Æ® »ý¼º, // // Dead À̺¥Æ®¿¡¼­´Â ¸ó½ºÅÍÀÇ °æ¿ì ¸îÃÊ ÈÄ¿¡ Destroy µÇµµ·Ï ÇØÁÖ¸ç, // PCÀÇ °æ¿ì 3ºÐ ÀÖ´Ù°¡ ¸¶À»¿¡¼­ ³ª¿Àµµ·Ï ÇØ ÁØ´Ù. 3ºÐ ³»¿¡´Â À¯Àú·ÎºÎÅÍ // ¸¶À»¿¡¼­ ½ÃÀÛÇÒ °ÇÁö, ¿©±â¼­ ½ÃÀÛÇÒ °ÇÁö °áÁ¤À» ¹Þ´Â´Ù. if (isDuel == false) { if (m_pkDeadEvent) { sys_log(1, "DEAD_EVENT_CANCEL: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent)); event_cancel(&m_pkDeadEvent); } if (IsStone()) ClearStone(); if (GetDungeon()) { GetDungeon()->DeadCharacter(this); } SCharDeadEventInfo* pEventInfo = AllocEventInfo(); if (IsPC()) { pEventInfo->isPC = true; pEventInfo->dwID = this->GetPlayerID(); m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(180)); } else { pEventInfo->isPC = false; pEventInfo->dwID = this->GetVID(); if (IsRevive() == false && HasReviverInParty() == true) { m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3)); } else { m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(10)); } } sys_log(1, "DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent)); } if (m_pkExchange != NULL) { m_pkExchange->Cancel(); } if (IsCubeOpen() == true) { Cube_close(this); } CShopManager::instance().StopShopping(this); CloseMyShop(); CloseSafebox(); if (true == IsMonster() && 2493 == GetMobTable().dwVnum) { if (NULL != pkKiller && NULL != pkKiller->GetGuild()) { CDragonLairManager::instance().OnDragonDead( this, pkKiller->GetGuild()->GetID() ); } else { sys_err("DragonLair: Dragon killed by nobody"); } } } struct FuncSetLastAttacked { FuncSetLastAttacked(DWORD dwTime) : m_dwTime(dwTime) { } void operator () (LPCHARACTER ch) { ch->SetLastAttacked(m_dwTime); } DWORD m_dwTime; }; void CHARACTER::SetLastAttacked(DWORD dwTime) { assert(m_pkMobInst != NULL); m_pkMobInst->m_dwLastAttackedTime = dwTime; m_pkMobInst->m_posLastAttacked = GetXYZ(); } void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag) { if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this)) { TPacketGCDamageInfo damageInfo; memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo)); damageInfo.header = HEADER_GC_DAMAGE_INFO; damageInfo.dwVID = (DWORD)GetVID(); damageInfo.flag = DamageFlag; damageInfo.damage = Damage; if (GetDesc() != NULL) { GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo)); } if (pAttacker->GetDesc() != NULL) { pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo)); } /* if (GetArenaObserverMode() == false && GetArena() != NULL) { GetArena()->SendPacketToObserver(&damageInfo, sizeof(TPacketGCDamageInfo)); } */ } } // // CHARACTER::Damage ¸Þ¼Òµå´Â this°¡ µ¥¹ÌÁö¸¦ ÀÔ°Ô ÇÑ´Ù. // // Arguments // pAttacker : °ø°ÝÀÚ // dam : µ¥¹ÌÁö // EDamageType : ¾î¶² Çü½ÄÀÇ °ø°ÝÀΰ¡? // // Return value // true : dead // false : not dead yet // bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead { if (DAMAGE_TYPE_MAGIC == type) { dam = (int)((float)dam * (100 + (pAttacker->GetPoint(POINT_MAGIC_ATT_BONUS_PER) + pAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100.f + 0.5f); } if (GetRaceNum() == 5001) { bool bDropMoney = false; int iPercent = (GetHP() * 100) / GetMaxHP(); if (iPercent <= 10 && GetMaxSP() < 5) { SetMaxSP(5); bDropMoney = true; } else if (iPercent <= 20 && GetMaxSP() < 4) { SetMaxSP(4); bDropMoney = true; } else if (iPercent <= 40 && GetMaxSP() < 3) { SetMaxSP(3); bDropMoney = true; } else if (iPercent <= 60 && GetMaxSP() < 2) { SetMaxSP(2); bDropMoney = true; } else if (iPercent <= 80 && GetMaxSP() < 1) { SetMaxSP(1); bDropMoney = true; } if (bDropMoney) { DWORD dwGold = 1000; int iSplitCount = number(10, 13); sys_log(0, "WAEGU DropGoldOnHit %d times", GetMaxSP()); for (int i = 1; i <= iSplitCount; ++i) { PIXEL_POSITION pos; LPITEM item; if ((item = ITEM_MANAGER::instance().CreateItem(1, dwGold / iSplitCount))) { if (i != 0) { pos.x = (number(-14, 14) + number(-14, 14)) * 20; pos.y = (number(-14, 14) + number(-14, 14)) * 20; pos.x += GetX(); pos.y += GetY(); } item->AddToGround(GetMapIndex(), pos); item->StartDestroyEvent(); } } } } // ÆòŸ°¡ ¾Æ´Ò ¶§´Â °øÆ÷ ó¸® if (type != DAMAGE_TYPE_NORMAL && type != DAMAGE_TYPE_NORMAL_RANGE) { if (IsAffectFlag(AFF_TERROR)) { int pct = GetSkillPower(SKILL_TERROR) / 400; if (number(1, 100) <= pct) return false; } } int iCurHP = GetHP(); int iCurSP = GetSP(); bool IsCritical = false; bool IsPenetrate = false; bool IsDeathBlow = false; enum DamageFlag { DAMAGE_NORMAL = (1 << 0), DAMAGE_POISON = (1 << 1), DAMAGE_DODGE = (1 << 2), DAMAGE_BLOCK = (1 << 3), DAMAGE_PENETRATE= (1 << 4), DAMAGE_CRITICAL = (1 << 5), }; //PROF_UNIT puAttr("Attr"); // // ¸¶¹ýÇü ½ºÅ³°ú, ·¹ÀÎÁöÇü ½ºÅ³Àº(±ÃÀÚ°´) Å©¸®Æ¼Äðú, °üÅë°ø°Ý °è»êÀ» ÇÑ´Ù. // ¿ø·¡´Â ÇÏÁö ¾Ê¾Æ¾ß Çϴµ¥ Nerf(´Ù¿î¹ë·±½º)ÆÐÄ¡¸¦ ÇÒ ¼ö ¾ø¾î¼­ Å©¸®Æ¼Äðú // °üÅë°ø°ÝÀÇ ¿ø·¡ °ªÀ» ¾²Áö ¾Ê°í, /2 ÀÌ»óÇÏ¿© Àû¿ëÇÑ´Ù. // // ¹«»ç À̾߱Ⱑ ¸¹¾Æ¼­ ¹Ð¸® ½ºÅ³µµ Ãß°¡ // // 20091109 : ¹«»ç°¡ °á°úÀûÀ¸·Î ¾öû³ª°Ô °­ÇØÁø °ÍÀ¸·Î °á·Ð³², µ¶ÀÏ ±âÁØ ¹«»ç ºñÀ² 70% À°¹Ú // if (type == DAMAGE_TYPE_MELEE || type == DAMAGE_TYPE_RANGE || type == DAMAGE_TYPE_MAGIC) { if (pAttacker) { // Å©¸®Æ¼Äà int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT); if (!IsPC()) iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS); if (iCriticalPct) { if (iCriticalPct >= 10) // 10º¸´Ù Å©¸é 5% + (4¸¶´Ù 1%¾¿ Áõ°¡), µû¶ó¼­ ¼öÄ¡°¡ 50À̸é 20% iCriticalPct = 5 + (iCriticalPct - 10) / 4; else // 10º¸´Ù ÀÛÀ¸¸é ´Ü¼øÈ÷ ¹ÝÀ¸·Î ±ðÀ½, 10 = 5% iCriticalPct /= 2; //Å©¸®Æ¼Äà ÀúÇ× °ª Àû¿ë. iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL); if (number(1, 100) <= iCriticalPct) { IsCritical = true; dam *= 2; EffectPacket(SE_CRITICAL); if (IsAffectFlag(AFF_MANASHIELD)) { RemoveAffect(AFF_MANASHIELD); } } } // °üÅë°ø°Ý int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT); if (!IsPC()) iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS); if (iPenetratePct) { { CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE); if (NULL != pkSk) { pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f); iPenetratePct -= static_cast(pkSk->kPointPoly.Eval()); } } if (iPenetratePct >= 10) { // 10º¸´Ù Å©¸é 5% + (4¸¶´Ù 1%¾¿ Áõ°¡), µû¶ó¼­ ¼öÄ¡°¡ 50À̸é 20% iPenetratePct = 5 + (iPenetratePct - 10) / 4; } else { // 10º¸´Ù ÀÛÀ¸¸é ´Ü¼øÈ÷ ¹ÝÀ¸·Î ±ðÀ½, 10 = 5% iPenetratePct /= 2; } //°üÅëŸ°Ý ÀúÇ× °ª Àû¿ë. iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE); if (number(1, 100) <= iPenetratePct) { IsPenetrate = true; if (test_server) ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°üÅë Ãß°¡ µ¥¹ÌÁö %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100); dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100; if (IsAffectFlag(AFF_MANASHIELD)) { RemoveAffect(AFF_MANASHIELD); } } } } } // // ÄÞº¸ °ø°Ý, È° °ø°Ý, Áï ÆòŸ ÀÏ ¶§¸¸ ¼Ó¼º°ªµéÀ» °è»êÀ» ÇÑ´Ù. // else if (type == DAMAGE_TYPE_NORMAL || type == DAMAGE_TYPE_NORMAL_RANGE) { if (type == DAMAGE_TYPE_NORMAL) { // ±ÙÁ¢ ÆòŸÀÏ °æ¿ì ¸·À» ¼ö ÀÖÀ½ if (GetPoint(POINT_BLOCK) && number(1, 100) <= GetPoint(POINT_BLOCK)) { if (test_server) { pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ºí·°! (%d%%)"), GetName(), GetPoint(POINT_BLOCK)); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ºí·°! (%d%%)"), GetName(), GetPoint(POINT_BLOCK)); } SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK); return false; } } else if (type == DAMAGE_TYPE_NORMAL_RANGE) { // ¿ø°Å¸® ÆòŸÀÇ °æ¿ì ÇÇÇÒ ¼ö ÀÖÀ½ if (GetPoint(POINT_DODGE) && number(1, 100) <= GetPoint(POINT_DODGE)) { if (test_server) { pAttacker->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȸÇÇ! (%d%%)"), GetName(), GetPoint(POINT_DODGE)); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s ȸÇÇ! (%d%%)"), GetName(), GetPoint(POINT_DODGE)); } SendDamagePacket(pAttacker, 0, DAMAGE_DODGE); return false; } } if (IsAffectFlag(AFF_JEONGWIHON)) dam = (int) (dam * (100 + GetSkillPower(SKILL_JEONGWI) * 25 / 100) / 100); if (IsAffectFlag(AFF_TERROR)) dam = (int) (dam * (95 - GetSkillPower(SKILL_TERROR) / 5) / 100); if (IsAffectFlag(AFF_HOSIN)) dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100; // // °ø°ÝÀÚ ¼Ó¼º Àû¿ë // if (pAttacker) { if (type == DAMAGE_TYPE_NORMAL) { // ¹Ý»ç if (GetPoint(POINT_REFLECT_MELEE)) { int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100; // NOTE: °ø°ÝÀÚ°¡ IMMUNE_REFLECT ¼Ó¼ºÀ» °®°íÀÖ´Ù¸é ¹Ý»ç¸¦ ¾È ÇÏ´Â °Ô // ¾Æ´Ï¶ó 1/3 µ¥¹ÌÁö·Î °íÁ¤Çؼ­ µé¾î°¡µµ·Ï ±âȹ¿¡¼­ ¿äû. if (pAttacker->IsImmune(IMMUNE_REFLECT)) reflectDamage = int(reflectDamage / 3.0f + 0.5f); pAttacker->Damage(this, reflectDamage, DAMAGE_TYPE_SPECIAL); } } // Å©¸®Æ¼Äà int iCriticalPct = pAttacker->GetPoint(POINT_CRITICAL_PCT); if (!IsPC()) iCriticalPct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_CRITICAL_BONUS); if (iCriticalPct) { //Å©¸®Æ¼Äà ÀúÇ× °ª Àû¿ë. iCriticalPct -= GetPoint(POINT_RESIST_CRITICAL); if (number(1, 100) <= iCriticalPct) { IsCritical = true; dam *= 2; EffectPacket(SE_CRITICAL); } } // °üÅë°ø°Ý int iPenetratePct = pAttacker->GetPoint(POINT_PENETRATE_PCT); if (!IsPC()) iPenetratePct += pAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_PENETRATE_BONUS); { CSkillProto* pkSk = CSkillManager::instance().Get(SKILL_RESIST_PENETRATE); if (NULL != pkSk) { pkSk->SetPointVar("k", 1.0f * GetSkillPower(SKILL_RESIST_PENETRATE) / 100.0f); iPenetratePct -= static_cast(pkSk->kPointPoly.Eval()); } } if (iPenetratePct) { //°üÅëŸ°Ý ÀúÇ× °ª Àû¿ë. iPenetratePct -= GetPoint(POINT_RESIST_PENETRATE); if (number(1, 100) <= iPenetratePct) { IsPenetrate = true; if (test_server) ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°üÅë Ãß°¡ µ¥¹ÌÁö %d"), GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100); dam += GetPoint(POINT_DEF_GRADE) * (100 + GetPoint(POINT_DEF_BONUS)) / 100; } } // HP ½ºÆ¿ if (pAttacker->GetPoint(POINT_STEAL_HP)) { int pct = 1; if (number(1, 10) <= pct) { int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100; if (iHP > 0 && GetHP() >= iHP) { CreateFly(FLY_HP_SMALL, pAttacker); pAttacker->PointChange(POINT_HP, iHP); PointChange(POINT_HP, -iHP); } } } // SP ½ºÆ¿ if (pAttacker->GetPoint(POINT_STEAL_SP)) { int pct = 1; if (number(1, 10) <= pct) { int iCur; if (IsPC()) iCur = iCurSP; else iCur = iCurHP; int iSP = MIN(dam, MAX(0, iCur)) * pAttacker->GetPoint(POINT_STEAL_SP) / 100; if (iSP > 0 && iCur >= iSP) { CreateFly(FLY_SP_SMALL, pAttacker); pAttacker->PointChange(POINT_SP, iSP); if (IsPC()) PointChange(POINT_SP, -iSP); } } } // µ· ½ºÆ¿ if (pAttacker->GetPoint(POINT_STEAL_GOLD)) { if (number(1, 100) <= pAttacker->GetPoint(POINT_STEAL_GOLD)) { int iAmount = number(1, GetLevel()); pAttacker->PointChange(POINT_GOLD, iAmount); DBManager::instance().SendMoneyLog(MONEY_LOG_MISC, 1, iAmount); } } // Ä¥ ¶§¸¶´Ù HPȸº¹ if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% È®·ü { int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; if (i) { CreateFly(FLY_HP_SMALL, pAttacker); pAttacker->PointChange(POINT_HP, i); } } // Ä¥ ¶§¸¶´Ù SPȸº¹ if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0) // 80% È®·ü { int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; if (i) { CreateFly(FLY_SP_SMALL, pAttacker); pAttacker->PointChange(POINT_SP, i); } } // »ó´ë¹æÀÇ ¸¶³ª¸¦ ¾ø¾Ø´Ù. if (pAttacker->GetPoint(POINT_MANA_BURN_PCT)) { if (number(1, 100) <= pAttacker->GetPoint(POINT_MANA_BURN_PCT)) PointChange(POINT_SP, -50); } } } // // ÆòŸ ¶Ç´Â ½ºÅ³·Î ÀÎÇÑ º¸³Ê½º ÇÇÇØ/¹æ¾î °è»ê // switch (type) { case DAMAGE_TYPE_NORMAL: case DAMAGE_TYPE_NORMAL_RANGE: if (pAttacker) if (pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) dam = dam * (100 + pAttacker->GetPoint(POINT_NORMAL_HIT_DAMAGE_BONUS)) / 100; dam = dam * (100 - MIN(99, GetPoint(POINT_NORMAL_HIT_DEFEND_BONUS))) / 100; break; case DAMAGE_TYPE_MELEE: case DAMAGE_TYPE_RANGE: case DAMAGE_TYPE_FIRE: case DAMAGE_TYPE_ICE: case DAMAGE_TYPE_ELEC: case DAMAGE_TYPE_MAGIC: if (pAttacker) if (pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) dam = dam * (100 + pAttacker->GetPoint(POINT_SKILL_DAMAGE_BONUS)) / 100; dam = dam * (100 - MIN(99, GetPoint(POINT_SKILL_DEFEND_BONUS))) / 100; break; default: break; } // // ¸¶³ª½¯µå(Èæ½Å¼öÈ£) // if (IsAffectFlag(AFF_MANASHIELD)) { // POINT_MANASHIELD ´Â ÀÛ¾ÆÁú¼ö·Ï ÁÁ´Ù int iDamageSPPart = dam / 3; int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100; int iSP = GetSP(); // SP°¡ ÀÖÀ¸¸é ¹«Á¶°Ç µ¥¹ÌÁö Àý¹Ý °¨¼Ò if (iDamageToSP <= iSP) { PointChange(POINT_SP, -iDamageToSP); dam -= iDamageSPPart; } else { // Á¤½Å·ÂÀÌ ¸ðÀÚ¶ó¼­ ÇÇ°¡ ´õ ±ï¿©¾ßÇÒ?? PointChange(POINT_SP, -GetSP()); dam -= iSP * 100 / MAX(GetPoint(POINT_MANASHIELD), 1); } } // // Àüü ¹æ¾î·Â »ó½Â (¸ô ¾ÆÀÌÅÛ) // if (GetPoint(POINT_MALL_DEFBONUS) > 0) { int dec_dam = MIN(200, dam * GetPoint(POINT_MALL_DEFBONUS) / 100); dam -= dec_dam; } if (pAttacker) { // // Àüü °ø°Ý·Â »ó½Â (¸ô ¾ÆÀÌÅÛ) // if (pAttacker->GetPoint(POINT_MALL_ATTBONUS) > 0) { int add_dam = MIN(300, dam * pAttacker->GetLimitPoint(POINT_MALL_ATTBONUS) / 100); dam += add_dam; } // // Á¦±¹À¸·Î ÀÎÇÑ º¸³Ê½º (Çѱ¹ ¿Ãµå ¹öÀü¸¸ Àû¿ë) // int iEmpire = GetEmpire(); int lMapIndex = GetMapIndex(); int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); if (LC_IsYMIR() == true) { if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) { dam += (dam * 30) / 100; } } if (pAttacker->IsPC()) { iEmpire = pAttacker->GetEmpire(); lMapIndex = pAttacker->GetMapIndex(); iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex); // ´Ù¸¥ Á¦±¹ »ç¶÷ÀÎ °æ¿ì µ¥¹ÌÁö 10% °¨¼Ò if (iEmpire && iMapEmpire && iEmpire != iMapEmpire) { int percent = 10; if (184 <= lMapIndex && lMapIndex <= 189) { if (LC_IsYMIR() == true) percent = 7; else percent = 9; } else { if (LC_IsYMIR() == true) percent = 8; else percent = 9; } dam = dam * percent / 10; } if (!IsPC() && GetMonsterDrainSPPoint()) { int iDrain = GetMonsterDrainSPPoint(); if (iDrain <= pAttacker->GetSP()) pAttacker->PointChange(POINT_SP, -iDrain); else { int iSP = pAttacker->GetSP(); pAttacker->PointChange(POINT_SP, -iSP); } } } else if (pAttacker->IsGuardNPC()) { SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD); Stun(); return true; } // // ±ºÁÖÀÇ ±Ý°­±Ç & »çÀÚÈÄ // if (pAttacker->IsPC() && CMonarch::instance().IsPowerUp(pAttacker->GetEmpire())) { // 10% ÇÇÇØ Áõ°¡ dam += dam / 10; } if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire())) { // 10% ÇÇÇØ °¨¼Ò dam -= dam / 10; } } //puAttr.Pop(); if (!GetSectree() || GetSectree()->IsAttr(GetX(), GetY(), ATTR_BANPK)) return false; if (!IsPC()) { if (m_pkParty && m_pkParty->GetLeader()) m_pkParty->GetLeader()->SetLastAttacked(get_dword_time()); else SetLastAttacked(get_dword_time()); // ¸ó½ºÅÍ ´ë»ç : ¸ÂÀ» ¶§ MonsterChat(MONSTER_CHAT_ATTACKED); } if (IsStun()) { Dead(pAttacker); return true; } if (IsDead()) return true; // µ¶ °ø°ÝÀ¸·Î Á×Áö ¾Êµµ·Ï ÇÔ. if (type == DAMAGE_TYPE_POISON) { if (GetHP() - dam <= 0) { dam = GetHP() - 1; } } // ------------------------ // µ¶ÀÏ ÇÁ¸®¹Ì¾ö ¸ðµå // ----------------------- if (LC_IsGermany() && pAttacker && pAttacker->IsPC()) { int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker); dam = dam * iDmgPct / 100; } // STONE SKIN : ÇÇÇØ ¹ÝÀ¸·Î °¨¼Ò if (IsMonster() && IsStoneSkinner()) { if (GetHPPct() < GetMobTable().bStoneSkinPoint) dam /= 2; } //PROF_UNIT puRest1("Rest1"); if (pAttacker) { // DEATH BLOW : È®·ü ÀûÀ¸·Î 4¹è ÇÇÇØ (!? ÇöÀç À̺¥Æ®³ª °ø¼ºÀü¿ë ¸ó½ºÅ͸¸ »ç¿ëÇÔ) if (pAttacker->IsMonster() && pAttacker->IsDeathBlower()) { if (pAttacker->IsDeathBlow()) { if (number(1, 4) == GetJob()) { IsDeathBlow = true; dam = dam * 4; } } } dam = BlueDragon_Damage(this, pAttacker, dam); BYTE damageFlag = 0; if (type == DAMAGE_TYPE_POISON) damageFlag = DAMAGE_POISON; else damageFlag = DAMAGE_NORMAL; if (IsCritical == true) damageFlag |= DAMAGE_CRITICAL; if (IsPenetrate == true) damageFlag |= DAMAGE_PENETRATE; //ÃÖÁ¾ µ¥¹ÌÁö º¸Á¤ float damMul = this->GetDamMul(); float tempDam = dam; dam = tempDam * damMul + 0.5f; if (pAttacker) SendDamagePacket(pAttacker, dam, damageFlag); if (test_server) { if(pAttacker) { pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s", GetName(), dam, GetHP(), (GetHP() * 100) / GetMaxHP(), IsCritical ? "crit " : "", IsPenetrate ? "pene " : "", IsDeathBlow ? "deathblow " : ""); } ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s", pAttacker ? pAttacker->GetName() : 0, dam, GetHP(), (GetHP() * 100) / GetMaxHP(), IsCritical ? "crit " : "", IsPenetrate ? "pene " : "", IsDeathBlow ? "deathblow " : ""); } if (m_bDetailLog) { ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s[%d]°¡ °ø°Ý À§Ä¡: %d %d"), pAttacker->GetName(), (DWORD) pAttacker->GetVID(), pAttacker->GetX(), pAttacker->GetY()); } } // // !!!!!!!!! ½ÇÁ¦ HP¸¦ ÁÙÀÌ´Â ºÎºÐ !!!!!!!!! // if (!cannot_dead) { PointChange(POINT_HP, -dam, false); } //puRest1.Pop(); //PROF_UNIT puRest2("Rest2"); if (pAttacker && dam > 0 && IsNPC()) { //PROF_UNIT puRest20("Rest20"); TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); if (it == m_map_kDamage.end()) { m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(dam, 0))); it = m_map_kDamage.find(pAttacker->GetVID()); } else { it->second.iTotalDamage += dam; } //puRest20.Pop(); //PROF_UNIT puRest21("Rest21"); StartRecoveryEvent(); // ¸ó½ºÅÍ´Â µ¥¹ÌÁö¸¦ ÀÔÀ¸¸é ȸº¹À» ½ÃÀÛÇÑ´Ù. //puRest21.Pop(); //PROF_UNIT puRest22("Rest22"); UpdateAggrPointEx(pAttacker, type, dam, it->second); //puRest22.Pop(); } //puRest2.Pop(); //PROF_UNIT puRest3("Rest3"); if (GetHP() <= 0) { Stun(); if (pAttacker && !pAttacker->IsNPC()) m_dwKillerPID = pAttacker->GetPlayerID(); else m_dwKillerPID = 0; } return false; } void CHARACTER::DistributeHP(LPCHARACTER pkKiller) { if (pkKiller->GetDungeon()) // ´øÁ¯³»¿¡¼± ¸¸µÎ°¡³ª¿ÀÁö¾Ê´Â´Ù return; } static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp) { // ·¹º§Â÷ °æÇèÄ¡ °¡°¨ºñÀ² iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp); // ¿ÜºÎ Å×½ºÆ® ¼­¹ö °æÇèÄ¡ 3¹è º¸³Ê½º if (distribution_test_server) iExp *= 3; int iBaseExp = iExp; // Á¡¼ú, ȸ»ç °æÇèÄ¡ À̺¥Æ® Àû¿ë iExp = iExp * (100 + CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT)) / 100; // °ÔÀÓ³» ±âº» Á¦°øµÇ´Â °æÇèÄ¡ º¸³Ê½º { // ³ëµ¿Àý ¸Þ´Þ if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL)) iExp += iExp * 20 /100; // »ç±ÍŸ¿ö °æÇèÄ¡ º¸³Ê½º if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000) iExp += iExp * 20 / 100; // 1.2¹è (20%) // ¾ÆÀÌÅÛ °æÇèÄ¡ µÎ¹è ¼Ó¼º if (to->GetPoint(POINT_EXP_DOUBLE_BONUS)) if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS)) iExp += iExp * 30 / 100; // 1.3¹è (30%) // °æÇèÀÇ ¹ÝÁö (2½Ã°£Â¥¸®) if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP)) iExp += iExp * 50 / 100; switch (to->GetMountVnum()) { case 20110: case 20111: case 20112: case 20113: if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) || to->IsEquipUniqueItem(71121) ) { iExp += iExp * 10 / 100; } break; case 20114: case 20120: case 20121: case 20122: case 20123: case 20124: case 20125: // ¹é»çÀÚ °æÇèÄ¡ º¸³Ê½º iExp += iExp * 30 / 100; break; } } // ¾ÆÀÌÅÛ ¸ô ÆǸŠ°æÇèÄ¡ º¸³Ê½º if (LC_IsHongKong() || LC_IsEurope() || LC_IsCanada()) { // ¾ÆÀÌÅÛ ¸ô: °æÇèÄ¡ °áÁ¦ if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += (iExp * 50 / 100); } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += (iExp * 50 / 100); } // PC¹æ ¾ÆÅÛ °æÄ¡ º¸³Ê½º if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // °áÈ¥ º¸³Ê½º iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } else if (/*LC_IsNewCIBN() || */LC_IsBrazil()) { // ¾ÆÀÌÅÛ ¸ô: °æÇèÄ¡ °áÁ¦ if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += iExp; } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += iExp; } // PC¹æ ¾ÆÅÛ °æÄ¡ º¸³Ê½º if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // °áÈ¥ º¸³Ê½º iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } else { // ¾ÆÀÌÅÛ ¸ô: °æÇèÄ¡ °áÁ¦ if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0) { iExp += (iExp * 20 / 100); } if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true) { iExp += (iExp * 20 / 100); } // PC¹æ ¾ÆÅÛ °æÄ¡ º¸³Ê½º if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0) { if (to->IsPCBang() == true) iExp += (iExp * to->GetPoint(POINT_PC_BANG_EXP_BONUS)/100); } // °áÈ¥ º¸³Ê½º iExp += iExp * to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS) / 100; } iExp += (iExp * to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP)/100); iExp += (iExp * to->GetPoint(POINT_MALL_EXPBONUS)/100); iExp += (iExp * to->GetPoint(POINT_EXP)/100); /* if (speed_server) { iExp += iExp * CSpeedServerManager::ExpBonus(); } */ if (test_server) { sys_log(0, "Bonus Exp : Ramadan Candy: %d MallExp: %d PointExp: %d", to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP), to->GetPoint(POINT_MALL_EXPBONUS), to->GetPoint(POINT_EXP) ); } // ±âȹÃø Á¶Á¤°ª 2005.04.21 ÇöÀç 85% iExp = iExp * CHARACTER_MANAGER::instance().GetMobExpRate(to) / 100; // °æÇèÄ¡ Çѹø ȹµæ·® Á¦ÇÑ iExp = MIN(to->GetNextExp() / 10, iExp); if (test_server) { if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && iBaseExp>0) to->ChatPacket(CHAT_TYPE_INFO, "exp bonus %d%%", (iExp-iBaseExp)*100/iBaseExp); } iExp = AdjustExpByLevel(to, iExp); to->PointChange(POINT_EXP, iExp, true); from->CreateFly(FLY_EXP, to); { LPCHARACTER you = to->GetMarryPartner(); // ºÎºÎ°¡ ¼­·Î ÆÄƼÁßÀÌ¸é ±Ý½½ÀÌ ¿À¸¥´Ù if (you) { // 1¾ïÀÌ 100% DWORD dwUpdatePoint = 2000*iExp/to->GetLevel()/to->GetLevel()/3; if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 || you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0) dwUpdatePoint = (DWORD)(dwUpdatePoint * 3); marriage::TMarriage* pMarriage = marriage::CManager::instance().Get(to->GetPlayerID()); // DIVORCE_NULL_BUG_FIX if (pMarriage && pMarriage->IsNear()) pMarriage->Update(dwUpdatePoint); // END_OF_DIVORCE_NULL_BUG_FIX } } } namespace NPartyExpDistribute { struct FPartyTotaler { int total; int member_count; int x, y; FPartyTotaler(LPCHARACTER center) : total(0), member_count(0), x(center->GetX()), y(center->GetY()) {}; void operator () (LPCHARACTER ch) { if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) { if (LC_IsYMIR()) total += ch->GetLevel(); else total += party_exp_distribute_table[ch->GetLevel()]; ++member_count; } } }; struct FPartyDistributor { int total; LPCHARACTER c; int x, y; DWORD _iExp; int m_iMode; int m_iMemberCount; FPartyDistributor(LPCHARACTER center, int member_count, int total, DWORD iExp, int iMode) : total(total), c(center), x(center->GetX()), y(center->GetY()), _iExp(iExp), m_iMode(iMode), m_iMemberCount(member_count) { if (m_iMemberCount == 0) m_iMemberCount = 1; }; void operator () (LPCHARACTER ch) { if (DISTANCE_APPROX(ch->GetX() - x, ch->GetY() - y) <= PARTY_DEFAULT_RANGE) { DWORD iExp2 = 0; switch (m_iMode) { case PARTY_EXP_DISTRIBUTION_NON_PARITY: if (LC_IsYMIR()) iExp2 = (DWORD) ((_iExp * ch->GetLevel()) / total); else iExp2 = (DWORD) (_iExp * (float) party_exp_distribute_table[ch->GetLevel()] / total); break; case PARTY_EXP_DISTRIBUTION_PARITY: iExp2 = _iExp / m_iMemberCount; break; default: sys_err("Unknown party exp distribution mode %d", m_iMode); return; } GiveExp(c, ch, iExp2); } } }; } typedef struct SDamageInfo { int iDam; LPCHARACTER pAttacker; LPPARTY pParty; void Clear() { pAttacker = NULL; pParty = NULL; } inline void Distribute(LPCHARACTER ch, int iExp) { if (pAttacker) GiveExp(ch, pAttacker, iExp); else if (pParty) { NPartyExpDistribute::FPartyTotaler f(ch); pParty->ForEachOnlineMember(f); if (pParty->IsPositionNearLeader(ch)) iExp = iExp * (100 + pParty->GetExpBonusPercent()) / 100; if (test_server) { if (quest::CQuestManager::instance().GetEventFlag("exp_bonus_log") && pParty->GetExpBonusPercent()) pParty->ChatPacketToAllMember(CHAT_TYPE_INFO, "exp party bonus %d%%", pParty->GetExpBonusPercent()); } // °æÇèÄ¡ ¸ô¾ÆÁÖ±â (ÆÄƼ°¡ ȹµæÇÑ °æÇèÄ¡¸¦ 5% »©¼­ ¸ÕÀú ÁÜ) if (pParty->GetExpCentralizeCharacter()) { LPCHARACTER tch = pParty->GetExpCentralizeCharacter(); if (DISTANCE_APPROX(ch->GetX() - tch->GetX(), ch->GetY() - tch->GetY()) <= PARTY_DEFAULT_RANGE) { int iExpCenteralize = (int) (iExp * 0.05f); iExp -= iExpCenteralize; GiveExp(ch, pParty->GetExpCentralizeCharacter(), iExpCenteralize); } } NPartyExpDistribute::FPartyDistributor fDist(ch, f.member_count, f.total, iExp, pParty->GetExpDistributionMode()); pParty->ForEachOnlineMember(fDist); } } } TDamageInfo; LPCHARACTER CHARACTER::DistributeExp() { int iExpToDistribute = GetExp(); if (iExpToDistribute <= 0) return NULL; int iTotalDam = 0; LPCHARACTER pkChrMostAttacked = NULL; int iMostDam = 0; typedef std::vector TDamageInfoTable; TDamageInfoTable damage_info_table; std::map map_party_damage; damage_info_table.reserve(m_map_kDamage.size()); TDamageMap::iterator it = m_map_kDamage.begin(); // ÀÏ´Ü ÁÖÀ§¿¡ ¾ø´Â »ç¶÷À» °É·¯ ³½´Ù. (50m) while (it != m_map_kDamage.end()) { const VID & c_VID = it->first; int iDam = it->second.iTotalDamage; ++it; LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); // NPC°¡ ¶§¸®±âµµ Çϳª? -.-; if (!pAttacker || pAttacker->IsNPC() || DISTANCE_APPROX(GetX()-pAttacker->GetX(), GetY()-pAttacker->GetY())>5000) continue; iTotalDam += iDam; if (!pkChrMostAttacked || iDam > iMostDam) { pkChrMostAttacked = pAttacker; iMostDam = iDam; } if (pAttacker->GetParty()) { std::map::iterator it = map_party_damage.find(pAttacker->GetParty()); if (it == map_party_damage.end()) { TDamageInfo di; di.iDam = iDam; di.pAttacker = NULL; di.pParty = pAttacker->GetParty(); map_party_damage.insert(std::make_pair(di.pParty, di)); } else { it->second.iDam += iDam; } } else { TDamageInfo di; di.iDam = iDam; di.pAttacker = pAttacker; di.pParty = NULL; //sys_log(0, "__ pq_damage %s %d", pAttacker->GetName(), iDam); //pq_damage.push(di); damage_info_table.push_back(di); } } for (std::map::iterator it = map_party_damage.begin(); it != map_party_damage.end(); ++it) { damage_info_table.push_back(it->second); //sys_log(0, "__ pq_damage_party [%u] %d", it->second.pParty->GetLeaderPID(), it->second.iDam); } SetExp(0); //m_map_kDamage.clear(); if (iTotalDam == 0) // µ¥¹ÌÁö ÁØ°Ô 0ÀÌ¸é ¸®ÅÏ return NULL; if (m_pkChrStone) // µ¹ÀÌ ÀÖÀ» °æ¿ì °æÇèÄ¡ÀÇ ¹ÝÀ» µ¹¿¡°Ô ³Ñ±ä´Ù. { //sys_log(0, "__ Give half to Stone : %d", iExpToDistribute>>1); int iExp = iExpToDistribute >> 1; m_pkChrStone->SetExp(m_pkChrStone->GetExp() + iExp); iExpToDistribute -= iExp; } sys_log(1, "%s total exp: %d, damage_info_table.size() == %d, TotalDam %d", GetName(), iExpToDistribute, damage_info_table.size(), iTotalDam); //sys_log(1, "%s total exp: %d, pq_damage.size() == %d, TotalDam %d", //GetName(), iExpToDistribute, pq_damage.size(), iTotalDam); if (damage_info_table.empty()) return NULL; // Á¦ÀÏ µ¥¹ÌÁö¸¦ ¸¹ÀÌ ÁØ »ç¶÷ÀÌ HP ȸº¹À» ÇÑ´Ù. DistributeHP(pkChrMostAttacked); // ¸¸µÎ ½Ã½ºÅÛ { // Á¦ÀÏ µ¥¹ÌÁö¸¦ ¸¹ÀÌ ÁØ »ç¶÷À̳ª ÆÄƼ°¡ ÃÑ °æÇèÄ¡ÀÇ 20% + ÀڱⰡ ¶§¸°¸¸Å­ÀÇ °æÇèÄ¡¸¦ ¸Ô´Â´Ù. TDamageInfoTable::iterator di = damage_info_table.begin(); { TDamageInfoTable::iterator it; for (it = damage_info_table.begin(); it != damage_info_table.end();++it) { if (it->iDam > di->iDam) di = it; } } int iExp = iExpToDistribute / 5; iExpToDistribute -= iExp; float fPercent = (float) di->iDam / iTotalDam; if (fPercent > 1.0f) { sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di->pAttacker->GetName()); fPercent = 1.0f; } iExp += (int) (iExpToDistribute * fPercent); //sys_log(0, "%s given exp percent %.1f + 20 dam %d", GetName(), fPercent * 100.0f, di.iDam); di->Distribute(this, iExp); // 100% ´Ù ¸Ô¾úÀ¸¸é ¸®ÅÏÇÑ´Ù. if (fPercent == 1.0f) return pkChrMostAttacked; di->Clear(); } { // ³²Àº 80%ÀÇ °æÇèÄ¡¸¦ ºÐ¹èÇÑ´Ù. TDamageInfoTable::iterator it; for (it = damage_info_table.begin(); it != damage_info_table.end(); ++it) { TDamageInfo & di = *it; float fPercent = (float) di.iDam / iTotalDam; if (fPercent > 1.0f) { sys_err("DistributeExp percent over 1.0 (fPercent %f name %s)", fPercent, di.pAttacker->GetName()); fPercent = 1.0f; } //sys_log(0, "%s given exp percent %.1f dam %d", GetName(), fPercent * 100.0f, di.iDam); di.Distribute(this, (int) (iExpToDistribute * fPercent)); } } return pkChrMostAttacked; } // È­»ì °³¼ö¸¦ ¸®ÅÏÇØ ÁÜ int CHARACTER::GetArrowAndBow(LPITEM * ppkBow, LPITEM * ppkArrow, int iArrowCount/* = 1 */) { LPITEM pkBow; if (!(pkBow = GetWear(WEAR_WEAPON)) || pkBow->GetProto()->bSubType != WEAPON_BOW) { return 0; } LPITEM pkArrow; if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON || pkArrow->GetProto()->bSubType != WEAPON_ARROW) { return 0; } iArrowCount = MIN(iArrowCount, pkArrow->GetCount()); *ppkBow = pkBow; *ppkArrow = pkArrow; return iArrowCount; } void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount) { int iCount = pkArrow->GetCount(); DWORD dwVnum = pkArrow->GetVnum(); iCount = iCount - MIN(iCount, dwArrowCount); pkArrow->SetCount(iCount); if (iCount == 0) { LPITEM pkNewArrow = FindSpecifyItem(dwVnum); sys_log(0, "UseArrow : FindSpecifyItem %u %p", dwVnum, get_pointer(pkNewArrow)); if (pkNewArrow) EquipItem(pkNewArrow); } } class CFuncShoot { public: LPCHARACTER m_me; BYTE m_bType; bool m_bSucceed; CFuncShoot(LPCHARACTER ch, BYTE bType) : m_me(ch), m_bType(bType), m_bSucceed(false) { } void operator () (DWORD dwTargetVID) { if (m_bType > 1) { if (g_bSkillDisable) return; m_me->m_SkillUseInfo[m_bType].SetMainTargetVID(dwTargetVID); /*if (m_bType == SKILL_BIPABU || m_bType == SKILL_KWANKYEOK) m_me->m_SkillUseInfo[m_bType].ResetHitCount();*/ } LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID); if (!pkVictim) return; // °ø°Ý ºÒ°¡ if (!battle_is_attackable(m_me, pkVictim)) return; if (m_me->IsNPC()) { if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000) return; } LPITEM pkBow, pkArrow; switch (m_bType) { case 0: // ÀϹÝÈ° { int iDam = 0; if (m_me->IsPC()) { if (m_me->GetJob() != JOB_ASSASSIN) return; if (0 == m_me->GetArrowAndBow(&pkBow, &pkArrow)) return; if (m_me->GetSkillGroup() != 0) if (!m_me->IsNPC() && m_me->GetSkillGroup() != 2) { if (m_me->GetSP() < 5) return; m_me->PointChange(POINT_SP, -5); } iDam = CalcArrowDamage(m_me, pkVictim, pkBow, pkArrow); m_me->UseArrow(pkArrow, 1); // check speed hack DWORD dwCurrentTime = get_dword_time(); if (IS_SPEED_HACK(m_me, pkVictim, dwCurrentTime)) iDam = 0; } else iDam = CalcMeleeDamage(m_me, pkVictim); NormalAttackAffect(m_me, pkVictim); // µ¥¹ÌÁö °è»ê iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100; //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam); m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_NORMAL_RANGE); // Ÿ°ÝÄ¡ °è»êºÎ ³¡ } break; case 1: // ÀÏ¹Ý ¸¶¹ý { int iDam; if (m_me->IsPC()) return; iDam = CalcMagicDamage(m_me, pkVictim); NormalAttackAffect(m_me, pkVictim); // µ¥¹ÌÁö °è»ê iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100; //sys_log(0, "%s arrow %s dam %d", m_me->GetName(), pkVictim->GetName(), iDam); m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); pkVictim->Damage(m_me, iDam, DAMAGE_TYPE_MAGIC); // Ÿ°ÝÄ¡ °è»êºÎ ³¡ } break; case SKILL_YEONSA: // ¿¬»ç { //int iUseArrow = 2 + (m_me->GetSkillPower(SKILL_YEONSA) *6/100); int iUseArrow = 1; // ÅäÅ»¸¸ °è»êÇϴ°æ¿ì { if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); m_me->ComputeSkill(m_bType, pkVictim); m_me->UseArrow(pkArrow, iUseArrow); if (pkVictim->IsDead()) break; } else break; } } break; case SKILL_KWANKYEOK: { int iUseArrow = 1; if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); sys_log(0, "%s kwankeyok %s", m_me->GetName(), pkVictim->GetName()); m_me->ComputeSkill(m_bType, pkVictim); m_me->UseArrow(pkArrow, iUseArrow); } } break; case SKILL_GIGUNG: { int iUseArrow = 1; if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); sys_log(0, "%s gigung %s", m_me->GetName(), pkVictim->GetName()); m_me->ComputeSkill(m_bType, pkVictim); m_me->UseArrow(pkArrow, iUseArrow); } } break; case SKILL_HWAJO: { int iUseArrow = 1; if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); sys_log(0, "%s hwajo %s", m_me->GetName(), pkVictim->GetName()); m_me->ComputeSkill(m_bType, pkVictim); m_me->UseArrow(pkArrow, iUseArrow); } } break; case SKILL_HORSE_WILDATTACK_RANGE: { int iUseArrow = 1; if (iUseArrow == m_me->GetArrowAndBow(&pkBow, &pkArrow, iUseArrow)) { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); sys_log(0, "%s horse_wildattack %s", m_me->GetName(), pkVictim->GetName()); m_me->ComputeSkill(m_bType, pkVictim); m_me->UseArrow(pkArrow, iUseArrow); } } break; case SKILL_MARYUNG: //case SKILL_GUMHWAN: case SKILL_TUSOK: case SKILL_BIPABU: case SKILL_NOEJEON: case SKILL_GEOMPUNG: case SKILL_SANGONG: case SKILL_MAHWAN: case SKILL_PABEOB: //case SKILL_CURSE: { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName()); m_me->ComputeSkill(m_bType, pkVictim); } break; case SKILL_CHAIN: { m_me->OnMove(true); pkVictim->OnMove(); if (pkVictim->CanBeginFight()) pkVictim->BeginFight(m_me); sys_log(0, "%s - Skill %d -> %s", m_me->GetName(), m_bType, pkVictim->GetName()); m_me->ComputeSkill(m_bType, pkVictim); // TODO ¿©·¯¸í¿¡°Ô ½µ ½µ ½µ Çϱâ } break; case SKILL_YONGBI: { m_me->OnMove(true); } break; /*case SKILL_BUDONG: { m_me->OnMove(true); pkVictim->OnMove(); DWORD * pdw; DWORD dwEI = AllocEventInfo(sizeof(DWORD) * 2, &pdw); pdw[0] = m_me->GetVID(); pdw[1] = pkVictim->GetVID(); event_create(budong_event_func, dwEI, PASSES_PER_SEC(1)); } break;*/ default: sys_err("CFuncShoot: I don't know this type [%d] of range attack.", (int) m_bType); break; } m_bSucceed = true; } }; bool CHARACTER::Shoot(BYTE bType) { sys_log(1, "Shoot %s type %u flyTargets.size %zu", GetName(), bType, m_vec_dwFlyTargets.size()); if (!CanMove()) { return false; } CFuncShoot f(this, bType); if (m_dwFlyTargetID != 0) { f(m_dwFlyTargetID); m_dwFlyTargetID = 0; } f = std::for_each(m_vec_dwFlyTargets.begin(), m_vec_dwFlyTargets.end(), f); m_vec_dwFlyTargets.clear(); return f.m_bSucceed; } void CHARACTER::FlyTarget(DWORD dwTargetVID, int x, int y, BYTE bHeader) { LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID); TPacketGCFlyTargeting pack; //pack.bHeader = HEADER_GC_FLY_TARGETING; pack.bHeader = (bHeader == HEADER_CG_FLY_TARGETING) ? HEADER_GC_FLY_TARGETING : HEADER_GC_ADD_FLY_TARGETING; pack.dwShooterVID = GetVID(); if (pkVictim) { pack.dwTargetVID = pkVictim->GetVID(); pack.x = pkVictim->GetX(); pack.y = pkVictim->GetY(); if (bHeader == HEADER_CG_FLY_TARGETING) m_dwFlyTargetID = dwTargetVID; else m_vec_dwFlyTargets.push_back(dwTargetVID); } else { pack.dwTargetVID = 0; pack.x = x; pack.y = y; } sys_log(1, "FlyTarget %s vid %d x %d y %d", GetName(), pack.dwTargetVID, pack.x, pack.y); PacketAround(&pack, sizeof(pack), this); } LPCHARACTER CHARACTER::GetNearestVictim(LPCHARACTER pkChr) { if (NULL == pkChr) pkChr = this; float fMinDist = 99999.0f; LPCHARACTER pkVictim = NULL; TDamageMap::iterator it = m_map_kDamage.begin(); // ÀÏ´Ü ÁÖÀ§¿¡ ¾ø´Â »ç¶÷À» °É·¯ ³½´Ù. while (it != m_map_kDamage.end()) { const VID & c_VID = it->first; ++it; LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID); if (!pAttacker) continue; if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || pAttacker->IsAffectFlag(AFF_INVISIBILITY) || pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE)) continue; float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY()); if (fDist < fMinDist) { pkVictim = pAttacker; fMinDist = fDist; } } return pkVictim; } void CHARACTER::SetVictim(LPCHARACTER pkVictim) { if (!pkVictim) { if (0 != (DWORD)m_kVIDVictim) MonsterLog("°ø°Ý ´ë»óÀ» ÇØÁ¦"); m_kVIDVictim.Reset(); battle_end(this); } else { if (m_kVIDVictim != pkVictim->GetVID()) MonsterLog("°ø°Ý ´ë»óÀ» ¼³Á¤: %s", pkVictim->GetName()); m_kVIDVictim = pkVictim->GetVID(); m_dwLastVictimSetTime = get_dword_time(); } } LPCHARACTER CHARACTER::GetVictim() const { return CHARACTER_MANAGER::instance().Find(m_kVIDVictim); } LPCHARACTER CHARACTER::GetProtege() const // º¸È£ÇØ¾ß ÇÒ ´ë»óÀ» ¸®ÅÏ { if (m_pkChrStone) return m_pkChrStone; if (m_pkParty) return m_pkParty->GetLeader(); return NULL; } int CHARACTER::GetAlignment() const { return m_iAlignment; } int CHARACTER::GetRealAlignment() const { return m_iRealAlignment; } void CHARACTER::ShowAlignment(bool bShow) { if (bShow) { if (m_iAlignment != m_iRealAlignment) { m_iAlignment = m_iRealAlignment; UpdatePacket(); } } else { if (m_iAlignment != 0) { m_iAlignment = 0; UpdatePacket(); } } } void CHARACTER::UpdateAlignment(int iAmount) { bool bShow = false; if (m_iAlignment == m_iRealAlignment) bShow = true; int i = m_iAlignment / 10; m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000); if (bShow) { m_iAlignment = m_iRealAlignment; if (i != m_iAlignment / 10) UpdatePacket(); } } void CHARACTER::SetKillerMode(bool isOn) { if ((isOn ? ADD_CHARACTER_STATE_KILLER : 0) == IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER)) return; if (isOn) SET_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); else REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); m_iKillerModePulse = thecore_pulse(); UpdatePacket(); sys_log(0, "SetKillerMode Update %s[%d]", GetName(), GetPlayerID()); } bool CHARACTER::IsKillerMode() const { return IS_SET(m_bAddChrState, ADD_CHARACTER_STATE_KILLER); } void CHARACTER::UpdateKillerMode() { if (!IsKillerMode()) return; int iKillerSeconds = ! LC_IsYMIR() ? 30 : 60; if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(iKillerSeconds)) SetKillerMode(false); } void CHARACTER::SetPKMode(BYTE bPKMode) { if (bPKMode >= PK_MODE_MAX_NUM) return; if (m_bPKMode == bPKMode) return; if (bPKMode == PK_MODE_GUILD && !GetGuild()) bPKMode = PK_MODE_FREE; m_bPKMode = bPKMode; UpdatePacket(); sys_log(0, "PK_MODE: %s %d", GetName(), m_bPKMode); } BYTE CHARACTER::GetPKMode() const { return m_bPKMode; } struct FuncForgetMyAttacker { LPCHARACTER m_ch; FuncForgetMyAttacker(LPCHARACTER ch) { m_ch = ch; } void operator()(LPENTITY ent) { if (ent->IsType(ENTITY_CHARACTER)) { LPCHARACTER ch = (LPCHARACTER) ent; if (ch->IsPC()) return; if (ch->m_kVIDVictim == m_ch->GetVID()) ch->SetVictim(NULL); } } }; struct FuncAggregateMonster { LPCHARACTER m_ch; FuncAggregateMonster(LPCHARACTER ch) { m_ch = ch; } void operator()(LPENTITY ent) { if (ent->IsType(ENTITY_CHARACTER)) { LPCHARACTER ch = (LPCHARACTER) ent; if (ch->IsPC()) return; if (!ch->IsMonster()) return; if (ch->GetVictim()) return; if (number(1, 100) <= 50) // Àӽ÷Π50% È®·ü·Î ÀûÀ» ²ø¾î¿Â´Ù if (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000) if (ch->CanBeginFight()) ch->BeginFight(m_ch); } } }; struct FuncAttractRanger { LPCHARACTER m_ch; FuncAttractRanger(LPCHARACTER ch) { m_ch = ch; } void operator()(LPENTITY ent) { if (ent->IsType(ENTITY_CHARACTER)) { LPCHARACTER ch = (LPCHARACTER) ent; if (ch->IsPC()) return; if (!ch->IsMonster()) return; if (ch->GetVictim() && ch->GetVictim() != m_ch) return; if (ch->GetMobAttackRange() > 150) { int iNewRange = 150;//(int)(ch->GetMobAttackRange() * 0.2); if (iNewRange < 150) iNewRange = 150; ch->AddAffect(AFFECT_BOW_DISTANCE, POINT_BOW_DISTANCE, iNewRange - ch->GetMobAttackRange(), AFF_NONE, 3*60, 0, false); } } } }; struct FuncPullMonster { LPCHARACTER m_ch; int m_iLength; FuncPullMonster(LPCHARACTER ch, int iLength = 300) { m_ch = ch; m_iLength = iLength; } void operator()(LPENTITY ent) { if (ent->IsType(ENTITY_CHARACTER)) { LPCHARACTER ch = (LPCHARACTER) ent; if (ch->IsPC()) return; if (!ch->IsMonster()) return; //if (ch->GetVictim() && ch->GetVictim() != m_ch) //return; float fDist = DISTANCE_APPROX(m_ch->GetX() - ch->GetX(), m_ch->GetY() - ch->GetY()); if (fDist > 3000 || fDist < 100) return; float fNewDist = fDist - m_iLength; if (fNewDist < 100) fNewDist = 100; float degree = GetDegreeFromPositionXY(ch->GetX(), ch->GetY(), m_ch->GetX(), m_ch->GetY()); float fx; float fy; GetDeltaByDegree(degree, fDist - fNewDist, &fx, &fy); int tx = (int)(ch->GetX() + fx); int ty = (int)(ch->GetY() + fy); ch->Sync(tx, ty); ch->Goto(tx, ty); ch->CalculateMoveDuration(); ch->SyncPacket(); } } }; void CHARACTER::ForgetMyAttacker() { LPSECTREE pSec = GetSectree(); if (pSec) { FuncForgetMyAttacker f(this); pSec->ForEachAround(f); } ReviveInvisible(5); } void CHARACTER::AggregateMonster() { LPSECTREE pSec = GetSectree(); if (pSec) { FuncAggregateMonster f(this); pSec->ForEachAround(f); } } void CHARACTER::AttractRanger() { LPSECTREE pSec = GetSectree(); if (pSec) { FuncAttractRanger f(this); pSec->ForEachAround(f); } } void CHARACTER::PullMonster() { LPSECTREE pSec = GetSectree(); if (pSec) { FuncPullMonster f(this); pSec->ForEachAround(f); } } void CHARACTER::UpdateAggrPointEx(LPCHARACTER pAttacker, EDamageType type, int dam, CHARACTER::TBattleInfo & info) { // ƯÁ¤ °ø°ÝŸÀÔ¿¡ µû¶ó ´õ ¿Ã¶ó°£´Ù switch (type) { case DAMAGE_TYPE_NORMAL_RANGE: dam = (int) (dam*1.2f); break; case DAMAGE_TYPE_RANGE: dam = (int) (dam*1.5f); break; case DAMAGE_TYPE_MAGIC: dam = (int) (dam*1.2f); break; default: break; } // °ø°ÝÀÚ°¡ ÇöÀç ´ë»óÀÎ °æ¿ì º¸³Ê½º¸¦ ÁØ´Ù. if (pAttacker == GetVictim()) dam = (int) (dam * 1.2f); info.iAggro += dam; if (info.iAggro < 0) info.iAggro = 0; //sys_log(0, "UpdateAggrPointEx for %s by %s dam %d total %d", GetName(), pAttacker->GetName(), dam, total); if (GetParty() && dam > 0 && type != DAMAGE_TYPE_SPECIAL) { LPPARTY pParty = GetParty(); // ¸®´õÀÎ °æ¿ì ¿µÇâ·ÂÀÌ Á»´õ °­ÇÏ´Ù int iPartyAggroDist = dam; if (pParty->GetLeaderPID() == GetVID()) iPartyAggroDist /= 2; else iPartyAggroDist /= 3; pParty->SendMessage(this, PM_AGGRO_INCREASE, iPartyAggroDist, pAttacker->GetVID()); } ChangeVictimByAggro(info.iAggro, pAttacker); } void CHARACTER::UpdateAggrPoint(LPCHARACTER pAttacker, EDamageType type, int dam) { if (IsDead() || IsStun()) return; TDamageMap::iterator it = m_map_kDamage.find(pAttacker->GetVID()); if (it == m_map_kDamage.end()) { m_map_kDamage.insert(TDamageMap::value_type(pAttacker->GetVID(), TBattleInfo(0, dam))); it = m_map_kDamage.find(pAttacker->GetVID()); } UpdateAggrPointEx(pAttacker, type, dam, it->second); } void CHARACTER::ChangeVictimByAggro(int iNewAggro, LPCHARACTER pNewVictim) { if (get_dword_time() - m_dwLastVictimSetTime < 3000) // 3ÃÊ´Â ±â´Ù·Á¾ßÇÑ´Ù return; if (pNewVictim == GetVictim()) { if (m_iMaxAggro < iNewAggro) { m_iMaxAggro = iNewAggro; return; } // Aggro°¡ °¨¼ÒÇÑ °æ¿ì TDamageMap::iterator it; TDamageMap::iterator itFind = m_map_kDamage.end(); for (it = m_map_kDamage.begin(); it != m_map_kDamage.end(); ++it) { if (it->second.iAggro > iNewAggro) { LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(it->first); if (ch && !ch->IsDead() && DISTANCE_APPROX(ch->GetX() - GetX(), ch->GetY() - GetY()) < 5000) { itFind = it; iNewAggro = it->second.iAggro; } } } if (itFind != m_map_kDamage.end()) { m_iMaxAggro = iNewAggro; SetVictim(CHARACTER_MANAGER::instance().Find(itFind->first)); m_dwStateDuration = 1; } } else { if (m_iMaxAggro < iNewAggro) { m_iMaxAggro = iNewAggro; SetVictim(pNewVictim); m_dwStateDuration = 1; } } }