856 lines
27 KiB
C++
856 lines
27 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "ItemExpDistributor.h"
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
//
|
|
#include <PublicMath.h>
|
|
#include "ServerOptionParser.h"
|
|
#include <ServerOptionParserEx.h>
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
//
|
|
#include "CharacterFormula.h"
|
|
#include "BattleRecord.h"
|
|
#include "PeakTimeEvent.h"
|
|
|
|
#include "Map.h"
|
|
#include "GameZone.h"
|
|
|
|
#include "GameRoom.h"
|
|
|
|
#include "GameParty/NewGameParty.h"
|
|
#include "GameParty/NewGamePartyManager.h"
|
|
|
|
#include "TargetManager.h"
|
|
#include "FatigueManager.h"
|
|
|
|
#include "Item.h"
|
|
#include "ItemManager.h"
|
|
#include "Player.h"
|
|
#include "./SkillSystem/PerkManager.h" // _NA002217_100728_EP2_PERK_SYSTEM_
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
//
|
|
ItemExpDistributor g_ItemExpDistributor;
|
|
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
// 소유권 결정( 경험치, 아이템 )
|
|
//
|
|
class GetOwnership
|
|
{
|
|
struct PartyOwnerPoint
|
|
{
|
|
BYTE m_byOwnerPoint;
|
|
DWORD m_TotDamage;
|
|
};
|
|
|
|
typedef STLX_MAP<WORD, PartyOwnerPoint> PARTYPOINT_MAP; // < PartyKey, PartyOwnerPoint >
|
|
typedef PARTYPOINT_MAP::iterator PARTYPOINT_MAP_ITER;
|
|
typedef std::pair<WORD, PartyOwnerPoint> PARTYPOINT_MAP_PAIR;
|
|
|
|
public: GetOwnership( GameField* pField, DWORD dwMaxHP )
|
|
: m_pField(pField), m_dwMaxHP(dwMaxHP), m_dwHighOwnerObjKey(0), m_byHighOwnerPoint(0) {}
|
|
~GetOwnership() {}
|
|
|
|
VOID operator () ( BattleRecord::Node* pRecord )
|
|
{
|
|
dwObjKey = pRecord->GetObjectKey();
|
|
Character *pAttacker = m_pField->FindCharacter( dwObjKey );
|
|
if( !pAttacker ) return;
|
|
|
|
WORD wPartyKey = pAttacker->GetPartyState().GetPartyKey();
|
|
|
|
// pAttacker가 소환체인 경우 그 주인을 찾아서 파티키를 셋팅해야한다.
|
|
if(pAttacker->IsEqualObjectKind(SUMMON_OBJECT))
|
|
{
|
|
Character *pSummoner = m_pField->FindCharacter( pAttacker->GetSummonerKey() );
|
|
if( pSummoner )
|
|
{
|
|
wPartyKey = pSummoner->GetPartyState().GetPartyKey();
|
|
dwObjKey = pSummoner->GetObjectKey();
|
|
}
|
|
}
|
|
|
|
// 소유권 포인트를 구한다.
|
|
if( wPartyKey )
|
|
{
|
|
// 파티원인 경우
|
|
// 한 몬스터에 대해서 2개 이상의 파티가 경쟁할 수 있다. 그래서 파티포인트를 맵으로 관리한다.
|
|
PARTYPOINT_MAP_ITER it = m_mapPartyPoint.find( wPartyKey );
|
|
if( it == m_mapPartyPoint.end() )
|
|
{
|
|
//현재 파티키에 해당하는 파티포인트가 아직 없다면...
|
|
stOwnerPoint.m_TotDamage = pRecord->GetTotDamage();
|
|
stOwnerPoint.m_byOwnerPoint = (BYTE)( 70 * pRecord->GetTotDamage() / m_dwMaxHP );
|
|
if( pRecord->IsFirstAttacker() ) stOwnerPoint.m_byOwnerPoint += 30;
|
|
|
|
m_mapPartyPoint.insert( PARTYPOINT_MAP_PAIR(wPartyKey, stOwnerPoint) );
|
|
|
|
byOwnerPoint = stOwnerPoint.m_byOwnerPoint;
|
|
}
|
|
else
|
|
{
|
|
//파티키에 해당하는 파티포인트가 존재한다면...
|
|
PartyOwnerPoint &PartyPoint = it->second;
|
|
PartyPoint.m_TotDamage += pRecord->GetTotDamage();
|
|
PartyPoint.m_byOwnerPoint = (BYTE)( 70 * PartyPoint.m_TotDamage / m_dwMaxHP );
|
|
if( pRecord->IsFirstAttacker() ) PartyPoint.m_byOwnerPoint += 30;
|
|
|
|
byOwnerPoint = PartyPoint.m_byOwnerPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 파티원이 아닌 경우
|
|
byOwnerPoint = (BYTE)( 70 * pRecord->GetTotDamage() / m_dwMaxHP );
|
|
if( pRecord->IsFirstAttacker() ) byOwnerPoint += 30;
|
|
}
|
|
|
|
// 최대 소유포인트를 갱신한다.
|
|
if( byOwnerPoint >= m_byHighOwnerPoint )
|
|
{
|
|
m_byHighOwnerPoint = byOwnerPoint;
|
|
m_dwHighOwnerObjKey = dwObjKey;
|
|
}
|
|
}
|
|
|
|
DWORD GetOwnerObjKey() { return m_dwHighOwnerObjKey; }
|
|
|
|
|
|
private: PARTYPOINT_MAP m_mapPartyPoint;
|
|
GameField * m_pField;
|
|
DWORD m_dwMaxHP;
|
|
|
|
DWORD m_dwHighOwnerObjKey;
|
|
BYTE m_byHighOwnerPoint;
|
|
|
|
// Local 변수
|
|
BYTE byOwnerPoint;
|
|
PartyOwnerPoint stOwnerPoint;
|
|
DWORD dwObjKey;
|
|
};
|
|
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
|
|
|
|
Character*
|
|
ItemExpDistributor::DecideExpOwnership( NPC *pNPC )
|
|
{
|
|
GameField *pField = pNPC->GetField();
|
|
if( !pField ) return NULL;
|
|
|
|
BattleRecord *pBattleRecord = pNPC->GetTargetManager()->GetBattleRecord();
|
|
if( !pBattleRecord->GetSize() )
|
|
return NULL;
|
|
|
|
GetOwnership Opr( pField, pNPC->GetMaxHP() );
|
|
pBattleRecord->ForEachRecord( Opr );
|
|
|
|
return pField->FindCharacter( Opr.GetOwnerObjKey() );
|
|
}
|
|
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
|
|
// NPC는 죽은 몬스터의 객체이다.
|
|
Player*
|
|
ItemExpDistributor::DecideItemOwnership( NPC *pNPC )
|
|
{
|
|
Character* pOwner = NULL;
|
|
|
|
pOwner = DecideExpOwnership( pNPC );
|
|
if( !pOwner ) return NULL;
|
|
|
|
if(pOwner->IsEqualObjectKind(PLAYER_OBJECT))
|
|
{
|
|
return (Player*)pOwner;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
// 경험치 분배
|
|
//
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
// 경험치 분배 루틴 수정 - pNPC Pointer를 사용하지 않고도 처리하는 방법으로 변경
|
|
class DistributeExpOpr : public std::unary_function<Player*, EXPTYPE>
|
|
{
|
|
// Process Options
|
|
public: enum ePolicy {
|
|
DEO_DISABLE_ALL = 0,
|
|
DEO_CHECK_ZONE_INTEGRITY = 1<< 0,
|
|
DEO_CHECK_PARTY_RETURN_ABUSE = 1<< 1,
|
|
DEO_APPLY_ADD_EXP = 1<< 2,
|
|
};
|
|
// Command Options
|
|
public: struct Option_Default;
|
|
struct Option_GetExp;
|
|
|
|
private:
|
|
const ePolicy Policy;
|
|
const DistributeExpOprArgs& rArgs;
|
|
const SERVER_COMMON_OPTION& rServerOption;
|
|
PeakTimeEvent* const m_pPeakTimeEvent;
|
|
DistributeExpOprResult* m_pOutputResult;
|
|
|
|
public: DistributeExpOpr(
|
|
const ePolicy policy
|
|
, const DistributeExpOprArgs& _rArgs
|
|
, DistributeExpOprResult* OUT _pOutputResult)
|
|
: Policy(policy)
|
|
, rArgs(_rArgs)
|
|
, rServerOption( ServerOptionParserEx::Instance()->GetServerOption() )
|
|
, m_pPeakTimeEvent( PeakTimeEvent::Instance() )
|
|
, m_pOutputResult( _pOutputResult )
|
|
{
|
|
}
|
|
|
|
BOOLEAN operator() ( Player* pPlayer ) const;
|
|
};
|
|
|
|
|
|
struct DistributeExpOpr::Option_Default : public DistributeExpOpr
|
|
{
|
|
Option_Default( const DistributeExpOprArgs& _rArgs )
|
|
: DistributeExpOpr( (ePolicy)
|
|
( DEO_CHECK_ZONE_INTEGRITY
|
|
| DEO_CHECK_PARTY_RETURN_ABUSE
|
|
| DEO_APPLY_ADD_EXP
|
|
)
|
|
, _rArgs
|
|
, NULL
|
|
)
|
|
{}
|
|
};
|
|
//struct DistributeExpOpr::Option_GetExp : public DistributeExpOpr
|
|
//{
|
|
// Option_GetExp( const DistributeExpOprArgs& _rArgs )
|
|
// : DistributeExpOpr( _rArgs )
|
|
// {}
|
|
//};
|
|
|
|
|
|
BOOLEAN
|
|
DistributeExpOpr::operator() ( Player* pPlayer ) const
|
|
{
|
|
GameZone* const pZone = pPlayer->GetGameZonePtr();
|
|
if( Policy & DEO_CHECK_ZONE_INTEGRITY )
|
|
{
|
|
if( FALSE == ( pZone && (rArgs.pGameZone==pZone) && pPlayer->IsAlive() ) )
|
|
return 0;
|
|
|
|
// Skip Logic
|
|
//if( ( pPlayer->GetObjectType() & PLAYER_OBJECT ) != PLAYER_OBJECT ) return;
|
|
|
|
if( rArgs.radius && ( Math_VectorLength2( *rArgs.pwvNpcPos, *pPlayer->GetPosPtr() ) > rArgs.fRadius_Q2 ) )
|
|
return 0;
|
|
}
|
|
|
|
if( Policy & DEO_CHECK_PARTY_RETURN_ABUSE ) //__NA_780_PREVENTION_PARTY_RETURN_ABUSE
|
|
{
|
|
GameField* const pFieldOfPlayer = pPlayer->GetField();
|
|
if( !rArgs.pGameField || (rArgs.pGameField != pFieldOfPlayer) )
|
|
return 0;
|
|
}
|
|
|
|
const LEVELTYPE player_level = pPlayer->GetLevel();
|
|
|
|
EXPTYPE resultExp = GetKillExpOfParty(
|
|
rArgs.roomBonusType
|
|
, rArgs.memberNum
|
|
, rArgs.totalLevel
|
|
, player_level
|
|
, rArgs.npcLevel
|
|
, rArgs.fNpcExpRatio
|
|
, rArgs.npcMaxHP // attack_damage
|
|
, rArgs.npcMaxHP // max HP
|
|
, rArgs.zoneType
|
|
, pPlayer
|
|
);
|
|
//
|
|
EXPTYPE groupEXP = resultExp; //보너스 경험치를 제외한 순수 경험치 임시 저장( 그룹 보너스를 위해 )
|
|
|
|
double benefit = 1.0f;
|
|
|
|
double var_for_debug = m_pPeakTimeEvent->GetEventExp( pPlayer ); // Peak Time 이벤트 경험치를 더 준다.
|
|
|
|
//_KR_0_20091021_FIRENDINVITATION_EXTEND // 친구 추천 이벤트 경험치를 가산한다.
|
|
var_for_debug += pPlayer->GetInvitateFriend().GetAddExpRate(rArgs.radius, rArgs.pwvNpcPos, rArgs.fRadius_Q2);
|
|
|
|
benefit += var_for_debug;
|
|
var_for_debug = pZone->GetBonusExp( pPlayer ); // 슈퍼방장 방인지 체크
|
|
benefit += var_for_debug;
|
|
var_for_debug = pPlayer->GetBonusExpRatio(); // 경험치 증가 속성이 있는지 체크
|
|
benefit += var_for_debug;
|
|
|
|
// PC방 유저이면 경험치 10%를 더 준다.(PC방 요금미납자가 아닌지 체크)
|
|
if( pPlayer->CanApplyPCBangBenefit( FALSE ) )
|
|
{ // (WAVERIX)(080912)(THINKING)
|
|
// - Singleton에 익숙하고 각종 옵션 구조를 직접 참조하는 것이 당연하게 생각하는 지금 m_fPCBANG_BONUS_EXP_RATIO같은 것을
|
|
// 별도로 추출하면 대판 욕먹을 수 있다.
|
|
var_for_debug = ( 1.0f + rServerOption.m_fPCBANG_BONUS_EXP_RATIO );
|
|
benefit *= var_for_debug;
|
|
}
|
|
|
|
// changed rule by _NA_002471_20110408_EXP_BENEFIT_UP
|
|
// 1101부터 경험치 보너스 최대값 제한 상향
|
|
if (benefit > 10.0f) {
|
|
benefit = 10.0f;
|
|
}
|
|
// (WAVERIX)(080912)(REPLACEMENT PROPOSAL)
|
|
// - 계산 상의 미스가 생기는 지는 정밀하게 테스트는 되지 않았지만, (뭐에서 문제가 생기는지 모름)
|
|
// 해당 Rounded는 반올림을 하는데 있어서 적어도 H/W 환경, compile option 또는 debug|release에서 항상 동일한 결과를 보일 것이다.
|
|
|
|
// (JSCHO)(Comment) - 피로도 시스템(플레이가 12시간이 넘으면 경험치 없음)
|
|
const int fatigue_exp_ratio = pPlayer->GetFatigueManager()->GetExpRatio();
|
|
const int nTotalBonusRatio = util::LL_Ceil(benefit * 100.f - 100.f);
|
|
const int nTotalBenefit = util::LL_Ceil(benefit * fatigue_exp_ratio);
|
|
resultExp = (EXPTYPE)util::LL_Ceil(double(resultExp) * double(nTotalBenefit) / 100.0f);
|
|
|
|
if (Policy & DEO_APPLY_ADD_EXP) {
|
|
pPlayer->AddExp(resultExp, rArgs.npc_key, nTotalBonusRatio);
|
|
#ifdef _NA002217_100728_EP2_PERK_SYSTEM_
|
|
// add only battle experience value
|
|
pPlayer->GetPerkManager()->AddExp(resultExp);
|
|
#endif
|
|
};
|
|
|
|
// __KR_001355_20090608_EXP_ACQUISITION_NOTIFICATION
|
|
#ifdef _NA_20101026_CHANGE_GROUP_BONUS_APPLY_WAY
|
|
if (pPlayer->GetGroupLowMemberInfo() == GROUP_HAVE_ONLINE_LOW)
|
|
{
|
|
pPlayer->GroupBonusAcceptForEXP( groupEXP, resultExp, eEXP_ACQUISITION_HUNTING);
|
|
}
|
|
#else
|
|
if (pPlayer->GetGroupHighMemberInfo() == GROUP_HAVE_OFFLINE_HIGH ||
|
|
pPlayer->GetGroupHighMemberInfo() == GROUP_HAVE_ONLINE_HIGH)
|
|
{
|
|
pPlayer->GroupBonusAcceptForEXP( groupEXP, resultExp, eEXP_ACQUISITION_HUNTING );
|
|
}
|
|
#endif
|
|
|
|
if( m_pOutputResult )
|
|
{
|
|
m_pOutputResult->TotalExp = resultExp;
|
|
m_pOutputResult->BonusExp = nTotalBonusRatio;
|
|
}
|
|
|
|
// (WAVERIX)(080912)(BUG-FIX) double, EXPTYPE, 등등 타입 정보가 메타문자열 포맷과 다르다.
|
|
/*bool show_log = true;
|
|
WAVERIX_WRAPPER((WAVERIX_LC_CALL1("waverix_log_distributed_exp", &show_log)));
|
|
if (show_log)
|
|
{
|
|
SUNLOG( eDEV_LOG,
|
|
"[DistributeExpOpr] Name = %s, dbBenefit = %0.3e, ExpRatio = %d, nTotalBenefit = %d,"
|
|
" ResultExp = %I64d, nTotalBonusRatio = %d"
|
|
, pPlayer->GetCharName(), benefit, fatigue_exp_ratio, nTotalBenefit, resultExp, nTotalBonusRatio );
|
|
}*/
|
|
// (WAVERIX)(080912)(PERFORMANCE MONITORING REPORT)
|
|
// - 몬스터가 제거된 시점의 성능 측정 결과는 상당히 엉망이다.
|
|
// - 연관된 이벤트인 스킬, 데미지 처리등은 극악이다. (이 부분은 언급하기조차 싫다)
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
//
|
|
BOOLEAN
|
|
ItemExpDistributor::GetCalcedExpValue(Player* const pPlayer,
|
|
const DistributeExpOprArgs* const pArgs,
|
|
DistributeExpOprResult* OUT pResult)
|
|
{
|
|
if(!(pArgs && pResult))
|
|
return FALSE;
|
|
|
|
ZeroMemory(pResult, sizeof(*pResult));
|
|
|
|
// 경험치분배 객체 생성
|
|
DistributeExpOpr actor(DistributeExpOpr::DEO_DISABLE_ALL, *pArgs, pResult);
|
|
return actor(pPlayer);
|
|
}
|
|
|
|
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
//
|
|
VOID
|
|
ItemExpDistributor::DistributeExp( NPC* pNPC, Character* pExpOwner )
|
|
{
|
|
// 변수 셋팅
|
|
GameZone* pZone = pNPC->GetGameZonePtr();
|
|
if( !pZone ) return;
|
|
if(pExpOwner->IsEqualObjectKind(PLAYER_OBJECT) == 0)
|
|
return;
|
|
|
|
const eZONETYPE zone_type = pZone->GetZoneType();
|
|
BYTE byRoomBonusType = 0; BYTE byRadius = 0;
|
|
|
|
// 헌팅맵의 경우
|
|
if( zone_type == eZONETYPE_HUNTING )
|
|
{
|
|
GameRoom * pGameRoom = (GameRoom *)pZone;
|
|
byRoomBonusType = pGameRoom->GetAdditionalInfo()->m_Bonus;
|
|
byRadius = 60;
|
|
}
|
|
else
|
|
{
|
|
byRadius = 60;
|
|
}
|
|
|
|
WORD PartyKey = pExpOwner->GetPartyState().GetPartyKey();
|
|
|
|
WORD wMemberNum = 1;
|
|
LEVELTYPE TotalLevel = pExpOwner->GetLevel();
|
|
|
|
NewGamePartyManager::Instance()->GetAreaLivePartyInfo( PartyKey, pZone->GetKey(), pNPC->GetPosPtr(), byRadius, wMemberNum, TotalLevel );
|
|
|
|
DistributeExpOprArgs args(
|
|
pZone
|
|
, pNPC->GetField()
|
|
, zone_type
|
|
, byRoomBonusType
|
|
, byRadius
|
|
, wMemberNum
|
|
, TotalLevel
|
|
, (FLOAT)byRadius*byRadius
|
|
, pNPC->GetObjectKey()
|
|
, pNPC->GetPosPtr()
|
|
, pNPC->GetGradeExpRatio()
|
|
, pNPC->GetMaxHP()
|
|
, pNPC->GetDisplayLevel());
|
|
|
|
// 경험치분배 객체 생성
|
|
DistributeExpOpr::Option_Default Opr( args );
|
|
|
|
if( !NewGamePartyManager::Instance()->For_eachPlayers( PartyKey, Opr ) )
|
|
{
|
|
if(pExpOwner->IsEqualObjectKind(PLAYER_OBJECT))
|
|
Opr( (Player*)pExpOwner );
|
|
}
|
|
}
|
|
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
// 아이템 분배
|
|
//
|
|
template<class PACKET_TYPE>
|
|
class DistributeItemOpr : public std::unary_function<Player *, void>
|
|
{
|
|
public:
|
|
DistributeItemOpr( Player *pOwner, Item *pItem, KEYTYPE ZoneKey, BYTE byRadius, PACKET_TYPE & packet ) : m_pOwner(pOwner),
|
|
m_pItem(pItem), m_ZoneKey(ZoneKey), m_byRadius(byRadius), m_Packet(packet) {}
|
|
~DistributeItemOpr() {}
|
|
|
|
VOID operator () ( Player *pMember ) const
|
|
{
|
|
GameZone *pZone = pMember->GetGameZonePtr();
|
|
if( !pZone ) return;
|
|
if( m_ZoneKey != pZone->GetKey() ) return;
|
|
if( pMember->IsDead() ) return;
|
|
if( pMember->GetObjectKey() == m_pOwner->GetObjectKey() ) return;
|
|
|
|
// 피로도 시스템
|
|
BYTE byPickRatio = pMember->GetFatigueManager()->GetShareItemPickupRatio();
|
|
if( random(1, 100) > byPickRatio ) return;
|
|
|
|
if( m_byRadius != 0 && Math_VectorLength( *m_pOwner->GetPosPtr(), *pMember->GetPosPtr() ) > m_byRadius ) return;
|
|
|
|
GameField* pOwnerField = m_pOwner->GetField();
|
|
GameField* pMemberField = pMember->GetField();
|
|
if( !pOwnerField || !pMemberField )
|
|
return;
|
|
if( pOwnerField->GetFieldCode() != pMemberField->GetFieldCode() )
|
|
return;
|
|
|
|
if( m_pItem )
|
|
{
|
|
SCItemSlot ItemSlot = m_pItem->GetItemSlot();
|
|
if( RC::RC_ITEM_SUCCESS == pMember->GetItemManager()->Pick( 1, ItemSlot, &m_Packet.m_ItemInfo ) )
|
|
pMember->SendPacket( &m_Packet, m_Packet.GetSize() );
|
|
}
|
|
}
|
|
|
|
private:
|
|
Player * m_pOwner;
|
|
Item * m_pItem;
|
|
KEYTYPE m_ZoneKey;
|
|
BYTE m_byRadius;
|
|
// Local 변수
|
|
//WzVector vOwnerPos;
|
|
//WzVector vMemberPos;
|
|
PACKET_TYPE & m_Packet;
|
|
//
|
|
__DISABLE_COPY(DistributeItemOpr<PACKET_TYPE>);
|
|
};
|
|
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
// 하임 분배
|
|
//
|
|
template<class PACKET_TYPE>
|
|
class DistributeMoneyOpr : public std::unary_function<Player *, void>
|
|
{
|
|
public:
|
|
DistributeMoneyOpr( Player *pOwner, MONEY Money, KEYTYPE ZoneKey, BYTE byRadius, PACKET_TYPE & packet ) :
|
|
m_pOwner(pOwner), m_Money(Money), m_ZoneKey(ZoneKey), m_byRadius(byRadius),m_Packet(packet) {}
|
|
~DistributeMoneyOpr() {}
|
|
|
|
VOID operator () ( Player *pMember ) const
|
|
{
|
|
GameZone *pZone = pMember->GetGameZonePtr();
|
|
if( !pZone ) return;
|
|
if( m_ZoneKey != pZone->GetKey() ) return;
|
|
if( pMember->IsDead() ) return;
|
|
if( pMember->GetObjectKey() == m_pOwner->GetObjectKey() ) return;
|
|
|
|
// 피로도 시스템
|
|
BYTE byPickRatio = pMember->GetFatigueManager()->GetShareItemPickupRatio();
|
|
#ifndef __NA_000632_20070817_CHINA_FATIGUE_HEIM
|
|
if( random(1, 100) > byPickRatio ) return;
|
|
#endif
|
|
|
|
if( m_byRadius != 0 && Math_VectorLength( *m_pOwner->GetPosPtr(), *pMember->GetPosPtr() ) > m_byRadius ) return;
|
|
|
|
GameField* pOwnerField = m_pOwner->GetField();
|
|
GameField* pMemberField = pMember->GetField();
|
|
if( !pOwnerField || !pMemberField )
|
|
return;
|
|
if( pOwnerField->GetFieldCode() != pMemberField->GetFieldCode() )
|
|
return;
|
|
|
|
MONEY DropMoney = m_Money;
|
|
// 하임증가 아이템이 있는지 체크
|
|
if( pMember->GetBonusMoneyRatio() )
|
|
{
|
|
DropMoney = (MONEY)(ceil)( DropMoney * ( 1.f + pMember->GetBonusMoneyRatio() ) );
|
|
}
|
|
|
|
#ifdef __NA_000632_20070817_CHINA_FATIGUE_HEIM
|
|
DropMoney = ( DropMoney / (float)(100.0/byPickRatio) );
|
|
#endif
|
|
|
|
pMember->PlusMoney( DropMoney );
|
|
m_Packet.m_Money = pMember->GetMoney();
|
|
m_Packet.m_BonusRatio = (WORD) ceil( pMember->GetBonusMoneyRatio() * 100 );
|
|
pMember->SendPacket( &m_Packet, sizeof(m_Packet) );
|
|
|
|
GAMELOG->LogMoney( MONEY_PICK, pMember, DropMoney );
|
|
if( pMember->GetGroupLowMemberInfo() == GROUP_HAVE_ONLINE_LOW ) //한명의 그룹원이라도 온라인이라면
|
|
{
|
|
//현재 그룹 머니는 천분율 기준 30으로 설정 되어있으나 스크립트 파서 적용 이후로 추후 변경
|
|
g_pGameServer->NotifyHeimAcquisition(pMember->GetCharGuid(), eHEIM_ACQUISITION_PICKUP, MONEY(DropMoney * 0.03f));
|
|
}
|
|
}
|
|
|
|
private:
|
|
Player * m_pOwner;
|
|
MONEY m_Money;
|
|
KEYTYPE m_ZoneKey;
|
|
BYTE m_byRadius;
|
|
|
|
// Local 변수
|
|
//WzVector vOwnerPos;
|
|
//WzVector vMemberPos;
|
|
PACKET_TYPE & m_Packet;
|
|
};
|
|
|
|
//
|
|
//VOID ItemExpDistributor::DistributeItem( Item *pItem, MONEY Money, Player *pItemOwner )
|
|
//{
|
|
// // 변수 셋팅
|
|
// GameZone *pZone = pItemOwner->GetGameZonePtr();
|
|
// if( !pZone ) return;
|
|
//
|
|
// // 미션맵의 경우
|
|
// BYTE byRadius = 0;
|
|
// if( pZone->GetZoneType() == eZONETYPE_MISSION )
|
|
// {
|
|
// byRadius = 0;
|
|
// }
|
|
// else
|
|
// {
|
|
// // 헌팅, 필드
|
|
// byRadius = 60;
|
|
// }
|
|
//
|
|
// GameParty *pParty = GamePartyManager::Instance()->FindParty( pItemOwner->GetPartyState().GetPartyKey() );
|
|
//
|
|
// // 아이템분배 객체 생성
|
|
// if( pParty )
|
|
// {
|
|
// DistributeItemOpr<MSG_CG_ITEM_PICK_MONEY_ACK> Opr( pItemOwner, pItem, Money, pZone->GetKey(), byRadius );
|
|
//
|
|
// switch(pZone->GetZoneType())
|
|
// {
|
|
// case eZONETYPE_FIELD:
|
|
// {
|
|
// if( pItem && pItem->GetItemSlot().GetItemInfo()->m_wType == eITEMTYPE_QUEST ||
|
|
// pItem && pItem->GetItemSlot().GetItemInfo()->m_wType == eITEMTYPE_AC )
|
|
// {
|
|
// pParty->ForEachMember( Opr );
|
|
// }
|
|
// }break;
|
|
// case eZONETYPE_MISSION:
|
|
// case eZONETYPE_HUNTING:
|
|
// case eZONETYPE_CHUNTING:
|
|
// {
|
|
// pParty->ForEachMember( Opr );
|
|
// }break;
|
|
// default:
|
|
// {
|
|
//
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
// 아이템/하임 분배 유효 제한 거리
|
|
//
|
|
__inline BYTE _GetRadius( GameZone * pZone )
|
|
{
|
|
if( !pZone ) return 0xFF;
|
|
if( pZone->GetZoneType() == eZONETYPE_MISSION )
|
|
{// 미션맵의 경우
|
|
return 0;
|
|
}
|
|
else
|
|
{// 헌팅, 필드
|
|
return 60;
|
|
}
|
|
}
|
|
|
|
|
|
//────────────────────────────────────────────────────────────────
|
|
// changed by _NA_0_20101206_PARTY_ITEM_DISTRIBUTE_CHANGE
|
|
VOID ItemExpDistributor::DistributeItem( Item * pItem, Player * pItemOwner )
|
|
{
|
|
if( !pItem ) return;
|
|
|
|
WORD PartyKey = pItemOwner->GetPartyState().GetPartyKey();
|
|
|
|
GameZone * pZone = pItemOwner->GetGameZonePtr();
|
|
if( !pZone ) return;
|
|
|
|
MSG_CG_ITEM_PICK_ACK amsg;
|
|
DistributeItemOpr<MSG_CG_ITEM_PICK_ACK> Opr( pItemOwner, pItem, pZone->GetKey(), _GetRadius(pZone), amsg );
|
|
|
|
switch(pZone->GetZoneType())
|
|
{
|
|
case eZONETYPE_FIELD:
|
|
case eZONETYPE_MISSION:
|
|
case eZONETYPE_HUNTING:
|
|
case eZONETYPE_CHUNTING:
|
|
case eZONETYPE_INSTANCE:
|
|
case eZONETYPE_DOMINATION_FIELD:
|
|
case eZONETYPE_SPA_FIELD: //_NA_008069_20150210_ADD_SPA_SYSTEM
|
|
{
|
|
const SCItemSlot& ItemSlot = pItem->GetItemSlot();
|
|
const BASE_ITEMINFO* ItemInfo = ItemSlot.GetItemInfo();
|
|
const eITEMTYPE type = static_cast<eITEMTYPE>(ItemInfo->m_wType);
|
|
|
|
bool all_party_member_obtain_type = false;
|
|
#ifdef _NA_004204_20120131_BATTLE_ZONE_PARTY_OBTAIN
|
|
sPOLICY_ZONE& Policyment = pZone->Policyment;
|
|
if(Policyment.FIELD & Policyment.ENABLE_ITEM_OBTAIN_ALL_PARTY_MEMBER) {
|
|
all_party_member_obtain_type = true;
|
|
}
|
|
#endif //_NA_004204_20120131_BATTLE_ZONE_PARTY_OBTAIN
|
|
|
|
if (type == eITEMTYPE_QUEST ||
|
|
type == eITEMTYPE_AC ||
|
|
type == eITEMTYPE_EVENT_FIELD_PARTY_ALL_MEMBER_PICK_UP
|
|
|| all_party_member_obtain_type
|
|
)
|
|
{
|
|
NewGamePartyManager::Instance()->For_eachPlayers( PartyKey, Opr );
|
|
}
|
|
}break;
|
|
default:
|
|
{
|
|
//SUNLOG(eCRITICAL_LOG, "Do!!! Coding to DistributeItem formula by ZoneType");
|
|
}
|
|
}
|
|
}
|
|
|
|
// implemented by _NA_002399_20110323_MONEY_DISTRIBUTE_METHOD_CHANGE
|
|
void ItemExpDistributor::DistributeMoney(MONEY money, DWORD monster_key, Player* item_owner)
|
|
{
|
|
// NOTE: f110413.2L, the below code block that is implemented by
|
|
// '_NA_002399_20110323_MONEY_DISTRIBUTE_METHOD_CHANGE' is logically integrity.
|
|
// however, time and space complexity is too expensive.
|
|
// for example, approximately (MAX_PARTYMEMBER_NUM + n) space allocating numbers based on
|
|
// to copy container of the member list with in sorting time complexity n·log(n) and,
|
|
// that of the linear container for selected members, etc...
|
|
#if SUN_CODE_RENEWAL
|
|
GameZone* const game_zone = (item_owner != NULL) ? item_owner->GetGameZonePtr()
|
|
: NULL;
|
|
if (game_zone == NULL) {
|
|
return;
|
|
};
|
|
GameField* const game_field = (item_owner != NULL) ? item_owner->GetField() : NULL;
|
|
if (game_field == NULL) {
|
|
return;
|
|
};
|
|
//
|
|
const WORD party_key = item_owner->GetPartyState().GetPartyKey();
|
|
const NewGameParty* const party = (party_key == 0)
|
|
? NULL
|
|
: static_cast<const NewGameParty*>(NewGamePartyManager::Instance()->FindParty(party_key));
|
|
|
|
if (party == NULL) {
|
|
return;
|
|
};
|
|
;{ // filter by the party game rule
|
|
DWORD number_of_party_members = party->GetMemberNum();
|
|
assert(number_of_party_members <= MAX_PARTYMEMBER_NUM);
|
|
if (number_of_party_members == 0) {
|
|
return;
|
|
};
|
|
};
|
|
|
|
struct SelectedMembers
|
|
{
|
|
SelectedMembers() { ZeroMemory(this, sizeof(*this)); working_it = players; }
|
|
Player** worked_end() const { return working_it; };
|
|
bool continued_work() const { return working_it != &players[_countof(players)]; }
|
|
Player** working_it;
|
|
Player* players[MAX_PARTYMEMBER_NUM];
|
|
} selected_members;
|
|
//
|
|
int pickable_radius_squared = _GetRadius(game_zone);
|
|
; pickable_radius_squared *= pickable_radius_squared;
|
|
const WzVector& center_pos = *item_owner->GetPosPtr();
|
|
|
|
typedef NewGameParty::MEMBER_MAP PartyMembers;
|
|
const PartyMembers& member_list = party->GetMemberList();
|
|
const PlayerManager* const player_manager = PlayerManager::Instance();
|
|
FOREACH_CONTAINER(const PartyMembers::value_type& member_node, member_list, PartyMembers)
|
|
{
|
|
// PartyMembers::value_type::second_type
|
|
const _PARTY_MEMBER_INFO& member_info = member_node.second;
|
|
Player* member = player_manager->FindPlayerByObjKey(member_info.m_dwMemberKey);
|
|
if (member == NULL || member->IsDead()) {
|
|
continue;
|
|
};
|
|
if (game_field != member->GetField()) {
|
|
continue;
|
|
};
|
|
if ((pickable_radius_squared != 0) &&
|
|
(Math_VectorLength2(center_pos, *member->GetPosPtr()) > pickable_radius_squared))
|
|
{
|
|
continue;
|
|
};
|
|
// meet the all conditions
|
|
*selected_members.working_it++ = member;
|
|
if (selected_members.continued_work() == false) {
|
|
assert(!"the number of members has grater than expected specification");
|
|
break;
|
|
};
|
|
}; //end 'FOREACH_CONTAINER'
|
|
//
|
|
// do members exist?
|
|
const size_t number_of_selected_members = static_cast<size_t>(
|
|
selected_members.working_it - selected_members.players);
|
|
if (number_of_selected_members == 0) {
|
|
return;
|
|
};
|
|
|
|
const MONEY each_allocated_money = (money / number_of_selected_members);
|
|
;{ // NOTE: main processing
|
|
Player** it = selected_members.players;
|
|
Player** const end = selected_members.worked_end();
|
|
for ( ; it != end; ++it)
|
|
{
|
|
Player* member = *it;
|
|
#if !defined(__NA_000632_20070817_CHINA_FATIGUE_HEIM)
|
|
// fatigue system content
|
|
if (FatigueManager* fatigue = member->GetFatigueManager())
|
|
{
|
|
//fatigue->GetShareItemPickupRatio
|
|
//fatigue->GetShareHeimPickupRatio
|
|
int pickable_ratio = fatigue->GetShareItemPickupRatio();
|
|
int seed = (random(1, 100));
|
|
if (seed > pickable_ratio) {
|
|
continue;
|
|
};
|
|
}
|
|
#endif
|
|
member->PickMoney(each_allocated_money, monster_key, item_owner);
|
|
}
|
|
};
|
|
#else //SUN_CODE_BACKUP
|
|
WORD party_key = item_owner->GetPartyState().GetPartyKey();
|
|
NewGameParty* pParty = (NewGameParty*)NewGamePartyManager::Instance()->FindParty(party_key);
|
|
if(!pParty)
|
|
return;
|
|
|
|
DWORD number_of_party_members = pParty->GetMemberNum();
|
|
if(number_of_party_members < 1)
|
|
return;
|
|
|
|
GameZone * game_zone = item_owner->GetGameZonePtr();
|
|
if( !game_zone ) return;
|
|
|
|
WORD pickable_radius = _GetRadius(game_zone);
|
|
|
|
BaseParty::MEMBER_MAP tempParty = pParty->GetMemberList();
|
|
STLX_VECTOR<DWORD> tempParty2;
|
|
tempParty2.reserve(MAX_PARTYMEMBER_NUM);
|
|
|
|
// 조건에 맞는 멤버를 찾는다
|
|
for(BaseParty::MEMBER_MAP::iterator iter = tempParty.begin(); iter != tempParty.end(); ++iter)
|
|
{
|
|
const _PARTY_MEMBER_INFO& memberInfo = iter->second;
|
|
|
|
Player *pMember = PlayerManager::Instance()->FindPlayerByObjKey( memberInfo.m_dwMemberKey );
|
|
if( !pMember ) continue;
|
|
if( pMember->IsDead() ) continue;
|
|
|
|
GameZone *pZone = pMember->GetGameZonePtr();
|
|
if( !pZone ) continue;
|
|
|
|
GameField* pOwnerField = item_owner->GetField();
|
|
GameField* pMemberField = pMember->GetField();
|
|
if( !pOwnerField || !pMemberField )
|
|
continue;
|
|
if( pOwnerField->GetFieldCode() != pMemberField->GetFieldCode() )
|
|
continue;
|
|
|
|
if( pickable_radius != 0 && Math_VectorLength( *item_owner->GetPosPtr(), *pMember->GetPosPtr() ) > pickable_radius ) continue;
|
|
|
|
tempParty2.push_back(memberInfo.m_dwMemberKey);
|
|
}
|
|
|
|
DWORD nParty2Num = tempParty2.size();
|
|
if(nParty2Num < 1)
|
|
return;
|
|
|
|
DWORD distributeMoney = money / nParty2Num;
|
|
for(STLX_VECTOR<DWORD>::iterator iter2 = tempParty2.begin(); iter2 != tempParty2.end(); ++iter2)
|
|
{
|
|
Player *pMember = PlayerManager::Instance()->FindPlayerByObjKey(*iter2);
|
|
if( !pMember ) continue;
|
|
if( pMember->IsDead() ) continue;
|
|
|
|
#if !defined(__NA_000632_20070817_CHINA_FATIGUE_HEIM)
|
|
// 피로도 시스템
|
|
BYTE byPickRatio = pMember->GetFatigueManager()->GetShareItemPickupRatio();
|
|
if( random(1, 100) > byPickRatio ) continue;
|
|
#endif
|
|
|
|
pMember->PickMoney(distributeMoney, monster_key, item_owner);
|
|
}
|
|
#endif //SUN_CODE_BACKUP
|
|
}
|