#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"
#ifdef ENABLE_WAR_KILL_NOTICE
#include "war_map.h"
#endif
#ifdef ENABLE_OFFLINE_SHOP_SYSTEM
#include "offlineshop.h"
#endif
#ifdef ENABLE_NEW_PET_SYSTEM
#include "New_PetSystem.h"
#endif
#ifdef ENABLE_SUPPORT_SHAMAN_SYSTEM
#include "support_shaman.h"
#endif
#define ENABLE_EFFECT_PENETRATE
static DWORD __GetPartyExpNP(const DWORD level)
{
if (!level || level > PLAYER_EXP_TABLE_MAX)
return 14000;
return party_exp_distribute_table[level];
}
static int __GetExpLossPerc(const DWORD level)
{
if (!level || level > PLAYER_EXP_TABLE_MAX)
return 1;
return aiExpLossPercents[level];
}
DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp)
{
if (PLAYER_MAX_LEVEL_CONST < 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<double>(exp);
if (ret < 1.0)
return 1;
return static_cast<DWORD>(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 (pkVictim->GetShopOwner())//@fixme242
return false;
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() || IsObserverMode()) //@fixme232
return false;
#ifdef ENABLE_PLAYER_SECURITY_SYSTEM
if (IsPC() && IsActivateSecurity())
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("GUVENLIK_KILIDI_ACIKKEN_ATAK_YAPAMASSIN."));
return false;
}
if (pkVictim && pkVictim->IsPC() && pkVictim->IsActivateSecurity())
{
if(IsPC())
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("GUVENLIK_KILIDI_ACIK_OLAN_KARAKTERE_ATAK_YAPAMASSIN."));
return false;
}
#endif
// CASTLE
if (IS_CASTLE_MAP(GetMapIndex()) && false == castle_can_attack(this, pkVictim))
return false;
// CASTLE
// @fixme131
if (!battle_is_attackable(this, pkVictim))
return false;
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);
#ifdef ENABLE_ACCE_SYSTEM
CloseAcce();
#endif
#ifdef ENABLE_AURA_SYSTEM
if (IsPC())
CloseAura();
#endif
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 (!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() * __GetExpLossPerc(GetLevel())) / 100);
iLoss = MIN(800000, iLoss);
if (bTown)
iLoss = 0;
if (IsEquipUniqueItem(UNIQUE_ITEM_TEARDROP_OF_GODNESS))
iLoss /= 2;
sys_log(0, "DEATH_PENALTY(%s) EXP_LOSS: %d percent %d%%", GetName(), iLoss, __GetExpLossPerc(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<char_event_info*>(event->info);
if (info == NULL)
{
sys_err("StunEvent> <Factor> Null pointer");
return 0;
}
LPCHARACTER ch = info->ch;
if (ch == NULL) { // <Factor>
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<char_event_info>();
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<SCharDeadEventInfo*>(event->info);
if (info == NULL)
{
sys_err("dead_event> <Factor> 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;
}
#ifndef GetGoldMultipler
#define GetGoldMultipler = 1;
#endif
#ifdef ENABLE_GOLD_REWARD_RENEWAL
void CHARACTER::RewardGold(LPCHARACTER pkAttacker)
{
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;
if (pkAttacker->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0 || pkAttacker->IsEquipUniqueGroup(UNIQUE_GROUP_LUCKY_GOLD))
iGoldPercent += iGoldPercent;
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);
if (number(1, 100) > iPercent)
return;
int iGoldMultipler = GetGoldMultipler;
if (1 == number(1, 50000))
iGoldMultipler *= 10;
else if (1 == number(1, 10000))
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));
int iGold = number(GetMobTable().dwGoldMin, GetMobTable().dwGoldMax);
iGold = iGold * CHARACTER_MANAGER::instance().GetMobGoldAmountRate(pkAttacker) / 100;
iGold *= iGoldMultipler;
int iSplitCount;
if (iGold >= 3)
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;
for (int i = 0; i < iSplitCount; ++i)
{
pkAttacker->GiveGold(iGold / iSplitCount);
}
}
DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER, GetRaceNum(), iTotalGold);
}
#else
#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;
// 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))
iGoldMultipler *= 10;
else if (1 == number(1, 10000))
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));
if (GetMobRank() >= MOB_RANK_BOSS && !IsStone() && GetMobTable().dwGoldMax != 0)
{
if (1 == number(1, iGold10DropPct))
iGoldMultipler *= 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);
}
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
}
}
}
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;
}
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)
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);
}
#endif
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());
#ifdef ENABLE_KILL_NOTICE
std::vector<int> monstersList{ 691 , 792 , 2091 , 2191 , 1901 , 1304 , 2206 , 2291, 1192 };
const char* MapName = GetMapName(GetMapIndex());
for (auto& i : monstersList) {
if (GetRaceNum() == i) {
auto pkMob = CMobManager::instance().Get(i);
if (pkMob) {
char szKillNotice[QUERY_MAX_LEN];
snprintf(szKillNotice, sizeof(szKillNotice), LC_TEXT("[CH%d]: %s, Lv.%d %s tarafindan %s'de katledildi!"), g_bChannel, pkMob->m_table.szLocaleName, pkAttacker->GetLevel(), pkAttacker->GetName(), MapName ? MapName : "NULL");
BroadcastNotice(szKillNotice);
}
}
}
#endif
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<LPITEM> 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)
{
#ifdef ENABLE_DICE_SYSTEM
if (pkAttacker->GetParty())
{
FPartyDropDiceRoll f(item, pkAttacker);
f.Process(this);
}
else
item->SetOwnership(pkAttacker);
#else
item->SetOwnership(pkAttacker);
#endif
}
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<std::pair<int, LPCHARACTER> > 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<LPCHARACTER> 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);
//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<LPCHARACTER>::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)
{
#ifdef ENABLE_DICE_SYSTEM
if (ch->GetParty())
{
FPartyDropDiceRoll f(item, ch);
f.Process(this);
}
else
item->SetOwnership(ch);
#else
item->SetOwnership(ch);
#endif
}
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 (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<std::pair<LPITEM, int> > 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<BYTE> 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;
#ifdef ENABLE_ITEM_SEALBIND_SYSTEM
if (pkItem->IsSealed())
continue;
#endif
#ifdef ENABLE_BASIC_ITEM_SYSTEM
if (pkItem->IsBasicItem())
continue;
#endif
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<BYTE> 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;
#ifdef ENABLE_ITEM_SEALBIND_SYSTEM
if (pkItem->IsSealed())
continue;
#endif
#ifdef ENABLE_BASIC_ITEM_SYSTEM
if (pkItem->IsBasicItem())
continue;
#endif
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;
#ifndef DISABLE_STOP_RIDING_WHEN_DIE
{
if (IsHorseRiding())
{
StopRiding();
}
else if (GetMountVnum())
{
#ifndef ENABLE_MOUNT_COSTUME_SYSTEM
RemoveAffect(AFFECT_MOUNT_BONUS);
#endif
m_dwMountVnum = 0;
UnEquipSpecialRideUniqueItem();
UpdatePacket();
}
}
#endif
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);
#ifdef ENABLE_WEAPON_RARITY_SYSTEM
LPITEM pkKillerWeapon = pkKiller->GetWear(WEAR_WEAPON);
if (pkKillerWeapon && IS_SET(pkKillerWeapon->GetFlag(), ITEM_FLAG_RARE_ABILITY))
pkKiller->IncreaseRarePoints(pkKillerWeapon, pkKiller, this);
#endif
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;
#ifdef ENABLE_WAR_KILL_NOTICE
CWarMap* pMap = pkKiller->GetWarMap();
if (pMap)
pMap->SendKillNotice(pkKiller->GetName(), GetName(), pkKiller->GetRaceNum(), GetRaceNum());
#endif
}
pkKiller->SetQuestNPCID(GetVID());
quest::CQuestManager::instance().Kill(pkKiller->GetPlayerID(), quest::QUEST_NO_NPC);
CGuildManager::instance().Kill(pkKiller, this);
}
}
#ifdef ENABLE_QUEST_DIE_EVENT
if (IsPC())
{
if (pkKiller)
SetQuestNPCID(pkKiller->GetVID());
// quest::CQuestManager::instance().Die(GetPlayerID(), quest::QUEST_NO_NPC);
quest::CQuestManager::instance().Die(GetPlayerID(), (pkKiller) ? pkKiller->GetRaceNum() : quest::QUEST_NO_NPC);
}
#endif
//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
{
#ifdef ENABLE_WEAPON_KILL_SYSTEM
LPITEM item = pkKiller->GetWear(WEAR_WEAPON);
if (item)
{
if (item->GetType() == ITEM_WEAPON)
{
std::map<DWORD, DWORD>::iterator LuiginaKillerIterator;
LuiginaKillerIterator = pkKiller->LuiginaKillList.find(GetPlayerID());
bool LuiginaReturn = true;
if (LuiginaKillerIterator != pkKiller->LuiginaKillList.end())
if (LuiginaKillerIterator->second + (60*60) > time(0))
LuiginaReturn = false;
if (LuiginaReturn)
{
const TPlayerItemAttribute& attrItem = item->GetAttribute(7);
item->SetForceAttribute(7, 0, attrItem.sValue+1);
pkKiller->LuiginaKillList[GetPlayerID()] = time(0);
}
else
{
pkKiller->ChatPacket(CHAT_TYPE_INFO, "Bu rakip'den le puan?zaten ald? 1 saat beklemelisin.");
}
}
}
#endif
#ifdef ENABLE_GUILD_STATISTICS
if (CWarMapManager::instance().IsWarMap(GetMapIndex()))
{
SetQuestFlag("loncaistatistik.olum", GetQuestFlag("loncaistatistik.olum")+1);
pkKiller->SetQuestFlag("loncaistatistik.oldurme", pkKiller->GetQuestFlag("loncaistatistik.oldurme")+1);
}
else
{
if (test_server)
{
SetQuestFlag("loncaistatistik.olum", GetQuestFlag("loncaistatistik.olum")+1);
pkKiller->SetQuestFlag("loncaistatistik.oldurme", pkKiller->GetQuestFlag("loncaistatistik.oldurme")+1);
}
}
#endif
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)
{
}
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 (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 (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 %ld", 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);
if (GetDesc() != NULL) {
//
//
itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
while (it != m_list_pkAffect.end())
SendAffectAddPacket(GetDesc(), *it++);
}
//
//
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<SCharDeadEventInfo>();
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(0));// @fixme202
}
}
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);
}
#ifdef ENABLE_ACCE_SYSTEM
if (IsPC())
CloseAcce();
#endif
CShopManager::instance().StopShopping(this);
CloseMyShop();
CloseSafebox();
#ifdef ENABLE_OFFLINE_SHOP_SYSTEM
if (GetOfflineShop())
{
GetOfflineShop()->RemoveGuest(this);
SetOfflineShop(NULL);
}
#endif
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));
}
*/
}
}
//
//
// Arguments
// pAttacker : 공격자
//
// Return value
// true : dead
// false : not dead yet
//
bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // returns true if dead
{
if (!pAttacker)// @fixme203
return false;
#ifdef ENABLE_NEW_PET_SYSTEM
if (IsImmortal())
return false;
#endif
#ifdef ENABLE_NEWSTUFF
if (pAttacker && IsStone() && pAttacker->IsPC())
{
if (GetEmpire() && GetEmpire() == pAttacker->GetEmpire())
{
SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
return false;
}
}
#endif
if (IsPC() && IsObserverMode())// @fixme204
return false;
#ifdef ENABLE_PLAYER_SECURITY_SYSTEM
if (IsPC() && IsActivateSecurity())
return false;
#endif
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 = 0; // @fixme136
if (GetMaxHP() >= 0)
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;
//PROF_UNIT puAttr("Attr");
//
//
//
//
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)
iCriticalPct = 5 + (iCriticalPct - 10) / 4;
else
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<int>(pkSk->kPointPoly.Eval());
}
}
if (iPenetratePct >= 10)
{
iPenetratePct = 5 + (iPenetratePct - 10) / 4;
}
else
{
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);
}
#ifdef ENABLE_EFFECT_PENETRATE
EffectPacket(SE_PENETRATE);
#endif
}
}
}
}
//
//
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;
#ifdef ENABLE_ITEM_BUFF_SYSTEM
if (IsAffectFlag(AFF_RESIST_BUFF))
dam = dam * (100 - GetPoint(POINT_RESIST_NORMAL_DAMAGE)) / 100;
#endif
//
//
if (pAttacker)
{
if (type == DAMAGE_TYPE_NORMAL)
{
if (GetPoint(POINT_REFLECT_MELEE))
{
int reflectDamage = dam * GetPoint(POINT_REFLECT_MELEE) / 100;
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<int>(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;
#ifdef ENABLE_EFFECT_PENETRATE
EffectPacket(SE_PENETRATE);
#endif
}
}
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);
}
}
}
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);
}
}
if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0)
{
int i = ((iCurHP >= 0) ? MIN(dam, iCurHP) : dam) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100; //@fixme107
if (i)
{
CreateFly(FLY_HP_SMALL, pAttacker);
pAttacker->PointChange(POINT_HP, i);
}
}
if (pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) && number(0, 4) > 0)
{
int i = ((iCurHP >= 0) ? MIN(dam, iCurHP) : dam) * pAttacker->GetPoint(POINT_HIT_SP_RECOVERY) / 100; //@fixme107
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))
{
int iDamageSPPart = dam / 3;
int iDamageToSP = iDamageSPPart * GetPoint(POINT_MANASHIELD) / 100;
int iSP = GetSP();
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;
}
if (pAttacker->IsPC())
{
int iEmpire = pAttacker->GetEmpire();
long lMapIndex = pAttacker->GetMapIndex();
int iMapEmpire = SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex);
if (iEmpire && iMapEmpire && iEmpire != iMapEmpire)
{
dam = dam * 9 / 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()))
{
dam += dam / 10;
}
if (IsPC() && CMonarch::instance().IsDefenceUp(GetEmpire()))
{
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;
}
}
#ifdef ENABLE_WOLFMAN_CHARACTER
else if (type == DAMAGE_TYPE_BLEEDING)
{
if (GetHP() - dam <= 0)
{
dam = GetHP();
}
}
#endif
// ------------------------
// -----------------------
if (pAttacker && pAttacker->IsPC())
{
int iDmgPct = CHARACTER_MANAGER::instance().GetUserDamageRate(pAttacker);
dam = dam * iDmgPct / 100;
}
if (IsMonster() && IsStoneSkinner())
{
if (GetHPPct() < GetMobTable().bStoneSkinPoint)
dam /= 2;
}
//PROF_UNIT puRest1("Rest1");
if (pAttacker)
{
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;
#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_MOB_BLEEDING_AS_POISON)
else if (type == DAMAGE_TYPE_BLEEDING)
damageFlag = DAMAGE_BLEEDING;
#elif defined(ENABLE_WOLFMAN_CHARACTER) && defined(USE_MOB_BLEEDING_AS_POISON)
else if (type == DAMAGE_TYPE_BLEEDING)
damageFlag = DAMAGE_POISON;
#endif
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;
// @fixme187 BEGIN
if (pAttacker->IsPC())
{
if (pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE) || IsAffectFlag(AFF_REVIVE_INVISIBLE))
{
dam = 0;
damageFlag = DAMAGE_DODGE;
}
}
// @fixme187 END
if (pAttacker)
SendDamagePacket(pAttacker, dam, damageFlag);
if (test_server)
{
int iTmpPercent = 0; // @fixme136
if (GetMaxHP() >= 0)
iTmpPercent = (GetHP() * 100) / GetMaxHP();
if (pAttacker)
{
pAttacker->ChatPacket(CHAT_TYPE_INFO, "-> %s, DAM %d HP %d(%d%%) %s%s",
GetName(),
dam,
GetHP(),
iTmpPercent,
IsCritical ? "crit " : "",
IsPenetrate ? "pene " : "",
IsDeathBlow ? "deathblow " : "");
}
ChatPacket(CHAT_TYPE_PARTY, "<- %s, DAM %d HP %d(%d%%) %s%s",
pAttacker ? pAttacker->GetName() : 0,
dam,
GetHP(),
iTmpPercent,
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());
}
}
//
//
if (!cannot_dead)
{
if (GetHP() - dam <= 0) // @fixme137
dam = GetHP();
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");
#ifdef ENABLE_POISON_UNAGGR
if (type != DAMAGE_TYPE_POISON)
UpdateAggrPointEx(pAttacker, type, dam, it->second);
#else
UpdateAggrPointEx(pAttacker, type, dam, it->second);
#endif
//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;
}
#define ENABLE_NEWEXP_CALCULATION
#ifdef ENABLE_NEWEXP_CALCULATION
#define NEW_GET_LVDELTA(me, victim) aiPercentByDeltaLev[MINMAX(0, (victim + 15) - me, MAX_EXP_DELTA_OF_LEV - 1)]
typedef long double rate_t;
static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp)
{
if (test_server && iExp < 0)
{
to->ChatPacket(CHAT_TYPE_INFO, "exp(%d) overflow", iExp);
return;
}
// decrease/increase exp based on player<>mob level
rate_t lvFactor = static_cast<rate_t>(NEW_GET_LVDELTA(to->GetLevel(), from->GetLevel())) / 100.0L;
iExp *= lvFactor;
// start calculating rate exp bonus
int iBaseExp = iExp;
rate_t rateFactor = 100;
if (distribution_test_server)
rateFactor *= 3;
rateFactor += CPrivManager::instance().GetPriv(to, PRIV_EXP_PCT);
if (to->IsEquipUniqueItem(UNIQUE_ITEM_LARBOR_MEDAL))
rateFactor += 20;
if (to->GetMapIndex() >= 660000 && to->GetMapIndex() < 670000)
rateFactor += 20;
if (to->GetPoint(POINT_EXP_DOUBLE_BONUS))
if (number(1, 100) <= to->GetPoint(POINT_EXP_DOUBLE_BONUS))
rateFactor += 30;
if (to->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_EXP))
rateFactor += 50;
switch (to->GetMountVnum())
{
case 20110:
case 20111:
case 20112:
case 20113:
if (to->IsEquipUniqueItem(71115) || to->IsEquipUniqueItem(71117) || to->IsEquipUniqueItem(71119) ||
to->IsEquipUniqueItem(71121))
{
rateFactor += 10;
}
break;
case 20114:
case 20120:
case 20121:
case 20122:
case 20123:
case 20124:
case 20125:
rateFactor += 30;
break;
}
if (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
rateFactor += 50;
if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP))
rateFactor += 50;
if (to->GetPoint(POINT_PC_BANG_EXP_BONUS) > 0)
{
if (to->IsPCBang())
rateFactor += to->GetPoint(POINT_PC_BANG_EXP_BONUS);
}
rateFactor += to->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_EXP_BONUS);
rateFactor += to->GetPoint(POINT_RAMADAN_CANDY_BONUS_EXP);
rateFactor += to->GetPoint(POINT_MALL_EXPBONUS);
rateFactor += to->GetPoint(POINT_EXP);
// useless (never used except for china intoxication) = always 100
rateFactor = rateFactor * static_cast<rate_t>(CHARACTER_MANAGER::instance().GetMobExpRate(to)) / 100.0L;
// fix underflow formula
iExp = std::max<int>(0, iExp);
rateFactor = std::max<rate_t>(100.0L, rateFactor);
// apply calculated rate bonus
iExp *= (rateFactor / 100.0L);
if (test_server)
to->ChatPacket(CHAT_TYPE_INFO, "base_exp(%d) * rate(%Lf) = exp(%d)", iBaseExp, rateFactor / 100.0L, iExp);
// you can get at maximum only 10% of the total required exp at once (so, you need to kill at least 10 mobs to level up) (useless)
iExp = MIN(to->GetNextExp() / 10, iExp);
// it recalculate the given exp if the player level is greater than the exp_table size (useless)
iExp = AdjustExpByLevel(to, iExp);
if (test_server)
to->ChatPacket(CHAT_TYPE_INFO, "exp+minGNE+adjust(%d)", iExp);
#ifdef ENABLE_NEW_PET_SYSTEM
if (to->GetNewPetSystem())
{
if (to->GetNewPetSystem()->IsActivePet() && to->GetNewPetSystem()->GetLevelStep() < 4)
{
int tmpexp = iExp * 9 / 20;
if (to->GetQuestFlag("PetSystems.petfullexp") == 1)
tmpexp = 0;
iExp = iExp - tmpexp;
to->GetNewPetSystem()->SetExp(tmpexp, 0);
}
}
#endif
#ifdef ENABLE_SUPPORT_SHAMAN_SYSTEM
if (to->GetSupportShaman())
{
if (to->GetSupportShaman()->IsActiveSupportShaman() && to->GetSupportShaman()->GetLevel() < 120)
{
int tmpexp = iExp * 9 / 20;
iExp = iExp - tmpexp;
to->GetSupportShaman()->SetExp(tmpexp);
}
}
#endif
#ifdef EXP_MULTIPLER
iExp *= EXP_MULTIPLER;
#endif
#ifdef ENABLE_RING_OF_SECRETS
if (!to->IsEquipUniqueItem(ITEM_ANTIEXP_RING))
#endif
{
to->PointChange(POINT_EXP, iExp, true);
from->CreateFly(FLY_EXP, to);
}
{
LPCHARACTER you = to->GetMarryPartner();
if (you)
{
// sometimes, this overflows
DWORD dwUpdatePoint = (2000.0L / to->GetLevel() / to->GetLevel() / 3) * iExp;
if (to->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0 ||
you->GetPremiumRemainSeconds(PREMIUM_MARRIAGE_FAST) > 0)
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
}
}
}
#else
static void GiveExp(LPCHARACTER from, LPCHARACTER to, int iExp)
{
iExp = CALCULATE_VALUE_LVDELTA(to->GetLevel(), from->GetLevel(), iExp);
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%)
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 (to->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
{
iExp += (iExp * 50 / 100);
}
if (to->IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_EXP) == true)
{
iExp += (iExp * 50 / 100);
}
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)
);
}
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);
to->ChatPacket(CHAT_TYPE_INFO, "exp(%d) base_exp(%d)", iExp, 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
}
}
}
#endif
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)
{
total += __GetPartyExpNP(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:
iExp2 = (DWORD)(_iExp * (float)__GetPartyExpNP(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());
}
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<TDamageInfo> TDamageInfoTable;
TDamageInfoTable damage_info_table;
std::map<LPPARTY, TDamageInfo> map_party_damage;
damage_info_table.reserve(m_map_kDamage.size());
TDamageMap::iterator it = m_map_kDamage.begin();
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);
if (!pAttacker || !pAttacker->IsPC() || pAttacker->IsNPC() || DISTANCE_APPROX(GetX() - pAttacker->GetX(), GetY() - pAttacker->GetY()) > 5000)// @fixme205
continue;
iTotalDam += iDam;
if (!pkChrMostAttacked || iDam > iMostDam)
{
pkChrMostAttacked = pAttacker;
iMostDam = iDam;
}
if (pAttacker->GetParty())
{
std::map<LPPARTY, TDamageInfo>::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<LPPARTY, TDamageInfo>::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)
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;
DistributeHP(pkChrMostAttacked);
{
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);
if (fPercent == 1.0f)
return pkChrMostAttacked;
di->Clear();
}
{
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;
#ifdef ENABLE_QUIVER_SYSTEM
if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON || (pkArrow->GetSubType() != WEAPON_ARROW && pkArrow->GetSubType() != WEAPON_QUIVER))
#else
if (!(pkArrow = GetWear(WEAR_ARROW)) || pkArrow->GetType() != ITEM_WEAPON || pkArrow->GetSubType() != WEAPON_ARROW)
#endif
{
return 0;
}
#ifdef ENABLE_QUIVER_SYSTEM
if (pkArrow->GetSubType() == WEAPON_QUIVER)
iArrowCount = MIN(iArrowCount, pkArrow->GetSocket(0) - time(0));
else
iArrowCount = MIN(iArrowCount, pkArrow->GetCount());
#else
iArrowCount = MIN(iArrowCount, pkArrow->GetCount());
#endif
*ppkBow = pkBow;
*ppkArrow = pkArrow;
return iArrowCount;
}
void CHARACTER::UseArrow(LPITEM pkArrow, DWORD dwArrowCount)
{
#ifdef ENABLE_QUIVER_SYSTEM
if (pkArrow->GetSubType() == WEAPON_QUIVER)
return;
#endif
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);
#ifdef ENABLE_PENDANT_SYSTEM
iDam = iDam * (100 - (pkVictim->GetPoint(POINT_RESIST_BOW) - pkVictim->GetPoint(POINT_ATTBONUS_BOW))) / 100;
#else
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100;
#endif
//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);
#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM
const int resist_magic = MINMAX(0, pkVictim->GetPoint(POINT_RESIST_MAGIC), 100);
const int resist_magic_reduction = MINMAX(0, (m_me->GetJob() == JOB_SURA) ? m_me->GetPoint(POINT_RESIST_MAGIC_REDUCTION) / 2 : m_me->GetPoint(POINT_RESIST_MAGIC_REDUCTION), 50);
const int total_res_magic = MINMAX(0, resist_magic - resist_magic_reduction, 100);
iDam = iDam * (100 - total_res_magic) / 100;
#else
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_MAGIC)) / 100;
#endif
//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);
}
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, long x, long 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;
}
#ifdef ENABLE_TITLE_SYSTEM
int CHARACTER::GetTitle() const
{
return m_iPrestige;
}
int CHARACTER::GetRealTitle() const
{
return m_iRealPrestige;
}
void CHARACTER::ShowTitle(bool bShow)
{
if (bShow)
{
if (m_iPrestige != m_iRealPrestige)
{
m_iPrestige = m_iRealPrestige;
UpdatePacket();
}
}
else
{
if (m_iPrestige != 0)
{
m_iPrestige = 0;
UpdatePacket();
}
}
}
void CHARACTER::UpdateTitle(int iAmount)
{
bool bShow = false;
if (m_iPrestige == m_iRealPrestige)
bShow = true;
int i = m_iPrestige;
m_iRealPrestige = MINMAX(0, m_iRealPrestige + iAmount, 19);
if (bShow)
{
m_iPrestige = m_iRealPrestige;
if (i != m_iPrestige)
UpdatePacket();
}
}
#endif
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;
#ifdef ENABLE_ALIGN_RENEWAL
int iOldAlignment = m_iRealAlignment;
m_iRealAlignment = MINMAX(-300000, m_iRealAlignment + iAmount, 300000);
OnAlignUpdate(iOldAlignment);
#else
m_iRealAlignment = MINMAX(-200000, m_iRealAlignment + iAmount, 200000);
#endif
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;
if (thecore_pulse() - m_iKillerModePulse >= PASSES_PER_SEC(30))
SetKillerMode(false);
}
void CHARACTER::SetPKMode(BYTE bPKMode)
{
#ifdef ENABLE_PLAYER_SECURITY_SYSTEM
if (IsActivateSecurity())
{
bPKMode = PK_MODE_PROTECT;
return;
}
#endif
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 (DISTANCE_APPROX(ch->GetX() - m_ch->GetX(), ch->GetY() - m_ch->GetY()) < 5000)// accelerated.
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);
long tx = (long)(ch->GetX() + fx);
long ty = (long)(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);
#ifdef ENABLE_AGGREGATE_MONSTER_EFFECT
EffectPacket(SE_AGGREGATE_MONSTER_EFFECT);
#endif
}
}
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());
}
#ifdef ENABLE_POISON_UNAGGR
if (type != DAMAGE_TYPE_POISON)
ChangeVictimByAggro(info.iAggro, pAttacker);
#else
ChangeVictimByAggro(info.iAggro, pAttacker);
#endif
}
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)
return;
if (pNewVictim == GetVictim())
{
if (m_iMaxAggro < iNewAggro)
{
m_iMaxAggro = iNewAggro;
return;
}
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;
}
}
}