489 lines
12 KiB
C++
489 lines
12 KiB
C++
#include "stdafx.h"
|
|
#include "GameRule.h"
|
|
#include <toolkit/XEnv.h>
|
|
#include <math.h>
|
|
//#include <string>
|
|
|
|
#include "SQuestMgr.h"
|
|
|
|
namespace GameRule
|
|
{
|
|
|
|
float fPartyEXPRate = 1.0f; // 파티 경치 보너스
|
|
float fEXPRate = 2.0f; // 경치 비율
|
|
float fGoldDropRate = 2.0f; // 돈 드랍 비율
|
|
float fItemDropRate = 2.0f; // 아이템 드랍 비율
|
|
float fChaosDropRate = 2.0f; // 혼돈 드랍 비율
|
|
bool bMonsterWandering = true;
|
|
// 플레이어가 최대로 들 수 있는 무게
|
|
|
|
float stamina_ratio[MAX_LEVEL];
|
|
|
|
int player_exp_limit[MAX_LEVEL];
|
|
|
|
int normal_summon_exp_limit[NORMAL_SUMMON_MAX_LEVEL];
|
|
int growth_summon_exp_limit[GROWTH_SUMMON_MAX_LEVEL];
|
|
int evolve_summon_exp_limit[EVOLVE_SUMMON_MAX_LEVEL];
|
|
|
|
int GetMaxWeight( int strength )
|
|
{
|
|
return ( strength * 100 ) + 3000;
|
|
}
|
|
|
|
// 물건을 주울 수 있는 거리
|
|
int GetPickableRange()
|
|
{
|
|
return 20;
|
|
}
|
|
|
|
// 등급에 따른 아이템 레벨 제한
|
|
int GetItemLevelLimit( int item_rank )
|
|
{
|
|
static int _table[] =
|
|
{
|
|
0, 20, 50, 80, 100, 120, 150, 180
|
|
};
|
|
|
|
if( item_rank < 1 ) item_rank = 1;
|
|
if( item_rank > 8 ) item_rank = 8;
|
|
|
|
return _table[ item_rank-1 ];
|
|
}
|
|
|
|
// 등급별 아이템 권장레벨 조정 테이블
|
|
int GetItemRecommendModTable( int item_rank )
|
|
{
|
|
//아이템 권장 레벨 6랭크 3, 7랭크 3->2 로 변경
|
|
//hunee: 2009-08-25
|
|
static int _table[] =
|
|
{
|
|
//0, 3, 3, 2, 2, 2, 3, 2
|
|
0, 3, 3, 2, 2, 3, 2, 2
|
|
};
|
|
|
|
if( item_rank < 1 ) item_rank = 1;
|
|
if( item_rank > 8 ) item_rank = 8;
|
|
|
|
return _table[ item_rank-1 ];
|
|
}
|
|
|
|
// 아이템 권장 레벨
|
|
int GetItemRecommendLevel( int item_min_level, int item_rank, int item_level )
|
|
{
|
|
int ret = 0;
|
|
if( item_min_level == 0 )
|
|
{
|
|
if( item_rank <= 1 )
|
|
ret = 0;
|
|
else
|
|
ret = GetItemLevelLimit( item_rank ) + (item_level-1) * GetItemRecommendModTable( item_rank );
|
|
}
|
|
else
|
|
ret = item_min_level + (item_level-1) * GetItemRecommendModTable( item_rank );
|
|
|
|
ret = std::min(ret, 200); // 최대 200으로 제한.
|
|
return ret;
|
|
}
|
|
|
|
// 아이템 권장 레벨에 따른 페널티 비율
|
|
float GetItemLevelPenalty( int creature_level, int item_min_level, int item_rank, int item_level, int item_limit_level )
|
|
{
|
|
const float A = 1.0f;
|
|
const float B = 0.05f;
|
|
|
|
int recommend_level = GetItemRecommendLevel( item_min_level, item_rank, item_level );
|
|
int limit_level = item_limit_level;
|
|
|
|
if(limit_level<=0)
|
|
limit_level = GetItemLevelLimit( item_rank );
|
|
|
|
if( item_level == 1 ||
|
|
creature_level < limit_level ||
|
|
creature_level >= recommend_level )
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
float result;
|
|
|
|
result = static_cast< float >( (recommend_level - creature_level ) ) / ( recommend_level - limit_level );
|
|
result = result * ( A - item_level * B );
|
|
|
|
return ( 1.0f - result );
|
|
}
|
|
|
|
// 내구도에 따른 페널티 비율
|
|
float GetItemEndurancePenalty( float item_endurance_percentage )
|
|
{
|
|
if( item_endurance_percentage >= 0.3f ) return 1.0f;
|
|
|
|
if( item_endurance_percentage >= 0.1f ) return 0.7f;
|
|
|
|
if( item_endurance_percentage > 0.0f ) return 0.5f;
|
|
|
|
return 0.2f;
|
|
}
|
|
|
|
// 수리 비용
|
|
int GetRepairCost( int item_price, int item_endurance_percentage )
|
|
{
|
|
return static_cast< int >( item_price * item_endurance_percentage * 0.1 );
|
|
}
|
|
|
|
// 각 레벨에 따른 성능 조정 (데미지, 방어구 동일)
|
|
int GetItemValue( float item_current_value, float item_endurance_percentage, int item_rank_value, int creature_level, int item_min_level,int item_rank, int item_level, int item_limit_level )
|
|
{
|
|
float v = ( item_current_value - item_rank_value ) * GetItemLevelPenalty( creature_level, item_min_level, item_rank, item_level, item_limit_level ) + item_rank_value;
|
|
|
|
v = v * GetItemEndurancePenalty( item_endurance_percentage );
|
|
|
|
return static_cast< int >( v );
|
|
}
|
|
|
|
int GetItemSellPrice( int price, int rank, int lv, bool isEquiment )
|
|
{
|
|
if( !isEquiment )
|
|
return price;
|
|
|
|
if( rank < 0 || rank >= 9 )
|
|
{
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
int add_price = 0;
|
|
|
|
// 표준가격 증가값 계산 (1~8랭크 모두 통합)
|
|
int k = price;
|
|
|
|
// 2013.06.03 - 기획팀에서 사용하고 있던 계산 테이블과 클라에서 가지고 있던 테이블이 달라 기획쪽테이블로 맞춤
|
|
float f[] = {1.35f, 0.2f, 0.115f, 0.092f, 0.085f, 0.1f, 0.1f, 0.1f}; // 랭크당 증가가격용 팩터 테이블 (from 기획팀- NPC_ItemUp.lua)
|
|
|
|
for( int i = 2; i <= lv; i++ )
|
|
{
|
|
if( rank == 0 ) add_price = (int)(k * f[rank ] * 0.1f) * 10;
|
|
else if( rank == 1 ) add_price = (int)(k * f[rank-1] * 0.1f) * 10;
|
|
else if( rank == 2 ) add_price = (int)(k * f[rank-1] * 0.01f) * 100;
|
|
else add_price = (int)(k * f[rank-1] * 0.001f) * 1000;
|
|
|
|
k = k + add_price;
|
|
}
|
|
|
|
price = k; // 최종적인 Lv별 표준가는 원래가격 + 추가가격 (Lv1일땐 add_price = 0이 할당된 상태임)
|
|
|
|
return price;
|
|
}
|
|
|
|
bool IsValidName( const char* name, int nBufferSize, int nLimitMin, int nLimitMax )
|
|
{
|
|
if( nBufferSize > nLimitMax+1 ){ nBufferSize = nLimitMax+1; }
|
|
|
|
unsigned char* c = (unsigned char*)name;
|
|
|
|
for( int i=0; i<=nBufferSize; i++,c++ )
|
|
{
|
|
// 허용가능한 이름
|
|
if( *c == 0 ) {
|
|
if( i < nLimitMin ) { return false; } else { return true; }
|
|
}
|
|
|
|
// 숫자, 영문의 경우는 pass
|
|
if((*c >= '0' && *c <= '9') || ( *c >= 'a' && *c <= 'z' ) || ( *c >= 'A' && *c <= 'Z' ))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// 첫코드가 한글이다.
|
|
if( *c >= 0xb0 && *c <= 0xc8 )
|
|
{
|
|
if( (i+1) >= nBufferSize ) { return false; }
|
|
c++;i++;
|
|
|
|
// 두번째 코드도 한글이면 pass
|
|
if( *c >= 0xa1 && *c <= 0xfe ) { continue; }
|
|
}
|
|
|
|
// 숫자, 영문, 한글이 아니므로 허용불가.
|
|
return false;
|
|
}
|
|
// 제한길이를 넘어선 이름이므로 허용불가.
|
|
return true;
|
|
}
|
|
|
|
float GetStaminaRatio( int level )
|
|
{
|
|
if( !stamina_ratio[level] )
|
|
{
|
|
stamina_ratio[level] = (int) ( pow( (float) (level + 2), 1.8f ) * 2 - pow( (float) (level + 2) , 1.75f ) * 2 + 2 ) * 0.2;
|
|
}
|
|
return stamina_ratio[level];
|
|
|
|
}
|
|
|
|
int GetSummonEXPLimit( int depth, int level )
|
|
{
|
|
if( depth == 1 ) // 기본형
|
|
{
|
|
if( !normal_summon_exp_limit[level] )
|
|
{
|
|
normal_summon_exp_limit[level] = (int)( pow( (float) level, 1.3f ) * 10.0f ) + 30;
|
|
|
|
normal_summon_exp_limit[level] += (int)( normal_summon_exp_limit[level] * 0.1 * (level / 100) );
|
|
}
|
|
|
|
return normal_summon_exp_limit[level];
|
|
|
|
}
|
|
else if( depth == 2 ) // 성장형
|
|
{
|
|
if( !growth_summon_exp_limit[level] )
|
|
{
|
|
growth_summon_exp_limit[level] = (int)( pow( (float) level, 0.83f ) * 90.0f ) + 400;
|
|
|
|
growth_summon_exp_limit[level] += (int)( growth_summon_exp_limit[level] * 0.1 * (level / 100) );
|
|
}
|
|
|
|
return growth_summon_exp_limit[level];
|
|
}
|
|
|
|
// 진화형
|
|
if( !evolve_summon_exp_limit[level] )
|
|
{
|
|
evolve_summon_exp_limit[level] = (int)( pow( (float) level, 0.7f ) * 220.0f ) + 1300;
|
|
|
|
evolve_summon_exp_limit[level] += (int)( evolve_summon_exp_limit[level] * 0.1 * (level / 100) );
|
|
}
|
|
|
|
return evolve_summon_exp_limit[level];
|
|
}
|
|
|
|
int GetPlayerEXPLimit( int level )
|
|
{
|
|
if( !player_exp_limit[level] )
|
|
{
|
|
player_exp_limit[level] = (int)( pow( (float) level, 1.8f ) * 5.0f ) + 40;
|
|
|
|
player_exp_limit[level] += (int)( player_exp_limit[level] * 0.1 * (level / 100) );
|
|
}
|
|
|
|
return player_exp_limit[level];
|
|
}
|
|
|
|
float GetSummonLevelPenalty( int master_level, int summon_level )
|
|
{
|
|
int level_diff = summon_level - master_level;
|
|
|
|
if( level_diff > 0 )
|
|
{
|
|
if( level_diff >= 30 )
|
|
{
|
|
return 10.0f;
|
|
}
|
|
|
|
return (int) ( level_diff * 0.25f * ( 2.34f - ( level_diff / 30.0f ) ) * 10.0f ) / 10.0f;
|
|
}
|
|
|
|
return level_diff;
|
|
}
|
|
|
|
float GetOverBreedLevelPenalty( int master_level, int overbreed_level, int summon_level )
|
|
{
|
|
int level_diff = overbreed_level + summon_level - master_level;
|
|
|
|
if( level_diff >= 10 )
|
|
{
|
|
if( level_diff >= 50 )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return overbreed_level * ( 120 - (int) ( (level_diff + 10) * 0.25 * (2 + (level_diff +10) / 10 ) * 10 ) / 10 ) / 100;
|
|
}
|
|
|
|
return overbreed_level;
|
|
}
|
|
|
|
float GetOverBreedStatPenalty( int master_level, int overbreed_level, int summon_level )
|
|
{
|
|
int level_diff = overbreed_level + summon_level - master_level;
|
|
|
|
if( level_diff >= 10 )
|
|
{
|
|
if( level_diff >= 50 )
|
|
{
|
|
return -0.2f;
|
|
}
|
|
|
|
return (int)( (level_diff * -0.005 + 0.05 ) * 100 ) / 100;
|
|
}
|
|
|
|
return overbreed_level;
|
|
}
|
|
|
|
float GetSummonStatPenalty( int master_level, int summon_level )
|
|
{
|
|
int level_diff = summon_level - master_level;
|
|
|
|
if( level_diff > 0 )
|
|
{
|
|
if( level_diff >= 50 )
|
|
{
|
|
return 0.7f;
|
|
}
|
|
|
|
return ( (int) ( ( 50.0f - level_diff ) * 0.6f ) + 70.0f ) / 100.0f;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
// sonador 0.2.2 국가별 로컬 비트셋 통합 및 유럽 분리
|
|
int GetCurrentLocalBitSet()
|
|
{
|
|
std::string strCountry;
|
|
strCountry = ENV().GetString("country");
|
|
|
|
if( strCountry != "<NULL>" )
|
|
{
|
|
if( strCountry == "HK" )
|
|
return LOCAL_BITSET::HK;
|
|
else if( strCountry == "US" )
|
|
return LOCAL_BITSET::US;
|
|
else if( strCountry == "DE" )
|
|
return LOCAL_BITSET::DE;
|
|
else if( strCountry == "JP" )
|
|
return LOCAL_BITSET::JP;
|
|
else if( strCountry == "TW" )
|
|
return LOCAL_BITSET::TW;
|
|
else if( strCountry == "CN" )
|
|
return LOCAL_BITSET::CN;
|
|
else if( strCountry == "FR" )
|
|
return LOCAL_BITSET::FR;
|
|
else if( strCountry == "RU" ) // #2.4.9.6
|
|
return LOCAL_BITSET::RU;
|
|
else if( strCountry == "MASG" )
|
|
return LOCAL_BITSET::MASG; // Malaysia (Singapore)
|
|
else if( strCountry == "PH" )
|
|
return LOCAL_BITSET::PH; // 필리핀
|
|
|
|
else if( strCountry == "VN" )
|
|
return LOCAL_BITSET::VN; // 베트남
|
|
else if( strCountry == "TL" )
|
|
return LOCAL_BITSET::TL; // 태국
|
|
else if( strCountry == "ME" )
|
|
return LOCAL_BITSET::ME; // 중동
|
|
else if( strCountry == "TR" )
|
|
return LOCAL_BITSET::TR; // 터키
|
|
else if( strCountry == "PL" )
|
|
return LOCAL_BITSET::PL; // 폴란드
|
|
else if( strCountry == "IT" )
|
|
return LOCAL_BITSET::IT; // 이탈리아
|
|
else if( strCountry == "BR" )
|
|
return LOCAL_BITSET::BR; // 2010.09.08 브라질 - prodongi
|
|
else if (strCountry == "ES")
|
|
return LOCAL_BITSET::ES; /// 2011.03.17 스페인 - prodongi
|
|
else if (strCountry == "ID")
|
|
return LOCAL_BITSET::ID; /// 2011.07.25 인도네시아 - prodongi
|
|
}
|
|
|
|
return LOCAL_BITSET::KR;
|
|
}
|
|
|
|
// 2010.07.12 - prodongi
|
|
bool isSoutheastAsia()
|
|
{
|
|
int bitset = GetCurrentLocalBitSet();
|
|
if (LOCAL_BITSET::TW == bitset ||
|
|
LOCAL_BITSET::MASG == bitset ||
|
|
LOCAL_BITSET::PH == bitset ||
|
|
LOCAL_BITSET::VN == bitset ||
|
|
LOCAL_BITSET::TL == bitset)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// 2011.10.07 - prodongi
|
|
bool isEurope()
|
|
{
|
|
int bitset = GetCurrentLocalBitSet();
|
|
if (LOCAL_BITSET::DE == bitset ||
|
|
LOCAL_BITSET::FR == bitset ||
|
|
LOCAL_BITSET::TR == bitset ||
|
|
LOCAL_BITSET::PL == bitset ||
|
|
LOCAL_BITSET::IT == bitset)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// #2.3.1.8 sonador
|
|
c_fixed10 GetQuestRewardFactor( QuestBase* questBase )
|
|
{
|
|
//랜덤 퀘일 경우 계산됨
|
|
if( questBase->nType == QuestBase::QUEST_RANDOM_KILL_INDIVIDUAL ||
|
|
questBase->nType == QuestBase::QUEST_RANDOM_COLLECT )
|
|
{
|
|
int nCount1 = SQuestMgr::GetInstance().GetQuestRandomValue( questBase->nCode, 2-1 );
|
|
int nCount2 = SQuestMgr::GetInstance().GetQuestRandomValue( questBase->nCode, 4-1 );
|
|
int nCount3 = SQuestMgr::GetInstance().GetQuestRandomValue( questBase->nCode, 6-1 );
|
|
|
|
c_fixed10 fValue1 = c_fixed10( questBase->nValue[ 4-1 ] ) / 100.f;
|
|
c_fixed10 fValue2 = c_fixed10( questBase->nValue[ 8-1 ] ) / 100.f;
|
|
c_fixed10 fValue3 = c_fixed10( questBase->nValue[ 12-1] ) / 100.f;
|
|
|
|
return ( ( fValue1 * nCount1 ) + ( fValue2 * nCount2 ) + ( fValue3 * nCount3 ) );
|
|
}
|
|
return 1.f;
|
|
}
|
|
|
|
// #2.1.2.6.1
|
|
const char* GetSetPartCompositionSlotOnEmoticonByPartId( int partId )
|
|
{
|
|
switch( partId )
|
|
{
|
|
case SETPART_NONE:
|
|
return "";
|
|
case SETPART_ARMO:
|
|
return "#@SETPART_ARMO_ON@#";
|
|
case SETPART_WEAPON1:
|
|
case SETPART_WEAPON2:
|
|
case SETPART_WEAPON3:
|
|
case SETPART_WEAPON4:
|
|
case SETPART_TWOHAND_WEAPON1:
|
|
case SETPART_TWOHAND_WEAPON2:
|
|
case SETPART_SHEILD:
|
|
return "#@SETPART_HAND_ON@#";
|
|
case SETPART_HELM:
|
|
return "#@SETPART_HELM_ON@#";
|
|
default:
|
|
return "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
const char* GetSetPartCompositionSlotOffEmoticonByPartId( int partId )
|
|
{
|
|
switch( partId )
|
|
{
|
|
case SETPART_NONE:
|
|
return "";
|
|
case SETPART_ARMO:
|
|
return "#@SETPART_ARMO_OFF@#";
|
|
case SETPART_WEAPON1:
|
|
case SETPART_WEAPON2:
|
|
case SETPART_WEAPON3:
|
|
case SETPART_WEAPON4:
|
|
case SETPART_TWOHAND_WEAPON1:
|
|
case SETPART_TWOHAND_WEAPON2:
|
|
case SETPART_SHEILD:
|
|
return "#@SETPART_HAND_OFF@#";
|
|
case SETPART_HELM:
|
|
return "#@SETPART_HELM_OFF@#";
|
|
default:
|
|
return "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
|
|
};
|