Files
2026-06-01 12:46:52 +02:00

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 "";
}
};