base init

This commit is contained in:
d1str4ught
2025-08-18 00:36:52 +02:00
parent cff3015742
commit 4e679320a3
527 changed files with 199969 additions and 0 deletions

335
src/game/BattleArena.cpp Normal file
View File

@@ -0,0 +1,335 @@
#include "stdafx.h"
#include "constants.h"
#include "BattleArena.h"
#include "start_position.h"
#include "char_manager.h"
#include "char.h"
#include "sectree_manager.h"
#include "regen.h"
#include "questmanager.h"
extern int passes_per_sec;
extern int test_server;
CBattleArena::CBattleArena()
: m_pEvent(NULL),
m_status(STATUS_CLOSE),
m_nMapIndex(0),
m_nEmpire(0),
m_bForceEnd(false)
{
}
bool CBattleArena::IsRunning()
{
return m_status == STATUS_CLOSE ? false : true;
}
bool CBattleArena::IsBattleArenaMap(int nMapIndex)
{
if ( nMapIndex == nBATTLE_ARENA_MAP[1] ||
nMapIndex == nBATTLE_ARENA_MAP[2] ||
nMapIndex == nBATTLE_ARENA_MAP[3] )
{
return true;
}
return false;
}
struct FWarpToHome
{
void operator() (LPENTITY ent)
{
if ( ent->IsType(ENTITY_CHARACTER) == true )
{
LPCHARACTER lpChar = (LPCHARACTER)ent;
if ( lpChar->IsPC() == true )
{
if ( !test_server )
{
if ( lpChar->GetGMLevel() != GM_PLAYER ) return;
}
int nEmpire, nMapIndex, x, y;
nEmpire = lpChar->GetEmpire();
nMapIndex = EMPIRE_START_MAP(nEmpire);
x = EMPIRE_START_X(nEmpire);
y = EMPIRE_START_Y(nEmpire);
lpChar->WarpSet(x, y, nMapIndex);
}
}
}
};
void CBattleArena::SetStatus(BATTLEARENA_STATUS status)
{
m_status = status;
}
EVENTINFO(SBattleArenaInfo)
{
int nEmpire;
int nMapIndex;
int state;
int wait_count;
SBattleArenaInfo()
: nEmpire( 0 )
, nMapIndex( 0 )
, state( 0 )
, wait_count( 0 )
{
}
};
EVENTFUNC(battle_arena_event)
{
SBattleArenaInfo * pInfo = dynamic_cast<SBattleArenaInfo *>(event->info );
if ( pInfo == NULL )
{
return 0; // cancel immediately
}
if (pInfo != NULL)
{
switch (pInfo->state)
{
case 0:
{
++pInfo->state;
BroadcastNotice(LC_TEXT("몬스터들의 공격까지 5분 남았습니다!!!"));
}
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*4);
case 1:
{
++pInfo->state;
BroadcastNotice(LC_TEXT("몬스터들의 공격까지 1분 남았습니다!!!"));
}
return test_server ? PASSES_PER_SEC(10) : PASSES_PER_SEC(60);
case 2:
{
++pInfo->state;
pInfo->wait_count = 0;
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0);
BroadcastNotice(LC_TEXT("몬스터들이 성을 공격하기 시작했습니다."));
LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex);
if ( sectree != NULL )
{
std::string strMap = LocaleService_GetMapPath();
if ( pInfo->nEmpire > 0 )
{
strMap += strRegen[pInfo->nEmpire];
regen_do(strMap.c_str(), pInfo->nMapIndex, sectree->m_setting.iBaseX, sectree->m_setting.iBaseY, NULL, false);
}
}
}
return test_server ? PASSES_PER_SEC(60*1) : PASSES_PER_SEC(60*5);
case 3 :
{
if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 )
{
pInfo->state = 6;
SendNoticeMap(LC_TEXT("중앙 제단에 악의 기운이 모여듭니다."), pInfo->nMapIndex, false);
}
else
{
pInfo->wait_count++;
if ( pInfo->wait_count >= 5 )
{
pInfo->state++;
SendNoticeMap(LC_TEXT("몬스터들이 물러갈 조짐을 보입니다."), pInfo->nMapIndex, false);
}
else
{
CBattleArena::instance().SpawnRandomStone();
}
}
}
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5);
case 4 :
{
pInfo->state++;
SendNoticeMap(LC_TEXT("몬스터들이 물러가기 시작했습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex);
}
return PASSES_PER_SEC(30);
case 5 :
{
LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex);
if ( sectree != NULL )
{
struct FWarpToHome f;
sectree->for_each( f );
}
CBattleArena::instance().End();
}
return 0;
case 6 :
{
pInfo->state++;
pInfo->wait_count = 0;
SendNoticeMap(LC_TEXT("몬스터들의 대장이 나타났습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("30분 내로 귀목령주를 물리쳐주세요."), pInfo->nMapIndex, false);
CBattleArena::instance().SpawnLastBoss();
}
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5);
case 7 :
{
if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 )
{
SendNoticeMap(LC_TEXT("귀목령주와 그의 부하들을 모두 물리쳤습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
pInfo->state = 5;
return PASSES_PER_SEC(60);
}
pInfo->wait_count++;
if ( pInfo->wait_count >= 6 )
{
SendNoticeMap(LC_TEXT("귀목령주가 퇴각하였습니다."), pInfo->nMapIndex, false);
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex);
SECTREE_MANAGER::instance().PurgeStonesInMap(pInfo->nMapIndex);
pInfo->state = 5;
return PASSES_PER_SEC(60);
}
else
{
CBattleArena::instance().SpawnRandomStone();
}
}
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5);
}
}
return 0;
}
bool CBattleArena::Start(int nEmpire)
{
if ( m_status != STATUS_CLOSE ) return false;
if ( nEmpire < 1 || nEmpire > 3 ) return false;
m_nMapIndex = nBATTLE_ARENA_MAP[nEmpire];
m_nEmpire = nEmpire;
char szBuf[1024];
snprintf(szBuf, sizeof(szBuf), LC_TEXT("%s의 성으로 몬스터들이 진군하고 있습니다."), EMPIRE_NAME(m_nEmpire));
BroadcastNotice(szBuf);
BroadcastNotice(LC_TEXT("10분 뒤 성을 공격할 예정입니다."));
if (m_pEvent != NULL) {
event_cancel(&m_pEvent);
}
SBattleArenaInfo* info = AllocEventInfo<SBattleArenaInfo>();
info->nMapIndex = m_nMapIndex;
info->nEmpire = m_nEmpire;
info->state = 0;
info->wait_count = 0;
m_pEvent = event_create(battle_arena_event, info, test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(5*60));
SetStatus(STATUS_BATTLE);
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", m_nMapIndex);
return true;
}
void CBattleArena::End()
{
if (m_pEvent != NULL) {
event_cancel(&m_pEvent);
}
m_bForceEnd = false;
m_nMapIndex = 0;
m_nEmpire = 0;
m_status = STATUS_CLOSE;
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0);
}
void CBattleArena::ForceEnd()
{
if ( m_bForceEnd == true ) return;
m_bForceEnd = true;
if (m_pEvent != NULL) {
event_cancel(&m_pEvent);
}
SBattleArenaInfo* info = AllocEventInfo<SBattleArenaInfo>();
info->nMapIndex = m_nMapIndex;
info->nEmpire = m_nEmpire;
info->state = 3;
event_create(battle_arena_event, info, PASSES_PER_SEC(5));
}
void CBattleArena::SpawnRandomStone()
{
static const DWORD vnum[7] = { 8012, 8013, 8014, 8024, 8025, 8026, 8027 };
static const int region_info[3][4] = {
{688900, 247600, 692700, 250000},
{740200, 251000, 744200, 247700},
{791400, 250900, 795900, 250400} };
int idx = m_nMapIndex - 190;
if ( idx < 0 || idx >= 3 ) return;
CHARACTER_MANAGER::instance().SpawnMobRange(
vnum[number(0, 6)],
m_nMapIndex,
region_info[idx][0], region_info[idx][1], region_info[idx][2], region_info[idx][3],
false, true);
}
void CBattleArena::SpawnLastBoss()
{
static const DWORD vnum = 8616;
static const int position[3][2] = {
{ 691000, 248900 },
{ 742300, 249000 },
{ 793600, 249300 } };
int idx = m_nMapIndex - 190;
if ( idx < 0 || idx >= 3 ) return;
CHARACTER_MANAGER::instance().SpawnMob(vnum, m_nMapIndex, position[idx][0], position[idx][1], 0);
}

42
src/game/BattleArena.h Normal file
View File

@@ -0,0 +1,42 @@
const static int nBATTLE_ARENA_MAP[] = { 0, 190, 191, 192 };
const static std::string strRegen[] =
{
"",
"/metin2_map_battlearena01/regen00.txt",
"/metin2_map_battlearena02/regen00.txt",
"/metin2_map_battlearena03/regen00.txt",
};
enum BATTLEARENA_STATUS
{
STATUS_CLOSE = 0,
STATUS_BATTLE,
STATUS_END,
};
class CBattleArena : public singleton<CBattleArena>
{
private :
LPEVENT m_pEvent;
BATTLEARENA_STATUS m_status;
int m_nMapIndex;
int m_nEmpire;
bool m_bForceEnd;
public :
CBattleArena();
static bool IsBattleArenaMap(int nMapIndex);
bool IsRunning();
void SetStatus(BATTLEARENA_STATUS status);
bool Start(int nEmpire);
void ForceEnd();
void End();
void SpawnLastBoss();
void SpawnRandomStone();
};

197
src/game/BlueDragon.cpp Normal file
View File

@@ -0,0 +1,197 @@
#include "stdafx.h"
#include "BlueDragon.h"
extern int test_server;
extern int passes_per_sec;
#include "vector.h"
#include "utils.h"
#include "char.h"
#include "mob_manager.h"
#include "sectree_manager.h"
#include "battle.h"
#include "affect.h"
#include "BlueDragon_Binder.h"
#include "BlueDragon_Skill.h"
#include "packet.h"
#include "motion.h"
time_t UseBlueDragonSkill(LPCHARACTER pChar, unsigned int idx)
{
LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap( pChar->GetMapIndex() );
if (NULL == pSecMap)
return 0;
int nextUsingTime = 0;
switch (idx)
{
case 0:
{
sys_log(0, "BlueDragon: Using Skill Breath");
FSkillBreath f(pChar);
pSecMap->for_each( f );
nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill0", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "period", "max"));
}
break;
case 1:
{
sys_log(0, "BlueDragon: Using Skill Weak Breath");
FSkillWeakBreath f(pChar);
pSecMap->for_each( f );
nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill1", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "period", "max"));
}
break;
case 2:
{
sys_log(0, "BlueDragon: Using Skill EarthQuake");
FSkillEarthQuake f(pChar);
pSecMap->for_each( f );
nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill2", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "period", "max"));
if (NULL != f.pFarthestChar)
{
pChar->BeginFight( f.pFarthestChar );
}
}
break;
default:
sys_err("BlueDragon: Wrong Skill Index: %d", idx);
return 0;
}
int addPct = BlueDragon_GetRangeFactor("hp_period", pChar->GetHPPct());
nextUsingTime += (nextUsingTime * addPct) / 100;
return nextUsingTime;
}
int BlueDragon_StateBattle(LPCHARACTER pChar)
{
if (pChar->GetHPPct() > 98)
return PASSES_PER_SEC(1);
const int SkillCount = 3;
int SkillPriority[SkillCount];
static time_t timeSkillCanUseTime[SkillCount];
if (pChar->GetHPPct() > 76)
{
SkillPriority[0] = 1;
SkillPriority[1] = 0;
SkillPriority[2] = 2;
}
else if (pChar->GetHPPct() > 31)
{
SkillPriority[0] = 0;
SkillPriority[1] = 1;
SkillPriority[2] = 2;
}
else
{
SkillPriority[0] = 0;
SkillPriority[1] = 2;
SkillPriority[2] = 1;
}
time_t timeNow = static_cast<time_t>(get_dword_time());
for (int i=0 ; i < SkillCount ; ++i)
{
const int SkillIndex = SkillPriority[i];
if (timeSkillCanUseTime[SkillIndex] < timeNow)
{
int SkillUsingDuration =
static_cast<int>(CMotionManager::instance().GetMotionDuration( pChar->GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_SPECIAL_1 + SkillIndex) ));
timeSkillCanUseTime[SkillIndex] = timeNow + (UseBlueDragonSkill( pChar, SkillIndex ) * 1000) + SkillUsingDuration + 3000;
pChar->SendMovePacket(FUNC_MOB_SKILL, SkillIndex, pChar->GetX(), pChar->GetY(), 0, timeNow);
return 0 == SkillUsingDuration ? PASSES_PER_SEC(1) : PASSES_PER_SEC(SkillUsingDuration);
}
}
return PASSES_PER_SEC(1);
}
int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam)
{
if (NULL == me || NULL == pAttacker)
return dam;
if (true == pAttacker->IsMonster() && 2493 == pAttacker->GetMobTable().dwVnum)
{
for (int i=1 ; i <= 4 ; ++i)
{
if (ATK_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
{
DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( pAttacker->GetMapIndex(), dwDragonStoneID );
dam += (dam * (val*cnt))/100;
break;
}
}
}
if (true == me->IsMonster() && 2493 == me->GetMobTable().dwVnum)
{
for (int i=1 ; i <= 4 ; ++i)
{
if (DEF_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
{
DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( me->GetMapIndex(), dwDragonStoneID );
dam -= (dam * (val*cnt))/100;
if (dam <= 0)
dam = 1;
break;
}
}
}
if (true == me->IsStone() && 0 != pAttacker->GetMountVnum())
{
for (int i=1 ; i <= 4 ; ++i)
{
if (me->GetMobTable().dwVnum == BlueDragon_GetIndexFactor("DragonStone", i, "vnum"))
{
if (pAttacker->GetMountVnum() == BlueDragon_GetIndexFactor("DragonStone", i, "enemy"))
{
size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "enemy_val");
dam *= val;
break;
}
}
}
}
return dam;
}

5
src/game/BlueDragon.h Normal file
View File

@@ -0,0 +1,5 @@
extern int BlueDragon_StateBattle (LPCHARACTER);
extern time_t UseBlueDragonSkill (LPCHARACTER, unsigned int);
extern int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER attacker, int dam);

View File

@@ -0,0 +1,218 @@
#include "stdafx.h"
#include "BlueDragon_Binder.h"
#include "questmanager.h"
unsigned int BlueDragon_GetSkillFactor(const size_t cnt, ...)
{
lua_State* L = quest::CQuestManager::instance().GetLuaState();
const int stack_top = lua_gettop(L);
lua_getglobal( L, "BlueDragonSetting" );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
return 0;
}
va_list vl;
va_start(vl, cnt);
for( size_t i=0 ; i < cnt ; ++i )
{
const char* key = va_arg(vl, const char*);
if (NULL == key)
{
va_end(vl);
lua_settop( L, stack_top );
sys_err("BlueDragon: wrong key list");
return 0;
}
lua_pushstring( L, key );
lua_gettable( L, -2 );
if (false == lua_istable(L, -1) && i != cnt-1)
{
va_end(vl);
lua_settop( L, stack_top );
sys_err("BlueDragon: wrong key table %s", key);
return 0;
}
}
va_end(vl);
if (false == lua_isnumber(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: Last key is not a number");
return 0;
}
int val = static_cast<int>(lua_tonumber( L, -1 ));
lua_settop( L, stack_top );
return val;
}
unsigned int BlueDragon_GetRangeFactor(const char* key, const int val)
{
lua_State* L = quest::CQuestManager::instance().GetLuaState();
const int stack_top = lua_gettop(L);
lua_getglobal( L, "BlueDragonSetting" );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
return 0;
}
lua_pushstring( L, key );
lua_gettable( L, -2 );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: no required table %s", key);
return 0;
}
const size_t cnt = static_cast<size_t>(luaL_getn(L, -1));
for( size_t i=1 ; i <= cnt ; ++i )
{
lua_rawgeti( L, -1, i );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: wrong table index %s %d", key, i);
return 0;
}
lua_pushstring( L, "min" );
lua_gettable( L, -2 );
if (false == lua_isnumber(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: no min value set %s", key);
return 0;
}
const int min = static_cast<int>(lua_tonumber(L, -1));
lua_pop(L, 1);
lua_pushstring( L, "max" );
lua_gettable( L, -2 );
if (false == lua_isnumber(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: no max value set %s", key);
return 0;
}
const int max = static_cast<int>(lua_tonumber(L, -1));
lua_pop(L, 1);
if (min <= val && val <= max)
{
lua_pushstring( L, "pct" );
lua_gettable( L, -2 );
if (false == lua_isnumber(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: no pct value set %s", key);
return 0;
}
const int pct = static_cast<int>(lua_tonumber(L, -1));
lua_settop( L, stack_top );
return pct;
}
lua_pop(L, 1);
}
lua_settop( L, stack_top );
return 0;
}
unsigned int BlueDragon_GetIndexFactor(const char* container, const size_t idx, const char* key)
{
lua_State* L = quest::CQuestManager::instance().GetLuaState();
const int stack_top = lua_gettop(L);
lua_getglobal( L, "BlueDragonSetting" );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
return 0;
}
lua_pushstring( L, container );
lua_gettable( L, -2 );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: no required table %s", key);
return 0;
}
lua_rawgeti( L, -1, idx );
if (false == lua_istable(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: wrong table index %s %d", key, idx);
return 0;
}
lua_pushstring( L, key );
lua_gettable( L, -2 );
if (false == lua_isnumber(L, -1))
{
lua_settop( L, stack_top );
sys_err("BlueDragon: no min value set %s", key);
return 0;
}
const unsigned int ret = static_cast<unsigned int>(lua_tonumber(L, -1));
lua_settop( L, stack_top );
return ret;
}

View File

@@ -0,0 +1,13 @@
enum BLUEDRAGON_STONE_EFFECT
{
DEF_BONUS = 1,
ATK_BONUS = 2,
REGEN_TIME_BONUS = 3,
REGEN_PECT_BONUS = 4,
};
extern unsigned int BlueDragon_GetRangeFactor (const char* key, const int val);
extern unsigned int BlueDragon_GetSkillFactor (const size_t cnt, ...);
extern unsigned int BlueDragon_GetIndexFactor (const char* container, const size_t idx, const char* key);

342
src/game/BlueDragon_Skill.h Normal file
View File

@@ -0,0 +1,342 @@
struct FSkillBreath
{
EJobs Set1;
EJobs Set2;
ESex gender;
LPCHARACTER pAttacker;
FSkillBreath(LPCHARACTER p)
{
pAttacker = p;
Set1 = static_cast<EJobs>(number(0,3));
Set2 = static_cast<EJobs>(number(0,3));
gender = static_cast<ESex>(number(0,2));
}
void operator()(LPENTITY ent)
{
if (NULL != ent)
{
if (true == ent->IsType(ENTITY_CHARACTER))
{
LPCHARACTER ch = static_cast<LPCHARACTER>(ent);
if (true == ch->IsPC() && false == ch->IsDead())
{
if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE))
return;
if ((signed)BlueDragon_GetSkillFactor(2, "Skill0", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()))
{
sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) );
return;
}
int overlapDamageCount = 0;
int pct = 0;
if (ch->GetJob() == Set1)
{
const char* ptr = NULL;
switch ( Set1 )
{
case JOB_WARRIOR: ptr = "musa"; break;
case JOB_ASSASSIN: ptr = "assa"; break;
case JOB_SURA: ptr = "sura"; break;
case JOB_SHAMAN: ptr = "muda"; break;
default:
case JOB_MAX_NUM: return;
}
int firstDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max"));
pct += firstDamagePercent;
if (firstDamagePercent > 0)
overlapDamageCount++;
}
if (ch->GetJob() == Set2)
{
const char* ptr = NULL;
switch ( Set2 )
{
case JOB_WARRIOR: ptr = "musa"; break;
case JOB_ASSASSIN: ptr = "assa"; break;
case JOB_SURA: ptr = "sura"; break;
case JOB_SHAMAN: ptr = "muda"; break;
default:
case JOB_MAX_NUM: return;
}
int secondDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max"));
pct += secondDamagePercent;
if (secondDamagePercent > 0)
overlapDamageCount++;
}
if (GET_SEX(ch) == gender)
{
const char* ptr = NULL;
switch (gender)
{
case SEX_MALE: ptr = "male"; break;
case SEX_FEMALE: ptr = "female"; break;
default: return;
}
int thirdDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "max"));
pct += thirdDamagePercent;
if (thirdDamagePercent > 0)
overlapDamageCount++;
}
switch (overlapDamageCount)
{
case 1:
ch->EffectPacket(SE_PERCENT_DAMAGE1);
break;
case 2:
ch->EffectPacket(SE_PERCENT_DAMAGE2);
break;
case 3:
ch->EffectPacket(SE_PERCENT_DAMAGE3);
break;
}
int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct());
pct += addPct;
int dam = number(BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "max"));
dam += (dam * addPct) / 100;
dam += (ch->GetMaxHP() * pct) / 100;
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE );
sys_log(0, "BlueDragon: Breath to %s pct(%d) dam(%d) overlap(%d)", ch->GetName(), pct, dam, overlapDamageCount);
}
}
}
}
};
struct FSkillWeakBreath
{
LPCHARACTER pAttacker;
FSkillWeakBreath(LPCHARACTER p)
{
pAttacker = p;
}
void operator()(LPENTITY ent)
{
if (NULL != ent)
{
if (true == ent->IsType(ENTITY_CHARACTER))
{
LPCHARACTER ch = static_cast<LPCHARACTER>(ent);
if (true == ch->IsPC() && false == ch->IsDead())
{
if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE))
return;
if ((signed)BlueDragon_GetSkillFactor(2, "Skill1", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()))
{
sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) );
return;
}
int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct());
int dam = number( BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "max") );
dam += (dam * addPct) / 100;
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE );
sys_log(0, "BlueDragon: WeakBreath to %s addPct(%d) dam(%d)", ch->GetName(), addPct, dam);
}
}
}
}
};
struct FSkillEarthQuake
{
EJobs Set1;
EJobs Set2;
ESex gender;
long MaxDistance;
LPCHARACTER pAttacker;
LPCHARACTER pFarthestChar;
FSkillEarthQuake(LPCHARACTER p)
{
pAttacker = p;
MaxDistance = 0;
pFarthestChar = NULL;
Set1 = static_cast<EJobs>(number(0,3));
Set2 = static_cast<EJobs>(number(0,3));
gender = static_cast<ESex>(number(0,2));
}
void operator()(LPENTITY ent)
{
if (NULL != ent)
{
if (true == ent->IsType(ENTITY_CHARACTER))
{
LPCHARACTER ch = static_cast<LPCHARACTER>(ent);
if (true == ch->IsPC() && false == ch->IsDead())
{
if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE))
return;
if ((signed)BlueDragon_GetSkillFactor(2, "Skill2", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()))
{
sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) );
return;
}
int sec = number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "max"));
if (ch->GetJob() == Set1)
{
const char* ptr = NULL;
switch ( Set1 )
{
case JOB_WARRIOR: ptr = "musa"; break;
case JOB_ASSASSIN: ptr = "assa"; break;
case JOB_SURA: ptr = "sura"; break;
case JOB_SHAMAN: ptr = "muda"; break;
default:
case JOB_MAX_NUM: return;
}
sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max"));
}
if (ch->GetJob() == Set2)
{
const char* ptr = NULL;
switch ( Set2 )
{
case JOB_WARRIOR: ptr = "musa"; break;
case JOB_ASSASSIN: ptr = "assa"; break;
case JOB_SURA: ptr = "sura"; break;
case JOB_SHAMAN: ptr = "muda"; break;
default:
case JOB_MAX_NUM: return;
}
sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max"));
}
if (GET_SEX(ch) == gender)
{
const char* ptr = NULL;
switch (gender)
{
case SEX_MALE: ptr = "male"; break;
case SEX_FEMALE: ptr = "female"; break;
default: return;
}
sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "max"));
}
int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct());
int dam = number( BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "max") );
dam += (dam * addPct) / 100;
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE);
SkillAttackAffect( ch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, sec, "BDRAGON_STUN" );
sys_log(0, "BlueDragon: EarthQuake to %s addPct(%d) dam(%d) sec(%d)", ch->GetName(), addPct, dam, sec);
VECTOR vec;
vec.x = static_cast<float>(pAttacker->GetX() - ch->GetX());
vec.y = static_cast<float>(pAttacker->GetY() - ch->GetY());
vec.z = 0.0f;
Normalize( &vec, &vec );
const int nFlyDistance = 1000;
long tx = ch->GetX() + vec.x * nFlyDistance;
long ty = ch->GetY() + vec.y * nFlyDistance;
for (int i=0 ; i < 5 ; ++i)
{
if (true == SECTREE_MANAGER::instance().IsMovablePosition( ch->GetMapIndex(), tx, ty ))
{
break;
}
switch( i )
{
case 0:
tx = ch->GetX() + vec.x * nFlyDistance * -1;
ty = ch->GetY() + vec.y * nFlyDistance * -1;
break;
case 1:
tx = ch->GetX() + vec.x * nFlyDistance * -1;
ty = ch->GetY() + vec.y * nFlyDistance;
break;
case 2:
tx = ch->GetX() + vec.x * nFlyDistance;
ty = ch->GetY() + vec.y * nFlyDistance * -1;
break;
case 3:
tx = ch->GetX() + vec.x * number(1,100);
ty = ch->GetY() + vec.y * number(1,100);
break;
case 4:
tx = ch->GetX() + vec.x * number(1,10);
ty = ch->GetY() + vec.y * number(1,10);
break;
}
}
ch->Sync( tx , ty );
ch->Goto( tx , ty );
ch->CalculateMoveDuration();
ch->SyncPacket();
long dist = DISTANCE_APPROX( pAttacker->GetX() - ch->GetX(), pAttacker->GetY() - ch->GetY() );
if (dist > MaxDistance)
{
MaxDistance = dist;
pFarthestChar = ch;
}
}
}
}
}
};

18
src/game/CMakeLists.txt Normal file
View File

@@ -0,0 +1,18 @@
file(GLOB_RECURSE GAME_SOURCES "*.h" "*.cpp")
add_executable(game ${GAME_SOURCES})
target_link_libraries(game
common
libgame
libpoly
libsql
libthecore
liblua
)
if (WIN32)
target_link_libraries(game ws2_32)
else()
target_link_libraries(game pthread md)
endif()

View File

@@ -0,0 +1,225 @@
#include "stdafx.h"
#include "ClientPackageCryptInfo.h"
#include "../../common/stl.h"
#ifndef __FreeBSD__
#include "../../libthecore/include/xdirent.h"
#endif
CClientPackageCryptInfo::CClientPackageCryptInfo() : m_pSerializedCryptKeyStream(NULL), m_nCryptKeyPackageCnt(0)
{
}
CClientPackageCryptInfo::~CClientPackageCryptInfo()
{
m_vecPackageCryptKeys.clear();
m_mapPackageSDB.clear();
if( m_pSerializedCryptKeyStream )
{
delete[] m_pSerializedCryptKeyStream;
m_pSerializedCryptKeyStream = NULL;
}
}
bool CClientPackageCryptInfo::LoadPackageCryptFile( const char* pCryptFile )
{
FILE * fp = fopen(pCryptFile, "rb");
if (!fp)
return false;
int iSDBDataOffset;
fread(&iSDBDataOffset, sizeof(int), 1, fp);
int iPackageCnt;
fread( &iPackageCnt, sizeof(int), 1, fp );
m_nCryptKeyPackageCnt += iPackageCnt;
int iCryptKeySize = iSDBDataOffset - 2*sizeof(int);
{
if (0 == iCryptKeySize)
{
sys_log(0, "[PackageCryptInfo] failed to load crypt key. (file: %s, key size: %d)", pCryptFile, iCryptKeySize);
m_nCryptKeyPackageCnt -= iPackageCnt;
}
else
{
int nCurKeySize = (int)m_vecPackageCryptKeys.size();
m_vecPackageCryptKeys.resize( nCurKeySize + sizeof(int) + iCryptKeySize);
memcpy( &m_vecPackageCryptKeys[nCurKeySize], &iCryptKeySize, sizeof(int));
fread( &m_vecPackageCryptKeys[nCurKeySize + sizeof(int)], sizeof(BYTE), iCryptKeySize, fp );
sys_log(0, "[PackageCryptInfo] %s loaded. (key size: %d, count: %d, total: %d)", pCryptFile, iCryptKeySize, iPackageCnt, m_nCryptKeyPackageCnt);
}
}
//about SDB data
//total packagecnt (4byte)
// for packagecnt
// db name hash 4byte( stl.h stringhash ) +child node size(4byte)
//stream to client
// sdb file cnt( 4byte )
// for sdb file cnt
// filename hash ( stl.h stringhash )
// related map name size(4), relate map name
// sdb block size( 1byte )
// sdb blocks
int iSDBPackageCnt;
fread(&iSDBPackageCnt, sizeof(int), 1, fp);
DWORD dwPackageNameHash, dwPackageStreamSize, dwSDBFileCnt, dwFileNameHash, dwMapNameSize;
std::string strRelatedMapName;
if (0 == iCryptKeySize && 0 == iSDBPackageCnt)
return false;
for( int i = 0; i < iSDBPackageCnt; ++i )
{
fread(&dwPackageNameHash, sizeof(DWORD), 1, fp);
fread(&dwPackageStreamSize, sizeof(DWORD), 1, fp);
fread(&dwSDBFileCnt, sizeof(DWORD), 1, fp);
sys_log(0, "[PackageCryptInfo] SDB Loaded. (Name Hash : %d, Stream Size: %d, File Count: %d)", dwPackageNameHash,dwPackageStreamSize, dwSDBFileCnt);
for( int j = 0; j < (int)dwSDBFileCnt; ++j )
{
fread(&dwFileNameHash, sizeof(DWORD), 1, fp);
fread(&dwMapNameSize, sizeof(DWORD), 1, fp);
strRelatedMapName.resize( dwMapNameSize );
fread(&strRelatedMapName[0], sizeof(BYTE), dwMapNameSize, fp);
sys_log(0, "[PackageCryptInfo] \t SDB each file info loaded.(MapName: %s, NameHash: %X)", strRelatedMapName.c_str(), dwFileNameHash);
BYTE bSDBStreamSize;
std::vector<BYTE> vecSDBStream;
fread(&bSDBStreamSize, sizeof(BYTE), 1, fp);
vecSDBStream.resize(bSDBStreamSize);
fread(&vecSDBStream[0], sizeof(BYTE), bSDBStreamSize, fp);
//reconstruct it
TPackageSDBMap::iterator it = m_mapPackageSDB.find( strRelatedMapName );
if( it == m_mapPackageSDB.end() )
{
TPerFileSDBInfo fileSDBInfo;
m_mapPackageSDB[strRelatedMapName] = fileSDBInfo;
}
TSupplementaryDataBlockInfo SDBInfo;
std::vector<TSupplementaryDataBlockInfo>& rSDBInfos = m_mapPackageSDB[strRelatedMapName].vecSDBInfos;
{
SDBInfo.dwPackageIdentifier = dwPackageNameHash;
SDBInfo.dwFileIdentifier = dwFileNameHash;
SDBInfo.vecSDBStream.resize( bSDBStreamSize );
memcpy(&SDBInfo.vecSDBStream[0], &vecSDBStream[0], bSDBStreamSize );
rSDBInfos.push_back( SDBInfo );
}
}
}
fclose(fp);
return true;
}
bool CClientPackageCryptInfo::LoadPackageCryptInfo( const char* pCryptInfoDir )
{
DIR * pDir = opendir(pCryptInfoDir);
if (!pDir)
return false;
m_nCryptKeyPackageCnt = 0;
if( m_pSerializedCryptKeyStream )
{
delete[] m_pSerializedCryptKeyStream;
m_pSerializedCryptKeyStream = NULL;
}
m_mapPackageSDB.clear();
m_vecPackageCryptKeys.clear();
const char szPrefixCryptInfoFile[] = "cshybridcrypt";
dirent * pDirEnt;
while ((pDirEnt = readdir(pDir)))
{
//if (strncmp( &(pDirEnt->d_name[0]), szPrefixCryptInfoFile, strlen(szPrefixCryptInfoFile)) )
if (std::string::npos == std::string(pDirEnt->d_name).find(szPrefixCryptInfoFile))
{
sys_log(0, "[PackageCryptInfo] %s is not crypt file. pass!", pDirEnt->d_name);
continue;
}
std::string strFullPathName = std::string(pCryptInfoDir) + std::string(pDirEnt->d_name);
sys_log(0, "[PackageCryptInfo] Try to load crypt file: %s", strFullPathName.c_str());
if (false == LoadPackageCryptFile( strFullPathName.c_str() ))
sys_err("[PackageCryptInfo] Failed to load %s", strFullPathName.c_str());
}
closedir(pDir);
return true;
}
void CClientPackageCryptInfo::GetPackageCryptKeys( BYTE** ppData, int& iDataSize )
{
int nCryptKeySize = m_vecPackageCryptKeys.size();
int iStreamSize = sizeof(int)+nCryptKeySize;
//NOTE : Crypt Key Info isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated )
//it`s not safe but due to performance benefit we don`t do re-serialize.
if( m_pSerializedCryptKeyStream )
{
*ppData = m_pSerializedCryptKeyStream;
iDataSize = iStreamSize;
return;
}
if( nCryptKeySize > 0 )
{
m_pSerializedCryptKeyStream = new BYTE[iStreamSize];
memcpy(&m_pSerializedCryptKeyStream[0], &m_nCryptKeyPackageCnt, sizeof(int) );
memcpy(&m_pSerializedCryptKeyStream[sizeof(int)], &m_vecPackageCryptKeys[0], nCryptKeySize );
*ppData = m_pSerializedCryptKeyStream;
iDataSize = iStreamSize;
}
else
{
*ppData = NULL;
iDataSize = 0;
}
}
bool CClientPackageCryptInfo::GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize )
{
std::string strLowerMapName = pMapName;
stl_lowers(strLowerMapName);
TPackageSDBMap::iterator it = m_mapPackageSDB.find( strLowerMapName.c_str() );
if( it == m_mapPackageSDB.end() || it->second.vecSDBInfos.size() == 0 )
{
//sys_err("GetRelatedMapSDBStreams Failed(%s)", strLowerMapName.c_str());
return false;
}
*ppData = it->second.GetSerializedStream();
iDataSize = it->second.GetSize();
//sys_log(0, "GetRelatedMapSDBStreams Size(%d)", iDataSize);
return true;
}

View File

@@ -0,0 +1,117 @@
#ifndef __INC_CLIENTPACKAGE_CRYPTINFO_H
#define __INC_CLIENTPACKAGE_CRYPTINFO_H
#include <unordered_map>
#pragma pack(1)
typedef struct SSupplementaryDataBlockInfo
{
DWORD dwPackageIdentifier;
DWORD dwFileIdentifier;
std::vector<BYTE> vecSDBStream;
void Serialize( BYTE* pStream )
{
memcpy(pStream, &dwPackageIdentifier, sizeof(DWORD));
memcpy(pStream+4, &dwFileIdentifier, sizeof(DWORD));
BYTE bSize = vecSDBStream.size();
memcpy(pStream+8, &bSize, sizeof(BYTE));
memcpy(pStream+9, &vecSDBStream[0], bSize);
}
DWORD GetSerializedSize() const
{
return sizeof(DWORD)*2 + sizeof(BYTE) + vecSDBStream.size();
}
} TSupplementaryDataBlockInfo;
#pragma pack()
class CClientPackageCryptInfo
{
public:
CClientPackageCryptInfo();
~CClientPackageCryptInfo();
bool LoadPackageCryptInfo( const char* pCryptInfoDir );
void GetPackageCryptKeys( BYTE** ppData, int& iDataSize );
bool GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize );
private:
bool LoadPackageCryptFile( const char* pCryptFile );
private:
int m_nCryptKeyPackageCnt;
std::vector<BYTE> m_vecPackageCryptKeys;
BYTE* m_pSerializedCryptKeyStream;
typedef struct SPerFileSDBInfo
{
SPerFileSDBInfo() : m_pSerializedStream(NULL) {}
~SPerFileSDBInfo()
{
if(m_pSerializedStream)
{
delete[]m_pSerializedStream;
}
}
DWORD GetSize() const
{
DWORD dwSize = 4; //initial vecSDBInfo count
for(int i = 0; i < (int)vecSDBInfos.size(); ++i)
{
dwSize += vecSDBInfos[i].GetSerializedSize();
}
return dwSize;
}
BYTE* GetSerializedStream()
{
//NOTE : SDB Data isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated )
//it`s not safe but due to performance benefit we don`t do re-serialize.
if(m_pSerializedStream)
return m_pSerializedStream;
m_pSerializedStream = new BYTE[GetSize()];
int iWrittenOffset = 0;
int iSDBInfoSize = vecSDBInfos.size();
//write size
memcpy( m_pSerializedStream, &iSDBInfoSize, sizeof(int) );
iWrittenOffset += sizeof(int);
for(int i = 0; i < iSDBInfoSize; ++i)
{
vecSDBInfos[i].Serialize( m_pSerializedStream + iWrittenOffset );
iWrittenOffset += vecSDBInfos[i].GetSerializedSize();
}
return m_pSerializedStream;
}
std::vector<TSupplementaryDataBlockInfo> vecSDBInfos;
private:
BYTE* m_pSerializedStream;
} TPerFileSDBInfo;
typedef std::unordered_map<std::string, TPerFileSDBInfo > TPackageSDBMap; //key: related map name
TPackageSDBMap m_mapPackageSDB;
};
#endif //__INC_CLIENTPACKAGE_CRYPTINFO_H

255
src/game/DragonLair.cpp Normal file
View File

@@ -0,0 +1,255 @@
#include "stdafx.h"
#include "DragonLair.h"
#include "entity.h"
#include "sectree_manager.h"
#include "char.h"
#include "guild.h"
#include "locale_service.h"
#include "regen.h"
#include "log.h"
#include "utils.h"
extern int passes_per_sec;
struct FWarpToDragronLairWithGuildMembers
{
DWORD dwGuildID;
long mapIndex;
long x, y;
FWarpToDragronLairWithGuildMembers( DWORD guildID, long map, long X, long Y )
: dwGuildID(guildID), mapIndex(map), x(X), y(Y)
{
}
void operator()(LPENTITY ent)
{
if (NULL != ent && true == ent->IsType(ENTITY_CHARACTER))
{
LPCHARACTER pChar = static_cast<LPCHARACTER>(ent);
if (true == pChar->IsPC())
{
if (NULL != pChar->GetGuild())
{
if (dwGuildID == pChar->GetGuild()->GetID())
{
pChar->WarpSet(x, y, mapIndex);
}
}
}
}
}
};
struct FWarpToVillage
{
void operator() (LPENTITY ent)
{
if (NULL != ent)
{
LPCHARACTER pChar = static_cast<LPCHARACTER>(ent);
if (NULL != pChar)
{
if (true == pChar->IsPC())
{
pChar->GoHome();
}
}
}
}
};
EVENTINFO(tag_DragonLair_Collapse_EventInfo)
{
int step;
CDragonLair* pLair;
long InstanceMapIndex;
tag_DragonLair_Collapse_EventInfo()
: step( 0 )
, pLair( 0 )
, InstanceMapIndex( 0 )
{
}
};
EVENTFUNC( DragonLair_Collapse_Event )
{
tag_DragonLair_Collapse_EventInfo* pInfo = dynamic_cast<tag_DragonLair_Collapse_EventInfo*>(event->info);
if ( pInfo == NULL )
{
sys_err( "DragonLair_Collapse_Event> <Factor> Null pointer" );
return 0;
}
if (0 == pInfo->step)
{
char buf[512];
snprintf(buf, 512, LC_TEXT("용가리가 %d 초만에 죽어써효ㅠㅠ"), pInfo->pLair->GetEstimatedTime());
SendNoticeMap(buf, pInfo->InstanceMapIndex, true);
pInfo->step++;
return PASSES_PER_SEC( 30 );
}
else if (1 == pInfo->step)
{
LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pInfo->InstanceMapIndex );
if (NULL != pMap)
{
FWarpToVillage f;
pMap->for_each( f );
}
pInfo->step++;
return PASSES_PER_SEC( 30 );
}
else
{
SECTREE_MANAGER::instance().DestroyPrivateMap( pInfo->InstanceMapIndex );
M2_DELETE(pInfo->pLair);
}
return 0;
}
CDragonLair::CDragonLair(DWORD guildID, long BaseMapID, long PrivateMapID)
: GuildID_(guildID), BaseMapIndex_(BaseMapID), PrivateMapIndex_(PrivateMapID)
{
StartTime_ = get_global_time();
}
CDragonLair::~CDragonLair()
{
}
DWORD CDragonLair::GetEstimatedTime() const
{
return get_global_time() - StartTime_;
}
void CDragonLair::OnDragonDead(LPCHARACTER pDragon)
{
sys_log(0, "DragonLair: 도라곤이 죽어써효");
LogManager::instance().DragonSlayLog( GuildID_, pDragon->GetMobTable().dwVnum, StartTime_, get_global_time() );
}
CDragonLairManager::CDragonLairManager()
{
}
CDragonLairManager::~CDragonLairManager()
{
}
bool CDragonLairManager::Start(long MapIndexFrom, long BaseMapIndex, DWORD GuildID)
{
long instanceMapIndex = SECTREE_MANAGER::instance().CreatePrivateMap(BaseMapIndex);
if (instanceMapIndex == 0) {
sys_err("CDragonLairManager::Start() : no private map index available");
return false;
}
LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(MapIndexFrom);
if (NULL != pMap)
{
LPSECTREE_MAP pTargetMap = SECTREE_MANAGER::instance().GetMap(BaseMapIndex);
if (NULL == pTargetMap)
{
return false;
}
const TMapRegion* pRegionInfo = SECTREE_MANAGER::instance().GetMapRegion( pTargetMap->m_setting.iIndex );
if (NULL != pRegionInfo)
{
FWarpToDragronLairWithGuildMembers f(GuildID, instanceMapIndex, 844000, 1066900);
pMap->for_each( f );
LairMap_.insert( std::make_pair(GuildID, M2_NEW CDragonLair(GuildID, BaseMapIndex, instanceMapIndex)) );
std::string strMapBasePath( LocaleService_GetMapPath() );
strMapBasePath += "/" + pRegionInfo->strMapName + "/instance_regen.txt";
sys_log(0, "%s", strMapBasePath.c_str());
regen_do(strMapBasePath.c_str(), instanceMapIndex, pTargetMap->m_setting.iBaseX, pTargetMap->m_setting.iBaseY, NULL, true);
return true;
}
}
return false;
}
void CDragonLairManager::OnDragonDead(LPCHARACTER pDragon, DWORD KillerGuildID)
{
if (NULL == pDragon)
return;
if (false == pDragon->IsMonster())
return;
std::unordered_map<DWORD, CDragonLair*>::iterator iter = LairMap_.find( KillerGuildID );
if (LairMap_.end() == iter)
{
return;
}
LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pDragon->GetMapIndex() );
if (NULL == iter->second || NULL == pMap)
{
LairMap_.erase( iter );
return;
}
iter->second->OnDragonDead( pDragon );
// 애들 다 집으로 보내고 맵 없애기
tag_DragonLair_Collapse_EventInfo* info;
info = AllocEventInfo<tag_DragonLair_Collapse_EventInfo>();
info->step = 0;
info->pLair = iter->second;
info->InstanceMapIndex = pDragon->GetMapIndex();
event_create(DragonLair_Collapse_Event, info, PASSES_PER_SEC(10));
LairMap_.erase( iter );
}

37
src/game/DragonLair.h Normal file
View File

@@ -0,0 +1,37 @@
#include <unordered_map>
#include "../../common/stl.h"
class CDragonLair
{
public:
CDragonLair (DWORD dwGuildID, long BaseMapID, long PrivateMapID);
virtual ~CDragonLair ();
DWORD GetEstimatedTime () const;
void OnDragonDead (LPCHARACTER pDragon);
private:
DWORD StartTime_;
DWORD GuildID_;
long BaseMapIndex_;
long PrivateMapIndex_;
};
class CDragonLairManager : public singleton<CDragonLairManager>
{
public:
CDragonLairManager ();
virtual ~CDragonLairManager ();
bool Start (long MapIndexFrom, long BaseMapIndex, DWORD GuildID);
void OnDragonDead (LPCHARACTER pDragon, DWORD KillerGuildID);
size_t GetLairCount () const { return LairMap_.size(); }
private:
std::unordered_map<DWORD, CDragonLair*> LairMap_;
};

1158
src/game/DragonSoul.cpp Normal file

File diff suppressed because it is too large Load Diff

60
src/game/DragonSoul.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef __INC_METIN_II_GAME_DRAGON_SOUL_H__
#define __INC_METIN_II_GAME_DRAGON_SOUL_H__
#include "../../common/length.h"
class CHARACTER;
class CItem;
class DragonSoulTable;
class DSManager : public singleton<DSManager>
{
public:
DSManager();
~DSManager();
bool ReadDragonSoulTableFile(const char * c_pszFileName);
void GetDragonSoulInfo(DWORD dwVnum, OUT BYTE& bType, OUT BYTE& bGrade, OUT BYTE& bStep, OUT BYTE& bRefine) const;
// fixme : titempos로
WORD GetBasePosition(const LPITEM pItem) const;
bool IsValidCellForThisItem(const LPITEM pItem, const TItemPos& Cell) const;
int GetDuration(const LPITEM pItem) const;
// 용혼석을 받아서 특정 용심을 추출하는 함수
bool ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtractor = NULL);
// 특정 용혼석(pItem)을 장비창에서 제거할 때에 성공 여부를 결정하고,
// 실패시 부산물을 주는 함수.(부산물은 dragon_soul_table.txt에 정의)
// DestCell에 invalid한 값을 넣으면 성공 시, 용혼석을 빈 공간에 자동 추가.
// 실패 시, 용혼석(pItem)은 delete됨.
// 추출아이템이 있다면 추출 성공 확률이 pExtractor->GetValue(0)%만큼 증가함.
// 부산물은 언제나 자동 추가.
bool PullOut(LPCHARACTER ch, TItemPos DestCell, IN OUT LPITEM& pItem, LPITEM pExtractor = NULL);
// 용혼석 업그레이드 함수
bool DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]);
bool DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]);
bool DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]);
bool DragonSoulItemInitialize(LPITEM pItem);
bool IsTimeLeftDragonSoul(LPITEM pItem) const;
int LeftTime(LPITEM pItem) const;
bool ActivateDragonSoul(LPITEM pItem);
bool DeactivateDragonSoul(LPITEM pItem, bool bSkipRefreshOwnerActiveState = false);
bool IsActiveDragonSoul(LPITEM pItem) const;
private:
void SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos);
// 캐릭터의 용혼석 덱을 살펴보고, 활성화 된 용혼석이 없다면, 캐릭터의 용혼석 활성 상태를 off 시키는 함수.
void RefreshDragonSoulState(LPCHARACTER ch);
DWORD MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine);
bool PutAttributes(LPITEM pDS);
bool RefreshItemAttributes(LPITEM pItem);
DragonSoulTable* m_pTable;
};
#endif

64
src/game/FSM.cpp Normal file
View File

@@ -0,0 +1,64 @@
// Local Includes
#include <cassert>
#include <cstdlib>
#include "FSM.h"
// Constructor
CFSM::CFSM()
{
// Initialize States
m_stateInitial.Set(this, &CFSM::BeginStateInitial, &CFSM::StateInitial, &CFSM::EndStateInitial);
// Initialize State Machine
m_pCurrentState = static_cast<CState *>(&m_stateInitial);
m_pNewState = NULL;
}
//======================================================================================================
// Global Functions
// Update
void CFSM::Update()
{
// Check New State
if (m_pNewState)
{
if (NULL != m_pCurrentState)
{
m_pCurrentState->ExecuteEndState();
}
// Set New State
m_pCurrentState = m_pNewState;
m_pNewState = 0;
// Execute Begin State
m_pCurrentState->ExecuteBeginState();
}
// Execute State
m_pCurrentState->ExecuteState();
}
//======================================================================================================
// State Functions
// Is State
bool CFSM::IsState(CState & State) const
{
return (m_pCurrentState == &State);
}
// Goto State
bool CFSM::GotoState(CState & NewState)
{
if (IsState(NewState) && m_pNewState == &NewState)
return true;
// Set New State
m_pNewState = &NewState;
return true;
}

34
src/game/FSM.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef _fsm_fsm_h
#define _fsm_fsm_h
// Local Includes
#include "state.h"
// FSM Class
class CFSM
{
protected:
CState * m_pCurrentState; // Current State
CState * m_pNewState; // New State
CStateTemplate<CFSM> m_stateInitial; // Initial State
public:
// Constructor
CFSM();
// Destructor
virtual ~CFSM() {}
// Global Functions
virtual void Update();
// State Functions
bool IsState(CState &State) const;
bool GotoState(CState &NewState);
virtual void BeginStateInitial() {}
virtual void StateInitial() {}
virtual void EndStateInitial() {}
};
#endif

View File

@@ -0,0 +1,136 @@
#include "stdafx.h"
#include "FileMonitor_FreeBSD.h"
#include "../../libthecore/include/log.h"
#define INVALID_KERNEL_EVENT -1
FileMonitorFreeBSD::FileMonitorFreeBSD()
{
m_KernelEventQueue = INVALID_KERNEL_EVENT;
}
FileMonitorFreeBSD::~FileMonitorFreeBSD()
{
if( m_KernelEventQueue != INVALID_KERNEL_EVENT )
{
close ( m_KernelEventQueue );
m_KernelEventQueue = INVALID_KERNEL_EVENT;
}
TMonitorFileHashMap::iterator it;
for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it )
{
close(it->second.fhMonitor);
}
m_FileLists.clear();
m_MonitoredEventLists.clear();
m_TriggeredEventLists.clear();
}
void FileMonitorFreeBSD::Update(DWORD dwPulses)
{
if( m_KernelEventQueue == INVALID_KERNEL_EVENT || m_FileLists.size() == 0 )
return;
int nEvent = kevent(m_KernelEventQueue, &m_TriggeredEventLists[0], (int)m_TriggeredEventLists.size(), &m_MonitoredEventLists[0], (int)m_MonitoredEventLists.size(), NULL );
if( nEvent == INVALID_KERNEL_EVENT )
{
return;
}
else if( nEvent > 0 )
{
for( int i = 0; i < nEvent; ++i )
{
int nEventFlags = m_MonitoredEventLists[i].flags;
eFileUpdatedOptions eUpdateOption = e_FileUpdate_None;
if (nEventFlags & EV_ERROR)
eUpdateOption = e_FileUpdate_Error;
else if (nEventFlags & NOTE_DELETE)
eUpdateOption = e_FileUpdate_Deleted;
else if (nEventFlags & NOTE_EXTEND || nEventFlags & NOTE_WRITE)
eUpdateOption = e_FileUpdate_Modified;
else if (nEventFlags & NOTE_ATTRIB)
eUpdateOption = e_FileUpdate_AttrModified;
else if (nEventFlags & NOTE_LINK)
eUpdateOption = e_FileUpdate_Linked;
else if (nEventFlags & NOTE_RENAME)
eUpdateOption = e_FileUpdate_Renamed;
else if (nEventFlags & NOTE_REVOKE)
eUpdateOption = e_FileUpdate_Revoked;
if( eUpdateOption != e_FileUpdate_None )
{
TMonitorFileHashMap::iterator it;
for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it )
{
FileIOContext_FreeBSD& context = it->second;
if( context.idxToEventList == i )
{
std::string strModifedFileName = it->first;
context.pListenFunc( strModifedFileName, eUpdateOption );
break;
}
}
}
}
}
}
void FileMonitorFreeBSD::AddWatch(const std::string& strFileName, PFN_FileChangeListener pListenerFunc)
{
int iFileHandle = -1;
if( (iFileHandle = open(strFileName.c_str(), O_RDONLY)) == -1)
{
sys_err("FileMonitorFreeBSD:AddWatch : can`t open file(%s).\n", strFileName.c_str());
return;
}
//create kqueue if not exists
if( m_KernelEventQueue == INVALID_KERNEL_EVENT )
m_KernelEventQueue = kqueue();
if( m_KernelEventQueue == INVALID_KERNEL_EVENT )
{
sys_err("FileMonitorFreeBSD:AddWatch : failed to create kqueue.\n");
return;
}
TMonitorFileHashMap::iterator it = m_FileLists.find( strFileName );
if( it != m_FileLists.end() )
{
sys_log(0, "FileMonitorFreeBSD:AddWatch : trying to add duplicated watch on file(%s).\n", strFileName.c_str() );
return;
}
//set file context
FileIOContext_FreeBSD context;
{
context.fhMonitor = iFileHandle;
context.idxToEventList = (int)m_MonitoredEventLists.size();
context.pListenFunc = pListenerFunc;
}
m_FileLists[strFileName] = context;
//set events
struct kevent kTriggerEvent, kMonitorEvent;
EV_SET(&kTriggerEvent, iFileHandle, EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE,
0, 0);
m_TriggeredEventLists.push_back( kTriggerEvent );
m_MonitoredEventLists.push_back( kMonitorEvent );
}

View File

@@ -0,0 +1,47 @@
#ifndef FILEMONITOR_FREEBSD_INCLUDED
#define FILEMONITOR_FREEBSD_INCLUDED
#include "IFileMonitor.h"
#include <unistd.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/time.h>
struct FileIOContext_FreeBSD
{
int fhMonitor;
int idxToEventList; // evtTrigger & evtMonitor index should be same
PFN_FileChangeListener pListenFunc;
};
class FileMonitorFreeBSD : public IFileMonitor
{
private:
FileMonitorFreeBSD(); //hidden
public:
virtual ~FileMonitorFreeBSD();
void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc);
void Update (DWORD dwPulses);
static FileMonitorFreeBSD& Instance()
{
static FileMonitorFreeBSD theMonitor;
return theMonitor;
}
private:
typedef std::unordered_map<std::string, FileIOContext_FreeBSD> TMonitorFileHashMap;
typedef std::vector<struct kevent> TEventList;
TMonitorFileHashMap m_FileLists;
TEventList m_MonitoredEventLists;
TEventList m_TriggeredEventLists;
int m_KernelEventQueue;
};
#endif //FILEMONITOR_FREEBSD_INCLUDED

54
src/game/HackShield.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include "stdafx.h"
#include "HackShield.h"
#include "HackShield_Impl.h"
#include "config.h"
bool CHackShieldManager::Initialize()
{
impl_ = M2_NEW CHackShieldImpl;
if (NULL == impl_)
{
return false;
}
return impl_->Initialize();
}
void CHackShieldManager::Release()
{
if (NULL != impl_)
{
impl_->Release();
M2_DELETE(impl_);
impl_ = NULL;
}
}
bool CHackShieldManager::CreateClientHandle(DWORD dwPlayerID)
{
return impl_->CreateClientHandle(dwPlayerID);
}
void CHackShieldManager::DeleteClientHandle(DWORD dwPlayerID)
{
impl_->DeleteClientHandle(dwPlayerID);
}
bool CHackShieldManager::SendCheckPacket(LPCHARACTER ch)
{
return impl_->SendCheckPacket(ch);
}
bool CHackShieldManager::VerifyAck(LPCHARACTER ch, const void* buf)
{
TPacketGCHSCheck* p = reinterpret_cast<TPacketGCHSCheck*>(const_cast<void*>(buf));
return impl_->VerifyAck(ch, p);
}

24
src/game/HackShield.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef HACK_SHIELD_MANAGER_H_
#define HACK_SHIELD_MANAGER_H_
class CHackShieldImpl;
class CHackShieldManager : public singleton<CHackShieldManager>
{
public:
bool Initialize ();
void Release ();
bool CreateClientHandle (DWORD dwPlayerID);
void DeleteClientHandle (DWORD dwPlayerID);
bool SendCheckPacket (LPCHARACTER ch);
bool VerifyAck (LPCHARACTER ch, const void* buf);
private:
CHackShieldImpl* impl_;
};
#endif /* HACK_SHIELD_MANAGER_H_ */

View File

@@ -0,0 +1,202 @@
#include "stdafx.h"
#include "HackShield_Impl.h"
#ifdef __FreeBSD__
#include "char.h"
#include "packet.h"
#include "desc.h"
#include "log.h"
bool CHackShieldImpl::Initialize()
{
handle_ = _AhnHS_CreateServerObject("metin2client.bin.hsb");
if (ANTICPX_INVALID_HANDLE_VALUE == handle_)
{
return false;
}
sys_log(0, "HShield: Success to CreateServerObject");
return true;
}
void CHackShieldImpl::Release()
{
_AhnHS_CloseServerHandle(handle_);
sys_log(0, "HShield: Server Handle Closed");
}
bool CHackShieldImpl::CreateClientHandle(DWORD dwPlayerID)
{
ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( dwPlayerID );
if (iter != CliehtHandleMap_.end())
{
sys_log(0, "HShield: Client Handle is already created for Player(%u)", dwPlayerID);
return false;
}
AHNHS_CLIENT_HANDLE handle = _AhnHS_CreateClientObject(handle_);
if (ANTICPX_INVALID_HANDLE_VALUE == handle)
{
sys_log(0, "HShield: Failed to create client handle for Player(%u)", dwPlayerID);
return false;
}
CliehtHandleMap_.insert( std::make_pair(dwPlayerID, handle) );
sys_log(0, "HShield: Success to create client handle for Player(%u)", dwPlayerID);
return true;
}
void CHackShieldImpl::DeleteClientHandle(DWORD dwPlayerID)
{
ClientHandleContainer::iterator iter = CliehtHandleMap_.find( dwPlayerID );
if (iter == CliehtHandleMap_.end())
{
sys_log(0, "HShield: there is no client handle for Player(%u)", dwPlayerID);
return;
}
_AhnHS_CloseClientHandle(iter->second);
CliehtHandleMap_.erase(iter);
sys_log(0, "HShield: client handle deleted for Player(%u)", dwPlayerID);
}
bool CHackShieldImpl::SendCheckPacket(LPCHARACTER ch)
{
if (NULL == ch)
{
return false;
}
ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( ch->GetPlayerID() );
if (iter == CliehtHandleMap_.end())
{
sys_log(0, "HShield: Client Handle not create for Player(%u)", ch->GetPlayerID());
return false;
}
TPacketGCHSCheck pack;
pack.bHeader = HEADER_GC_HS_REQUEST;
memset( &pack.Req, 0, sizeof(pack.Req));
unsigned long ret = _AhnHS_MakeRequest( iter->second, &(pack.Req) );
if (0 != ret)
{
sys_log(0, "HShield: _AhnHS_MakeRequest return error(%u) for Player(%u)", ret, ch->GetPlayerID());
return false;
}
else
{
sys_log(0, "HShield: _AhnHS_MakeRequest success ret(%d)", ret);
}
if (NULL != ch->GetDesc())
{
ch->GetDesc()->Packet( &pack, sizeof(pack) );
sys_log(0, "HShield: Send Check Request for Player(%u)", ch->GetPlayerID());
return true;
}
sys_log(0, "HShield: Failed to get DESC for Player(%u)", ch->GetPlayerID());
return false;
}
bool CHackShieldImpl::VerifyAck(LPCHARACTER ch, TPacketGCHSCheck* buf)
{
if (NULL == ch)
{
return false;
}
bool NeedDisconnect = false;
ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( ch->GetPlayerID() );
if (iter == CliehtHandleMap_.end())
{
sys_log(0, "HShield: Cannot Find ClientHandle For Verify");
NeedDisconnect = true;
}
unsigned long dwError = 0;
unsigned long ret = _AhnHS_VerifyResponseEx( iter->second, buf->Req.byBuffer, buf->Req.nLength, &dwError );
if (ANTICPX_RECOMMAND_CLOSE_SESSION == ret)
{
sys_log(0, "HShield: not a valid ack ret(%u) error(%u) from Player(%u)", ret, dwError, ch->GetPlayerID());
NeedDisconnect = true;
ch->StopHackShieldCheckCycle();
}
if (NULL != ch->GetDesc())
{
if (true == NeedDisconnect)
{
ch->GetDesc()->SetPhase(PHASE_CLOSE);
LogManager::instance().HackShieldLog(dwError, ch);
return false;
}
else
{
ch->SetHackShieldCheckMode(false);
}
}
sys_log(0, "HShield: Valid Ack from Player(%u)", ch->GetPlayerID());
return true;
}
#else
bool CHackShieldImpl::Initialize()
{
return true;
}
void CHackShieldImpl::Release()
{
}
bool CHackShieldImpl::CreateClientHandle(DWORD dwPlayerID)
{
return true;
}
void CHackShieldImpl::DeleteClientHandle(DWORD dwPlayerID)
{
}
bool CHackShieldImpl::SendCheckPacket(LPCHARACTER ch)
{
return true;
}
bool CHackShieldImpl::VerifyAck(LPCHARACTER ch, TPacketGCHSCheck* buf)
{
return true;
}
#endif

View File

@@ -0,0 +1,51 @@
#ifndef HACK_SHIELD_IMPL_H_
#define HACK_SHIELD_IMPL_H_
#include <unordered_map>
#ifdef __FreeBSD__
// Live build only
#define UNIX
#include <AntiCpXSvr.h>
#undef UNIX
#endif
#pragma pack(1)
typedef struct SPacketGCHSCheck
{
BYTE bHeader;
#ifdef __FreeBSD__
AHNHS_TRANS_BUFFER Req;
#endif
} TPacketGCHSCheck;
#pragma pack()
class CHackShieldImpl
{
public:
bool Initialize ();
void Release ();
bool CreateClientHandle (DWORD dwPlayerID);
void DeleteClientHandle (DWORD dwPlayerID);
bool SendCheckPacket (LPCHARACTER ch);
bool VerifyAck (LPCHARACTER ch, TPacketGCHSCheck* buf);
private:
#ifdef __FreeBSD__
AHNHS_SERVER_HANDLE handle_;
typedef std::unordered_map<DWORD, AHNHS_CLIENT_HANDLE> ClientHandleContainer;
ClientHandleContainer CliehtHandleMap_;
typedef std::unordered_map<DWORD, bool> ClientCheckContainer;
ClientCheckContainer ClientCheckMap_;
#endif
};
#endif /* HACK_SHIELD_IMPL_H_ */

30
src/game/IFileMonitor.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef IFILEMONITOR_INCLUDED
#define IFILEMONITOR_INCLUDED
//#include <boost/function.hpp>
#include <unordered_map>
enum eFileUpdatedOptions
{
e_FileUpdate_None = -1,
e_FileUpdate_Error,
e_FileUpdate_Deleted,
e_FileUpdate_Modified,
e_FileUpdate_AttrModified,
e_FileUpdate_Linked,
e_FileUpdate_Renamed,
e_FileUpdate_Revoked,
};
// TODO : in FreeBSD boost function doesn`t work with boost bind
// so currently we only support for static function ptr only
//typedef boost::function< void ( const std::string&, eFileUpdatedOptions ) > PFN_FileChangeListener;
typedef void (* PFN_FileChangeListener )(const std::string&, eFileUpdatedOptions);
struct IFileMonitor
{
virtual void Update (DWORD dwPulses) = 0;
virtual void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc) = 0;
};
#endif // IFILEMONITOR_INCLUDED

135
src/game/MarkConvert.cpp Normal file
View File

@@ -0,0 +1,135 @@
#include "stdafx.h"
#include "MarkManager.h"
#ifdef __WIN32__
#include <direct.h>
#endif
#define OLD_MARK_INDEX_FILENAME "guild_mark.idx"
#define OLD_MARK_DATA_FILENAME "guild_mark.tga"
static Pixel * LoadOldGuildMarkImageFile()
{
FILE * fp = fopen(OLD_MARK_DATA_FILENAME, "rb");
if (!fp)
{
sys_err("cannot open %s", OLD_MARK_INDEX_FILENAME);
return NULL;
}
int dataSize = 512 * 512 * sizeof(Pixel);
Pixel * dataPtr = (Pixel *) malloc(dataSize);
fread(dataPtr, dataSize, 1, fp);
fclose(fp);
return dataPtr;
}
bool GuildMarkConvert(const std::vector<DWORD> & vecGuildID)
{
// 폴더 생성
#ifndef __WIN32__
mkdir("mark", S_IRWXU);
#else
_mkdir("mark");
#endif
// 인덱스 파일이 있나?
#ifndef __WIN32__
if (0 != access(OLD_MARK_INDEX_FILENAME, F_OK))
#else
if (0 != _access(OLD_MARK_INDEX_FILENAME, 0))
#endif
return true;
// 인덱스 파일 열기
FILE* fp = fopen(OLD_MARK_INDEX_FILENAME, "r");
if (NULL == fp)
return false;
// 이미지 파일 열기
Pixel * oldImagePtr = LoadOldGuildMarkImageFile();
if (NULL == oldImagePtr)
{
fclose(fp);
return false;
}
/*
// guild_mark.tga가 실제 targa 파일이 아니고, 512 * 512 * 4 크기의 raw 파일이다.
// 눈으로 확인하기 위해 실제 targa 파일로 만든다.
CGuildMarkImage * pkImage = new CGuildMarkImage;
pkImage->Build("guild_mark_real.tga");
pkImage->Load("guild_mark_real.tga");
pkImage->PutData(0, 0, 512, 512, oldImagePtr);
pkImage->Save("guild_mark_real.tga");
*/
sys_log(0, "Guild Mark Converting Start.");
char line[256];
DWORD guild_id;
DWORD mark_id;
Pixel mark[SGuildMark::SIZE];
while (fgets(line, sizeof(line)-1, fp))
{
sscanf(line, "%u %u", &guild_id, &mark_id);
if (find(vecGuildID.begin(), vecGuildID.end(), guild_id) == vecGuildID.end())
{
sys_log(0, " skipping guild ID %u", guild_id);
continue;
}
// mark id -> 이미지에서의 위치 찾기
uint row = mark_id / 32;
uint col = mark_id % 32;
if (row >= 42)
{
sys_err("invalid mark_id %u", mark_id);
continue;
}
uint sx = col * 16;
uint sy = row * 12;
Pixel * src = oldImagePtr + sy * 512 + sx;
Pixel * dst = mark;
// 옛날 이미지에서 마크 한개 복사
for (int y = 0; y != SGuildMark::HEIGHT; ++y)
{
for (int x = 0; x != SGuildMark::WIDTH; ++x)
*(dst++) = *(src+x);
src += 512;
}
// 새 길드 마크 시스템에 넣는다.
CGuildMarkManager::instance().SaveMark(guild_id, (BYTE *) mark);
line[0] = '\0';
}
free(oldImagePtr);
fclose(fp);
// 컨버트는 한번만 하면되므로 파일을 옮겨준다.
#ifndef __WIN32__
system("mv -f guild_mark.idx guild_mark.idx.removable");
system("mv -f guild_mark.tga guild_mark.tga.removable");
#else
system("move /Y guild_mark.idx guild_mark.idx.removable");
system("move /Y guild_mark.tga guild_mark.tga.removable");
#endif
sys_log(0, "Guild Mark Converting Complete.");
return true;
}

299
src/game/MarkImage.cpp Normal file
View File

@@ -0,0 +1,299 @@
#include "stdafx.h"
#include "MarkImage.h"
#include "crc32.h"
#include "lzo_manager.h"
#define CLZO LZOManager
CGuildMarkImage * NewMarkImage()
{
return M2_NEW CGuildMarkImage;
}
void DeleteMarkImage(CGuildMarkImage * pkImage)
{
M2_DELETE(pkImage);
}
CGuildMarkImage::CGuildMarkImage()
: m_uImg(INVALID_HANDLE)
{
memset( &m_apxImage, 0, sizeof(m_apxImage) );
}
CGuildMarkImage::~CGuildMarkImage()
{
Destroy();
}
void CGuildMarkImage::Destroy()
{
if (INVALID_HANDLE == m_uImg)
return;
ilDeleteImages(1, &m_uImg);
m_uImg = INVALID_HANDLE;
}
void CGuildMarkImage::Create()
{
if (INVALID_HANDLE != m_uImg)
return;
ilGenImages(1, &m_uImg);
}
bool CGuildMarkImage::Save(const char* c_szFileName)
{
ilEnable(IL_FILE_OVERWRITE);
ilBindImage(m_uImg);
if (!ilSave(IL_TGA, (const ILstring)c_szFileName))
return false;
return true;
}
bool CGuildMarkImage::Build(const char * c_szFileName)
{
sys_log(0, "GuildMarkImage: creating new file %s", c_szFileName);
Destroy();
Create();
ilBindImage(m_uImg);
ilEnable(IL_ORIGIN_SET);
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
BYTE * data = (BYTE *) malloc(sizeof(Pixel) * WIDTH * HEIGHT);
memset(data, 0, sizeof(Pixel) * WIDTH * HEIGHT);
if (!ilTexImage(WIDTH, HEIGHT, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, data))
{
sys_err("GuildMarkImage: cannot initialize image");
return false;
}
free(data);
ilEnable(IL_FILE_OVERWRITE);
if (!ilSave(IL_TGA, (const ILstring)c_szFileName))
return false;
return true;
}
bool CGuildMarkImage::Load(const char * c_szFileName)
{
Destroy();
Create();
ilBindImage(m_uImg);
ilEnable(IL_ORIGIN_SET);
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
if (!ilLoad(IL_TYPE_UNKNOWN, (const ILstring) c_szFileName))
{
sys_err("GuildMarkImage: %s cannot open file.", c_szFileName);
return false;
}
if (ilGetInteger(IL_IMAGE_WIDTH) != WIDTH)
{
sys_err("GuildMarkImage: %s width must be %u", c_szFileName, WIDTH);
return false;
}
if (ilGetInteger(IL_IMAGE_HEIGHT) != HEIGHT)
{
sys_err("GuildMarkImage: %s height must be %u", c_szFileName, HEIGHT);
return false;
}
ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE);
BuildAllBlocks();
return true;
}
void CGuildMarkImage::PutData(UINT x, UINT y, UINT width, UINT height, void * data)
{
ilBindImage(m_uImg);
ilSetPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
void CGuildMarkImage::GetData(UINT x, UINT y, UINT width, UINT height, void * data)
{
ilBindImage(m_uImg);
ilCopyPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
// 이미지 = 512x512
// 블럭 = 마크 4 x 4
// 마크 = 16 x 12
// 한 이미지의 블럭 = 8 x 10
// SERVER
bool CGuildMarkImage::SaveMark(DWORD posMark, BYTE * pbImage)
{
if (posMark >= MARK_TOTAL_COUNT)
{
sys_err("GuildMarkImage::CopyMarkFromData: Invalid mark position %u", posMark);
return false;
}
// 마크를 전체 이미지에 그린다.
DWORD colMark = posMark % MARK_COL_COUNT;
DWORD rowMark = posMark / MARK_COL_COUNT;
printf("PutMark pos %u %ux%u\n", posMark, colMark * SGuildMark::WIDTH, rowMark * SGuildMark::HEIGHT);
PutData(colMark * SGuildMark::WIDTH, rowMark * SGuildMark::HEIGHT, SGuildMark::WIDTH, SGuildMark::HEIGHT, pbImage);
// 그려진 곳의 블럭을 업데이트
DWORD rowBlock = rowMark / SGuildMarkBlock::MARK_PER_BLOCK_HEIGHT;
DWORD colBlock = colMark / SGuildMarkBlock::MARK_PER_BLOCK_WIDTH;
Pixel apxBuf[SGuildMarkBlock::SIZE];
GetData(colBlock * SGuildMarkBlock::WIDTH, rowBlock * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf);
m_aakBlock[rowBlock][colBlock].Compress(apxBuf);
return true;
}
bool CGuildMarkImage::DeleteMark(DWORD posMark)
{
Pixel image[SGuildMark::SIZE];
memset(&image, 0, sizeof(image));
return SaveMark(posMark, (BYTE *) &image);
}
// CLIENT
bool CGuildMarkImage::SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize)
{
if (posBlock >= BLOCK_TOTAL_COUNT)
return false;
Pixel apxBuf[SGuildMarkBlock::SIZE];
lzo_uint sizeBuf = sizeof(apxBuf);
if (LZO_E_OK != lzo1x_decompress_safe(pbComp, dwCompSize, (BYTE *) apxBuf, &sizeBuf, CLZO::Instance().GetWorkMemory()))
{
sys_err("GuildMarkImage::CopyBlockFromCompressedData: cannot decompress, compressed size = %u", dwCompSize);
return false;
}
if (sizeBuf != sizeof(apxBuf))
{
sys_err("GuildMarkImage::CopyBlockFromCompressedData: image corrupted, decompressed size = %u", sizeBuf);
return false;
}
DWORD rowBlock = posBlock / BLOCK_COL_COUNT;
DWORD colBlock = posBlock % BLOCK_COL_COUNT;
PutData(colBlock * SGuildMarkBlock::WIDTH, rowBlock * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf);
m_aakBlock[rowBlock][colBlock].CopyFrom(pbComp, dwCompSize, GetCRC32((const char *) apxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE));
return true;
}
void CGuildMarkImage::BuildAllBlocks() // 이미지 전체를 블럭화
{
Pixel apxBuf[SGuildMarkBlock::SIZE];
sys_log(0, "GuildMarkImage::BuildAllBlocks");
for (UINT row = 0; row < BLOCK_ROW_COUNT; ++row)
for (UINT col = 0; col < BLOCK_COL_COUNT; ++col)
{
GetData(col * SGuildMarkBlock::WIDTH, row * SGuildMarkBlock::HEIGHT, SGuildMarkBlock::WIDTH, SGuildMarkBlock::HEIGHT, apxBuf);
m_aakBlock[row][col].Compress(apxBuf);
}
}
DWORD CGuildMarkImage::GetEmptyPosition()
{
SGuildMark kMark;
for (DWORD row = 0; row < MARK_ROW_COUNT; ++row)
{
for (DWORD col = 0; col < MARK_COL_COUNT; ++col)
{
GetData(col * SGuildMark::WIDTH, row * SGuildMark::HEIGHT, SGuildMark::WIDTH, SGuildMark::HEIGHT, kMark.m_apxBuf);
if (kMark.IsEmpty())
return (row * MARK_COL_COUNT + col);
}
}
return INVALID_MARK_POSITION;
}
void CGuildMarkImage::GetDiffBlocks(const DWORD * crcList, std::map<BYTE, const SGuildMarkBlock *> & mapDiffBlocks)
{
BYTE posBlock = 0;
for (DWORD row = 0; row < BLOCK_ROW_COUNT; ++row)
for (DWORD col = 0; col < BLOCK_COL_COUNT; ++col)
{
if (m_aakBlock[row][col].m_crc != *crcList)
{
mapDiffBlocks.insert(std::map<BYTE, const SGuildMarkBlock *>::value_type(posBlock, &m_aakBlock[row][col]));
}
++crcList;
++posBlock;
}
}
void CGuildMarkImage::GetBlockCRCList(DWORD * crcList)
{
for (DWORD row = 0; row < BLOCK_ROW_COUNT; ++row)
for (DWORD col = 0; col < BLOCK_COL_COUNT; ++col)
*(crcList++) = m_aakBlock[row][col].GetCRC();
}
////////////////////////////////////////////////////////////////////////////////
void SGuildMark::Clear()
{
for (DWORD iPixel = 0; iPixel < SIZE; ++iPixel)
m_apxBuf[iPixel] = 0xff000000;
}
bool SGuildMark::IsEmpty()
{
for (DWORD iPixel = 0; iPixel < SIZE; ++iPixel)
if (m_apxBuf[iPixel] != 0x00000000)
return false;
return true;
}
////////////////////////////////////////////////////////////////////////////////
DWORD SGuildMarkBlock::GetCRC() const
{
return m_crc;
}
void SGuildMarkBlock::CopyFrom(const BYTE * pbCompBuf, DWORD dwCompSize, DWORD crc)
{
if (dwCompSize > MAX_COMP_SIZE)
return;
m_sizeCompBuf = dwCompSize;
thecore_memcpy(m_abCompBuf, pbCompBuf, dwCompSize);
m_crc = crc;
//printf("SGuildMarkBlock::CopyFrom: %u > %u crc %u\n", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf, m_crc);
}
void SGuildMarkBlock::Compress(const Pixel * pxBuf)
{
m_sizeCompBuf = MAX_COMP_SIZE;
if (LZO_E_OK != lzo1x_1_compress((const BYTE *) pxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE, m_abCompBuf, &m_sizeCompBuf, CLZO::Instance().GetWorkMemory()))
{
sys_err("SGuildMarkBlock::Compress: Error! %u > %u", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf);
return;
}
//sys_log(0, "SGuildMarkBlock::Compress %u > %u", sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf);
m_crc = GetCRC32((const char *) pxBuf, sizeof(Pixel) * SGuildMarkBlock::SIZE);
}

111
src/game/MarkImage.h Normal file
View File

@@ -0,0 +1,111 @@
#ifndef __INC_METIN_II_MARKIMAGE_H__
#define __INC_METIN_II_MARKIMAGE_H__
#include <IL/il.h>
#include "minilzo.h"
typedef unsigned long Pixel;
struct SGuildMark
{
enum
{
WIDTH = 16,
HEIGHT = 12,
SIZE = WIDTH * HEIGHT,
};
///////////////////////////////////////////////////////////////////////////////
Pixel m_apxBuf[SIZE]; // 실제 이미지
///////////////////////////////////////////////////////////////////////////////
void Clear();
bool IsEmpty();
};
struct SGuildMarkBlock
{
enum
{
MARK_PER_BLOCK_WIDTH = 4,
MARK_PER_BLOCK_HEIGHT = 4,
WIDTH = SGuildMark::WIDTH * MARK_PER_BLOCK_WIDTH,
HEIGHT = SGuildMark::HEIGHT * MARK_PER_BLOCK_HEIGHT,
SIZE = WIDTH * HEIGHT,
MAX_COMP_SIZE = (SIZE * sizeof(Pixel)) + ((SIZE * sizeof(Pixel)) >> 4) + 64 + 3
};
///////////////////////////////////////////////////////////////////////////////
Pixel m_apxBuf[SIZE]; // 실제 이미지
BYTE m_abCompBuf[MAX_COMP_SIZE]; // 압축된 데이터
lzo_uint m_sizeCompBuf; // 압축된 크기
DWORD m_crc; // 압축된 데이터의 CRC
///////////////////////////////////////////////////////////////////////////////
DWORD GetCRC() const;
void CopyFrom(const BYTE * pbCompBuf, DWORD dwCompSize, DWORD crc);
void Compress(const Pixel * pxBuf);
};
class CGuildMarkImage
{
public:
enum
{
WIDTH = 512,
HEIGHT = 512,
BLOCK_ROW_COUNT = HEIGHT / SGuildMarkBlock::HEIGHT, // 10
BLOCK_COL_COUNT = WIDTH / SGuildMarkBlock::WIDTH, // 8
BLOCK_TOTAL_COUNT = BLOCK_ROW_COUNT * BLOCK_COL_COUNT, // 80
MARK_ROW_COUNT = BLOCK_ROW_COUNT * SGuildMarkBlock::MARK_PER_BLOCK_HEIGHT, // 40
MARK_COL_COUNT = BLOCK_COL_COUNT * SGuildMarkBlock::MARK_PER_BLOCK_WIDTH, // 32
MARK_TOTAL_COUNT = MARK_ROW_COUNT * MARK_COL_COUNT, // 1280
INVALID_MARK_POSITION = 0xffffffff,
};
CGuildMarkImage();
virtual ~CGuildMarkImage();
void Create();
void Destroy();
bool Build(const char * c_szFileName);
bool Save(const char* c_szFileName);
bool Load(const char* c_szFileName);
void PutData(UINT x, UINT y, UINT width, UINT height, void* data);
void GetData(UINT x, UINT y, UINT width, UINT height, void* data);
bool SaveMark(DWORD posMark, BYTE * pbMarkImage);
bool DeleteMark(DWORD posMark);
bool SaveBlockFromCompressedData(DWORD posBlock, const BYTE * pbComp, DWORD dwCompSize); // 서버 -> 클라이언트
DWORD GetEmptyPosition(); // 빈 마크 위치를 얻는다.
void GetBlockCRCList(DWORD * crcList);
void GetDiffBlocks(const DWORD * crcList, std::map<BYTE, const SGuildMarkBlock *> & mapDiffBlocks);
private:
enum
{
INVALID_HANDLE = 0xffffffff,
};
void BuildAllBlocks();
SGuildMarkBlock m_aakBlock[BLOCK_ROW_COUNT][BLOCK_COL_COUNT];
Pixel m_apxImage[WIDTH * HEIGHT * sizeof(Pixel)];
ILuint m_uImg;
};
#endif

473
src/game/MarkManager.cpp Normal file
View File

@@ -0,0 +1,473 @@
#include "stdafx.h"
#include "MarkManager.h"
#include "crc32.h"
CGuildMarkImage * CGuildMarkManager::__NewImage()
{
return M2_NEW CGuildMarkImage;
}
void CGuildMarkManager::__DeleteImage(CGuildMarkImage * pkImgDel)
{
M2_DELETE(pkImgDel);
}
CGuildMarkManager::CGuildMarkManager()
{
// 남은 mark id 셋을 만든다. (서버용)
for (DWORD i = 0; i < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT; ++i)
m_setFreeMarkID.insert(i);
}
CGuildMarkManager::~CGuildMarkManager()
{
for (std::map<DWORD, CGuildMarkImage *>::iterator it = m_mapIdx_Image.begin(); it != m_mapIdx_Image.end(); ++it)
__DeleteImage(it->second);
m_mapIdx_Image.clear();
}
bool CGuildMarkManager::GetMarkImageFilename(DWORD imgIdx, std::string & path) const
{
if (imgIdx >= MAX_IMAGE_COUNT)
return false;
char buf[64];
snprintf(buf, sizeof(buf), "mark/%s_%u.tga", m_pathPrefix.c_str(), imgIdx);
path = buf;
return true;
}
void CGuildMarkManager::SetMarkPathPrefix(const char * prefix)
{
m_pathPrefix = prefix;
}
// 마크 인덱스 불러오기 (서버에서만 사용)
bool CGuildMarkManager::LoadMarkIndex()
{
char buf[64];
snprintf(buf, sizeof(buf), "mark/%s_index", m_pathPrefix.c_str());
FILE * fp = fopen(buf, "r");
if (!fp)
return false;
DWORD guildID;
DWORD markID;
char line[256];
while (fgets(line, sizeof(line)-1, fp))
{
sscanf(line, "%u %u", &guildID, &markID);
line[0] = '\0';
AddMarkIDByGuildID(guildID, markID);
}
LoadMarkImages();
fclose(fp);
return true;
}
bool CGuildMarkManager::SaveMarkIndex()
{
char buf[64];
snprintf(buf, sizeof(buf), "mark/%s_index", m_pathPrefix.c_str());
FILE * fp = fopen(buf, "w");
if (!fp)
{
sys_err("MarkManager::SaveMarkIndex: cannot open index file.");
return false;
}
for (std::map<DWORD, DWORD>::iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it)
fprintf(fp, "%u %u\n", it->first, it->second);
fclose(fp);
sys_log(0, "MarkManager::SaveMarkIndex: index count %d", m_mapGID_MarkID.size());
return true;
}
void CGuildMarkManager::LoadMarkImages()
{
bool isMarkExists[MAX_IMAGE_COUNT];
memset(isMarkExists, 0, sizeof(isMarkExists));
for (std::map<DWORD, DWORD>::iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it)
{
DWORD markID = it->second;
if (markID < MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT)
isMarkExists[markID / CGuildMarkImage::MARK_TOTAL_COUNT] = true;
}
for (DWORD i = 0; i < MAX_IMAGE_COUNT; ++i)
if (isMarkExists[i])
__GetImage(i);
}
void CGuildMarkManager::SaveMarkImage(DWORD imgIdx)
{
std::string path;
if (GetMarkImageFilename(imgIdx, path))
if (!__GetImage(imgIdx)->Save(path.c_str()))
sys_err("%s Save failed\n", path.c_str());
}
CGuildMarkImage * CGuildMarkManager::__GetImage(DWORD imgIdx)
{
std::map<DWORD, CGuildMarkImage *>::iterator it = m_mapIdx_Image.find(imgIdx);
if (it == m_mapIdx_Image.end())
{
std::string imagePath;
if (GetMarkImageFilename(imgIdx, imagePath))
{
CGuildMarkImage * pkImage = __NewImage();
m_mapIdx_Image.insert(std::map<DWORD, CGuildMarkImage *>::value_type(imgIdx, pkImage));
if (!pkImage->Load(imagePath.c_str()))
{
pkImage->Build(imagePath.c_str());
pkImage->Load(imagePath.c_str());
}
return pkImage;
}
else
return NULL;
}
else
return it->second;
}
bool CGuildMarkManager::AddMarkIDByGuildID(DWORD guildID, DWORD markID)
{
if (markID >= MAX_IMAGE_COUNT * CGuildMarkImage::MARK_TOTAL_COUNT)
return false;
//sys_log(0, "MarkManager: guild_id=%d mark_id=%d", guildID, markID);
m_mapGID_MarkID.insert(std::map<DWORD, DWORD>::value_type(guildID, markID));
m_setFreeMarkID.erase(markID);
return true;
}
DWORD CGuildMarkManager::GetMarkID(DWORD guildID)
{
std::map<DWORD, DWORD>::iterator it = m_mapGID_MarkID.find(guildID);
if (it == m_mapGID_MarkID.end())
return INVALID_MARK_ID;
return it->second;
}
DWORD CGuildMarkManager::__AllocMarkID(DWORD guildID)
{
std::set<DWORD>::iterator it = m_setFreeMarkID.lower_bound(0);
if (it == m_setFreeMarkID.end())
return INVALID_MARK_ID;
DWORD markID = *it;
DWORD imgIdx = markID / CGuildMarkImage::MARK_TOTAL_COUNT;
CGuildMarkImage * pkImage = __GetImage(imgIdx); // 이미지가 없다면 만들기 위해
if (pkImage && AddMarkIDByGuildID(guildID, markID))
return markID;
return INVALID_MARK_ID;
}
DWORD CGuildMarkManager::GetMarkImageCount() const
{
return m_mapIdx_Image.size();
}
DWORD CGuildMarkManager::GetMarkCount() const
{
return m_mapGID_MarkID.size();
}
// SERVER
void CGuildMarkManager::CopyMarkIdx(char * pcBuf) const
{
WORD * pwBuf = (WORD *) pcBuf;
for (std::map<DWORD, DWORD>::const_iterator it = m_mapGID_MarkID.begin(); it != m_mapGID_MarkID.end(); ++it)
{
*(pwBuf++) = it->first; // guild id
*(pwBuf++) = it->second; // mark id
}
}
// SERVER
DWORD CGuildMarkManager::SaveMark(DWORD guildID, BYTE * pbMarkImage)
{
DWORD idMark;
if ((idMark = GetMarkID(guildID)) == INVALID_MARK_ID)
{
if ((idMark = __AllocMarkID(guildID)) == INVALID_MARK_ID)
{
sys_err("CGuildMarkManager: cannot alloc mark id %u", guildID);
return false;
}
else
sys_log(0, "SaveMark: mark id alloc %u", idMark);
}
else
sys_log(0, "SaveMark: mark id found %u", idMark);
DWORD imgIdx = (idMark / CGuildMarkImage::MARK_TOTAL_COUNT);
CGuildMarkImage * pkImage = __GetImage(imgIdx);
if (pkImage)
{
pkImage->SaveMark(idMark % CGuildMarkImage::MARK_TOTAL_COUNT, pbMarkImage);
SaveMarkImage(imgIdx);
SaveMarkIndex();
}
return idMark;
}
// SERVER
void CGuildMarkManager::DeleteMark(DWORD guildID)
{
std::map<DWORD, DWORD>::iterator it = m_mapGID_MarkID.find(guildID);
if (it == m_mapGID_MarkID.end())
return;
CGuildMarkImage * pkImage;
if ((pkImage = __GetImage(it->second / CGuildMarkImage::MARK_TOTAL_COUNT)) != NULL)
pkImage->DeleteMark(it->second % CGuildMarkImage::MARK_TOTAL_COUNT);
m_setFreeMarkID.insert(it->second);
m_mapGID_MarkID.erase(it);
SaveMarkIndex();
}
// SERVER
void CGuildMarkManager::GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::map<BYTE, const SGuildMarkBlock *> & mapDiffBlocks)
{
mapDiffBlocks.clear();
// 클라이언트에서 서버에 없는 이미지를 요청할 수는 없다.
if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx))
{
sys_err("invalid idx %u", imgIdx);
return;
}
CGuildMarkImage * p = __GetImage(imgIdx);
if (p)
p->GetDiffBlocks(crcList, mapDiffBlocks);
}
// CLIENT
bool CGuildMarkManager::SaveBlockFromCompressedData(DWORD imgIdx, DWORD posBlock, const BYTE * pbBlock, DWORD dwSize)
{
CGuildMarkImage * pkImage = __GetImage(imgIdx);
if (pkImage)
pkImage->SaveBlockFromCompressedData(posBlock, pbBlock, dwSize);
return false;
}
// CLIENT
bool CGuildMarkManager::GetBlockCRCList(DWORD imgIdx, DWORD * crcList)
{
// 클라이언트에서 서버에 없는 이미지를 요청할 수는 없다.
if (m_mapIdx_Image.end() == m_mapIdx_Image.find(imgIdx))
{
sys_err("invalid idx %u", imgIdx);
return false;
}
CGuildMarkImage * p = __GetImage(imgIdx);
if (p)
p->GetBlockCRCList(crcList);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////
// Symbol
///////////////////////////////////////////////////////////////////////////////////////
const CGuildMarkManager::TGuildSymbol * CGuildMarkManager::GetGuildSymbol(DWORD guildID)
{
std::map<DWORD, TGuildSymbol>::iterator it = m_mapSymbol.find(guildID);
if (it == m_mapSymbol.end())
return NULL;
return &it->second;
}
bool CGuildMarkManager::LoadSymbol(const char* filename)
{
FILE* fp = fopen(filename, "rb");
if (!fp)
return true;
else
{
DWORD symbolCount;
fread(&symbolCount, 4, 1, fp);
for (DWORD i = 0; i < symbolCount; i++)
{
DWORD guildID;
DWORD dwSize;
fread(&guildID, 4, 1, fp);
fread(&dwSize, 4, 1, fp);
TGuildSymbol gs;
gs.raw.resize(dwSize);
fread(&gs.raw[0], 1, dwSize, fp);
gs.crc = GetCRC32(reinterpret_cast<const char*>(&gs.raw[0]), dwSize);
m_mapSymbol.insert(std::make_pair(guildID, gs));
}
}
fclose(fp);
return true;
}
void CGuildMarkManager::SaveSymbol(const char* filename)
{
FILE* fp = fopen(filename, "wb");
if (!fp)
{
sys_err("Cannot open Symbol file (name: %s)", filename);
return;
}
DWORD symbolCount = m_mapSymbol.size();
fwrite(&symbolCount, 4, 1, fp);
for (std::map<DWORD, TGuildSymbol>::iterator it = m_mapSymbol.begin(); it != m_mapSymbol.end(); ++it)
{
DWORD guildID = it->first;
DWORD dwSize = it->second.raw.size();
fwrite(&guildID, 4, 1, fp);
fwrite(&dwSize, 4, 1, fp);
fwrite(&it->second.raw[0], 1, dwSize, fp);
}
fclose(fp);
}
void CGuildMarkManager::UploadSymbol(DWORD guildID, int iSize, const BYTE* pbyData)
{
sys_log(0, "GuildSymbolUpload guildID %u Size %d", guildID, iSize);
if (m_mapSymbol.find(guildID) == m_mapSymbol.end())
m_mapSymbol.insert(std::make_pair(guildID, TGuildSymbol()));
TGuildSymbol& rSymbol = m_mapSymbol[guildID];
rSymbol.raw.clear();
if (iSize > 0)
{
rSymbol.raw.reserve(iSize);
std::copy(pbyData, (pbyData + iSize), std::back_inserter(rSymbol.raw));
rSymbol.crc = GetCRC32(reinterpret_cast<const char*>(pbyData), iSize);
}
}
#ifdef __UNITTEST__
#include "lzo_manager.h"
void heartbeat(LPHEART ht, int pulse)
{
return;
}
void SaveMark(DWORD guildID, const char * filename)
{
ILuint m_uImg;
ilGenImages(1, &m_uImg);
ilBindImage(m_uImg);
ilEnable(IL_ORIGIN_SET);
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
if (ilLoad(IL_TYPE_UNKNOWN, (const ILstring) filename))
{
ILuint width = ilGetInteger(IL_IMAGE_WIDTH);
ILuint height = ilGetInteger(IL_IMAGE_HEIGHT);
ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE);
BYTE * data = (BYTE *) malloc(sizeof(DWORD) * width * height);
ilCopyPixels(0, 0, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
ilDeleteImages(1, &m_uImg);
printf("%s w%u h%u ", filename, width, height);
CGuildMarkManager::instance().SaveMark(guildID, data);
}
else
printf("%s cannot open file.\n", filename);
}
int main(int argc, char **argv)
{
LZOManager lzo;
CGuildMarkManager mgr;
char f[64];
srandomdev();
ilInit(); // DevIL Initialize
thecore_init(25, heartbeat);
mgr.SetMarkPathPrefix("mark");
mgr.LoadMarkIndex();
for (int i = 0; i < 1279; ++i)
{
snprintf(f, sizeof(f), "%u.jpg", (random() % 5) + 1);
SaveMark(i, f);
//mgr.DeleteMark(i);
}
//snprintf(f, sizeof(f), "%u.jpg", (random() % 5) + 1);
//SaveMark(1, f);
DWORD idx_client[CGuildMarkImage::BLOCK_TOTAL_COUNT];
DWORD idx_server[CGuildMarkImage::BLOCK_TOTAL_COUNT];
mgr.GetBlockCRCList(0, idx_client);
mgr.GetBlockCRCList(1, idx_server);
std::map<BYTE, const SGuildMarkBlock *> mapDiff;
mgr.GetDiffBlocks(1, idx_client, mapDiff);
printf("#1 Diff %u\n", mapDiff.size());
for (itertype(mapDiff) it = mapDiff.begin(); it != mapDiff.end(); ++it)
{
printf("Put Block pos %u crc %u\n", it->first, it->second->m_crc);
mgr.SaveBlockFromCompressedData(0, it->first, it->second->m_abCompBuf, it->second->m_sizeCompBuf);
}
mgr.GetBlockCRCList(0, idx_client);
mgr.GetDiffBlocks(1, idx_client, mapDiff);
printf("#2 Diff %u\n", mapDiff.size());
return 1;
}
#endif

82
src/game/MarkManager.h Normal file
View File

@@ -0,0 +1,82 @@
#ifndef __INC_METIN_II_GUILDLIB_MARK_MANAGER_H__
#define __INC_METIN_II_GUILDLIB_MARK_MANAGER_H__
#include "MarkImage.h"
class CGuildMarkManager : public singleton<CGuildMarkManager>
{
public:
enum
{
MAX_IMAGE_COUNT = 5,
INVALID_MARK_ID = 0xffffffff,
};
// Symbol
struct TGuildSymbol
{
DWORD crc;
std::vector<BYTE> raw;
};
CGuildMarkManager();
virtual ~CGuildMarkManager();
const TGuildSymbol * GetGuildSymbol(DWORD GID);
bool LoadSymbol(const char* filename);
void SaveSymbol(const char* filename);
void UploadSymbol(DWORD guildID, int iSize, const BYTE* pbyData);
//
// Mark
//
void SetMarkPathPrefix(const char * prefix);
bool LoadMarkIndex(); // 마크 인덱스 불러오기 (서버에서만 사용)
bool SaveMarkIndex(); // 마크 인덱스 저장하기
void LoadMarkImages(); // 모든 마크 이미지를 불러오기
void SaveMarkImage(DWORD imgIdx); // 마크 이미지 저장
bool GetMarkImageFilename(DWORD imgIdx, std::string & path) const;
bool AddMarkIDByGuildID(DWORD guildID, DWORD markID);
DWORD GetMarkImageCount() const;
DWORD GetMarkCount() const;
DWORD GetMarkID(DWORD guildID);
// SERVER
void CopyMarkIdx(char * pcBuf) const;
DWORD SaveMark(DWORD guildID, BYTE * pbMarkImage);
void DeleteMark(DWORD guildID);
void GetDiffBlocks(DWORD imgIdx, const DWORD * crcList, std::map<BYTE, const SGuildMarkBlock *> & mapDiffBlocks);
// CLIENT
bool SaveBlockFromCompressedData(DWORD imgIdx, DWORD idBlock, const BYTE * pbBlock, DWORD dwSize);
bool GetBlockCRCList(DWORD imgIdx, DWORD * crcList);
private:
//
// Mark
//
CGuildMarkImage * __NewImage();
void __DeleteImage(CGuildMarkImage * pkImgDel);
DWORD __AllocMarkID(DWORD guildID);
CGuildMarkImage * __GetImage(DWORD imgIdx);
CGuildMarkImage * __GetImagePtr(DWORD idMark);
std::map<DWORD, CGuildMarkImage *> m_mapIdx_Image; // index = image index
std::map<DWORD, DWORD> m_mapGID_MarkID; // index = guild id
std::set<DWORD> m_setFreeMarkID;
std::string m_pathPrefix;
private:
//
// Symbol
//
std::map<DWORD, TGuildSymbol> m_mapSymbol;
};
#endif

441
src/game/OXEvent.cpp Normal file
View File

@@ -0,0 +1,441 @@
#include "stdafx.h"
#include "constants.h"
#include "config.h"
#include "questmanager.h"
#include "start_position.h"
#include "packet.h"
#include "buffer_manager.h"
#include "log.h"
#include "char.h"
#include "char_manager.h"
#include "OXEvent.h"
#include "desc.h"
bool COXEventManager::Initialize()
{
m_timedEvent = NULL;
m_map_char.clear();
m_map_attender.clear();
m_vec_quiz.clear();
SetStatus(OXEVENT_FINISH);
return true;
}
void COXEventManager::Destroy()
{
CloseEvent();
m_map_char.clear();
m_map_attender.clear();
m_vec_quiz.clear();
SetStatus(OXEVENT_FINISH);
}
OXEventStatus COXEventManager::GetStatus()
{
BYTE ret = quest::CQuestManager::instance().GetEventFlag("oxevent_status");
switch (ret)
{
case 0 :
return OXEVENT_FINISH;
case 1 :
return OXEVENT_OPEN;
case 2 :
return OXEVENT_CLOSE;
case 3 :
return OXEVENT_QUIZ;
default :
return OXEVENT_ERR;
}
return OXEVENT_ERR;
}
void COXEventManager::SetStatus(OXEventStatus status)
{
BYTE val = 0;
switch (status)
{
case OXEVENT_OPEN :
val = 1;
break;
case OXEVENT_CLOSE :
val = 2;
break;
case OXEVENT_QUIZ :
val = 3;
break;
case OXEVENT_FINISH :
case OXEVENT_ERR :
default :
val = 0;
break;
}
quest::CQuestManager::instance().RequestSetEventFlag("oxevent_status", val);
}
bool COXEventManager::Enter(LPCHARACTER pkChar)
{
if (GetStatus() == OXEVENT_FINISH)
{
sys_log(0, "OXEVENT : map finished. but char enter. %s", pkChar->GetName());
return false;
}
PIXEL_POSITION pos = pkChar->GetXYZ();
if (pos.x == 896500 && pos.y == 24600)
{
return EnterAttender(pkChar);
}
else if (pos.x == 896300 && pos.y == 28900)
{
return EnterAudience(pkChar);
}
else
{
sys_log(0, "OXEVENT : wrong pos enter %d %d", pos.x, pos.y);
return false;
}
return false;
}
bool COXEventManager::EnterAttender(LPCHARACTER pkChar)
{
DWORD pid = pkChar->GetPlayerID();
m_map_char.insert(std::make_pair(pid, pid));
m_map_attender.insert(std::make_pair(pid, pid));
return true;
}
bool COXEventManager::EnterAudience(LPCHARACTER pkChar)
{
DWORD pid = pkChar->GetPlayerID();
m_map_char.insert(std::make_pair(pid, pid));
return true;
}
bool COXEventManager::AddQuiz(unsigned char level, const char* pszQuestion, bool answer)
{
if (m_vec_quiz.size() < (size_t) level + 1)
m_vec_quiz.resize(level + 1);
struct tag_Quiz tmpQuiz;
tmpQuiz.level = level;
strlcpy(tmpQuiz.Quiz, pszQuestion, sizeof(tmpQuiz.Quiz));
tmpQuiz.answer = answer;
m_vec_quiz[level].push_back(tmpQuiz);
return true;
}
bool COXEventManager::ShowQuizList(LPCHARACTER pkChar)
{
int c = 0;
for (size_t i = 0; i < m_vec_quiz.size(); ++i)
{
for (size_t j = 0; j < m_vec_quiz[i].size(); ++j, ++c)
{
pkChar->ChatPacket(CHAT_TYPE_INFO, "%d %s %s", m_vec_quiz[i][j].level, m_vec_quiz[i][j].Quiz, m_vec_quiz[i][j].answer ? LC_TEXT("") : LC_TEXT("芭窿"));
}
}
pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("醚 柠令 荐: %d"), c);
return true;
}
void COXEventManager::ClearQuiz()
{
for (unsigned int i = 0; i < m_vec_quiz.size(); ++i)
{
m_vec_quiz[i].clear();
}
m_vec_quiz.clear();
}
EVENTINFO(OXEventInfoData)
{
bool answer;
OXEventInfoData()
: answer( false )
{
}
};
EVENTFUNC(oxevent_timer)
{
static BYTE flag = 0;
OXEventInfoData* info = dynamic_cast<OXEventInfoData*>(event->info);
if ( info == NULL )
{
sys_err( "oxevent_timer> <Factor> Null pointer" );
return 0;
}
switch (flag)
{
case 0:
SendNoticeMap(LC_TEXT("10檬第 魄沥窍摆嚼聪促."), OXEVENT_MAP_INDEX, true);
flag++;
return PASSES_PER_SEC(10);
case 1:
SendNoticeMap(LC_TEXT("沥翠篮"), OXEVENT_MAP_INDEX, true);
if (info->answer == true)
{
COXEventManager::instance().CheckAnswer(true);
SendNoticeMap(LC_TEXT("O 涝聪促"), OXEVENT_MAP_INDEX, true);
}
else
{
COXEventManager::instance().CheckAnswer(false);
SendNoticeMap(LC_TEXT("X 涝聪促"), OXEVENT_MAP_INDEX, true);
}
if (LC_IsJapan())
{
SendNoticeMap("娫堘偊偨曽乆傪奜偵堏摦偝偣傑偡丅", OXEVENT_MAP_INDEX, true);
}
else
{
SendNoticeMap(LC_TEXT("5檬 第 撇府脚 盒甸阑 官冰栏肺 捞悼 矫虐摆嚼聪促."), OXEVENT_MAP_INDEX, true);
}
flag++;
return PASSES_PER_SEC(5);
case 2:
COXEventManager::instance().WarpToAudience();
COXEventManager::instance().SetStatus(OXEVENT_CLOSE);
SendNoticeMap(LC_TEXT("促澜 巩力 霖厚秦林技夸."), OXEVENT_MAP_INDEX, true);
flag = 0;
break;
}
return 0;
}
bool COXEventManager::Quiz(unsigned char level, int timelimit)
{
if (m_vec_quiz.size() == 0) return false;
if (level > m_vec_quiz.size()) level = m_vec_quiz.size() - 1;
if (m_vec_quiz[level].size() <= 0) return false;
if (timelimit < 0) timelimit = 30;
int idx = number(0, m_vec_quiz[level].size()-1);
SendNoticeMap(LC_TEXT("巩力 涝聪促."), OXEVENT_MAP_INDEX, true);
SendNoticeMap(m_vec_quiz[level][idx].Quiz, OXEVENT_MAP_INDEX, true);
SendNoticeMap(LC_TEXT("嘎栏搁 O, 撇府搁 X肺 捞悼秦林技夸"), OXEVENT_MAP_INDEX, true);
if (m_timedEvent != NULL) {
event_cancel(&m_timedEvent);
}
OXEventInfoData* answer = AllocEventInfo<OXEventInfoData>();
answer->answer = m_vec_quiz[level][idx].answer;
timelimit -= 15;
m_timedEvent = event_create(oxevent_timer, answer, PASSES_PER_SEC(timelimit));
SetStatus(OXEVENT_QUIZ);
m_vec_quiz[level].erase(m_vec_quiz[level].begin()+idx);
return true;
}
bool COXEventManager::CheckAnswer(bool answer)
{
if (m_map_attender.size() <= 0) return true;
itertype(m_map_attender) iter = m_map_attender.begin();
itertype(m_map_attender) iter_tmp;
m_map_miss.clear();
int rect[4];
if (answer != true)
{
rect[0] = 892600;
rect[1] = 22900;
rect[2] = 896300;
rect[3] = 26400;
}
else
{
rect[0] = 896600;
rect[1] = 22900;
rect[2] = 900300;
rect[3] = 26400;
}
LPCHARACTER pkChar = NULL;
PIXEL_POSITION pos;
for (; iter != m_map_attender.end();)
{
pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second);
if (pkChar != NULL)
{
pos = pkChar->GetXYZ();
if (pos.x < rect[0] || pos.x > rect[2] || pos.y < rect[1] || pos.y > rect[3])
{
pkChar->EffectPacket(SE_FAIL);
iter_tmp = iter;
iter++;
m_map_attender.erase(iter_tmp);
m_map_miss.insert(std::make_pair(pkChar->GetPlayerID(), pkChar->GetPlayerID()));
}
else
{
pkChar->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("沥翠涝聪促!"));
// pkChar->CreateFly(number(FLY_FIREWORK1, FLY_FIREWORK6), pkChar);
char chatbuf[256];
int len = snprintf(chatbuf, sizeof(chatbuf),
"%s %u %u", number(0, 1) == 1 ? "cheer1" : "cheer2", (DWORD)pkChar->GetVID(), 0);
// 府畔蔼捞 sizeof(chatbuf) 捞惑老 版快 truncate登菌促绰 舵..
if (len < 0 || len >= (int) sizeof(chatbuf))
len = sizeof(chatbuf) - 1;
// \0 巩磊 器窃
++len;
TPacketGCChat pack_chat;
pack_chat.header = HEADER_GC_CHAT;
pack_chat.size = sizeof(TPacketGCChat) + len;
pack_chat.type = CHAT_TYPE_COMMAND;
pack_chat.id = 0;
TEMP_BUFFER buf;
buf.write(&pack_chat, sizeof(TPacketGCChat));
buf.write(chatbuf, len);
pkChar->PacketAround(buf.read_peek(), buf.size());
pkChar->EffectPacket(SE_SUCCESS);
++iter;
}
}
else
{
itertype(m_map_char) err = m_map_char.find(iter->first);
if (err != m_map_char.end()) m_map_char.erase(err);
itertype(m_map_miss) err2 = m_map_miss.find(iter->first);
if (err2 != m_map_miss.end()) m_map_miss.erase(err2);
iter_tmp = iter;
++iter;
m_map_attender.erase(iter_tmp);
}
}
return true;
}
void COXEventManager::WarpToAudience()
{
if (m_map_miss.size() <= 0) return;
itertype(m_map_miss) iter = m_map_miss.begin();
LPCHARACTER pkChar = NULL;
for (; iter != m_map_miss.end(); ++iter)
{
pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second);
if (pkChar != NULL)
{
switch ( number(0, 3))
{
case 0 : pkChar->Show(OXEVENT_MAP_INDEX, 896300, 28900); break;
case 1 : pkChar->Show(OXEVENT_MAP_INDEX, 890900, 28100); break;
case 2 : pkChar->Show(OXEVENT_MAP_INDEX, 896600, 20500); break;
case 3 : pkChar->Show(OXEVENT_MAP_INDEX, 902500, 28100); break;
default : pkChar->Show(OXEVENT_MAP_INDEX, 896300, 28900); break;
}
}
}
m_map_miss.clear();
}
bool COXEventManager::CloseEvent()
{
if (m_timedEvent != NULL) {
event_cancel(&m_timedEvent);
}
itertype(m_map_char) iter = m_map_char.begin();
LPCHARACTER pkChar = NULL;
for (; iter != m_map_char.end(); ++iter)
{
pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second);
if (pkChar != NULL)
pkChar->WarpSet(EMPIRE_START_X(pkChar->GetEmpire()), EMPIRE_START_Y(pkChar->GetEmpire()));
}
m_map_char.clear();
return true;
}
bool COXEventManager::LogWinner()
{
itertype(m_map_attender) iter = m_map_attender.begin();
for (; iter != m_map_attender.end(); ++iter)
{
LPCHARACTER pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second);
if (pkChar)
LogManager::instance().CharLog(pkChar, 0, "OXEVENT", "LastManStanding");
}
return true;
}
bool COXEventManager::GiveItemToAttender(DWORD dwItemVnum, BYTE count)
{
itertype(m_map_attender) iter = m_map_attender.begin();
for (; iter != m_map_attender.end(); ++iter)
{
LPCHARACTER pkChar = CHARACTER_MANAGER::instance().FindByPID(iter->second);
if (pkChar)
{
pkChar->AutoGiveItem(dwItemVnum, count);
LogManager::instance().ItemLog(pkChar->GetPlayerID(), 0, count, dwItemVnum, "OXEVENT_REWARD", "", pkChar->GetDesc()->GetHostName(), dwItemVnum);
}
}
return true;
}

65
src/game/OXEvent.h Normal file
View File

@@ -0,0 +1,65 @@
#define OXEVENT_MAP_INDEX 113
struct tag_Quiz
{
char level;
char Quiz[256];
bool answer;
};
enum OXEventStatus
{
OXEVENT_FINISH = 0, // OX이벤트가 완전히 끝난 상태
OXEVENT_OPEN = 1, // OX이벤트가 시작됨. 을두지(20012)를 통해서 입장가능
OXEVENT_CLOSE = 2, // OX이벤트의 참가가 끝남. 을두지(20012)를 통한 입장이 차단됨
OXEVENT_QUIZ = 3, // 퀴즈를 출제함.
OXEVENT_ERR = 0xff
};
class COXEventManager : public singleton<COXEventManager>
{
private :
std::map<DWORD, DWORD> m_map_char;
std::map<DWORD, DWORD> m_map_attender;
std::map<DWORD, DWORD> m_map_miss;
std::vector<std::vector<tag_Quiz> > m_vec_quiz;
LPEVENT m_timedEvent;
protected :
bool CheckAnswer();
bool EnterAudience(LPCHARACTER pChar);
bool EnterAttender(LPCHARACTER pChar);
public :
bool Initialize();
void Destroy();
OXEventStatus GetStatus();
void SetStatus(OXEventStatus status);
bool LoadQuizScript(const char* szFileName);
bool Enter(LPCHARACTER pChar);
bool CloseEvent();
void ClearQuiz();
bool AddQuiz(unsigned char level, const char* pszQuestion, bool answer);
bool ShowQuizList(LPCHARACTER pChar);
bool Quiz(unsigned char level, int timelimit);
bool GiveItemToAttender(DWORD dwItemVnum, BYTE count);
bool CheckAnswer(bool answer);
void WarpToAudience();
bool LogWinner();
DWORD GetAttenderCount() { return m_map_attender.size(); }
};

637
src/game/PetSystem.cpp Normal file
View File

@@ -0,0 +1,637 @@
#include "stdafx.h"
#include "utils.h"
#include "vector.h"
#include "char.h"
#include "sectree_manager.h"
#include "char_manager.h"
#include "mob_manager.h"
#include "PetSystem.h"
#include "../../common/VnumHelper.h"
#include "packet.h"
#include "item_manager.h"
#include "item.h"
extern int passes_per_sec;
EVENTINFO(petsystem_event_info)
{
CPetSystem* pPetSystem;
};
// PetSystem을 update 해주는 event.
// PetSystem은 CHRACTER_MANAGER에서 기존 FSM으로 update 해주는 기존 chracters와 달리,
// Owner의 STATE를 update 할 때 _UpdateFollowAI 함수로 update 해준다.
// 그런데 owner의 state를 update를 CHRACTER_MANAGER에서 해주기 때문에,
// petsystem을 update하다가 pet을 unsummon하는 부분에서 문제가 생겼다.
// (CHRACTER_MANAGER에서 update 하면 chracter destroy가 pending되어, CPetSystem에서는 dangling 포인터를 가지고 있게 된다.)
// 따라서 PetSystem만 업데이트 해주는 event를 발생시킴.
EVENTFUNC(petsystem_update_event)
{
petsystem_event_info* info = dynamic_cast<petsystem_event_info*>( event->info );
if ( info == NULL )
{
sys_err( "check_speedhack_event> <Factor> Null pointer" );
return 0;
}
CPetSystem* pPetSystem = info->pPetSystem;
if (NULL == pPetSystem)
return 0;
pPetSystem->Update(0);
// 0.25초마다 갱신.
return PASSES_PER_SEC(1) / 4;
}
/// NOTE: 1캐릭터가 몇개의 펫을 가질 수 있는지 제한... 캐릭터마다 개수를 다르게 할거라면 변수로 넣등가... 음..
/// 가질 수 있는 개수와 동시에 소환할 수 있는 개수가 틀릴 수 있는데 이런건 기획 없으니 일단 무시
const float PET_COUNT_LIMIT = 3;
///////////////////////////////////////////////////////////////////////////////////////
// CPetActor
///////////////////////////////////////////////////////////////////////////////////////
CPetActor::CPetActor(LPCHARACTER owner, DWORD vnum, DWORD options)
{
m_dwVnum = vnum;
m_dwVID = 0;
m_dwOptions = options;
m_dwLastActionTime = 0;
m_pkChar = 0;
m_pkOwner = owner;
m_originalMoveSpeed = 0;
m_dwSummonItemVID = 0;
m_dwSummonItemVnum = 0;
}
CPetActor::~CPetActor()
{
this->Unsummon();
m_pkOwner = 0;
}
void CPetActor::SetName(const char* name)
{
std::string petName = m_pkOwner->GetName();
if (0 != m_pkOwner &&
0 == name &&
0 != m_pkOwner->GetName())
{
petName += "'s Pet";
}
else
petName += name;
if (true == IsSummoned())
m_pkChar->SetName(petName);
m_name = petName;
}
bool CPetActor::Mount()
{
if (0 == m_pkOwner)
return false;
if (true == HasOption(EPetOption_Mountable))
m_pkOwner->MountVnum(m_dwVnum);
return m_pkOwner->GetMountVnum() == m_dwVnum;;
}
void CPetActor::Unmount()
{
if (0 == m_pkOwner)
return;
if (m_pkOwner->IsHorseRiding())
m_pkOwner->StopRiding();
}
void CPetActor::Unsummon()
{
if (true == this->IsSummoned())
{
// 버프 삭제
this->ClearBuff();
this->SetSummonItem(NULL);
if (NULL != m_pkOwner)
m_pkOwner->ComputePoints();
if (NULL != m_pkChar)
M2_DESTROY_CHARACTER(m_pkChar);
m_pkChar = 0;
m_dwVID = 0;
}
}
DWORD CPetActor::Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar)
{
long x = m_pkOwner->GetX();
long y = m_pkOwner->GetY();
long z = m_pkOwner->GetZ();
if (true == bSpawnFar)
{
x += (number(0, 1) * 2 - 1) * number(2000, 2500);
y += (number(0, 1) * 2 - 1) * number(2000, 2500);
}
else
{
x += number(-100, 100);
y += number(-100, 100);
}
if (0 != m_pkChar)
{
m_pkChar->Show (m_pkOwner->GetMapIndex(), x, y);
m_dwVID = m_pkChar->GetVID();
return m_dwVID;
}
m_pkChar = CHARACTER_MANAGER::instance().SpawnMob(
m_dwVnum,
m_pkOwner->GetMapIndex(),
x, y, z,
false, (int)(m_pkOwner->GetRotation()+180), false);
if (0 == m_pkChar)
{
sys_err("[CPetSystem::Summon] Failed to summon the pet. (vnum: %d)", m_dwVnum);
return 0;
}
m_pkChar->SetPet();
// m_pkOwner->DetailLog();
// m_pkChar->DetailLog();
//펫의 국가를 주인의 국가로 설정함.
m_pkChar->SetEmpire(m_pkOwner->GetEmpire());
m_dwVID = m_pkChar->GetVID();
this->SetName(petName);
// SetSummonItem(pSummonItem)를 부른 후에 ComputePoints를 부르면 버프 적용됨.
this->SetSummonItem(pSummonItem);
m_pkOwner->ComputePoints();
m_pkChar->Show(m_pkOwner->GetMapIndex(), x, y, z);
return m_dwVID;
}
bool CPetActor::_UpdatAloneActionAI(float fMinDist, float fMaxDist)
{
float fDist = number(fMinDist, fMaxDist);
float r = (float)number (0, 359);
float dest_x = GetOwner()->GetX() + fDist * cos(r);
float dest_y = GetOwner()->GetY() + fDist * sin(r);
//m_pkChar->SetRotation(number(0, 359)); // 방향은 랜덤으로 설정
//GetDeltaByDegree(m_pkChar->GetRotation(), fDist, &fx, &fy);
// 느슨한 못감 속성 체크; 최종 위치와 중간 위치가 갈수없다면 가지 않는다.
//if (!(SECTREE_MANAGER::instance().IsMovablePosition(m_pkChar->GetMapIndex(), m_pkChar->GetX() + (int) fx, m_pkChar->GetY() + (int) fy)
// && SECTREE_MANAGER::instance().IsMovablePosition(m_pkChar->GetMapIndex(), m_pkChar->GetX() + (int) fx/2, m_pkChar->GetY() + (int) fy/2)))
// return true;
m_pkChar->SetNowWalking(true);
//if (m_pkChar->Goto(m_pkChar->GetX() + (int) fx, m_pkChar->GetY() + (int) fy))
// m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
if (!m_pkChar->IsStateMove() && m_pkChar->Goto(dest_x, dest_y))
m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
m_dwLastActionTime = get_dword_time();
return true;
}
// char_state.cpp StateHorse함수 그냥 C&P -_-;
bool CPetActor::_UpdateFollowAI()
{
if (0 == m_pkChar->m_pkMobData)
{
//sys_err("[CPetActor::_UpdateFollowAI] m_pkChar->m_pkMobData is NULL");
return false;
}
// NOTE: 캐릭터(펫)의 원래 이동 속도를 알아야 하는데, 해당 값(m_pkChar->m_pkMobData->m_table.sMovingSpeed)을 직접적으로 접근해서 알아낼 수도 있지만
// m_pkChar->m_pkMobData 값이 invalid한 경우가 자주 발생함. 현재 시간관계상 원인은 다음에 파악하고 일단은 m_pkChar->m_pkMobData 값을 아예 사용하지 않도록 함.
// 여기서 매번 검사하는 이유는 최초 초기화 할 때 정상 값을 제대로 못얻어오는 경우도 있음.. -_-;; ㅠㅠㅠㅠㅠㅠㅠㅠㅠ
if (0 == m_originalMoveSpeed)
{
const CMob* mobData = CMobManager::Instance().Get(m_dwVnum);
if (0 != mobData)
m_originalMoveSpeed = mobData->m_table.sMovingSpeed;
}
float START_FOLLOW_DISTANCE = 300.0f; // 이 거리 이상 떨어지면 쫓아가기 시작함
float START_RUN_DISTANCE = 900.0f; // 이 거리 이상 떨어지면 뛰어서 쫓아감.
float RESPAWN_DISTANCE = 4500.f; // 이 거리 이상 멀어지면 주인 옆으로 소환함.
int APPROACH = 200; // 접근 거리
bool bDoMoveAlone = true; // 캐릭터와 가까이 있을 때 혼자 여기저기 움직일건지 여부 -_-;
bool bRun = false; // 뛰어야 하나?
DWORD currentTime = get_dword_time();
long ownerX = m_pkOwner->GetX(); long ownerY = m_pkOwner->GetY();
long charX = m_pkChar->GetX(); long charY = m_pkChar->GetY();
float fDist = DISTANCE_APPROX(charX - ownerX, charY - ownerY);
if (fDist >= RESPAWN_DISTANCE)
{
float fOwnerRot = m_pkOwner->GetRotation() * 3.141592f / 180.f;
float fx = -APPROACH * cos(fOwnerRot);
float fy = -APPROACH * sin(fOwnerRot);
if (m_pkChar->Show(m_pkOwner->GetMapIndex(), ownerX + fx, ownerY + fy))
{
return true;
}
}
if (fDist >= START_FOLLOW_DISTANCE)
{
if( fDist >= START_RUN_DISTANCE)
{
bRun = true;
}
m_pkChar->SetNowWalking(!bRun); // NOTE: 함수 이름보고 멈추는건줄 알았는데 SetNowWalking(false) 하면 뛰는거임.. -_-;
Follow(APPROACH);
m_pkChar->SetLastAttacked(currentTime);
m_dwLastActionTime = currentTime;
}
//else
//{
// if (fabs(m_pkChar->GetRotation() - GetDegreeFromPositionXY(charX, charY, ownerX, ownerX)) > 10.f || fabs(m_pkChar->GetRotation() - GetDegreeFromPositionXY(charX, charY, ownerX, ownerX)) < 350.f)
// {
// m_pkChar->Follow(m_pkOwner, APPROACH);
// m_pkChar->SetLastAttacked(currentTime);
// m_dwLastActionTime = currentTime;
// }
//}
// Follow 중이지만 주인과 일정 거리 이내로 가까워졌다면 멈춤
else
m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
//else if (currentTime - m_dwLastActionTime > number(5000, 12000))
//{
// this->_UpdatAloneActionAI(START_FOLLOW_DISTANCE / 2, START_FOLLOW_DISTANCE);
//}
return true;
}
bool CPetActor::Update(DWORD deltaTime)
{
bool bResult = true;
// 펫 주인이 죽었거나, 소환된 펫의 상태가 이상하다면 펫을 없앰. (NOTE: 가끔가다 이런 저런 이유로 소환된 펫이 DEAD 상태에 빠지는 경우가 있음-_-;)
// 펫을 소환한 아이템이 없거나, 내가 가진 상태가 아니라면 펫을 없앰.
if (m_pkOwner->IsDead() || (IsSummoned() && m_pkChar->IsDead())
|| NULL == ITEM_MANAGER::instance().FindByVID(this->GetSummonItemVID())
|| ITEM_MANAGER::instance().FindByVID(this->GetSummonItemVID())->GetOwner() != this->GetOwner()
)
{
this->Unsummon();
return true;
}
if (this->IsSummoned() && HasOption(EPetOption_Followable))
bResult = bResult && this->_UpdateFollowAI();
return bResult;
}
//NOTE : 주의!!! MinDistance를 크게 잡으면 그 변위만큼의 변화동안은 follow하지 않는다,
bool CPetActor::Follow(float fMinDistance)
{
// 가려는 위치를 바라봐야 한다.
if( !m_pkOwner || !m_pkChar)
return false;
float fOwnerX = m_pkOwner->GetX();
float fOwnerY = m_pkOwner->GetY();
float fPetX = m_pkChar->GetX();
float fPetY = m_pkChar->GetY();
float fDist = DISTANCE_SQRT(fOwnerX - fPetX, fOwnerY - fPetY);
if (fDist <= fMinDistance)
return false;
m_pkChar->SetRotationToXY(fOwnerX, fOwnerY);
float fx, fy;
float fDistToGo = fDist - fMinDistance;
GetDeltaByDegree(m_pkChar->GetRotation(), fDistToGo, &fx, &fy);
if (!m_pkChar->Goto((int)(fPetX+fx+0.5f), (int)(fPetY+fy+0.5f)) )
return false;
m_pkChar->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0, 0);
return true;
}
void CPetActor::SetSummonItem (LPITEM pItem)
{
if (NULL == pItem)
{
m_dwSummonItemVID = 0;
m_dwSummonItemVnum = 0;
return;
}
m_dwSummonItemVID = pItem->GetVID();
m_dwSummonItemVnum = pItem->GetVnum();
}
void CPetActor::GiveBuff()
{
// 파황 펫 버프는 던전에서만 발생함.
if (34004 == m_dwVnum || 34009 == m_dwVnum)
{
if (NULL == m_pkOwner->GetDungeon())
{
return;
}
}
LPITEM item = ITEM_MANAGER::instance().FindByVID(m_dwSummonItemVID);
if (NULL != item)
item->ModifyPoints(true);
return ;
}
void CPetActor::ClearBuff()
{
if (NULL == m_pkOwner)
return ;
TItemTable* item_proto = ITEM_MANAGER::instance().GetTable(m_dwSummonItemVnum);
if (NULL == item_proto)
return;
for (int i = 0; i < ITEM_APPLY_MAX_NUM; i++)
{
if (item_proto->aApplies[i].bType == APPLY_NONE)
continue;
m_pkOwner->ApplyPoint(item_proto->aApplies[i].bType, -item_proto->aApplies[i].lValue);
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////////
// CPetSystem
///////////////////////////////////////////////////////////////////////////////////////
CPetSystem::CPetSystem(LPCHARACTER owner)
{
// assert(0 != owner && "[CPetSystem::CPetSystem] Invalid owner");
m_pkOwner = owner;
m_dwUpdatePeriod = 400;
m_dwLastUpdateTime = 0;
}
CPetSystem::~CPetSystem()
{
Destroy();
}
void CPetSystem::Destroy()
{
for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
{
CPetActor* petActor = iter->second;
if (0 != petActor)
{
delete petActor;
}
}
event_cancel(&m_pkPetSystemUpdateEvent);
m_petActorMap.clear();
}
/// 펫 시스템 업데이트. 등록된 펫들의 AI 처리 등을 함.
bool CPetSystem::Update(DWORD deltaTime)
{
bool bResult = true;
DWORD currentTime = get_dword_time();
// CHARACTER_MANAGER에서 캐릭터류 Update할 때 매개변수로 주는 (Pulse라고 되어있는)값이 이전 프레임과의 시간차이인줄 알았는데
// 전혀 다른 값이라서-_-; 여기에 입력으로 들어오는 deltaTime은 의미가 없음ㅠㅠ
if (m_dwUpdatePeriod > currentTime - m_dwLastUpdateTime)
return true;
std::vector <CPetActor*> v_garbageActor;
for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
{
CPetActor* petActor = iter->second;
if (0 != petActor && petActor->IsSummoned())
{
LPCHARACTER pPet = petActor->GetCharacter();
if (NULL == CHARACTER_MANAGER::instance().Find(pPet->GetVID()))
{
v_garbageActor.push_back(petActor);
}
else
{
bResult = bResult && petActor->Update(deltaTime);
}
}
}
for (std::vector<CPetActor*>::iterator it = v_garbageActor.begin(); it != v_garbageActor.end(); it++)
DeletePet(*it);
m_dwLastUpdateTime = currentTime;
return bResult;
}
/// 관리 목록에서 펫을 지움
void CPetSystem::DeletePet(DWORD mobVnum)
{
TPetActorMap::iterator iter = m_petActorMap.find(mobVnum);
if (m_petActorMap.end() == iter)
{
sys_err("[CPetSystem::DeletePet] Can't find pet on my list (VNUM: %d)", mobVnum);
return;
}
CPetActor* petActor = iter->second;
if (0 == petActor)
sys_err("[CPetSystem::DeletePet] Null Pointer (petActor)");
else
delete petActor;
m_petActorMap.erase(iter);
}
/// 관리 목록에서 펫을 지움
void CPetSystem::DeletePet(CPetActor* petActor)
{
for (TPetActorMap::iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
{
if (iter->second == petActor)
{
delete petActor;
m_petActorMap.erase(iter);
return;
}
}
sys_err("[CPetSystem::DeletePet] Can't find petActor(0x%x) on my list(size: %d) ", petActor, m_petActorMap.size());
}
void CPetSystem::Unsummon(DWORD vnum, bool bDeleteFromList)
{
CPetActor* actor = this->GetByVnum(vnum);
if (0 == actor)
{
sys_err("[CPetSystem::GetByVnum(%d)] Null Pointer (petActor)", vnum);
return;
}
actor->Unsummon();
if (true == bDeleteFromList)
this->DeletePet(actor);
bool bActive = false;
for (TPetActorMap::iterator it = m_petActorMap.begin(); it != m_petActorMap.end(); it++)
{
bActive |= it->second->IsSummoned();
}
if (false == bActive)
{
event_cancel(&m_pkPetSystemUpdateEvent);
m_pkPetSystemUpdateEvent = NULL;
}
}
CPetActor* CPetSystem::Summon(DWORD mobVnum, LPITEM pSummonItem, const char* petName, bool bSpawnFar, DWORD options)
{
CPetActor* petActor = this->GetByVnum(mobVnum);
// 등록된 펫이 아니라면 새로 생성 후 관리 목록에 등록함.
if (0 == petActor)
{
petActor = M2_NEW CPetActor(m_pkOwner, mobVnum, options);
m_petActorMap.insert(std::make_pair(mobVnum, petActor));
}
DWORD petVID = petActor->Summon(petName, pSummonItem, bSpawnFar);
if (NULL == m_pkPetSystemUpdateEvent)
{
petsystem_event_info* info = AllocEventInfo<petsystem_event_info>();
info->pPetSystem = this;
m_pkPetSystemUpdateEvent = event_create(petsystem_update_event, info, PASSES_PER_SEC(1) / 4); // 0.25초
}
return petActor;
}
CPetActor* CPetSystem::GetByVID(DWORD vid) const
{
CPetActor* petActor = 0;
bool bFound = false;
for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
{
petActor = iter->second;
if (0 == petActor)
{
sys_err("[CPetSystem::GetByVID(%d)] Null Pointer (petActor)", vid);
continue;
}
bFound = petActor->GetVID() == vid;
if (true == bFound)
break;
}
return bFound ? petActor : 0;
}
/// 등록 된 펫 중에서 주어진 몹 VNUM을 가진 액터를 반환하는 함수.
CPetActor* CPetSystem::GetByVnum(DWORD vnum) const
{
CPetActor* petActor = 0;
TPetActorMap::const_iterator iter = m_petActorMap.find(vnum);
if (m_petActorMap.end() != iter)
petActor = iter->second;
return petActor;
}
size_t CPetSystem::CountSummoned() const
{
size_t count = 0;
for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
{
CPetActor* petActor = iter->second;
if (0 != petActor)
{
if (petActor->IsSummoned())
++count;
}
}
return count;
}
void CPetSystem::RefreshBuff()
{
for (TPetActorMap::const_iterator iter = m_petActorMap.begin(); iter != m_petActorMap.end(); ++iter)
{
CPetActor* petActor = iter->second;
if (0 != petActor)
{
if (petActor->IsSummoned())
{
petActor->GiveBuff();
}
}
}
}

163
src/game/PetSystem.h Normal file
View File

@@ -0,0 +1,163 @@
#ifndef __HEADER_PET_SYSTEM__
#define __HEADER_PET_SYSTEM__
class CHARACTER;
// TODO: 펫으로서의 능력치? 라던가 친밀도, 배고픔 기타등등... 수치
struct SPetAbility
{
};
/**
*/
class CPetActor //: public CHARACTER
{
public:
enum EPetOptions
{
EPetOption_Followable = 1 << 0,
EPetOption_Mountable = 1 << 1,
EPetOption_Summonable = 1 << 2,
EPetOption_Combatable = 1 << 3,
};
protected:
friend class CPetSystem;
CPetActor(LPCHARACTER owner, DWORD vnum, DWORD options = EPetOption_Followable | EPetOption_Summonable);
// CPetActor(LPCHARACTER owner, DWORD vnum, const SPetAbility& petAbility, DWORD options = EPetOption_Followable | EPetOption_Summonable);
virtual ~CPetActor();
virtual bool Update(DWORD deltaTime);
protected:
virtual bool _UpdateFollowAI(); ///< 주인을 따라다니는 AI 처리
virtual bool _UpdatAloneActionAI(float fMinDist, float fMaxDist); ///< 주인 근처에서 혼자 노는 AI 처리
/// @TODO
//virtual bool _UpdateCombatAI();
private:
bool Follow(float fMinDistance = 50.f);
public:
LPCHARACTER GetCharacter() const { return m_pkChar; }
LPCHARACTER GetOwner() const { return m_pkOwner; }
DWORD GetVID() const { return m_dwVID; }
DWORD GetVnum() const { return m_dwVnum; }
bool HasOption(EPetOptions option) const { return m_dwOptions & option; }
void SetName(const char* petName);
bool Mount();
void Unmount();
DWORD Summon(const char* petName, LPITEM pSummonItem, bool bSpawnFar = false);
void Unsummon();
bool IsSummoned() const { return 0 != m_pkChar; }
void SetSummonItem (LPITEM pItem);
DWORD GetSummonItemVID () { return m_dwSummonItemVID; }
// 버프 주는 함수와 거두는 함수.
// 이게 좀 괴랄한게, 서버가 ㅄ라서,
// POINT_MOV_SPEED, POINT_ATT_SPEED, POINT_CAST_SPEED는 PointChange()란 함수만 써서 변경해 봐야 소용이 없는게,
// PointChange() 이후에 어디선가 ComputePoints()를 하면 싹다 초기화되고,
// 더 웃긴건, ComputePoints()를 부르지 않으면 클라의 POINT는 전혀 변하지 않는다는 거다.
// 그래서 버프를 주는 것은 ComputePoints() 내부에서 petsystem->RefreshBuff()를 부르도록 하였고,
// 버프를 빼는 것은 ClearBuff()를 부르고, ComputePoints를 하는 것으로 한다.
void GiveBuff();
void ClearBuff();
private:
DWORD m_dwVnum;
DWORD m_dwVID;
DWORD m_dwOptions;
DWORD m_dwLastActionTime;
DWORD m_dwSummonItemVID;
DWORD m_dwSummonItemVnum;
short m_originalMoveSpeed;
std::string m_name;
LPCHARACTER m_pkChar; // Instance of pet(CHARACTER)
LPCHARACTER m_pkOwner;
// SPetAbility m_petAbility; // 능력치
};
/**
*/
class CPetSystem
{
public:
typedef std::unordered_map<DWORD, CPetActor*> TPetActorMap; /// <VNUM, PetActor> map. (한 캐릭터가 같은 vnum의 펫을 여러개 가질 일이 있을까..??)
public:
CPetSystem(LPCHARACTER owner);
virtual ~CPetSystem();
CPetActor* GetByVID(DWORD vid) const;
CPetActor* GetByVnum(DWORD vnum) const;
bool Update(DWORD deltaTime);
void Destroy();
size_t CountSummoned() const; ///< 현재 소환된(실체화 된 캐릭터가 있는) 펫의 개수
public:
void SetUpdatePeriod(DWORD ms);
CPetActor* Summon(DWORD mobVnum, LPITEM pSummonItem, const char* petName, bool bSpawnFar, DWORD options = CPetActor::EPetOption_Followable | CPetActor::EPetOption_Summonable);
void Unsummon(DWORD mobVnum, bool bDeleteFromList = false);
void Unsummon(CPetActor* petActor, bool bDeleteFromList = false);
// TODO: 진짜 펫 시스템이 들어갈 때 구현. (캐릭터가 보유한 펫의 정보를 추가할 때 라던가...)
CPetActor* AddPet(DWORD mobVnum, const char* petName, const SPetAbility& ability, DWORD options = CPetActor::EPetOption_Followable | CPetActor::EPetOption_Summonable | CPetActor::EPetOption_Combatable);
void DeletePet(DWORD mobVnum);
void DeletePet(CPetActor* petActor);
void RefreshBuff();
private:
TPetActorMap m_petActorMap;
LPCHARACTER m_pkOwner; ///< 펫 시스템의 Owner
DWORD m_dwUpdatePeriod; ///< 업데이트 주기 (ms단위)
DWORD m_dwLastUpdateTime;
LPEVENT m_pkPetSystemUpdateEvent;
};
/**
// Summon Pet
CPetSystem* petSystem = mainChar->GetPetSystem();
CPetActor* petActor = petSystem->Summon(~~~);
DWORD petVID = petActor->GetVID();
if (0 == petActor)
{
ERROR_LOG(...)
};
// Unsummon Pet
petSystem->Unsummon(petVID);
// Mount Pet
petActor->Mount()..
CPetActor::Update(...)
{
// AI : Follow, actions, etc...
}
*/
#endif //__HEADER_PET_SYSTEM__

399
src/game/SpeedServer.cpp Normal file
View File

@@ -0,0 +1,399 @@
#include "stdafx.h"
#include <time.h>
#include "SpeedServer.h"
#include "locale_service.h"
// 쾌도 서버 보너스 경험치 시스템
// by rtsummit
CSpeedServerManager::CSpeedServerManager()
{
}
CSpeedServerManager::~CSpeedServerManager()
{
}
CSpeedServerEmpireExp::CSpeedServerEmpireExp()
{
}
CSpeedServerEmpireExp::~CSpeedServerEmpireExp()
{
}
bool CSpeedServerManager::Initialize()
{
for (int i = 1; i < EMPIRE_MAX_NUM; i++)
{
sys_log (0,"speed manager init");
if(!Empire[i].Initialize (i))
{
sys_err ("EMPIRE %d Exp Bonus Manager Init fail",i);
return false;
}
}
return true;
}
bool CSpeedServerEmpireExp::Initialize (BYTE e)
{
empire = e;
sys_log (0, "empire exp init %d", empire);
snprintf (file_name, sizeof(file_name), "%s/exp_bonus_table_%d.txt", LocaleService_GetBasePath().c_str(), empire);
for (int i = 1; i < 6; i++)
{
wday_exp_table[i].push_back (HME (18, 0, 50));
wday_exp_table[i].push_back (HME (24, 0, 100));
}
wday_exp_table[0].push_back (HME (18, 0, 100));
wday_exp_table[0].push_back (HME (24, 0, 150));
wday_exp_table[6].push_back (HME (18, 0, 100));
wday_exp_table[6].push_back (HME (24, 0, 150));
LoadExpTable();
return true;
}
bool CSpeedServerEmpireExp::LoadWdayExpTable(int wday, char *str)
{
std::list <HME> &lst = wday_exp_table[wday];
lst.clear();
char *p, *n;
const char *delim = " \t\r\n";
char *t;
char *h, *m, *e;
int hour, min, exp;
sys_log (0, "str %s", str);
strtok (str, delim);
p = strtok (NULL, ";");
n = strtok (NULL, ";");
while (p != NULL)
{
t = strtok (p, delim);
e = strtok (NULL, delim);
h = strtok (t, ":");
m = strtok (NULL, delim);
if (!str_to_number (hour, h) || !str_to_number (min, m) || !str_to_number (exp, e))
{
sys_log (0, "h m e : %s %s %s",h, m, e);
sys_err ("Invalid argument. Please insert hh:mm exp");
return false;
}
sys_log (0, "h m e : %s %s %s",h, m, e);
lst.push_back (HME (hour, min, exp));
p = strtok (n, ";");
n = strtok (NULL, ";");
}
return true;
}
bool CSpeedServerManager::WriteExpTableOfEmpire(BYTE empire)
{
return Empire[empire].WriteExpTable();
}
bool CSpeedServerEmpireExp::WriteExpTable()
{
FILE *fp;
sys_log (0, "write");
if (0==file_name || 0==file_name[0])
return false;
if ((fp = fopen(file_name, "w"))==0)
{
return false;
}
char wday_name[7][4] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
for (int i = 0; i < 7; i++)
{
fprintf (fp, "%s", wday_name[i]);
for (std::list <HME>::iterator it = wday_exp_table[i].begin(); it != wday_exp_table[i].end(); it++)
{
fprintf (fp, " %d:%d %d;", it->hour, it->min, it->exp);
}
fprintf(fp, "\n");
}
for (std::map <Date, std::list <HME> >::iterator holi_it = holiday_map.begin(); holi_it != holiday_map.end(); holi_it++)
{
fprintf (fp, "HOLIDAY %d.%d.%d", holi_it->first.year + 1900, holi_it->first.mon + 1, holi_it->first.day);
for (std::list <HME>::iterator it = holi_it->second.begin(); it != holi_it->second.end(); it++)
{
fprintf (fp, " %d:%d %d;", it->hour, it->min, it->exp);
}
fprintf(fp, "\n");
}
fclose (fp);
return true;
}
bool CSpeedServerEmpireExp::LoadExpTable()
{
FILE *fp;
char one_line[256];
char temp[256];
const char *delim = " \t\r\n";
sys_log (0, "load");
if (0==file_name || 0==file_name[0])
return false;
if ((fp = fopen(file_name, "r"))==0)
return false;
while (fgets(one_line, 256, fp))
{
if (one_line[0]=='#')
continue;
strcpy(temp, one_line);
const char* token_string = strtok(one_line, delim);
if (NULL==token_string)
continue;
TOKEN("SUN")
{
LoadWdayExpTable (0, temp);
}
else TOKEN("MON")
{
LoadWdayExpTable (1, temp);
}
else TOKEN("TUE")
{
LoadWdayExpTable (2, temp);
}
else TOKEN("WED")
{
LoadWdayExpTable (3, temp);
}
else TOKEN("THU")
{
LoadWdayExpTable (4, temp);
}
else TOKEN("FRI")
{
LoadWdayExpTable (5, temp);
}
else TOKEN("SAT")
{
LoadWdayExpTable (6, temp);
}
else TOKEN("HOLIDAY")
{
std::list <HME> lst;
lst.clear();
char *p, *n;
char *t, *v;
char *h, *m, *e;
int hour, min, exp;
v = strtok (temp, delim);
v = strtok (NULL, delim);
sys_log (0, "holiday %s", v);
p = strtok (NULL, ";");
n = strtok (NULL, ";");
while (p != NULL)
{
t = strtok (p, delim);
e = strtok (NULL, delim);
h = strtok (t, ":");
m = strtok (NULL, delim);
if (!str_to_number (hour, h) || !str_to_number (min, m) || !str_to_number (exp, e))
{
sys_log (0, "h m e : %s %s %s",h, m, e);
sys_err ("Invalid argument. Please insert hh:mm exp");
return false;
}
sys_log (0, "h m e : %s %s %s",h, m, e);
lst.push_back (HME (hour, min, exp));
p = strtok (n, ";");
n = strtok (NULL, ";");
}
int year, mon, day;
if (!str_to_number (year, strtok (v, "."))
|| !str_to_number ( mon, strtok (NULL, "."))
|| !str_to_number ( day, strtok (NULL, ".")))
{
sys_err ("Invalid Date");
return false;
}
sys_log (0, "y m d %d %d %d",year, mon, day);
holiday_map.insert (std::pair <Date, std::list <HME> > (Date (year - 1900, mon - 1, day), lst));
}
}
fclose(fp);
return true;
}
std::list <HME>& CSpeedServerManager::GetWdayExpTableOfEmpire(BYTE empire, int wday)
{
return Empire[empire].GetWdayExpTable(wday);
}
std::list <HME>& CSpeedServerEmpireExp::GetWdayExpTable(int wday)
{
return wday_exp_table[wday];
}
void CSpeedServerManager::SetWdayExpTableOfEmpire (BYTE empire, int wday, HME hme)
{
Empire[empire].SetWdayExpTable (wday, hme);
}
void CSpeedServerEmpireExp::SetWdayExpTable (int wday, HME hme)
{
wday_exp_table[wday].push_back (hme);
WriteExpTable();
}
void CSpeedServerManager::InitWdayExpTableOfEmpire (BYTE empire, int wday)
{
if (empire > EMPIRE_MAX_NUM)
{
sys_err ("invalid empire");
return;
}
Empire[empire].InitWdayExpTable (wday);
}
void CSpeedServerEmpireExp::InitWdayExpTable(int wday)
{
wday_exp_table[wday].clear();
}
std::list <HME>& CSpeedServerManager::GetHolidayExpTableOfEmpire(BYTE empire, Date date, bool &is_exist)
{
return Empire[empire].GetHolidayExpTable(date, is_exist);
}
std::list <HME>& CSpeedServerEmpireExp::GetHolidayExpTable(Date date, bool &is_exist)
{
std::map <Date, std::list <HME> >::iterator it = holiday_map.find(date);
if (it != holiday_map.end())
{
is_exist = true;
return it->second;
}
else
{
is_exist = false;
sys_err ("Cannot find Holiday %d %d %d",date.year, date.mon, date.day);
}
return it->second;
}
void CSpeedServerManager::SetHolidayExpTableOfEmpire (BYTE empire, Date date, HME hme)
{
Empire[empire].SetHolidayExpTable (date, hme);
}
void CSpeedServerEmpireExp::SetHolidayExpTable (Date date, HME hme)
{
std::map <Date, std::list <HME> >::iterator it = holiday_map.find(date);
if (it != holiday_map.end())
{
it->second.push_back (hme);
}
WriteExpTable();
}
void CSpeedServerManager::InitHolidayExpTableOfEmpire (BYTE empire, Date date)
{
if (empire > EMPIRE_MAX_NUM)
{
sys_err ("invalid empire");
return;
}
Empire[empire].InitHolidayExpTable (date);
}
void CSpeedServerEmpireExp::InitHolidayExpTable(Date date)
{
sys_log (0, "init holiday");
std::map <Date, std::list <HME> >::iterator it = holiday_map.find(date);
if (it == holiday_map.end())
{
std::list <HME> lst;
holiday_map.insert (std::pair <Date, std::list <HME> > (date, lst));
}
else
{
it->second.clear();
}
}
HME CSpeedServerManager::GetCurrentExpPrivOfEmpire (BYTE empire, int &duration, bool &is_change)
{
return Empire[empire].GetCurrentExpPriv (duration, is_change);
}
HME CSpeedServerEmpireExp::GetCurrentExpPriv(int &duration, bool &is_change)
{
struct tm* datetime;
time_t t;
t = time(NULL);
datetime = localtime(&t);
Date date (datetime -> tm_year, datetime -> tm_mon,
datetime -> tm_mday);
std::map <Date, std::list <HME> >::iterator holi_it = holiday_map.find(date);
int total_sec = datetime->tm_hour * 3600 + datetime->tm_min * 60 + datetime->tm_sec;
HME hme;
// 현재 날짜가 holiday이면 holiday bonus를 도입한다.
if (holi_it != holiday_map.end())
{
for (std::list <HME>::iterator it = holi_it->second.begin();
it != wday_exp_table[datetime->tm_wday].end(); it++)
{
// 현재 시각이 시간 구간 안에 포함되면,
if (total_sec < (it->hour * 3600 + it->min * 60 ))
{
hme = *it;
break;
}
}
}
else
{
for (std::list <HME>::iterator it = wday_exp_table[datetime->tm_wday].begin();
it != wday_exp_table[datetime->tm_wday].end(); it++)
{
// 현재 시각이 시간 구간 안에 포함되면,
if (total_sec < (it->hour * 3600 + it->min * 60 ))
{
hme = *it;
break;
}
}
}
duration = hme.hour * 3600 + hme.min * 60 - total_sec;
is_change = !(hme == current_hme);
current_hme = hme;
return hme;
}

129
src/game/SpeedServer.h Normal file
View File

@@ -0,0 +1,129 @@
#ifndef __INC_METIN_II_GAME_SPEEDSERVER_H__
#define __INC_METIN_II_GAME_SPEEDSERVER_H__
#include "../../common/length.h"
#include <list>
// castle.cpp 에 있는 것을 복붙 하였다
#define EMPIRE_NONE 0 // 아무국가 아님
#define EMPIRE_RED 1 // 신수
#define EMPIRE_YELLOW 2 // 천조
#define EMPIRE_BLUE 3 // 진노
class HME
{
public :
int hour;
int min;
int exp;
HME (int h=0, int m=0, int e=0){
hour = h; min = m;
exp = e;
}
HME& operator=(const HME &rhs)
{
hour = rhs.hour;
min = rhs.min;
exp = rhs.exp;
return *this;
}
bool operator==(const HME &rhs) const
{
return hour == rhs.hour
&& min == rhs.min
&& exp == rhs.exp;
}
bool operator<(const HME &rhs) const
{
return (hour<rhs.hour)
|| (hour==rhs.hour) && (min<rhs.min);
}
};
class Date
{
public :
int year;
int mon;
int day;
Date (int y = 0, int m = 0, int d = 0)
{
year = y; mon = m; day = d;
}
bool operator==(const Date &rhs) const
{
return year == rhs.year
&& mon == rhs.mon
&& day == rhs.day;
}
bool operator<(const Date &rhs) const
{
return (year<rhs.year)
|| (year==rhs.year) && (mon<rhs.mon)
|| (year==rhs.year) && (mon==rhs.mon) && (day<rhs.day);
}
};
class CSpeedServerEmpireExp
{
public :
CSpeedServerEmpireExp();
~CSpeedServerEmpireExp();
bool Initialize (BYTE empire);
std::list <HME>& GetWdayExpTable(int wday);
void SetWdayExpTable(int wday, HME hme);
std::list <HME>& GetHolidayExpTable(Date date, bool &is_exist);
void SetHolidayExpTable(Date date, HME hme);
void InitWdayExpTable(int wday);
void InitHolidayExpTable(Date date);
HME GetCurrentExpPriv (int &duration, bool &is_change);
bool WriteExpTable();
private :
bool LoadExpTable ();
bool LoadWdayExpTable (int wday, char *str);
BYTE empire;
char file_name [256];
HME current_hme;
std::map <Date, std::list <HME> > holiday_map;
std::list <HME> wday_exp_table[7];
};
class CSpeedServerManager : public singleton<CSpeedServerManager>
{
public:
CSpeedServerManager();
~CSpeedServerManager();
bool Initialize ();
std::list <HME>& GetWdayExpTableOfEmpire (BYTE empire, int wday);
void SetWdayExpTableOfEmpire (BYTE empire, int wday, HME hme);
void InitWdayExpTableOfEmpire (BYTE empire, int wday);
std::list <HME>& GetHolidayExpTableOfEmpire (BYTE empire, Date date, bool &is_exist);
void SetHolidayExpTableOfEmpire (BYTE empire, Date date, HME hme);
void InitHolidayExpTableOfEmpire (BYTE empire, Date date);
bool WriteExpTableOfEmpire (BYTE empire);
HME GetCurrentExpPrivOfEmpire (BYTE empire, int &duration, bool &is_change);
private:
CSpeedServerEmpireExp Empire[EMPIRE_MAX_NUM];
};
#endif

View File

@@ -0,0 +1,92 @@
/**
*
* @file TrafficProfiler.cpp
* @brief TrafficProfiler class implementation file
* @author Bang2ni
* @version 05/07/07 Bang2ni - First release.
*
*/
#include "stdafx.h"
#include "TrafficProfiler.h"
TrafficProfiler::TrafficProfiler()
: m_pfProfileLogFile(NULL), m_dwFlushCycle(0), m_tmProfileStartTime(0), m_dwTotalTraffic(0), m_dwTotalPacket(0)
{
m_aTrafficVec[ 0 ].resize( 256 );
m_aTrafficVec[ 1 ].resize( 256 );
}
TrafficProfiler::~TrafficProfiler()
{
if ( m_pfProfileLogFile )
fclose( m_pfProfileLogFile );
}
bool TrafficProfiler::Initialize( DWORD dwFlushCycle, const char* pszFileName )
{
m_pfProfileLogFile = fopen( pszFileName, "w" );
if ( !m_pfProfileLogFile )
return false;
m_dwFlushCycle = dwFlushCycle;
InitializeProfiling();
return true;
}
bool TrafficProfiler::Flush()
{
if ( !m_pfProfileLogFile )
return false;
//
// Profling result write to file
//
fprintf( m_pfProfileLogFile, "# Profile Start: %s", ctime( &m_tmProfileStartTime ) );
fprintf( m_pfProfileLogFile, "Total traffic: %u bytes\n", m_dwTotalTraffic );
fprintf( m_pfProfileLogFile, "Total used packet: %u\n", m_dwTotalPacket );
fprintf( m_pfProfileLogFile, "------------------ Input ------------------\n" );
for ( int idx = 0; idx < (int)IODIR_MAX; idx++ )
{
fprintf( m_pfProfileLogFile, "Packet\tCount\tTotal Size\tAverage\n" );
BYTE byHeader = 0;
for ( TrafficVec::iterator it = m_aTrafficVec[ idx ].begin(); it != m_aTrafficVec[ idx ].end(); ++it, byHeader++ )
{
if ( it->second )
fprintf( m_pfProfileLogFile, "%d\t%u\t%u\t\t%u\n", byHeader, it->second, it->first, it->first / it->second );
}
fprintf( m_pfProfileLogFile, "------------------ Output -----------------\n" );
}
time_t cur = time( NULL );
fprintf( m_pfProfileLogFile, "# Profile End(Flush): %s", ctime( &cur ) );
fflush( m_pfProfileLogFile );
//
// Initialization
//
InitializeProfiling();
return true;
}
void TrafficProfiler::InitializeProfiling()
{
m_tmProfileStartTime = time( NULL );
m_dwTotalPacket = 0;
m_dwTotalTraffic = 0;
TrafficInfo empty( 0, 0 );
for ( int idx = 0; idx < (int)IODIR_MAX; idx++ )
{
for ( TrafficVec::iterator it = m_aTrafficVec[ idx ].begin(); it != m_aTrafficVec[ idx ].end(); ++it )
*it = empty;
}
}

115
src/game/TrafficProfiler.h Normal file
View File

@@ -0,0 +1,115 @@
/**
*
* @file TrafficProfiler.h
* @brief TrafficProfiler class definition file
* @author Bang2ni
* @version 05/07/07 Bang2ni - First release.
*
*/
#ifndef _METIN_II_TRAFFICPROFILER_H_
#define _METIN_II_TRAFFICPROFILER_H_
/**
* @class TrafficProfiler
* @brief Network I/O traffic 을 패킷 단위로 측정하는 profiler.
* @author Bang2ni
* @version 05/07/07 Bang2ni - First release.
*
* 시간대 별로 Network I/O 의 traffic 을 패킷 단위로 측정하고, Text file 형태로 보고서를 작성한다.
*/
class TrafficProfiler : public singleton< TrafficProfiler >
{
public:
/// I/O 방향
enum IODirection {
IODIR_INPUT = 0, ///< Input
IODIR_OUTPUT, ///< Output
IODIR_MAX
};
public:
/// Constructor
TrafficProfiler( void );
/// Destructor
~TrafficProfiler( void );
/// Profiling 에 필요한 초기화를 한다.
/**
* @param [in] dwFlushCycle Flush 주기. 초 단위이다.
* @param [in] pszLogFileName Profiling log file 의 이름
* @return false 일 경우 profiling log file 을 open 하지 못했다.
*
* profiling log file 을 open(생성) 한다.
*/
bool Initialize( DWORD dwFlushCycle, const char* pszLogFileName );
/// Profiling 을 위해 전송됐거나 전송 할 Packet 을 Report 한다.
/**
* @param [in] dir Profiling 할 Packet 의 방향
* @param [in] byHeader Packet 헤더
* @param [in] dwSize Packet 의 총 size
* @return Initialize 되지 않았다면 false 를 반환한다.
*
* Packet 에 해당하는 size 를 누적시킨다.
* Initialize 이후나 최근 Flush 된 이후에 Flush 주기 만큼 시간이 흐른 후 호출된다면 Report 이후 Flush 한다.
*/
bool Report( IODirection dir, BYTE byHeader, DWORD dwSize )
{
ComputeTraffic( dir, byHeader, dwSize );
if ( (DWORD)(time( NULL ) - m_tmProfileStartTime) >= m_dwFlushCycle )
return Flush();
return true;
}
/// 현재까지 Report 된 내용을 파일에 쓴다.
/**
* @return Initialize 되지 않았다.
*/
bool Flush( void );
private:
/// Profling 에 관련된 variables 를 초기화 한다.
void InitializeProfiling( void );
/// Report 된 Packet 의 traffic 를 계산한다.
/**
* @param [in] dir Profiling 할 Packet 의 방향
* @param [in] byHeader Packet 헤더
* @param [in] dwSize Packet 의 총 size
*/
void ComputeTraffic( IODirection dir, BYTE byHeader, DWORD dwSize )
{
TrafficInfo& rTrafficInfo = m_aTrafficVec[ dir ][ byHeader ];
m_dwTotalTraffic += dwSize;
m_dwTotalPacket += !rTrafficInfo.second;
rTrafficInfo.first += dwSize;
rTrafficInfo.second++;
}
/// Traffic info type.
/**
* first: 누적된 총 size
* second: 이 packet 이 전송된 횟수
*/
typedef std::pair< DWORD, DWORD > TrafficInfo;
/// Traffic info vector.
typedef std::vector< TrafficInfo > TrafficVec;
FILE* m_pfProfileLogFile; ///< Profile log file pointer
DWORD m_dwFlushCycle; ///< Flush 주기
time_t m_tmProfileStartTime; ///< 프로파일을 시작한 시간. Flush 될 때마다 Update 된다.
DWORD m_dwTotalTraffic; ///< Report 된 총 Traffic 용량
DWORD m_dwTotalPacket; ///< Report 된 총 Packet 수
TrafficVec m_aTrafficVec[ IODIR_MAX ]; ///< Report 된 Traffic 을 저장할 vector의 배열. 각 방향마다 vector 를 가진다.
};
#endif // _METIN_II_TRAFFICPROFILER_H_

329
src/game/XTrapManager.cpp Normal file
View File

@@ -0,0 +1,329 @@
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)
#include <io.h>
#include <windows.h>
#include <tchar.h>
#else
#include <dlfcn.h>
#include <unistd.h>
#endif
#include <XTrap_S_Interface.h>
#include "char.h"
#include "config.h"
#include "event.h"
#include "log.h"
#include "desc.h"
#include "packet.h"
#include "XTrapManager.h"
#define CSFILE_NUM 2
#define XTRAP_CS1_CHECK_CYCLE PASSES_PER_SEC(20) // per 20sec
unsigned char g_XTrap_ClientMap[CSFILE_NUM][XTRAP_CS4_BUFSIZE_MAP];
struct CXTrapManager::sXTrapContext
{
//API function pointers
PFN_XTrap_S_Start XTrap_S_Start;
PFN_XTrap_S_SessionInit XTrap_S_SessionInit;
PFN_XTrap_CS_Step1 XTrap_CS_Step1;
PFN_XTrap_CS_Step3 XTrap_CS_Step3;
PFN_XTrap_S_SetActiveCode XTrap_S_SetActiveCode;
PFN_XTrap_S_SetOption XTrap_S_SetOption;
PFN_XTrap_S_SetAllowDelay XTrap_S_SetAllowDelay;
PFN_XTrap_S_SendGamePacket XTrap_S_SendGamePacket;
PFN_XTrap_S_RecvGamePacket XTrap_S_RecvGamePacket;
//handle
void* hXTrap4Server;
};
CXTrapManager::CXTrapManager()
{
m_pImpl = M2_NEW sXTrapContext;
memset( m_pImpl, 0x00, sizeof(sXTrapContext) );
}
CXTrapManager::~CXTrapManager()
{
#ifdef __FreeBSD__
if (m_pImpl->hXTrap4Server)
{
dlclose(m_pImpl->hXTrap4Server);
}
#endif
M2_DELETE(m_pImpl);
}
#ifdef __FreeBSD__
void CXTrapManager::MapReloadSignalHandler( int signal )
{
for(int i=0; i<CSFILE_NUM; ++i )
{
if( Instance().LoadClientMapFile(i) )
sys_log(0, "client map file(map%d).CS3 is reloaded", i+1 );
}
}
void CXTrapManager::NotifyMapFileChanged( const std::string& fileName, eFileUpdatedOptions eUpdateOption )
{
MapReloadSignalHandler(1);
}
#endif
bool CXTrapManager::LoadXTrapModule()
{
#ifdef __FreeBSD__
//first load client mapfile
bool bClientMapFileLoaded = false;
for(int i=0; i<CSFILE_NUM; ++i )
{
if( LoadClientMapFile(i) )
{
bClientMapFileLoaded = true;
}
}
if( !bClientMapFileLoaded )
{
sys_err("XTrap-failed to load at least one client map file. map file name should be map1.CS3 or map2.CS3");
return false;
}
//load shared objects
char sDllBinFile[] ="./libXTrap4Server.so";
m_pImpl->hXTrap4Server = dlopen(sDllBinFile, RTLD_LAZY);
if (m_pImpl->hXTrap4Server == 0)
{
sys_err("XTrap-failed to load so reason:%s", dlerror()) ;
return false;
}
void* hXTrapHandle = m_pImpl->hXTrap4Server;
m_pImpl->XTrap_S_Start = (PFN_XTrap_S_Start) dlsym(hXTrapHandle, "XTrap_S_Start");
m_pImpl->XTrap_S_SessionInit = (PFN_XTrap_S_SessionInit) dlsym(hXTrapHandle, "XTrap_S_SessionInit");
m_pImpl->XTrap_CS_Step1 = (PFN_XTrap_CS_Step1) dlsym(hXTrapHandle, "XTrap_CS_Step1");
m_pImpl->XTrap_CS_Step3 = (PFN_XTrap_CS_Step3) dlsym(hXTrapHandle, "XTrap_CS_Step3");
m_pImpl->XTrap_S_SetActiveCode = (PFN_XTrap_S_SetActiveCode) dlsym(hXTrapHandle, "XTrap_S_SetActiveCode");
m_pImpl->XTrap_S_SetOption = (PFN_XTrap_S_SetOption) dlsym(hXTrapHandle, "XTrap_S_SetOption");
m_pImpl->XTrap_S_SetAllowDelay = (PFN_XTrap_S_SetAllowDelay) dlsym(hXTrapHandle, "XTrap_S_SetAllowDelay");
m_pImpl->XTrap_S_SendGamePacket = (PFN_XTrap_S_SendGamePacket) dlsym(hXTrapHandle, "XTrap_S_SendGamePacket");
m_pImpl->XTrap_S_RecvGamePacket = (PFN_XTrap_S_RecvGamePacket) dlsym(hXTrapHandle, "XTrap_S_RecvGamePacket");
if (m_pImpl->XTrap_S_Start == NULL ||
m_pImpl->XTrap_S_SessionInit == NULL ||
m_pImpl->XTrap_CS_Step1 == NULL ||
m_pImpl->XTrap_CS_Step3 == NULL ||
m_pImpl->XTrap_S_SetOption == NULL ||
m_pImpl->XTrap_S_SetAllowDelay == NULL ||
m_pImpl->XTrap_S_SendGamePacket == NULL ||
m_pImpl->XTrap_S_RecvGamePacket == NULL)
{
sys_err("XTrap-failed to load function ptrs");
return false;
}
//start server module
m_pImpl->XTrap_S_Start( 600, CSFILE_NUM, g_XTrap_ClientMap, NULL );
//NOTE : 일단 XProtect모듈에 버그가 있어서 코드영역 체크를 끈다.
m_pImpl->XTrap_S_SetActiveCode( XTRAP_ACTIVE_CODE_THEMIDA );
//setup signal
signal(SIGUSR2, CXTrapManager::MapReloadSignalHandler);
#endif
return true;
}
bool CXTrapManager::LoadClientMapFile( unsigned int iMapIndex )
{
#ifdef __FreeBSD__
//index check
if( iMapIndex >= CSFILE_NUM )
{
return false;
}
char szFileName[1024] = {0,};
snprintf(szFileName, sizeof(szFileName), "map%d.CS3", iMapIndex+1);
FILE* fi = 0;
fi = fopen(szFileName, "rb");
if (fi == NULL)
{
return false;
}
fread(g_XTrap_ClientMap[iMapIndex], XTRAP_CS4_BUFSIZE_MAP, 1, fi);
fclose(fi);
#endif
return true;
}
EVENTINFO(xtrap_cs1_check_info)
{
DynamicCharacterPtr ptrPC;
};
EVENTFUNC(xtrap_cs1_check_event)
{
xtrap_cs1_check_info* info = dynamic_cast<xtrap_cs1_check_info*>( event->info );
if ( info == NULL )
{
sys_err( "<xtrap_event> info null pointer" );
return 0;
}
TPacketXTrapCSVerify pack;
pack.bHeader = HEADER_GC_XTRAP_CS1_REQUEST;
bool bSuccess = CXTrapManager::instance().Verify_CSStep1( info->ptrPC, pack.bPacketData );
LPDESC lpClientDesc = info->ptrPC.Get()->GetDesc();
if( !lpClientDesc )
{
sys_err( "<xtrap_event> client session is invalid" );
return 0;
}
lpClientDesc->Packet( &pack, sizeof(pack) );
if( bSuccess )
{
return XTRAP_CS1_CHECK_CYCLE;
}
sys_err( "XTrap: hack is detected %s", lpClientDesc->GetHostName() );
info->ptrPC.Get()->Disconnect("XTrapCheckInvalid");
lpClientDesc->SetPhase(PHASE_CLOSE);
return 0;
}
bool CXTrapManager::CreateClientSession( LPCHARACTER lpCharSession )
{
if( !bXTrapEnabled )
return true;
if( !lpCharSession )
return false;
DWORD dwSessionID = lpCharSession->GetPlayerID();
ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID );
if( it != m_mapClientSessions.end() )
{
sys_err("XTrap: client session is alreay registered");
return false;
}
//init session info
sSessionInfo infoData;
//xtrap session init
DWORD dwReturn = m_pImpl->XTrap_S_SessionInit( 600, CSFILE_NUM, g_XTrap_ClientMap, infoData.szSessionBuf );
if( dwReturn != 0 )
{
sys_err("XTrap: client session init failed");
}
xtrap_cs1_check_info* event_info = AllocEventInfo<xtrap_cs1_check_info>();
event_info->ptrPC = lpCharSession;
infoData.m_pCheckEvent = event_create(xtrap_cs1_check_event, event_info, XTRAP_CS1_CHECK_CYCLE);
m_mapClientSessions[dwSessionID] = infoData;
return true;
}
void CXTrapManager::DestroyClientSession( LPCHARACTER lpCharSession )
{
if( !bXTrapEnabled )
return;
if( !lpCharSession )
return;
DWORD dwSessionID = lpCharSession->GetPlayerID();
ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID );
if( it == m_mapClientSessions.end() )
{
sys_err("XTrap: client session is already destroyed");
return;
}
event_cancel(&(it->second.m_pCheckEvent) );
m_mapClientSessions.erase(it);
}
bool CXTrapManager::Verify_CSStep1( LPCHARACTER lpCharSession, BYTE* pBufData )
{
if( !bXTrapEnabled )
return false;
if( !lpCharSession )
return false;
DWORD dwSessionID = lpCharSession->GetPlayerID();
ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID );
if( it == m_mapClientSessions.end() )
{
sys_err("XTrap: client session is already destroyed");
return false;
}
int nReturn = m_pImpl->XTrap_CS_Step1( it->second.szSessionBuf, it->second.szPackBuf );
memcpy( pBufData, it->second.szPackBuf, VERIFY_PACK_LEN );
return (nReturn == 0) ? true : false;
}
void CXTrapManager::Verify_CSStep3( LPCHARACTER lpCharSession, BYTE* pBufData )
{
if( !bXTrapEnabled )
return;
if( !lpCharSession )
return;
DWORD dwSessionID = lpCharSession->GetPlayerID();
ClientSessionMap::iterator it = m_mapClientSessions.find( dwSessionID );
if( it == m_mapClientSessions.end() )
{
sys_log(0, "XTrap: client session is alreay destroyed");
return;
}
memcpy( it->second.szPackBuf, pBufData, VERIFY_PACK_LEN );
m_pImpl->XTrap_CS_Step3( it->second.szSessionBuf, it->second.szPackBuf );
//if( XTRAP_API_RETURN_DETECTHACK == m_pImpl->XTrap_CS_Step3( it->second.szSessionBuf, pBufData ) )
//{
// sys_error(0, "XTrap: client session is alreay destroyed");
//}
}

67
src/game/XTrapManager.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef _XTRAP_MANAGER_H_
#define _XTRAP_MANAGER_H_
#include "IFileMonitor.h"
#define SESSION_BUF_LEN 320
#define VERIFY_PACK_LEN 128
#define SESSION_CSSTEP1_LEN 256
#pragma pack(1)
typedef struct PacketXTrapVerify
{
BYTE bHeader;
BYTE bPacketData[VERIFY_PACK_LEN];
} TPacketXTrapCSVerify;
#pragma pack()
class CXTrapManager : public singleton<CXTrapManager>
{
public:
CXTrapManager();
virtual ~CXTrapManager();
bool LoadXTrapModule();
bool LoadClientMapFile( unsigned int iMapIndex );
bool CreateClientSession( LPCHARACTER lpCharSession );
void DestroyClientSession( LPCHARACTER lpCharSession );
bool Verify_CSStep1( LPCHARACTER lpCharSession, BYTE* pOutBufData );
void Verify_CSStep3( LPCHARACTER lpCharSession, BYTE* pBufData );
#ifdef __FreeBSD__
static void MapReloadSignalHandler( int signal );
static void NotifyMapFileChanged( const std::string& fileName, eFileUpdatedOptions eUpdateOption );
#endif
private:
//pimpl`s idiom
struct sXTrapContext;
sXTrapContext* m_pImpl;
struct sSessionInfo
{
sSessionInfo()
{
m_pCheckEvent = NULL;
memset(szSessionBuf, 0x00, sizeof(szSessionBuf) );
memset(szPackBuf, 0x00, sizeof(szPackBuf) );
}
BYTE szSessionBuf[SESSION_BUF_LEN];
BYTE szPackBuf[VERIFY_PACK_LEN];
LPEVENT m_pCheckEvent;
};
typedef std::unordered_map<DWORD, sSessionInfo> ClientSessionMap;
ClientSessionMap m_mapClientSessions;
};
#endif /* _XTRAP_MANAGER_H_ */

31
src/game/affect.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "stdafx.h"
#ifndef DEBUG_ALLOC
#include <boost/pool/object_pool.hpp>
#endif
#include "affect.h"
#ifndef DEBUG_ALLOC
boost::object_pool<CAffect> affect_pool;
#endif
CAffect* CAffect::Acquire()
{
#ifndef DEBUG_ALLOC
return affect_pool.malloc();
#else
return M2_NEW CAffect;
#endif
}
void CAffect::Release(CAffect* p)
{
#ifndef DEBUG_ALLOC
affect_pool.free(p);
#else
M2_DELETE(p);
#endif
}

183
src/game/affect.h Normal file
View File

@@ -0,0 +1,183 @@
#ifndef __INC_AFFECT_H
#define __INC_AFFECT_H
class CAffect
{
public:
DWORD dwType;
BYTE bApplyOn;
long lApplyValue;
DWORD dwFlag;
long lDuration;
long lSPCost;
static CAffect* Acquire();
static void Release(CAffect* p);
};
enum EAffectTypes
{
AFFECT_NONE,
AFFECT_MOV_SPEED = 200,
AFFECT_ATT_SPEED,
AFFECT_ATT_GRADE,
AFFECT_INVISIBILITY,
AFFECT_STR,
AFFECT_DEX, // 205
AFFECT_CON,
AFFECT_INT,
AFFECT_FISH_MIND_PILL,
AFFECT_POISON,
AFFECT_STUN, // 210
AFFECT_SLOW,
AFFECT_DUNGEON_READY,
AFFECT_DUNGEON_UNIQUE,
AFFECT_BUILDING,
AFFECT_REVIVE_INVISIBLE, // 215
AFFECT_FIRE,
AFFECT_CAST_SPEED,
AFFECT_HP_RECOVER_CONTINUE,
AFFECT_SP_RECOVER_CONTINUE,
AFFECT_POLYMORPH, // 220
AFFECT_MOUNT,
AFFECT_WAR_FLAG, // 222
AFFECT_BLOCK_CHAT, // 223
AFFECT_CHINA_FIREWORK,
AFFECT_BOW_DISTANCE, // 225
AFFECT_DEF_GRADE, // 226
AFFECT_PREMIUM_START = 500,
AFFECT_EXP_BONUS = 500, // 경험의 반지
AFFECT_ITEM_BONUS = 501, // 도둑의 장갑
AFFECT_SAFEBOX = 502, // PREMIUM_SAFEBOX,
AFFECT_AUTOLOOT = 503, // PREMIUM_AUTOLOOT,
AFFECT_FISH_MIND = 504, // PREMIUM_FISH_MIND,
AFFECT_MARRIAGE_FAST = 505, // 원앙의 깃털
AFFECT_GOLD_BONUS = 506, // 돈 드롭확률 50%증가
AFFECT_PREMIUM_END = 509,
AFFECT_MALL = 510, // 몰 아이템 에펙트
AFFECT_NO_DEATH_PENALTY = 511, // 용신의 가호 (경험치가 패널티를 한번 막아준다)
AFFECT_SKILL_BOOK_BONUS = 512, // 선인의 교훈 (책 수련 성공 확률이 50% 증가)
AFFECT_SKILL_NO_BOOK_DELAY = 513, // 주안술서
AFFECT_HAIR = 514, // 헤어 효과
AFFECT_COLLECT = 515, //수집퀘스트
AFFECT_EXP_BONUS_EURO_FREE = 516, // 경험의 반지 (유럽 버전 14 레벨 이하 기본 효과)
AFFECT_EXP_BONUS_EURO_FREE_UNDER_15 = 517,
AFFECT_UNIQUE_ABILITY = 518,
AFFECT_CUBE_1,
AFFECT_CUBE_2,
AFFECT_CUBE_3,
AFFECT_CUBE_4,
AFFECT_CUBE_5,
AFFECT_CUBE_6,
AFFECT_CUBE_7,
AFFECT_CUBE_8,
AFFECT_CUBE_9,
AFFECT_CUBE_10,
AFFECT_CUBE_11,
AFFECT_CUBE_12,
AFFECT_BLEND,
AFFECT_HORSE_NAME,
AFFECT_MOUNT_BONUS,
AFFECT_AUTO_HP_RECOVERY = 534,
AFFECT_AUTO_SP_RECOVERY = 535,
AFFECT_DRAGON_SOUL_QUALIFIED = 540,
AFFECT_DRAGON_SOUL_DECK_0 = 541,
AFFECT_DRAGON_SOUL_DECK_1 = 542,
AFFECT_RAMADAN_ABILITY = 300,
AFFECT_RAMADAN_RING = 301,
AFFECT_NOG_ABILITY = 302,
AFFECT_HOLLY_STONE_POWER = 303,
AFFECT_QUEST_START_IDX = 1000
};
enum EAffectBits
{
AFF_NONE,
AFF_YMIR,
AFF_INVISIBILITY,
AFF_SPAWN,
AFF_POISON,
AFF_SLOW,
AFF_STUN,
AFF_DUNGEON_READY, // 던전에서 준비 상태
AFF_DUNGEON_UNIQUE, // 던전 유니크 (클라이언트에서 컬링되지않음)
AFF_BUILDING_CONSTRUCTION_SMALL,
AFF_BUILDING_CONSTRUCTION_LARGE,
AFF_BUILDING_UPGRADE,
AFF_MOV_SPEED_POTION,
AFF_ATT_SPEED_POTION,
AFF_FISH_MIND,
AFF_JEONGWIHON, // 전귀혼
AFF_GEOMGYEONG, // 검경
AFF_CHEONGEUN, // 천근추
AFF_GYEONGGONG, // 경공술
AFF_EUNHYUNG, // 은형법
AFF_GWIGUM, // 귀검
AFF_TERROR, // 공포
AFF_JUMAGAP, // 주마갑
AFF_HOSIN, // 호신
AFF_BOHO, // 보호
AFF_KWAESOK, // 쾌속
AFF_MANASHIELD, // 마나쉴드
AFF_MUYEONG, // 무영진 affect
AFF_REVIVE_INVISIBLE, // 부활시 잠시동안 무적
AFF_FIRE, // 지속 불 데미지
AFF_GICHEON, // 기천대공
AFF_JEUNGRYEOK, // 증력술
AFF_TANHWAN_DASH, // 탄환격용 달리기어펙트
AFF_PABEOP, // 파법술
AFF_CHEONGEUN_WITH_FALL, // 천근추
AFF_POLYMORPH,
AFF_WAR_FLAG1,
AFF_WAR_FLAG2,
AFF_WAR_FLAG3,
AFF_CHINA_FIREWORK,
AFF_HAIR, // 헤어
AFF_GERMANY, // 독일
AFF_BITS_MAX
};
extern void SendAffectAddPacket(LPDESC d, CAffect * pkAff);
// AFFECT_DURATION_BUG_FIX
enum AffectVariable
{
// Affect가 무한대로 들어가 있어야 할 경우 사용.
// 시간을 계속 줄이기 때문에 매우 큰값으로 무한대를 에뮬레이션함.
//// 24비트는 적으므로 25비트를 사용.
// ... 25비트 사용한다고 해놓고선 29bit 사용하고 있는 엄청난 주석이란...
// collect quest에서 무한 시간을 60년으로 사용하고 있으므로, 여기도 60년으로 하자.
INFINITE_AFFECT_DURATION = 60 * 365 * 24 * 60 * 60
};
// END_AFFECT_DURATION_BUG_FIX
#endif

69
src/game/affect_flag.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef __INC_METIN_II_AFFECT_FLAG_H__
#define __INC_METIN_II_AFFECT_FLAG_H__
#ifndef IS_SET
#define IS_SET(flag, bit) ((flag) & (bit))
#endif
#ifndef SET_BIT
#define SET_BIT(var, bit) ((var) |= (bit))
#endif
#ifndef REMOVE_BIT
#define REMOVE_BIT(var, bit) ((var) &= ~(bit))
#endif
#ifndef TOGGLE_BIT
#define TOGGLE_BIT(var, bit) ((var) = (var) ^ (bit))
#endif
struct TAffectFlag
{
DWORD bits[2];
inline TAffectFlag() { bits[0] = 0; bits[1] = 0; }
inline TAffectFlag(DWORD v1, DWORD v2 = 0) {bits[0] = v1; bits[1] = v2;}
inline bool IsSet(int flag) const
{
if (AFF_BITS_MAX <= flag || 0 >= flag)
return false;
return IS_SET(bits[(flag - 1) >> 5], (((DWORD)1) << ((flag - 1) & 31)));
}
inline void Set(int flag)
{
if (AFF_BITS_MAX <= flag || 0 >= flag)
return;
SET_BIT(bits[(flag-1)>>5], (((DWORD)1)<<((flag-1)&31)));
}
inline void Reset(int flag)
{
if (AFF_BITS_MAX <= flag || 0 >= flag)
return;
REMOVE_BIT(bits[(flag-1)>>5], (((DWORD)1)<<((flag-1)&31)));
}
inline TAffectFlag& operator = (const TAffectFlag& rhs)
{
bits[0] = rhs.bits[0];
bits[1] = rhs.bits[1];
return *this;
}
};
inline bool operator == (const TAffectFlag& lhs, const TAffectFlag& rhs)
{
return lhs.bits[0] == rhs.bits[0] && lhs.bits[1] == rhs.bits[1];
}
inline bool operator != (const TAffectFlag& lhs, const TAffectFlag& rhs)
{
return !(lhs == rhs);
}
#endif

371
src/game/ani.cpp Normal file
View File

@@ -0,0 +1,371 @@
/*********************************************************************
* date : 2007.11.16
* file : ani.cpp
* author : mhh
* description :
*/
#define _ani_cpp_
#include "stdafx.h"
#include "char.h"
#include "item.h"
#include "ani.h"
#include "dev_log.h"
const char* FN_race_name(int race)
{
#define FN_NAME(race) case race: return #race
switch (race)
{
FN_NAME(MAIN_RACE_WARRIOR_M);
FN_NAME(MAIN_RACE_ASSASSIN_W);
FN_NAME(MAIN_RACE_SURA_M);
FN_NAME(MAIN_RACE_SHAMAN_W);
FN_NAME(MAIN_RACE_WARRIOR_W);
FN_NAME(MAIN_RACE_ASSASSIN_M);
FN_NAME(MAIN_RACE_SURA_W);
FN_NAME(MAIN_RACE_SHAMAN_M);
FN_NAME(MAIN_RACE_MAX_NUM);
}
return "UNKNOWN";
#undef FN_NAME
}
const char* FN_weapon_type(int weapon)
{
#define FN_NAME(weapon) case weapon: return #weapon
switch (weapon)
{
FN_NAME(WEAPON_SWORD);
FN_NAME(WEAPON_DAGGER);
FN_NAME(WEAPON_BOW);
FN_NAME(WEAPON_TWO_HANDED);
FN_NAME(WEAPON_BELL);
FN_NAME(WEAPON_FAN);
FN_NAME(WEAPON_ARROW);
FN_NAME(WEAPON_MOUNT_SPEAR);
FN_NAME(WEAPON_NUM_TYPES);
}
return "UNKNOWN";
#undef FN_NAME
}
class ANI
{
protected:
// [종족][일반0탈것1][무기][콤보]
DWORD m_speed[MAIN_RACE_MAX_NUM][2][WEAPON_NUM_TYPES][9];
public:
ANI();
public:
bool load();
bool load_one_race(int race, const char *dir_name);
DWORD load_one_weapon(const char *dir_name, int weapon, BYTE combo, bool horse);
DWORD attack_speed(int race, int weapon, BYTE combo = 0, bool horse = false);
void print_attack_speed();
};
static class ANI s_ANI;
DWORD FN_attack_speed_from_file(const char *file)
{
FILE * fp = fopen(file, "r");
if (NULL == fp)
return 0;
int speed = 1000;
const char *key = "DirectInputTime";
const char *delim = " \t\r\n";
const char *field, *value;
char buf[1024];
while (fgets(buf, 1024, fp))
{
field = strtok(buf, delim);
value = strtok(NULL, delim);
if (field && value)
{
if (0 == strcasecmp(field, key))
{
float f_speed = strtof(value, NULL);
speed = (int) (f_speed * 1000.0);
break;
}
}
}
fclose(fp);
return speed;
}
ANI::ANI()
{
// set default value
for (int race = 0; race < MAIN_RACE_MAX_NUM; ++race)
{
for (int weapon = 0; weapon < WEAPON_NUM_TYPES; ++weapon)
{
for (BYTE combo = 0; combo <= 8; ++combo)
{
m_speed[race][0][weapon][combo] = 1000;
m_speed[race][1][weapon][combo] = 1000;
}
}
}
}
bool ANI::load()
{
const char* dir_name[MAIN_RACE_MAX_NUM] = {
"data/pc/warrior", // 무사(남)
"data/pc/assassin", // 자객(여)
"data/pc/sura", // 수라(남)
"data/pc/shaman", // 무당(여)
"data/pc2/warrior", // 무사(여)
"data/pc2/assassin", // 자객(남)
"data/pc2/sura", // 수라(여)
"data/pc2/shaman" // 무당(남)
};
for (int race = 0; race <MAIN_RACE_MAX_NUM; ++race)
{
if (false == load_one_race(race, dir_name[race]))
{
sys_err("ANI directory = %s", dir_name[race]);
return false;
}
}
return true;
}
DWORD ANI::load_one_weapon(const char *dir_name, int weapon, BYTE combo, bool horse)
{
char format[128];
char filename[256];
switch (weapon)
{
case WEAPON_SWORD:
strlcpy(format, "%s/%sonehand_sword/combo_%02d.msa", sizeof(format));
break;
case WEAPON_DAGGER:
strlcpy(format, "%s/%sdualhand_sword/combo_%02d.msa", sizeof(format));
break;
case WEAPON_BOW:
strlcpy(format, "%s/%sbow/attack.msa", sizeof(format));
break;
case WEAPON_TWO_HANDED:
strlcpy(format, "%s/%stwohand_sword/combo_%02d.msa", sizeof(format));
break;
case WEAPON_BELL:
strlcpy(format, "%s/%sbell/combo_%02d.msa", sizeof(format));
break;
case WEAPON_FAN:
strlcpy(format, "%s/%sfan/combo_%02d.msa", sizeof(format));
break;
default:
return 1000;
}
snprintf(filename, sizeof(filename), format, dir_name, horse ? "horse_" : "", combo);
DWORD speed = FN_attack_speed_from_file(filename);
if (speed == 0)
return 1000;
return speed;
}
bool ANI::load_one_race(int race, const char *dir_name)
{
if (NULL == dir_name || '\0' == dir_name[0])
return false;
for (int weapon = WEAPON_SWORD; weapon < WEAPON_NUM_TYPES; ++weapon)
{
dev_log(LOG_DEB0, "ANI (%s,%s)", FN_race_name(race), FN_weapon_type(weapon));
for (BYTE combo = 1; combo <= 8; ++combo)
{
// 말 안탔을 때
m_speed[race][0][weapon][combo] = load_one_weapon(dir_name, weapon, combo, false);
m_speed[race][0][weapon][0] = MIN(m_speed[race][0][weapon][0], m_speed[race][0][weapon][combo]); // 최소값
// 말 탔을 때
m_speed[race][1][weapon][combo] = load_one_weapon(dir_name, weapon, combo, true);
m_speed[race][1][weapon][0] = MIN(m_speed[race][1][weapon][0], m_speed[race][1][weapon][combo]); // 최소값
dev_log(LOG_DEB0, "combo%02d speed=%d horse=%d",
combo, m_speed[race][0][weapon][combo], m_speed[race][1][weapon][combo]);
}
dev_log(LOG_DEB0, "minspeed=%u", m_speed[race][0][weapon][0]);
}
return true;
}
DWORD ANI::attack_speed(int race, int weapon, BYTE combo, bool horse)
{
switch (race)
{
case MAIN_RACE_WARRIOR_M:
case MAIN_RACE_ASSASSIN_W:
case MAIN_RACE_SURA_M:
case MAIN_RACE_SHAMAN_W:
case MAIN_RACE_WARRIOR_W:
case MAIN_RACE_ASSASSIN_M:
case MAIN_RACE_SURA_W:
case MAIN_RACE_SHAMAN_M:
break;
default:
return 1000;
}
switch (weapon)
{
case WEAPON_SWORD:
case WEAPON_DAGGER:
case WEAPON_BOW:
case WEAPON_TWO_HANDED:
case WEAPON_BELL:
case WEAPON_FAN:
case WEAPON_ARROW:
case WEAPON_MOUNT_SPEAR:
break;
default:
return 1000;
}
return m_speed[race][horse ? 1 : 0][weapon][combo];
}
const char* FN_race_string(int race)
{
switch (race)
{
case MAIN_RACE_WARRIOR_M: return "WARRIOR_M";
case MAIN_RACE_ASSASSIN_W: return "ASSASSIN_W";
case MAIN_RACE_SURA_M: return "SURA_M";
case MAIN_RACE_SHAMAN_W: return "SHAMAN_W";
case MAIN_RACE_WARRIOR_W: return "WARRIOR_W";
case MAIN_RACE_ASSASSIN_M: return "ASSASSIN_M";
case MAIN_RACE_SURA_W: return "SURA_W";
case MAIN_RACE_SHAMAN_M: return "SHAMAN_M";
}
return "UNKNOWN_RACE";
}
const char* FN_weapon_string(int weapon)
{
switch (weapon)
{
case WEAPON_SWORD: return "SWORD";
case WEAPON_DAGGER: return "DAGGER";
case WEAPON_BOW: return "BOW";
case WEAPON_TWO_HANDED: return "TWO_HANDED";
case WEAPON_BELL: return "BELL";
case WEAPON_FAN: return "FAN";
case WEAPON_ARROW: return "ARROW";
case WEAPON_MOUNT_SPEAR:return "WEAPON_MOUNT_SPEAR";
}
return "UNKNOWN";
}
void ANI::print_attack_speed()
{
for (int race = 0; race < MAIN_RACE_MAX_NUM; ++race)
{
for (int weapon = 0; weapon < WEAPON_NUM_TYPES; ++weapon)
{
printf("[%s][%s] = %u\n",
FN_race_string(race),
FN_weapon_string(weapon),
attack_speed(race, weapon));
}
printf("\n");
}
}
void ani_init()
{
s_ANI.load();
}
DWORD ani_attack_speed(LPCHARACTER ch)
{
DWORD speed = 1000;
if (NULL == ch)
return speed;
LPITEM item = ch->GetWear(WEAR_WEAPON);
if (NULL == item)
return speed;
if (ITEM_WEAPON != item->GetType())
return speed;
int race = ch->GetRaceNum();
int weapon = item->GetSubType();
/*
dev_log(LOG_DEB0, "%s : (race,weapon) = (%s,%s) POINT_ATT_SPEED = %d",
ch->GetName(),
FN_race_name(race),
FN_weapon_type(weapon),
ch->GetPoint(POINT_ATT_SPEED));
*/
/* 투핸디드 소드의 경우 삼연참공격과 승마시 */
/* 오류가 많아 한손검 속도로 생각하자 */
if (weapon == WEAPON_TWO_HANDED)
weapon = WEAPON_SWORD;
return s_ANI.attack_speed(race, weapon);
}
DWORD ani_combo_speed(LPCHARACTER ch, BYTE combo)
{
LPITEM item = ch->GetWear(WEAR_WEAPON);
if (NULL == item || combo > 8)
return 1000;
return s_ANI.attack_speed(ch->GetRaceNum(), item->GetSubType(), combo, ch->IsRiding());
}
void ani_print_attack_speed()
{
s_ANI.print_attack_speed();
}
#if 0
int main(int argc, char **argv)
{
ani_init();
ani_print_attack_speed();
exit(0);
}
#endif

19
src/game/ani.h Normal file
View File

@@ -0,0 +1,19 @@
/*********************************************************************
* date : 2007.11.16
* file : ani.h
* author : mhh
* description :
*/
#ifndef _ani_h_
#define _ani_h_
void ani_init();
DWORD ani_attack_speed(LPCHARACTER ch);
void ani_print_attack_speed();
DWORD ani_combo_speed(LPCHARACTER ch, BYTE combo);
#endif /* _ani_h_ */

67
src/game/any_function.h Normal file
View File

@@ -0,0 +1,67 @@
// See http://www.boost.org/libs/any for Documentation.
#ifndef __IPKN_ANY_FUNCTION_VARIATION_OF_BOOST_ANY_INCLUDED
#define __IPKN_ANY_FUNCTION_VARIATION_OF_BOOST_ANY_INCLUDED
// what: variant type boost::any
// who: contributed by Kevlin Henney,
// with features contributed and bugs found by
// Ed Brey, Mark Rodgers, Peter Dimov, and James Curran
// when: July 2001
// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95
#include <algorithm>
#include <typeinfo>
#define boost _boost_func_of_SQLMsg
#define func_arg_type SQLMsg*
#define func_arg pmsg
#include "any_function.inc"
#undef func_arg
#undef func_arg_type
#undef boost
typedef _boost_func_of_SQLMsg::any any_function;
#define boost _boost_func_of_void
#define func_arg_type
#define func_arg
#include "any_function.inc"
#undef func_arg
#undef func_arg_type
#undef boost
typedef _boost_func_of_void::any any_void_function;
template <class F>
class void_binder
{
protected:
F f;
typename F::argument_type value;
public:
void_binder(const F& f, const typename F::argument_type x)
: f(f), value(x) {}
void operator()() const {
return f(value);
}
};
template <class F, class Arg>
inline void_binder<F> void_bind(const F& f, const Arg& arg)
{
typedef typename F::argument_type arg_type;
return void_binder<F>(f, arg_type(arg));
}
// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.
#endif

116
src/game/any_function.inc Normal file
View File

@@ -0,0 +1,116 @@
namespace boost
{
class any
{
public: // structors
any()
: content(0)
{
}
template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value))
{
}
any(const any & other)
: content(other.content ? other.content->clone() : 0)
{
}
~any()
{
delete content;
}
public: // modifiers
any & swap(any & rhs)
{
std::swap(content, rhs.content);
return *this;
}
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
any(rhs).swap(*this);
return *this;
}
any & operator=(const any & rhs)
{
any(rhs).swap(*this);
return *this;
}
void operator ()(func_arg_type func_arg)
{
(*content)(func_arg);
}
public: // queries
bool empty() const
{
return !content;
}
private: // types
class placeholder
{
public: // structors
virtual ~placeholder()
{
}
public: // queries
virtual placeholder * clone() const = 0;
virtual void operator()(func_arg_type func_arg) = 0;
};
template<typename ValueType>
class holder : public placeholder
{
public: // structors
holder(const ValueType & value)
: held(value)
{
}
public: // queries
virtual placeholder * clone() const
{
return new holder(held);
}
virtual void operator ()(func_arg_type func_arg)
{
held(func_arg);
}
public: // representation
ValueType held;
};
private: // representation
template<typename ValueType>
friend ValueType * any_cast(any *);
placeholder * content;
};
}

1138
src/game/arena.cpp Normal file

File diff suppressed because it is too large Load Diff

139
src/game/arena.h Normal file
View File

@@ -0,0 +1,139 @@
#ifndef __CLASS_ARENA_MANAGER__
#define __CLASS_ARENA_MANAGER__
#include <lua.h>
enum MEMBER_IDENTITY
{
MEMBER_NO,
MEMBER_DUELIST,
MEMBER_OBSERVER,
MEMBER_MAX
};
class CArena
{
friend class CArenaMap;
private :
DWORD m_dwPIDA;
DWORD m_dwPIDB;
LPEVENT m_pEvent;
LPEVENT m_pTimeOutEvent;
PIXEL_POSITION m_StartPointA;
PIXEL_POSITION m_StartPointB;
PIXEL_POSITION m_ObserverPoint;
DWORD m_dwSetCount;
DWORD m_dwSetPointOfA;
DWORD m_dwSetPointOfB;
std::map<DWORD, LPCHARACTER> m_mapObserver;
protected :
CArena(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y);
bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5);
bool IsEmpty() const { return ((m_dwPIDA==0) && (m_dwPIDB==0)); }
bool IsMember(DWORD dwPID) const { return ((m_dwPIDA==dwPID) || (m_dwPIDB==dwPID)); }
bool CheckArea(WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y);
void Clear();
bool CanAttack(DWORD dwPIDA, DWORD dwPIDB);
bool OnDead(DWORD dwPIDA, DWORD dwPIDB);
bool IsObserver(DWORD pid);
bool IsMyObserver(WORD ObserverX, WORD ObserverY);
bool AddObserver(LPCHARACTER pChar);
bool RegisterObserverPtr(LPCHARACTER pChar);
public :
DWORD GetPlayerAPID() { return m_dwPIDA; }
DWORD GetPlayerBPID() { return m_dwPIDB; }
LPCHARACTER GetPlayerA() { return CHARACTER_MANAGER::instance().FindByPID(m_dwPIDA); }
LPCHARACTER GetPlayerB() { return CHARACTER_MANAGER::instance().FindByPID(m_dwPIDB); }
PIXEL_POSITION GetStartPointA() { return m_StartPointA; }
PIXEL_POSITION GetStartPointB() { return m_StartPointB; }
PIXEL_POSITION GetObserverPoint() { return m_ObserverPoint; }
void EndDuel();
void ClearEvent() { m_pEvent = NULL; }
void OnDisconnect(DWORD pid);
void RemoveObserver(DWORD pid);
void SendPacketToObserver(const void * c_pvData, int iSize);
void SendChatPacketToObserver(BYTE type, const char * format, ...);
};
class CArenaMap
{
friend class CArenaManager;
private :
DWORD m_dwMapIndex;
std::list<CArena*> m_listArena;
protected :
void Destroy();
bool AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y);
void SendArenaMapListTo(LPCHARACTER pChar, DWORD dwMapIndex);
bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5);
void EndAllDuel();
bool EndDuel(DWORD pid);
int GetDuelList(lua_State* L, int index);
bool CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim);
bool OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim);
bool AddObserver(LPCHARACTER pChar, WORD ObserverX, WORD ObserverY);
bool RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY);
MEMBER_IDENTITY IsMember(DWORD PID);
};
class CArenaManager : public singleton<CArenaManager>
{
private :
std::map<DWORD, CArenaMap*> m_mapArenaMap;
public :
bool Initialize();
void Destroy();
bool StartDuel(LPCHARACTER pCharFrom, LPCHARACTER pCharTo, int nSetPoint, int nMinute = 5);
bool AddArena(DWORD mapIdx, WORD startA_X, WORD startA_Y, WORD startB_X, WORD startB_Y);
void SendArenaMapListTo(LPCHARACTER pChar);
void EndAllDuel();
bool EndDuel(DWORD pid);
void GetDuelList(lua_State* L);
bool CanAttack(LPCHARACTER pCharAttacker, LPCHARACTER pCharVictim);
bool OnDead(LPCHARACTER pCharKiller, LPCHARACTER pCharVictim);
bool AddObserver(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY);
bool RegisterObserverPtr(LPCHARACTER pChar, DWORD mapIdx, WORD ObserverX, WORD ObserverY);
bool IsArenaMap(DWORD dwMapIndex);
MEMBER_IDENTITY IsMember(DWORD dwMapIndex, DWORD PID);
bool IsLimitedItem( long lMapIndex, DWORD dwVnum );
};
#endif /*__CLASS_ARENA_MANAGER__*/

1378
src/game/auction_manager.cpp Normal file

File diff suppressed because it is too large Load Diff

218
src/game/auction_manager.h Normal file
View File

@@ -0,0 +1,218 @@
#ifndef __INC_AUCTION_MANAGER_H
#define __INC_AUCTION_MANAGER_H
#include "../../libsql/AsyncSQL.h"
#include "../../common/auction_table.h"
#include <unordered_map>
#include <algorithm>
#define GRADE_LOW 30
#define GRADE_MID 60
#define GRADE_HIGH 90
template<>
class hash<std::pair <DWORD, DWORD> >
{ // hash functor
public:
typedef std::pair <DWORD, DWORD> _Kty;
size_t operator()(const _Kty& _Keyval) const
{ // hash _Keyval to size_t value by pseudorandomizing transform
ldiv_t _Qrem = ldiv((size_t)_Keyval.first + (size_t)_Keyval.second, 127773);
_Qrem.rem = 16807 * _Qrem.rem - 2836 * _Qrem.quot;
if (_Qrem.rem < 0)
_Qrem.rem += 2147483647;
return ((size_t)_Qrem.rem);
}
};
bool CompareItemInfoByItemNameAC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByItemNameDC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByCategoryAC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByCategoryDC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByTimeAC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByTimeDC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByCharNameAC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByCharNameDC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByPriceAC (TAuctionItemInfo* i, TAuctionItemInfo* j);
bool CompareItemInfoByPriceDC (TAuctionItemInfo* i, TAuctionItemInfo* j);
class AuctionBoard
{
public:
AuctionBoard() {}
~AuctionBoard() {}
TAuctionItemInfo* GetItemInfo (DWORD key);
bool DeleteItemInfo (DWORD key);
bool InsertItemInfo (TAuctionItemInfo* item_info);
bool UpdateItemInfo (TAuctionItemInfo* item_info);
private:
typedef std::unordered_map <DWORD, TAuctionItemInfo*> TItemInfoMap;
TItemInfoMap item_map;
typedef std::map <DWORD, TAuctionItemInfo*> TItemMap;
typedef std::unordered_map <DWORD, TItemMap*> TPCMap;
TPCMap offer_map;
// sorting을 위한 members
public:
typedef std::vector <TAuctionItemInfo*> TItemInfoVec;
private:
typedef std::map <std::string, TItemInfoVec*> SortByItemName;
SortByItemName item_name_map;
void Sort(TItemInfoVec& vec, BYTE order);
public:
void SortedItemInfos (TItemInfoVec& vec, BYTE grade, BYTE category, int start_idx, BYTE size, BYTE order[5]);
// 나의 경매장을 위한 함수.
void YourItemInfoList (TItemInfoVec& vec, DWORD player_id, int start_idx, BYTE size);
};
class SaleBoard
{
private:
typedef std::unordered_map <DWORD, TSaleItemInfo*> TItemInfoMap;
TItemInfoMap item_map;
typedef std::map <DWORD, TSaleItemInfo*> TItemMap;
typedef std::unordered_map <DWORD, TItemMap*> TPCMap;
TPCMap wisher_map;
TPCMap seller_map;
bool DeleteFromPCMap (TPCMap& pc_map, DWORD player_id, DWORD item_id);
bool InsertInPCMap (TPCMap& pc_map, DWORD player_id, TSaleItemInfo* item_info);
public:
SaleBoard() {}
~SaleBoard() {}
typedef std::vector <TSaleItemInfo*> TItemInfoVec;
void WisherItemInfoList (TItemInfoVec& vec, DWORD wisher_id, int start_idx, BYTE size);
TSaleItemInfo* GetItemInfo (DWORD key);
bool DeleteItemInfo (DWORD key);
bool InsertItemInfo (TSaleItemInfo* item_info);
};
class WishBoard
{
private:
typedef std::map <DWORD, TWishItemInfo*> TItemMap;
typedef std::unordered_map <DWORD, TItemMap*> TPCMap;
TPCMap wisher_map;
public:
typedef TWishItemInfo ItemInfo;
WishBoard() {}
~WishBoard() {}
TWishItemInfo* GetItemInfo (DWORD wisher_id, DWORD item_num);
bool DeleteItemInfo (DWORD wisher_id, DWORD item_num);
bool InsertItemInfo (TWishItemInfo* item_info);
};
class MyBidBoard
{
private:
typedef std::pair <int, bool> BidInfo;
typedef std::map <DWORD, BidInfo > TItemMap;
typedef std::unordered_map <DWORD, TItemMap*> TMyBidBoard;
// bidder_id가 key
TMyBidBoard pc_map;
public:
MyBidBoard() {}
~MyBidBoard() {}
typedef std::vector <DWORD> TItemVec;
void YourBidInfo (TItemVec& vec, DWORD bidder_id, int start_idx, int size);
BidInfo GetMoney (DWORD player_id, DWORD item_id);
bool Delete (DWORD player_id, DWORD item_id);
// 이미 있으면 덮어 씌운다.
void Insert (DWORD player_id, DWORD item_id, int money);
void Lock (DWORD player_id, DWORD item_id);
void UnLock (DWORD player_id, DWORD item_id);
};
class AuctionManager : public singleton <AuctionManager>
{
private :
typedef std::unordered_map<DWORD, LPITEM> TItemMap;
TItemMap auction_item_map;
// auction에 등록된 정보 중 가격, 등등 아이템 테이블에 포함되지 않는 정보들을 관리하는 것들
AuctionBoard Auction;
SaleBoard Sale;
WishBoard Wish;
MyBidBoard MyBid;
public:
bool InsertItem (LPITEM item);
bool InsertItem (TPlayerItem* player_item);
LPITEM GetInventoryItem (DWORD item_id);
bool DeleteItem (DWORD item_id);
bool InsertAuctionItemInfo (TAuctionItemInfo* item_info);
TAuctionItemInfo* GetAuctionItemInfo (DWORD item_id)
{
return Auction.GetItemInfo (item_id);
}
bool InsertSaleItemInfo (TSaleItemInfo* item_info);
TSaleItemInfo* GetSaleItemInfo (DWORD item_id)
{
return Sale.GetItemInfo (item_id);
}
bool InsertWishItemInfo (TWishItemInfo* item_info);
TWishItemInfo* GetWishItemInfo (DWORD wisher_id, DWORD item_id)
{
return Wish.GetItemInfo (wisher_id, item_id);
}
void YourBidItemInfoList (AuctionBoard::TItemInfoVec& vec, DWORD bidder_id, int start_idx, int size);
void Boot (const char* &pdata, WORD size);
void get_auction_list (LPCHARACTER ch, int start_idx, int size, int cond);
void get_my_auction_list (LPCHARACTER ch, int start_idx, int size);
void get_my_purchase_list (LPCHARACTER ch, int start_idx, int size);
void enroll_auction (LPCHARACTER ch, LPITEM item, BYTE empire, int bidPrice, int immidiatePurchasePrice);
void recv_result_auction (DWORD commander_id, TPacketDGResultAuction* cmd_result);
void bid (LPCHARACTER ch, DWORD item_id, int price);
void immediate_purchase (LPCHARACTER ch, DWORD item_id);
void enroll_sale (LPCHARACTER ch, LPITEM item, DWORD wisher_id, int salePrice);
void enroll_wish (LPCHARACTER ch, DWORD item_num, BYTE empire, int wishPrice);
void get_auctioned_item (LPCHARACTER ch, DWORD item_id, DWORD item_num);
void buy_sold_item (LPCHARACTER ch, DWORD item_id);
void cancel_auction (LPCHARACTER ch, DWORD item_id);
void cancel_wish (LPCHARACTER ch, DWORD item_num);
void cancel_sale (LPCHARACTER ch, DWORD item_id);
void rebid (LPCHARACTER ch, DWORD item_id, int price);
void bid_cancel (LPCHARACTER ch, DWORD item_id);
/*
void close_auction (LPCHARACTER ch);*/
};
#endif

View File

@@ -0,0 +1,5 @@
typedef struct packet_auction_simple_item_info
{
BYTE header;
BYTE size;
} TPacketGCAuctionItemSimpleInfo;

182
src/game/auth_brazil.cpp Normal file
View File

@@ -0,0 +1,182 @@
/* vi: set sw=4 ts=8 cino=g0,\:0 : */
/*********************************************************************
* date : 2010.4.7
* file : auth_brazil.c
* author : mhh
* description :
*/
#include "stdafx.h"
#ifndef __WIN32__
#include <unistd.h>
#include <stdint.h>
#endif
#include <stdio.h>
#include <string.h>
#ifdef __FreeBSD__
#include <md5.h>
#else
#include "../../libthecore/include/xmd5.h"
#endif
#include "auth_brazil.h"
static const char* FN_md5(const char *src)
{
static char s_buffer[512];
memset(s_buffer, 0x00, sizeof(s_buffer));
unsigned char digest[16] = {0};
MD5_CTX md5;
MD5Init(&md5);
MD5Update(&md5, (const unsigned char*) src, strlen(src));
MD5Final(digest, &md5);
int offset = 0;
for (int i=0; i<16; ++i) {
offset += sprintf(s_buffer + offset, "%02x", digest[i]);
}
return s_buffer;
}
static int FN_make_request(const char *login, const char *password, /*out*/ char *dst, int dst_size)
{
int len = snprintf(dst, dst_size,
// "GET /metin2/game_auth.php?ID=%s&PW=%s HTTP/1.1\r\n"
"GET /metin2/?ID=%s&PW=%s HTTP/1.1\r\n"
"Host: auth.ongame.com.br\r\n"
"Connection: Close\r\n\r\n",
login, FN_md5(password));
return len;
}
static int FN_parse_reply(char *reply)
{
char buffer[2048];
strlcpy(buffer, reply, sizeof(buffer));
const char *delim = "\r\n";
char *last = 0;
char *v = strtok_r(buffer, delim, &last);
char *result = 0;
while (v)
{
result = v;
v = strtok_r(NULL, delim, &last);
}
if (result)
{
if (0 == strcasecmp("true", result))
return AUTH_BRAZIL_SUCC;
else if (0 == strcasecmp("false", result))
return AUTH_BRAZIL_WRONGPWD;
else if (0 == strcasecmp("unknown", result))
return AUTH_BRAZIL_NOID;
else if (0 == strcasecmp("flash", result))
return AUTH_BRAZIL_FLASHUSER;
}
return AUTH_BRAZIL_SERVER_ERR;
}
extern void socket_timeout(socket_t s, long sec, long usec);
int auth_brazil(const char *login, const char *pwd)
{
const char *host = "auth.ongame.com.br";
int port = 80;
socket_t fd = socket_connect(host, port);
if (fd < 0)
{
sys_err("[AUTH_BRAZIL] : could not connect to gsp server(%s)", host);
return AUTH_BRAZIL_SERVER_ERR;
}
socket_block(fd);
socket_timeout(fd, 3, 0);
// send request
{
char request[512];
int len = FN_make_request(login, pwd, request, sizeof(request));
#ifndef __WIN32__
if (write(fd, request, len) < 0)
#else
if (_write(fd, request, len) < 0)
#endif
{
sys_err("[AUTH_BRAZIL] : could not send auth-request (%s)", login);
close(fd);
return AUTH_BRAZIL_SERVER_ERR;
}
}
// read reply
{
char reply[1024] = {0};
int len = read(fd, reply, sizeof(reply));
close(fd);
if (len <= 0)
{
sys_err("[AUTH_BRAZIL] : could not recv auth-reply (%s)", login);
return AUTH_BRAZIL_SERVER_ERR;
}
// 응답받은 경우에만 query count를 늘린다.
auth_brazil_inc_query_count();
return FN_parse_reply(reply);
}
}
static int s_query_count = 0;
int auth_brazil_inc_query_count()
{
return ++s_query_count;
}
void auth_brazil_log()
{
FILE *fp = 0;
// open and try backup
{
fp = fopen("AUTH_COUNT.log", "a");
if (0 == fp)
return;
struct stat sb;
fstat(fileno(fp), &sb);
if (sb.st_size > 1024 * 1024)
{
fclose(fp);
rename("AUTH_COUNT.log", "AUTH_COUNT.log.old");
fp = fopen("AUTH_COUNT.log", "a");
}
}
// write log
{
fprintf(fp, "%d\n", s_query_count);
fclose(fp);
}
// reset query count
s_query_count = 0;
}

23
src/game/auth_brazil.h Normal file
View File

@@ -0,0 +1,23 @@
/* vi: set sw=4 ts=8 cino=g0,\:0 : */
/*********************************************************************
* date : 2010.4.7
* file : auth_brazil.h
* author : mhh
* description :
*/
#ifndef __auth_brazil_h_1270647899__
#define __auth_brazil_h_1270647899__
#define AUTH_BRAZIL_SERVER_ERR 0
#define AUTH_BRAZIL_SUCC 1
#define AUTH_BRAZIL_NOID 2
#define AUTH_BRAZIL_WRONGPWD 3
#define AUTH_BRAZIL_FLASHUSER 4
int auth_brazil(const char *login, const char *pwd);
int auth_brazil_inc_query_count();
void auth_brazil_log();
#endif // __auth_brazil_h_1270647899__

126
src/game/banword.cpp Normal file
View File

@@ -0,0 +1,126 @@
#include "stdafx.h"
#include "constants.h"
#include "banword.h"
#include "config.h"
extern void SendLog(const char * c_pszBuf); // ¿î¿µÀÚ¿¡°Ô¸¸ °øÁö
CBanwordManager::CBanwordManager()
{
}
CBanwordManager::~CBanwordManager()
{
}
bool CBanwordManager::Initialize(TBanwordTable * p, WORD wSize)
{
m_hashmap_words.clear();
for (WORD i = 0; i < wSize; ++i, ++p)
m_hashmap_words[p->szWord] = true;
char szBuf[256];
snprintf(szBuf, sizeof(szBuf), "Banword reloaded! (total %zu banwords)", m_hashmap_words.size());
SendLog(szBuf);
return true;
}
bool CBanwordManager::Find(const char * c_pszString)
{
return m_hashmap_words.end() != m_hashmap_words.find(c_pszString);
}
bool CBanwordManager::CheckString(const char * c_pszString, size_t _len)
{
if (m_hashmap_words.empty())
return false;
__typeof(m_hashmap_words.begin()) it = m_hashmap_words.begin();
while (it != m_hashmap_words.end())
{
const std::string & r = it->first;
const char * tmp = c_pszString;
ssize_t len = _len;
while (len > 0)
{
if (is_twobyte(tmp))
{
if (!strncmp(tmp, r.c_str(), r.size()))
return true;
tmp += 2;
len -= 2;
}
else
{
if (!strncmp(tmp, r.c_str(), r.size()))
return true;
++tmp;
--len;
}
}
it++;
}
return false;
}
void CBanwordManager::ConvertString(char * c_pszString, size_t _len)
{
__typeof(m_hashmap_words.begin()) it = m_hashmap_words.begin();
while (it != m_hashmap_words.end())
{
const std::string & r = it->first;
char * tmp = c_pszString;
ssize_t len = _len;
while (len > 0)
{
if (is_twobyte(tmp))
{
if (!strncmp(tmp, r.c_str(), r.size()))
{
memset(tmp, '*', r.size());
tmp += r.size();
len -= r.size();
}
else
{
tmp += 2;
len -= 2;
}
}
else
{
if (*tmp == '*')
{
++tmp;
--len;
continue;
}
if (!strncmp(tmp, r.c_str(), r.size()))
{
memset(tmp, '*', r.size());
tmp += r.size();
len -= r.size();
}
else
{
++tmp;
--len;
}
}
}
it++;
}
}

24
src/game/banword.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef BANWORD_MANAGER_H_
#define BANWORD_MANAGER_H_
#include <unordered_map>
class CBanwordManager : public singleton<CBanwordManager>
{
public:
CBanwordManager();
virtual ~CBanwordManager();
bool Initialize(TBanwordTable * p, WORD wSize);
bool Find(const char * c_pszString);
bool CheckString(const char * c_pszString, size_t _len);
void ConvertString(char * c_pszString, size_t _len);
protected:
typedef std::unordered_map<std::string, bool> TBanwordHashmap;
TBanwordHashmap m_hashmap_words;
};
#endif /* BANWORD_MANAGER_H_ */

823
src/game/battle.cpp Normal file
View File

@@ -0,0 +1,823 @@
#include "stdafx.h"
#include "utils.h"
#include "config.h"
#include "desc.h"
#include "char.h"
#include "char_manager.h"
#include "battle.h"
#include "item.h"
#include "item_manager.h"
#include "mob_manager.h"
#include "vector.h"
#include "packet.h"
#include "pvp.h"
#include "profiler.h"
#include "guild.h"
#include "affect.h"
#include "unique_item.h"
#include "lua_incl.h"
#include "arena.h"
#include "castle.h"
#include "sectree.h"
#include "ani.h"
#include "locale_service.h"
int battle_hit(LPCHARACTER ch, LPCHARACTER victim, int & iRetDam);
bool battle_distance_valid_by_xy(long x, long y, long tx, long ty)
{
long distance = DISTANCE_APPROX(x - tx, y - ty);
if (distance > 170)
return false;
return true;
}
bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim)
{
return battle_distance_valid_by_xy(ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY());
}
bool timed_event_cancel(LPCHARACTER ch)
{
if (ch->m_pkTimedEvent)
{
event_cancel(&ch->m_pkTimedEvent);
return true;
}
/* RECALL_DELAY
차후 전투로 인해 귀환부 딜레이가 취소 되어야 할 경우 주석 해제
if (ch->m_pk_RecallEvent)
{
event_cancel(&ch->m_pkRecallEvent);
return true;
}
END_OF_RECALL_DELAY */
return false;
}
bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim)
{
// 상대방이 죽었으면 중단한다.
if (victim->IsDead())
return false;
// 안전지대면 중단
{
SECTREE *sectree = NULL;
sectree = ch->GetSectree();
if (sectree && sectree->IsAttr(ch->GetX(), ch->GetY(), ATTR_BANPK))
return false;
sectree = victim->GetSectree();
if (sectree && sectree->IsAttr(victim->GetX(), victim->GetY(), ATTR_BANPK))
return false;
}
// 내가 죽었으면 중단한다.
if (ch->IsStun() || ch->IsDead())
return false;
if (ch->IsPC() && victim->IsPC())
{
CGuild* g1 = ch->GetGuild();
CGuild* g2 = victim->GetGuild();
if (g1 && g2)
{
if (g1->UnderWar(g2->GetID()))
return true;
}
}
if (IS_CASTLE_MAP(ch->GetMapIndex()) && false==castle_can_attack(ch, victim))
return false;
if (CArenaManager::instance().CanAttack(ch, victim) == true)
return true;
return CPVPManager::instance().CanAttack(ch, victim);
}
int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
{
if (test_server&&ch->IsPC())
sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName());
if (!victim || ch == victim)
return BATTLE_NONE;
if (test_server&&ch->IsPC())
sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName());
if (!battle_is_attackable(ch, victim))
return BATTLE_NONE;
if (test_server&&ch->IsPC())
sys_log(0, "battle_melee_attack : [%s] attack to [%s]", ch->GetName(), victim->GetName());
// 거리 체크
int distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY());
if (!victim->IsBuilding())
{
int max = 300;
if (false == ch->IsPC())
{
// 몬스터의 경우 몬스터 공격 거리를 사용
max = (int) (ch->GetMobAttackRange() * 1.15f);
}
else
{
// PC일 경우 상대가 melee 몹일 경우 몹의 공격 거리가 최대 공격 거리
if (false == victim->IsPC() && BATTLE_TYPE_MELEE == victim->GetMobBattleType())
max = MAX(300, (int) (victim->GetMobAttackRange() * 1.15f));
}
if (distance > max)
{
if (test_server)
sys_log(0, "VICTIM_FAR: %s distance: %d max: %d", ch->GetName(), distance, max);
return BATTLE_NONE;
}
}
if (timed_event_cancel(ch))
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("전투가 시작 되어 취소 되었습니다."));
if (timed_event_cancel(victim))
victim->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("전투가 시작 되어 취소 되었습니다."));
ch->SetPosition(POS_FIGHTING);
ch->SetVictim(victim);
const PIXEL_POSITION & vpos = victim->GetXYZ();
ch->SetRotationToXY(vpos.x, vpos.y);
int dam;
int ret = battle_hit(ch, victim, dam);
return (ret);
}
// 실제 GET_BATTLE_VICTIM을 NULL로 만들고 이벤트를 캔슬 시킨다.
void battle_end_ex(LPCHARACTER ch)
{
if (ch->IsPosition(POS_FIGHTING))
ch->SetPosition(POS_STANDING);
}
void battle_end(LPCHARACTER ch)
{
battle_end_ex(ch);
}
// AG = Attack Grade
// AL = Attack Limit
int CalcBattleDamage(int iDam, int iAttackerLev, int iVictimLev)
{
if (iDam < 3)
iDam = number(1, 5);
//return CALCULATE_DAMAGE_LVDELTA(iAttackerLev, iVictimLev, iDam);
return iDam;
}
int CalcMagicDamageWithValue(int iDam, LPCHARACTER pkAttacker, LPCHARACTER pkVictim)
{
return CalcBattleDamage(iDam, pkAttacker->GetLevel(), pkVictim->GetLevel());
}
int CalcMagicDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim)
{
int iDam = 0;
if (pkAttacker->IsNPC())
{
iDam = CalcMeleeDamage(pkAttacker, pkVictim, false, false);
}
iDam += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS);
return CalcMagicDamageWithValue(iDam, pkAttacker, pkVictim);
}
float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating)
{
int iARSrc;
int iERSrc;
if (LC_IsYMIR()) // 천마
{
iARSrc = MIN(90, pkAttacker->GetPolymorphPoint(POINT_DX));
iERSrc = MIN(90, pkVictim->GetPolymorphPoint(POINT_DX));
}
else
{
int attacker_dx = pkAttacker->GetPolymorphPoint(POINT_DX);
int attacker_lv = pkAttacker->GetLevel();
int victim_dx = pkVictim->GetPolymorphPoint(POINT_DX);
int victim_lv = pkAttacker->GetLevel();
iARSrc = MIN(90, (attacker_dx * 4 + attacker_lv * 2) / 6);
iERSrc = MIN(90, (victim_dx * 4 + victim_lv * 2) / 6);
}
float fAR = ((float) iARSrc + 210.0f) / 300.0f; // fAR = 0.7 ~ 1.0
if (bIgnoreTargetRating)
return fAR;
// ((Edx * 2 + 20) / (Edx + 110)) * 0.3
float fER = ((float) (iERSrc * 2 + 5) / (iERSrc + 95)) * 3.0f / 10.0f;
return fAR - fER;
}
int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk)
{
// PvP에는 적용하지않음
if (!pkVictim->IsPC())
iAtk += pkAttacker->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_ATTACK_BONUS);
// PvP에는 적용하지않음
if (!pkAttacker->IsPC())
{
int iReduceDamagePct = pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_TRANSFER_DAMAGE);
iAtk = iAtk * (100 + iReduceDamagePct) / 100;
}
if (pkAttacker->IsNPC() && pkVictim->IsPC())
{
iAtk = (iAtk * CHARACTER_MANAGER::instance().GetMobDamageRate(pkAttacker)) / 100;
}
if (pkVictim->IsNPC())
{
if (pkVictim->IsRaceFlag(RACE_FLAG_ANIMAL))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ANIMAL)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_UNDEAD))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_UNDEAD)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_DEVIL))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_DEVIL)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_HUMAN))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_HUMAN)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_ORC))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ORC)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_MILGYO))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_MILGYO)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_INSECT))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_INSECT)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_FIRE))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_FIRE)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_ICE))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ICE)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_DESERT))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_DESERT)) / 100;
else if (pkVictim->IsRaceFlag(RACE_FLAG_TREE))
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_TREE)) / 100;
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_MONSTER)) / 100;
}
else if (pkVictim->IsPC())
{
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_HUMAN)) / 100;
switch (pkVictim->GetJob())
{
case JOB_WARRIOR:
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_WARRIOR)) / 100;
break;
case JOB_ASSASSIN:
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_ASSASSIN)) / 100;
break;
case JOB_SURA:
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_SURA)) / 100;
break;
case JOB_SHAMAN:
iAtk += (iAtk * pkAttacker->GetPoint(POINT_ATTBONUS_SHAMAN)) / 100;
break;
}
}
if (pkAttacker->IsPC() == true)
{
switch (pkAttacker->GetJob())
{
case JOB_WARRIOR:
iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_WARRIOR)) / 100;
break;
case JOB_ASSASSIN:
iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_ASSASSIN)) / 100;
break;
case JOB_SURA:
iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_SURA)) / 100;
break;
case JOB_SHAMAN:
iAtk -= (iAtk * pkVictim->GetPoint(POINT_RESIST_SHAMAN)) / 100;
break;
}
}
//[ mob -> PC ] 원소 속성 방어 적용
//2013/01/17
//몬스터 속성공격 데미지의 30%에 해당하는 수치에만 저항이 적용됨.
if (pkAttacker->IsNPC() && pkVictim->IsPC())
{
if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ELEC))
iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_ELEC)) / 10000;
else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_FIRE))
iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_FIRE)) / 10000;
else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_ICE))
iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_ICE)) / 10000;
else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_WIND))
iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_WIND)) / 10000;
else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_EARTH))
iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_EARTH)) / 10000;
else if (pkAttacker->IsRaceFlag(RACE_FLAG_ATT_DARK))
iAtk -= (iAtk * 30 * pkVictim->GetPoint(POINT_RESIST_DARK)) / 10000;
}
return iAtk;
}
void Item_GetDamage(LPITEM pkItem, int* pdamMin, int* pdamMax)
{
*pdamMin = 0;
*pdamMax = 1;
if (!pkItem)
return;
switch (pkItem->GetType())
{
case ITEM_ROD:
case ITEM_PICK:
return;
}
if (pkItem->GetType() != ITEM_WEAPON)
sys_err("Item_GetDamage - !ITEM_WEAPON vnum=%d, type=%d", pkItem->GetOriginalVnum(), pkItem->GetType());
*pdamMin = pkItem->GetValue(3);
*pdamMax = pkItem->GetValue(4);
}
int CalcMeleeDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreDefense, bool bIgnoreTargetRating)
{
LPITEM pWeapon = pkAttacker->GetWear(WEAR_WEAPON);
bool bPolymorphed = pkAttacker->IsPolymorphed();
if (pWeapon && !(bPolymorphed && !pkAttacker->IsPolyMaintainStat()))
{
if (pWeapon->GetType() != ITEM_WEAPON)
return 0;
switch (pWeapon->GetSubType())
{
case WEAPON_SWORD:
case WEAPON_DAGGER:
case WEAPON_TWO_HANDED:
case WEAPON_BELL:
case WEAPON_FAN:
case WEAPON_MOUNT_SPEAR:
break;
case WEAPON_BOW:
sys_err("CalcMeleeDamage should not handle bows (name: %s)", pkAttacker->GetName());
return 0;
default:
return 0;
}
}
int iDam = 0;
float fAR = CalcAttackRating(pkAttacker, pkVictim, bIgnoreTargetRating);
int iDamMin = 0, iDamMax = 0;
// TESTSERVER_SHOW_ATTACKINFO
int DEBUG_iDamCur = 0;
int DEBUG_iDamBonus = 0;
// END_OF_TESTSERVER_SHOW_ATTACKINFO
if (bPolymorphed && !pkAttacker->IsPolyMaintainStat())
{
// MONKEY_ROD_ATTACK_BUG_FIX
Item_GetDamage(pWeapon, &iDamMin, &iDamMax);
// END_OF_MONKEY_ROD_ATTACK_BUG_FIX
DWORD dwMobVnum = pkAttacker->GetPolymorphVnum();
const CMob * pMob = CMobManager::instance().Get(dwMobVnum);
if (pMob)
{
int iPower = pkAttacker->GetPolymorphPower();
iDamMin += pMob->m_table.dwDamageRange[0] * iPower / 100;
iDamMax += pMob->m_table.dwDamageRange[1] * iPower / 100;
}
}
else if (pWeapon)
{
// MONKEY_ROD_ATTACK_BUG_FIX
Item_GetDamage(pWeapon, &iDamMin, &iDamMax);
// END_OF_MONKEY_ROD_ATTACK_BUG_FIX
}
else if (pkAttacker->IsNPC())
{
iDamMin = pkAttacker->GetMobDamageMin();
iDamMax = pkAttacker->GetMobDamageMax();
}
iDam = number(iDamMin, iDamMax) * 2;
// TESTSERVER_SHOW_ATTACKINFO
DEBUG_iDamCur = iDam;
// END_OF_TESTSERVER_SHOW_ATTACKINFO
//
int iAtk = 0;
// level must be ignored when multiply by fAR, so subtract it before calculation.
iAtk = pkAttacker->GetPoint(POINT_ATT_GRADE) + iDam - (pkAttacker->GetLevel() * 2);
iAtk = (int) (iAtk * fAR);
iAtk += pkAttacker->GetLevel() * 2; // and add again
if (pWeapon)
{
iAtk += pWeapon->GetValue(5) * 2;
// 2004.11.12.myevan.TESTSERVER_SHOW_ATTACKINFO
DEBUG_iDamBonus = pWeapon->GetValue(5) * 2;
///////////////////////////////////////////////
}
iAtk += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS); // party attacker role bonus
iAtk = (int) (iAtk * (100 + (pkAttacker->GetPoint(POINT_ATT_BONUS) + pkAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100);
iAtk = CalcAttBonus(pkAttacker, pkVictim, iAtk);
int iDef = 0;
if (!bIgnoreDefense)
{
iDef = (pkVictim->GetPoint(POINT_DEF_GRADE) * (100 + pkVictim->GetPoint(POINT_DEF_BONUS)) / 100);
if (!pkAttacker->IsPC())
iDef += pkVictim->GetMarriageBonus(UNIQUE_ITEM_MARRIAGE_DEFENSE_BONUS);
}
if (pkAttacker->IsNPC())
iAtk = (int) (iAtk * pkAttacker->GetMobDamageMultiply());
iDam = MAX(0, iAtk - iDef);
if (test_server)
{
int DEBUG_iLV = pkAttacker->GetLevel()*2;
int DEBUG_iST = int((pkAttacker->GetPoint(POINT_ATT_GRADE) - DEBUG_iLV) * fAR);
int DEBUG_iPT = pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS);
int DEBUG_iWP = 0;
int DEBUG_iPureAtk = 0;
int DEBUG_iPureDam = 0;
char szRB[32] = "";
char szGradeAtkBonus[32] = "";
DEBUG_iWP = int(DEBUG_iDamCur * fAR);
DEBUG_iPureAtk = DEBUG_iLV + DEBUG_iST + DEBUG_iWP+DEBUG_iDamBonus;
DEBUG_iPureDam = iAtk - iDef;
if (pkAttacker->IsNPC())
{
snprintf(szGradeAtkBonus, sizeof(szGradeAtkBonus), "=%d*%.1f", DEBUG_iPureAtk, pkAttacker->GetMobDamageMultiply());
DEBUG_iPureAtk = int(DEBUG_iPureAtk * pkAttacker->GetMobDamageMultiply());
}
if (DEBUG_iDamBonus != 0)
snprintf(szRB, sizeof(szRB), "+RB(%d)", DEBUG_iDamBonus);
char szPT[32] = "";
if (DEBUG_iPT != 0)
snprintf(szPT, sizeof(szPT), ", PT=%d", DEBUG_iPT);
char szUnknownAtk[32] = "";
if (iAtk != DEBUG_iPureAtk)
snprintf(szUnknownAtk, sizeof(szUnknownAtk), "+?(%d)", iAtk-DEBUG_iPureAtk);
char szUnknownDam[32] = "";
if (iDam != DEBUG_iPureDam)
snprintf(szUnknownDam, sizeof(szUnknownDam), "+?(%d)", iDam-DEBUG_iPureDam);
char szMeleeAttack[128];
snprintf(szMeleeAttack, sizeof(szMeleeAttack),
"%s(%d)-%s(%d)=%d%s, ATK=LV(%d)+ST(%d)+WP(%d)%s%s%s, AR=%.3g%s",
pkAttacker->GetName(),
iAtk,
pkVictim->GetName(),
iDef,
iDam,
szUnknownDam,
DEBUG_iLV,
DEBUG_iST,
DEBUG_iWP,
szRB,
szUnknownAtk,
szGradeAtkBonus,
fAR,
szPT);
pkAttacker->ChatPacket(CHAT_TYPE_TALKING, "%s", szMeleeAttack);
pkVictim->ChatPacket(CHAT_TYPE_TALKING, "%s", szMeleeAttack);
}
return CalcBattleDamage(iDam, pkAttacker->GetLevel(), pkVictim->GetLevel());
}
int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow, LPITEM pkArrow, bool bIgnoreDefense)
{
if (!pkBow || pkBow->GetType() != ITEM_WEAPON || pkBow->GetSubType() != WEAPON_BOW)
return 0;
if (!pkArrow)
return 0;
// 타격치 계산부
int iDist = (int) (DISTANCE_SQRT(pkAttacker->GetX() - pkVictim->GetX(), pkAttacker->GetY() - pkVictim->GetY()));
//int iGap = (iDist / 100) - 5 - pkBow->GetValue(5) - pkAttacker->GetPoint(POINT_BOW_DISTANCE);
int iGap = (iDist / 100) - 5 - pkAttacker->GetPoint(POINT_BOW_DISTANCE);
int iPercent = 100 - (iGap * 5);
if (iPercent <= 0)
return 0;
else if (iPercent > 100)
iPercent = 100;
int iDam = 0;
float fAR = CalcAttackRating(pkAttacker, pkVictim, false);
iDam = number(pkBow->GetValue(3), pkBow->GetValue(4)) * 2 + pkArrow->GetValue(3);
int iAtk;
// level must be ignored when multiply by fAR, so subtract it before calculation.
iAtk = pkAttacker->GetPoint(POINT_ATT_GRADE) + iDam - (pkAttacker->GetLevel() * 2);
iAtk = (int) (iAtk * fAR);
iAtk += pkAttacker->GetLevel() * 2; // and add again
// Refine Grade
iAtk += pkBow->GetValue(5) * 2;
iAtk += pkAttacker->GetPoint(POINT_PARTY_ATTACKER_BONUS);
iAtk = (int) (iAtk * (100 + (pkAttacker->GetPoint(POINT_ATT_BONUS) + pkAttacker->GetPoint(POINT_MELEE_MAGIC_ATT_BONUS_PER))) / 100);
iAtk = CalcAttBonus(pkAttacker, pkVictim, iAtk);
int iDef = 0;
if (!bIgnoreDefense)
iDef = (pkVictim->GetPoint(POINT_DEF_GRADE) * (100 + pkAttacker->GetPoint(POINT_DEF_BONUS)) / 100);
if (pkAttacker->IsNPC())
iAtk = (int) (iAtk * pkAttacker->GetMobDamageMultiply());
iDam = MAX(0, iAtk - iDef);
int iPureDam = iDam;
iPureDam = (iPureDam * iPercent) / 100;
if (test_server)
{
pkAttacker->ChatPacket(CHAT_TYPE_INFO, "ARROW %s -> %s, DAM %d DIST %d GAP %d %% %d",
pkAttacker->GetName(),
pkVictim->GetName(),
iPureDam,
iDist, iGap, iPercent);
}
return iPureDam;
//return iDam;
}
void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim)
{
// 독 공격은 특이하므로 특수 처리
if (pkAttacker->GetPoint(POINT_POISON_PCT) && !pkVictim->IsAffectFlag(AFF_POISON))
{
if (number(1, 100) <= pkAttacker->GetPoint(POINT_POISON_PCT))
pkVictim->AttackedByPoison(pkAttacker);
}
int iStunDuration = 2;
if (pkAttacker->IsPC() && !pkVictim->IsPC())
iStunDuration = 4;
AttackAffect(pkAttacker, pkVictim, POINT_STUN_PCT, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iStunDuration, "STUN");
AttackAffect(pkAttacker, pkVictim, POINT_SLOW_PCT, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 20, "SLOW");
}
int battle_hit(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int & iRetDam)
{
//PROF_UNIT puHit("Hit");
if (test_server)
sys_log(0, "battle_hit : [%s] attack to [%s] : dam :%d type :%d", pkAttacker->GetName(), pkVictim->GetName(), iRetDam);
int iDam = CalcMeleeDamage(pkAttacker, pkVictim);
if (iDam <= 0)
return (BATTLE_DAMAGE);
NormalAttackAffect(pkAttacker, pkVictim);
// 데미지 계산
//iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST)) / 100;
LPITEM pkWeapon = pkAttacker->GetWear(WEAR_WEAPON);
if (pkWeapon)
switch (pkWeapon->GetSubType())
{
case WEAPON_SWORD:
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_SWORD)) / 100;
break;
case WEAPON_TWO_HANDED:
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_TWOHAND)) / 100;
break;
case WEAPON_DAGGER:
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_DAGGER)) / 100;
break;
case WEAPON_BELL:
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BELL)) / 100;
break;
case WEAPON_FAN:
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_FAN)) / 100;
break;
case WEAPON_BOW:
iDam = iDam * (100 - pkVictim->GetPoint(POINT_RESIST_BOW)) / 100;
break;
}
//최종적인 데미지 보정. (2011년 2월 현재 대왕거미에게만 적용.)
float attMul = pkAttacker->GetAttMul();
float tempIDam = iDam;
iDam = attMul * tempIDam + 0.5f;
iRetDam = iDam;
//PROF_UNIT puDam("Dam");
if (pkVictim->Damage(pkAttacker, iDam, DAMAGE_TYPE_NORMAL))
return (BATTLE_DEAD);
return (BATTLE_DAMAGE);
}
DWORD GET_ATTACK_SPEED(LPCHARACTER ch)
{
if (NULL == ch)
return 1000;
LPITEM item = ch->GetWear(WEAR_WEAPON);
DWORD default_bonus = SPEEDHACK_LIMIT_BONUS * 3; // 유두리 공속(기본 80) (일반 유저가 speed hack 에 걸리는 것을 막기 위해 *3 추가. 2013.09.11 CYH)
DWORD riding_bonus = 0;
if (ch->IsRiding())
{
// 뭔가를 탔으면 추가공속 50
riding_bonus = 50;
}
DWORD ani_speed = ani_attack_speed(ch);
DWORD real_speed = (ani_speed * 100) / (default_bonus + ch->GetPoint(POINT_ATT_SPEED) + riding_bonus);
// 단검의 경우 공속 2배
if (item && item->GetSubType() == WEAPON_DAGGER)
real_speed /= 2;
return real_speed;
}
void SET_ATTACK_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time)
{
if (NULL == ch || NULL == victim)
return;
if (!ch->IsPC())
return;
ch->m_kAttackLog.dwVID = victim->GetVID();
ch->m_kAttackLog.dwTime = current_time;
}
void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time)
{
if (NULL == ch || NULL == victim)
return;
if (!ch->IsPC())
return;
victim->m_AttackedLog.dwPID = ch->GetPlayerID();
victim->m_AttackedLog.dwAttackedTime= current_time;
}
bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time)
{
// 2013 09 11 CYH debugging log
/*sys_log(0, "%s attack test log! time (delta, limit)=(%u, %u). ch->m_kAttackLog.dwvID(%u) victim->GetVID(%u)",
ch->GetName(),
current_time - ch->m_kAttackLog.dwTime,
GET_ATTACK_SPEED(ch),
ch->m_kAttackLog.dwVID,
victim->GetVID()
);
sys_log(0, "%s attack test log! time (delta, limit)=(%u, %u). victim->m_AttackedLog.dwPID(%u) ch->GetPlayerID(%u)",
ch->GetName(),
current_time - victim->m_AttackedLog.dwAttackedTime,
GET_ATTACK_SPEED(ch),
victim->m_AttackedLog.dwPID,
ch->GetPlayerID()
);*/
if (ch->m_kAttackLog.dwVID == victim->GetVID())
{
if (current_time - ch->m_kAttackLog.dwTime < GET_ATTACK_SPEED(ch))
{
INCREASE_SPEED_HACK_COUNT(ch);
if (test_server)
{
sys_log(0, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d",
ch->GetName(),
current_time - ch->m_kAttackLog.dwTime,
GET_ATTACK_SPEED(ch),
ch->m_speed_hack_count);
ch->ChatPacket(CHAT_TYPE_INFO, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d",
ch->GetName(),
current_time - ch->m_kAttackLog.dwTime,
GET_ATTACK_SPEED(ch),
ch->m_speed_hack_count);
}
SET_ATTACK_TIME(ch, victim, current_time);
SET_ATTACKED_TIME(ch, victim, current_time);
return true;
}
}
SET_ATTACK_TIME(ch, victim, current_time);
if (victim->m_AttackedLog.dwPID == ch->GetPlayerID())
{
if (current_time - victim->m_AttackedLog.dwAttackedTime < GET_ATTACK_SPEED(ch))
{
INCREASE_SPEED_HACK_COUNT(ch);
if (test_server)
{
sys_log(0, "%s Attack Speed HACK! time (delta, limit)=(%u, %u), hack_count = %d",
ch->GetName(),
current_time - victim->m_AttackedLog.dwAttackedTime,
GET_ATTACK_SPEED(ch),
ch->m_speed_hack_count);
ch->ChatPacket(CHAT_TYPE_INFO, "Attack Speed Hack(%s), (delta, limit)=(%u, %u)",
ch->GetName(),
current_time - victim->m_AttackedLog.dwAttackedTime,
GET_ATTACK_SPEED(ch));
}
SET_ATTACKED_TIME(ch, victim, current_time);
return true;
}
}
SET_ATTACKED_TIME(ch, victim, current_time);
return false;
}

100
src/game/battle.h Normal file
View File

@@ -0,0 +1,100 @@
#ifndef __INC_METIN_II_GAME_BATTLE_H__
#define __INC_METIN_II_GAME_BATTLE_H__
#include "char.h"
enum EBattleTypes // »ó´ë¹æ ±âÁØ
{
BATTLE_NONE,
BATTLE_DAMAGE,
BATTLE_DEFENSE,
BATTLE_DEAD
};
extern int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk);
extern int CalcBattleDamage(int iDam, int iAttackerLev, int iVictimLev);
extern int CalcMeleeDamage(LPCHARACTER pAttacker, LPCHARACTER pVictim, bool bIgnoreDefense = false, bool bIgnoreTargetRating = false);
extern int CalcMagicDamage(LPCHARACTER pAttacker, LPCHARACTER pVictim);
extern int CalcArrowDamage(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, LPITEM pkBow, LPITEM pkArrow, bool bIgnoreDefense = false);
extern float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating = false);
extern bool battle_is_attackable(LPCHARACTER ch, LPCHARACTER victim);
extern int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim);
extern void battle_end(LPCHARACTER ch);
extern bool battle_distance_valid_by_xy(long x, long y, long tx, long ty);
extern bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim);
extern int battle_count_attackers(LPCHARACTER ch);
extern void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim);
// Ư¼º °ø°Ý
inline void AttackAffect(LPCHARACTER pkAttacker,
LPCHARACTER pkVictim,
BYTE att_point,
DWORD immune_flag,
DWORD affect_idx,
BYTE affect_point,
long affect_amount,
DWORD affect_flag,
int time,
const char* name)
{
if (pkAttacker->GetPoint(att_point) && !pkVictim->IsAffectFlag(affect_flag))
{
if (number(1, 100) <= pkAttacker->GetPoint(att_point) && !pkVictim->IsImmune(immune_flag))
{
pkVictim->AddAffect(affect_idx, affect_point, affect_amount, affect_flag, time, 0, true);
if (test_server)
{
pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s %s(%ld%%) SUCCESS", pkAttacker->GetName(), name, pkAttacker->GetPoint(att_point));
}
}
else if (test_server)
{
pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s %s(%ld%%) FAIL", pkAttacker->GetName(), name, pkAttacker->GetPoint(att_point));
}
}
}
inline void SkillAttackAffect(LPCHARACTER pkVictim,
int success_pct,
DWORD immune_flag,
DWORD affect_idx,
BYTE affect_point,
long affect_amount,
DWORD affect_flag,
int time,
const char* name)
{
if (success_pct && !pkVictim->IsAffectFlag(affect_flag))
{
if (number(1, 1000) <= success_pct && !pkVictim->IsImmune(immune_flag))
{
pkVictim->AddAffect(affect_idx, affect_point, affect_amount, affect_flag, time, 0, true);
// SKILL_ATTACK_NO_LOG_TARGET_NAME_FIX
if (test_server)
pkVictim->ChatPacket(CHAT_TYPE_PARTY,
"%s(%d%%) -> %s SUCCESS", name, success_pct, name);
// END_OF_SKILL_ATTACK_LOG_NO_TARGET_NAME_FIX
}
else if (test_server)
{
// SKILL_ATTACK_NO_LOG_TARGET_NAME_FIX
pkVictim->ChatPacket(CHAT_TYPE_PARTY, "%s(%d%%) -> %s FAIL", name, success_pct, name);
// END_OF_SKILL_ATTACK_LOG_NO_TARGET_NAME_FIX
}
}
}
#define GET_SPEED_HACK_COUNT(ch) ((ch)->m_speed_hack_count)
#define INCREASE_SPEED_HACK_COUNT(ch) (++GET_SPEED_HACK_COUNT(ch))
DWORD GET_ATTACK_SPEED(LPCHARACTER ch);
void SET_ATTACK_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time);
void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time);
bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time);
#endif

View File

@@ -0,0 +1,108 @@
#ifndef __HEADER_BELT_INVENTORY_HELPER__
#define __HEADER_BELT_INVENTORY_HELPER__
#include "char.h"
#include "item.h"
class CBeltInventoryHelper
{
public:
typedef BYTE TGradeUnit;
static TGradeUnit GetBeltGradeByRefineLevel(int level)
{
static TGradeUnit beltGradeByLevelTable[] =
{
0, // 벨트+0
1, // +1
1, // +2
2, // +3
2, // +4,
3, // +5
4, // +6,
5, // +7,
6, // +8,
7, // +9
};
if (level >= _countof(beltGradeByLevelTable))
{
sys_err("CBeltInventoryHelper::GetBeltGradeByRefineLevel - Overflow level (%d", level);
return 0;
}
return beltGradeByLevelTable[level];
}
// 현재 벨트 레벨을 기준으로, 어떤 셀들을 이용할 수 있는지 리턴
static const TGradeUnit* GetAvailableRuleTableByGrade()
{
/**
벨트는 총 +0 ~ +9 레벨을 가질 수 있으며, 레벨에 따라 7단계 등급으로 구분되어 인벤토리가 활성 화 된다.
벨트 레벨에 따른 사용 가능한 셀은 아래 그림과 같음. 현재 등급 >= 활성가능 등급이면 사용 가능.
(단, 현재 레벨이 0이면 무조건 사용 불가, 괄호 안의 숫자는 등급)
2(1) 4(2) 6(4) 8(6)
5(3) 5(3) 6(4) 8(6)
7(5) 7(5) 7(5) 8(6)
9(7) 9(7) 9(7) 9(7)
벨트 인벤토리의 크기는 4x4 (16칸)
*/
static TGradeUnit availableRuleByGrade[BELT_INVENTORY_SLOT_COUNT] = {
1, 2, 4, 6,
3, 3, 4, 6,
5, 5, 5, 6,
7, 7, 7, 7
};
return availableRuleByGrade;
}
static bool IsAvailableCell(WORD cell, int beltGrade /*int beltLevel*/)
{
// 기획 또 바뀜.. 아놔...
//const TGradeUnit beltGrade = GetBeltGradeByRefineLevel(beltLevel);
const TGradeUnit* ruleTable = GetAvailableRuleTableByGrade();
return ruleTable[cell] <= beltGrade;
}
/// pc의 벨트 인벤토리에 아이템이 하나라도 존재하는 지 검사하는 함수.
static bool IsExistItemInBeltInventory(LPCHARACTER pc)
{
for (WORD i = BELT_INVENTORY_SLOT_START; i < BELT_INVENTORY_SLOT_END; ++i)
{
LPITEM beltInventoryItem = pc->GetInventoryItem(i);
if (NULL != beltInventoryItem)
return true;
}
return false;
}
/// item이 벨트 인벤토리에 들어갈 수 있는 타입인지 검사하는 함수. (이 규칙은 기획자가 결정함)
static bool CanMoveIntoBeltInventory(LPITEM item)
{
bool canMove = false;
if (item->GetType() == ITEM_USE)
{
switch (item->GetSubType())
{
case USE_POTION:
case USE_POTION_NODELAY:
case USE_ABILITY_UP:
canMove = true;
break;
}
}
return canMove;
}
};
#endif //__HEADER_BELT_INVENTORY_HELPER__

249
src/game/blend_item.cpp Normal file
View File

@@ -0,0 +1,249 @@
/*********************************************************************
* date : 2007.02.24
* file : blend_item.cpp
* author : mhh
* description :
*/
#define _blend_item_cpp_
#include "stdafx.h"
#include "constants.h"
#include "log.h"
#include "dev_log.h"
#include "locale_service.h"
#include "item.h"
#include "blend_item.h"
#define DO_ALL_BLEND_INFO(iter) for (iter=s_blend_info.begin(); iter!=s_blend_info.end(); ++iter)
struct BLEND_ITEM_INFO
{
DWORD item_vnum;
int apply_type;
int apply_value[MAX_BLEND_ITEM_VALUE];
int apply_duration[MAX_BLEND_ITEM_VALUE];
};
typedef std::vector<BLEND_ITEM_INFO*> T_BLEND_ITEM_INFO;
T_BLEND_ITEM_INFO s_blend_info;
bool Blend_Item_init()
{
BLEND_ITEM_INFO *blend_item_info = NULL;
T_BLEND_ITEM_INFO::iterator iter;
char file_name[256];
snprintf (file_name, sizeof(file_name), "%s/blend.txt", LocaleService_GetBasePath().c_str());
sys_log(0, "Blend_Item_init %s ", file_name);
DO_ALL_BLEND_INFO(iter)
{
blend_item_info = *iter;
M2_DELETE(blend_item_info);
}
s_blend_info.clear();
if (false==Blend_Item_load(file_name))
{
sys_err("<Blend_Item_init> fail");
return false;
}
return true;
}
bool Blend_Item_load(char *file)
{
FILE *fp;
char one_line[256];
const char *delim = " \t\r\n";
char *v;
BLEND_ITEM_INFO *blend_item_info;
if (0==file || 0==file[0])
return false;
if ((fp = fopen(file, "r"))==0)
return false;
while (fgets(one_line, 256, fp))
{
if (one_line[0]=='#')
continue;
const char* token_string = strtok(one_line, delim);
if (NULL==token_string)
continue;
TOKEN("section")
{
blend_item_info = M2_NEW BLEND_ITEM_INFO;
memset(blend_item_info, 0x00, sizeof(BLEND_ITEM_INFO));
}
else TOKEN("item_vnum")
{
v = strtok(NULL, delim);
if (NULL==v)
{
fclose(fp);
return false;
}
str_to_number(blend_item_info->item_vnum, v);
}
else TOKEN("apply_type")
{
v = strtok(NULL, delim);
if (NULL==v)
{
fclose(fp);
return false;
}
if (0 == (blend_item_info->apply_type = FN_get_apply_type(v)))
{
sys_err ("Invalid apply_type(%s)", v);
return false;
}
}
else TOKEN("apply_value")
{
for (int i=0; i<MAX_BLEND_ITEM_VALUE; ++i)
{
v = strtok(NULL, delim);
if (NULL==v)
{
fclose(fp);
return false;
}
str_to_number(blend_item_info->apply_value[i], v);
}
}
else TOKEN("apply_duration")
{
for (int i=0; i<MAX_BLEND_ITEM_VALUE; ++i)
{
v = strtok(NULL, delim);
if (NULL==v)
{
fclose(fp);
return false;
}
str_to_number(blend_item_info->apply_duration[i], v);
}
}
else TOKEN("end")
{
s_blend_info.push_back(blend_item_info);
}
}
fclose(fp);
return true;
}
static int FN_random_index()
{
int percent = number(1,100);
if (percent<=10) // level 1 :10%
return 0;
else if (percent<=30) // level 2 : 20%
return 1;
else if (percent<=70) // level 3 : 40%
return 2;
else if (percent<=90) // level 4 : 20%
return 3;
else
return 4; // level 5 : 10%
return 0;
}
// 충기환의 확률 테이블
// blend.txt에서 확률도 받도록 고치면 깔끔하겠지만
// 각 나라별로 item proto 등을 따로 관리하므로,
// 혼란이 올 수 있어 이렇게 추가한다.
// by rtsummit
static int FN_ECS_random_index()
{
int percent = number(1,100);
if (percent<=5) // level 1 : 5%
return 0;
else if (percent<=15) // level 2 : 10%
return 1;
else if (percent<=60) // level 3 : 45%
return 2;
else if (percent<=85) // level 4 : 25%
return 3;
else
return 4; // level 5 : 15%
return 0;
}
bool Blend_Item_set_value(LPITEM item)
{
BLEND_ITEM_INFO *blend_info;
T_BLEND_ITEM_INFO::iterator iter;
DO_ALL_BLEND_INFO(iter)
{
blend_info = *iter;
if (blend_info->item_vnum == item->GetVnum())
{
int apply_type;
int apply_value;
int apply_duration;
if (item->GetVnum() == 51002)
{
apply_type = blend_info->apply_type;
apply_value = blend_info->apply_value [FN_ECS_random_index()];
apply_duration = blend_info->apply_duration [FN_ECS_random_index()];
}
else
{
apply_type = blend_info->apply_type;
apply_value = blend_info->apply_value [FN_random_index()];
apply_duration = blend_info->apply_duration [FN_random_index()];
}
sys_log (0, "blend_item : type : %d, value : %d, du : %d", apply_type, apply_value, apply_duration);
item->SetSocket(0, apply_type);
item->SetSocket(1, apply_value);
item->SetSocket(2, apply_duration);
return true;
}
}
return false;
}
bool Blend_Item_find(DWORD item_vnum)
{
BLEND_ITEM_INFO *blend_info;
T_BLEND_ITEM_INFO::iterator iter;
DO_ALL_BLEND_INFO(iter)
{
blend_info = *iter;
if (blend_info->item_vnum == item_vnum)
return true;
}
return false;
}

20
src/game/blend_item.h Normal file
View File

@@ -0,0 +1,20 @@
/*********************************************************************
* date : 2007.02.24
* file : blend_item.h
* author : mhh
* description :
*/
#ifndef _blend_item_h_
#define _blend_item_h_
#define MAX_BLEND_ITEM_VALUE 5
bool Blend_Item_init();
bool Blend_Item_load(char *file);
bool Blend_Item_set_value(LPITEM item);
bool Blend_Item_find(DWORD item_vnum);
#endif /* _blend_item_h_ */

151
src/game/block_country.cpp Normal file
View File

@@ -0,0 +1,151 @@
/*********************************************************************
* date : 2007.05.31
* file : block_country.cpp
* author : mhh
* description :
*/
#define _block_country_cpp_
#include "stdafx.h"
#include "constants.h"
#include "block_country.h"
#include "dev_log.h"
#define DEC_ITER(iter) std::vector<T_BLOCK_IP*>::iterator iter
#define DO_ALL_BLOCKED_IP(iter) for ((iter)=s_blocked_ip.begin(); (iter)!=s_blocked_ip.end(); ++(iter))
#define DEC_EXCEPTION_ITER(iter) std::set<std::string>::iterator iter
typedef struct {
DWORD ip_from;
DWORD ip_to;
} T_BLOCK_IP;
//--------------------------------------
// static variables
std::vector<T_BLOCK_IP*> s_blocked_ip;
std::set<std::string> s_block_exception;
// static variables
//--------------------------------------
//--------------------------------------
// static functions
static void __add_block_exception(const char *login)
{
dev_log(LOG_DEB0, "BLOCK_EXCEPTION_ADD : %s", login);
DEC_EXCEPTION_ITER(iter);
std::string string_login(login);
iter = s_block_exception.find(string_login);
// can not find
if (iter==s_block_exception.end())
{
s_block_exception.insert(string_login);
}
}
static void __del_block_exception(const char *login)
{
dev_log(LOG_DEB0, "BLOCK_EXCEPTION_DEL : %s", login);
DEC_EXCEPTION_ITER(iter);
std::string string_login(login);
iter = s_block_exception.find(string_login);
// ok : find
if (iter!=s_block_exception.end())
{
s_block_exception.erase(iter);
}
}
// static functions
//--------------------------------------
void add_blocked_country_ip(TPacketBlockCountryIp *data)
{
T_BLOCK_IP *block_ip = M2_NEW T_BLOCK_IP;
block_ip->ip_from = data->ip_from;
block_ip->ip_to = data->ip_to;
s_blocked_ip.push_back(block_ip);
dev_log(LOG_DEB0, "BLOCKED_IP = %u - %u", block_ip->ip_from, block_ip->ip_to);
}
void block_exception(TPacketBlockException *data)
{
if (NULL==data) return;
if (BLOCK_EXCEPTION_CMD_ADD!=data->cmd && BLOCK_EXCEPTION_CMD_DEL!=data->cmd)
return;
switch (data->cmd)
{
case BLOCK_EXCEPTION_CMD_ADD:
__add_block_exception(data->login);
break;
case BLOCK_EXCEPTION_CMD_DEL:
__del_block_exception(data->login);
break;
}
}
bool is_blocked_country_ip(const char *user_ip)
{
DEC_ITER(iter);
T_BLOCK_IP *block_ip;
DWORD ip_number;
struct in_addr st_addr;
#ifndef __WIN32__
if (0 == inet_aton(user_ip, &st_addr))
#else
unsigned long in_address;
in_address = inet_addr(user_ip);
st_addr.s_addr = in_address;
if (INADDR_NONE == in_address)
#endif
{
dev_log(LOG_INFO, "BLOCKED_COUNTRY_IP (%s) : YES", user_ip);
return true; // 아이피가 괴상하니 일단 블럭처리
}
ip_number = htonl(st_addr.s_addr);
DO_ALL_BLOCKED_IP(iter)
{
block_ip = *iter;
if ( block_ip->ip_from <= ip_number && ip_number <= block_ip->ip_to )
{
dev_log(LOG_INFO, "BLOCKED_COUNTRY_IP (%s) : YES", user_ip);
return true;
}
}
dev_log(LOG_INFO, "BLOCKED_COUNTRY_IP (%s) : NO", user_ip);
return false;
}
bool is_block_exception(const char *login)
{
std::string login_string(login);
std::set<std::string>::iterator iter;
iter = s_block_exception.find(login_string);
if (iter != s_block_exception.end())
return true;
return false;
}

18
src/game/block_country.h Normal file
View File

@@ -0,0 +1,18 @@
/*********************************************************************
* date : 2007.05.31
* file : block_country.h
* author : mhh
* description :
*/
#ifndef _block_country_h_
#define _block_country_h_
void add_blocked_country_ip(TPacketBlockCountryIp *data);
void block_exception(TPacketBlockException *data);
bool is_blocked_country_ip(const char *user_ip);
bool is_block_exception(const char *login);
#endif // _block_country_h_

View File

@@ -0,0 +1,184 @@
#include "stdafx.h"
#include "../../common/tables.h"
#include "item.h"
#include "char.h"
#include "buff_on_attributes.h"
#include <algorithm>
CBuffOnAttributes::CBuffOnAttributes(LPCHARACTER pOwner, BYTE point_type, std::vector <BYTE>* p_vec_buff_wear_targets)
: m_pBuffOwner(pOwner), m_bPointType(point_type), m_p_vec_buff_wear_targets(p_vec_buff_wear_targets)
{
Initialize();
}
CBuffOnAttributes::~CBuffOnAttributes()
{
Off();
}
void CBuffOnAttributes::Initialize()
{
m_bBuffValue = 0;
m_map_additional_attrs.clear();
}
void CBuffOnAttributes::RemoveBuffFromItem(LPITEM pItem)
{
if (0 == m_bBuffValue)
return ;
if (NULL != pItem)
{
if (pItem->GetCell() < INVENTORY_MAX_NUM)
return;
std::vector <BYTE>::iterator it = find (m_p_vec_buff_wear_targets->begin(), m_p_vec_buff_wear_targets->end(), pItem->GetCell() - INVENTORY_MAX_NUM);
if (m_p_vec_buff_wear_targets->end() == it)
return;
int m = pItem->GetAttributeCount();
for (int j = 0; j < m; j++)
{
TPlayerItemAttribute attr = pItem->GetAttribute(j);
TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType);
// m_map_additional_attrs에서 해당 attribute type에 대한 값을 제거하고,
// 변경된 값의 (m_bBuffValue)%만큼의 버프 효과 감소
if (it != m_map_additional_attrs.end())
{
int& sum_of_attr_value = it->second;
int old_value = sum_of_attr_value * m_bBuffValue / 100;
int new_value = (sum_of_attr_value - attr.sValue) * m_bBuffValue / 100;
m_pBuffOwner->ApplyPoint(attr.bType, new_value - old_value);
sum_of_attr_value -= attr.sValue;
}
else
{
sys_err ("Buff ERROR(type %d). This item(%d) attr_type(%d) was not in buff pool", m_bPointType, pItem->GetVnum(), attr.bType);
return;
}
}
}
}
void CBuffOnAttributes::AddBuffFromItem(LPITEM pItem)
{
if (0 == m_bBuffValue)
return ;
if (NULL != pItem)
{
if (pItem->GetCell() < INVENTORY_MAX_NUM)
return;
std::vector <BYTE>::iterator it = find (m_p_vec_buff_wear_targets->begin(), m_p_vec_buff_wear_targets->end(), pItem->GetCell() - INVENTORY_MAX_NUM);
if (m_p_vec_buff_wear_targets->end() == it)
return;
int m = pItem->GetAttributeCount();
for (int j = 0; j < m; j++)
{
TPlayerItemAttribute attr = pItem->GetAttribute(j);
TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType);
// m_map_additional_attrs에서 해당 attribute type에 대한 값이 없다면 추가.
// 추가된 값의 (m_bBuffValue)%만큼의 버프 효과 추가
if (it == m_map_additional_attrs.end())
{
m_pBuffOwner->ApplyPoint(attr.bType, attr.sValue * m_bBuffValue / 100);
m_map_additional_attrs.insert(TMapAttr::value_type(attr.bType, attr.sValue));
}
// m_map_additional_attrs에서 해당 attribute type에 대한 값이 있다면, 그 값을 증가시키고,
// 변경된 값의 (m_bBuffValue)%만큼의 버프 효과 추가
else
{
int& sum_of_attr_value = it->second;
int old_value = sum_of_attr_value * m_bBuffValue / 100;
int new_value = (sum_of_attr_value + attr.sValue) * m_bBuffValue / 100;
m_pBuffOwner->ApplyPoint(attr.bType, new_value - old_value);
sum_of_attr_value += attr.sValue;
}
}
}
}
void CBuffOnAttributes::ChangeBuffValue(BYTE bNewValue)
{
if (0 == m_bBuffValue)
On(bNewValue);
else if (0 == bNewValue)
Off();
else
{
// 기존에, m_map_additional_attrs의 값의 (m_bBuffValue)%만큼이 버프로 들어가 있었으므로,
// (bNewValue)%만큼으로 값을 변경함.
for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++)
{
int& sum_of_attr_value = it->second;
int old_value = sum_of_attr_value * m_bBuffValue / 100;
int new_value = sum_of_attr_value * bNewValue / 100;
m_pBuffOwner->ApplyPoint(it->first, -sum_of_attr_value * m_bBuffValue / 100);
}
m_bBuffValue = bNewValue;
}
}
void CBuffOnAttributes::GiveAllAttributes()
{
if (0 == m_bBuffValue)
return;
for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++)
{
BYTE apply_type = it->first;
int apply_value = it->second * m_bBuffValue / 100;
m_pBuffOwner->ApplyPoint(apply_type, apply_value);
}
}
bool CBuffOnAttributes::On(BYTE bValue)
{
if (0 != m_bBuffValue || 0 == bValue)
return false;
int n = m_p_vec_buff_wear_targets->size();
m_map_additional_attrs.clear();
for (int i = 0; i < n; i++)
{
LPITEM pItem = m_pBuffOwner->GetWear(m_p_vec_buff_wear_targets->at(i));
if (NULL != pItem)
{
int m = pItem->GetAttributeCount();
for (int j = 0; j < m; j++)
{
TPlayerItemAttribute attr = pItem->GetAttribute(j);
TMapAttr::iterator it = m_map_additional_attrs.find(attr.bType);
if (it != m_map_additional_attrs.end())
{
it->second += attr.sValue;
}
else
{
m_map_additional_attrs.insert(TMapAttr::value_type(attr.bType, attr.sValue));
}
}
}
}
for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++)
{
m_pBuffOwner->ApplyPoint(it->first, it->second * bValue / 100);
}
m_bBuffValue = bValue;
return true;
}
void CBuffOnAttributes::Off()
{
if (0 == m_bBuffValue)
return ;
for (TMapAttr::iterator it = m_map_additional_attrs.begin(); it != m_map_additional_attrs.end(); it++)
{
m_pBuffOwner->ApplyPoint(it->first, -it->second * m_bBuffValue / 100);
}
Initialize();
}

View File

@@ -0,0 +1,42 @@
#ifndef __METIN2_BUFF_ON_ATTRIBUTES_H
#define __METIN2_BUFF_ON_ATTRIBUTES_H
class CHARACTER;
class CBuffOnAttributes
{
public:
CBuffOnAttributes(LPCHARACTER pOwner, BYTE m_point_type, std::vector <BYTE>* vec_buff_targets);
~CBuffOnAttributes();
// 장착 중 이면서, m_p_vec_buff_wear_targets에 해당하는 아이템인 경우, 해당 아이템으로 인해 붙은 효과를 제거.
void RemoveBuffFromItem(LPITEM pItem);
// m_p_vec_buff_wear_targets에 해당하는 아이템인 경우, 해당 아이템의 attribute에 대한 효과 추가.
void AddBuffFromItem(LPITEM pItem);
// m_bBuffValue를 바꾸고, 버프의 값도 바꿈.
void ChangeBuffValue(BYTE bNewValue);
// CHRACTRE::ComputePoints에서 속성치를 초기화하고 다시 계산하므로,
// 버프 속성치들을 강제적으로 owner에게 줌.
void GiveAllAttributes();
// m_p_vec_buff_wear_targets에 해당하는 모든 아이템의 attribute를 type별로 합산하고,
// 그 attribute들의 (m_bBuffValue)% 만큼을 버프로 줌.
bool On(BYTE bValue);
// 버프 제거 후, 초기화
void Off();
void Initialize();
private:
LPCHARACTER m_pBuffOwner;
BYTE m_bPointType;
BYTE m_bBuffValue;
std::vector <BYTE>* m_p_vec_buff_wear_targets;
// apply_type, apply_value 페어의 맵
typedef std::map <BYTE, int> TMapAttr;
// m_p_vec_buff_wear_targets에 해당하는 모든 아이템의 attribute를 type별로 합산하여 가지고 있음.
TMapAttr m_map_additional_attrs;
};
#endif

View File

@@ -0,0 +1,38 @@
#include "stdafx.h"
#include "buffer_manager.h"
TEMP_BUFFER::TEMP_BUFFER(int Size, bool bForceDelete)
{
forceDelete = bForceDelete;
if (forceDelete)
Size = MAX(Size, 1024 * 128);
buf = buffer_new(Size);
}
TEMP_BUFFER::~TEMP_BUFFER()
{
buffer_delete(buf);
}
const void * TEMP_BUFFER::read_peek()
{
return (buffer_read_peek(buf));
}
void TEMP_BUFFER::write(const void * data, int size)
{
buffer_write(buf, data, size);
}
int TEMP_BUFFER::size()
{
return buffer_size(buf);
}
void TEMP_BUFFER::reset()
{
buffer_reset(buf);
}

22
src/game/buffer_manager.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef __INC_METIN_II_GAME_BUFFER_MANAGER_H__
#define __INC_METIN_II_GAME_BUFFER_MANAGER_H__
class TEMP_BUFFER
{
public:
TEMP_BUFFER(int Size = 8192, bool ForceDelete = false );
~TEMP_BUFFER();
const void * read_peek();
void write(const void * data, int size);
int size();
void reset();
LPBUFFER getptr() { return buf; }
protected:
LPBUFFER buf;
bool forceDelete;
};
#endif

1272
src/game/building.cpp Normal file

File diff suppressed because it is too large Load Diff

152
src/game/building.h Normal file
View File

@@ -0,0 +1,152 @@
#ifndef __INC_METIN_II_BUILDING_H__
#define __INC_METIN_II_BUILDING_H__
#include "../../common/building.h"
namespace building
{
class CLand;
class CObject : public CEntity
{
public:
CObject(TObject * pData, TObjectProto * pProto);
virtual ~CObject();
void Destroy();
virtual void EncodeInsertPacket(LPENTITY entity);
virtual void EncodeRemovePacket(LPENTITY entity);
DWORD GetID() { return m_data.dwID; }
void SetVID(DWORD dwVID);
DWORD GetVID() { return m_dwVID; }
bool Show(long lMapIndex, long x, long y);
void Save();
void SetLand(CLand * pkLand) { m_pkLand = pkLand; }
CLand * GetLand() { return m_pkLand; }
DWORD GetVnum() { return m_pProto ? m_pProto->dwVnum : 0; }
DWORD GetGroup() { return m_pProto ? m_pProto->dwGroupVnum : 0; }
void RegenNPC();
// BUILDING_NPC
void ApplySpecialEffect();
void RemoveSpecialEffect();
void Reconstruct(DWORD dwVnum);
LPCHARACTER GetNPC() { return m_chNPC; }
// END_OF_BUILDING_NPC
protected:
TObjectProto * m_pProto;
TObject m_data;
DWORD m_dwVID;
CLand * m_pkLand;
LPCHARACTER m_chNPC;
};
class CLand
{
public:
CLand(TLand * pData);
~CLand();
void Destroy();
const TLand & GetData();
void PutData(const TLand * data);
DWORD GetID() const { return m_data.dwID; }
void SetOwner(DWORD dwGID);
DWORD GetOwner() const { return m_data.dwGuildID; }
void InsertObject(LPOBJECT pkObj);
LPOBJECT FindObject(DWORD dwID);
LPOBJECT FindObjectByVID(DWORD dwVID);
LPOBJECT FindObjectByVnum(DWORD dwVnum);
LPOBJECT FindObjectByGroup(DWORD dwGroupVnum);
LPOBJECT FindObjectByNPC(LPCHARACTER npc);
void DeleteObject(DWORD dwID);
bool RequestCreateObject(DWORD dwVnum, long lMapIndex, long x, long y, float xRot, float yRot, float zRot, bool checkAnother);
void RequestDeleteObject(DWORD dwID);
void RequestDeleteObjectByVID(DWORD dwVID);
void RequestUpdate(DWORD dwGuild);
// LAND_CLEAR
void ClearLand();
// END_LAND_CLEAR
// BUILD_WALL
bool RequestCreateWall(long nMapIndex, float rot);
void RequestDeleteWall();
bool RequestCreateWallBlocks(DWORD dwVnum, long nMapIndex, char wallSize, bool doorEast, bool doorWest, bool doorSouth, bool doorNorth);
void RequestDeleteWallBlocks(DWORD dwVnum);
// END_BUILD_WALL
DWORD GetMapIndex() { return m_data.lMapIndex; }
protected:
TLand m_data;
std::map<DWORD, LPOBJECT> m_map_pkObject;
std::map<DWORD, LPOBJECT> m_map_pkObjectByVID;
// BUILD_WALL
private :
void DrawWall(DWORD dwVnum, long nMapIndex, long& centerX, long& centerY, char length, float zRot);
// END_BUILD_WALL
};
class CManager : public singleton<CManager>
{
public:
CManager();
virtual ~CManager();
void Destroy();
void FinalizeBoot();
bool LoadObjectProto(const TObjectProto * pProto, int size);
TObjectProto * GetObjectProto(DWORD dwVnum);
bool LoadLand(TLand * pTable);
CLand * FindLand(DWORD dwID);
CLand * FindLand(long lMapIndex, long x, long y);
CLand * FindLandByGuild(DWORD GID);
void UpdateLand(TLand * pTable);
bool LoadObject(TObject * pTable, bool isBoot=false);
void DeleteObject(DWORD dwID);
void UnregisterObject(LPOBJECT pkObj);
LPOBJECT FindObjectByVID(DWORD dwVID);
void SendLandList(LPDESC d, long lMapIndex);
// LAND_CLEAR
void ClearLand(DWORD dwLandID);
void ClearLandByGuildID(DWORD dwGuildID);
// END_LAND_CLEAR
protected:
std::vector<TObjectProto> m_vec_kObjectProto;
std::map<DWORD, TObjectProto *> m_map_pkObjectProto;
std::map<DWORD, CLand *> m_map_pkLand;
std::map<DWORD, LPOBJECT> m_map_pkObjByID;
std::map<DWORD, LPOBJECT> m_map_pkObjByVID;
};
}
#endif

1079
src/game/castle.cpp Normal file

File diff suppressed because it is too large Load Diff

77
src/game/castle.h Normal file
View File

@@ -0,0 +1,77 @@
/*********************************************************************
* date : 2007.04.07
* file : castle.h
* author : mhh
* description :
*/
#ifndef _castle_h_
#define _castle_h_
#define MAX_CASTLE_GUARD_REGION 4 // 경비병 배치 구역
#define MAX_CASTLE_GUARD_PER_REGION 10 // 한지역에 배치할수있는 경비병그룹
#define MAX_CASTLE_FROG 20 // 황금 두꺼비
#define MAX_CASTLE_TOWER 10 // 봉화 최대 소환 개수
#define MIN_CASTLE_TOWER 5 // 봉화 최소 소환 개수
#define CASTLE_FROG_PRICE 100000000 // 황금두꺼비 가격 (1억)
#define CASTLE_FROG_VNUM 11505 // 황금두꺼비 번호
//#define CASTLE_TOWER_VNUM 11506 // 봉화 번호
#define IS_CASTLE_MAP(map) (181==(map)||182==(map)||(183)==(map))
//#define IS_CASTLE_TOWER(vnum) (11506==(vnum)||11507==(vnum)||11508==(vnum)||11509==(vnum) || 11510==(vnum))
enum CASTLE_STATE
{
CASTLE_SIEGE_NONE, // 평화모드
CASTLE_SIEGE_STRUGGLE, // 공성중
CASTLE_SIEGE_END // 수성에 성공했다면 1시간동안 봉화를 깰 수 있다.
};
struct CASTLE_DATA
{
LPCHARACTER frog[MAX_CASTLE_FROG]; // 황금두꺼비
LPCHARACTER guard[MAX_CASTLE_GUARD_REGION][MAX_CASTLE_GUARD_PER_REGION]; // 경비병 리더
DWORD guard_group[MAX_CASTLE_GUARD_REGION][MAX_CASTLE_GUARD_PER_REGION]; // 경비병 리더
LPCHARACTER tower[MAX_CASTLE_TOWER]; // 봉화
LPEVENT siege_event;
LPEVENT stone_event;
};
/* extern functions */
bool castle_boot();
void castle_save();
int castle_siege(int empire, int tower_count);
void castle_start_siege(int empire, int tower_count);
void castle_end_siege();
LPCHARACTER castle_spawn_frog(int empire);
LPCHARACTER castle_spawn_guard(int empire, DWORD group_vnum, int region_index);
bool castle_spawn_tower(int empire, int tower_count);
void castle_guard_die(LPCHARACTER ch, LPCHARACTER killer);
void castle_frog_die(LPCHARACTER ch, LPCHARACTER killer);
void castle_tower_die(LPCHARACTER ch, LPCHARACTER killer);
int castle_guard_count(int empire, int region_index);
int castle_frog_count(int empire);
bool castle_is_guard_vnum(DWORD vnum);
int castle_cost_of_hiring_guard(DWORD vnum);
bool castle_can_attack(LPCHARACTER ch, LPCHARACTER victim);
bool castle_frog_to_empire_money(LPCHARACTER ch);
bool castle_is_my_castle(int empire, int map_index);
bool castle_is_tower_vnum(DWORD vnum);
#endif /* _castle_h_ */

7267
src/game/char.cpp Normal file

File diff suppressed because it is too large Load Diff

2065
src/game/char.h Normal file

File diff suppressed because it is too large Load Diff

842
src/game/char_affect.cpp Normal file
View File

@@ -0,0 +1,842 @@
#include "stdafx.h"
#include "config.h"
#include "char.h"
#include "char_manager.h"
#include "affect.h"
#include "packet.h"
#include "buffer_manager.h"
#include "desc_client.h"
#include "battle.h"
#include "guild.h"
#include "utils.h"
#include "locale_service.h"
#include "lua_incl.h"
#include "arena.h"
#include "horsename_manager.h"
#include "item.h"
#include "DragonSoul.h"
#define IS_NO_SAVE_AFFECT(type) ((type) == AFFECT_WAR_FLAG || (type) == AFFECT_REVIVE_INVISIBLE || ((type) >= AFFECT_PREMIUM_START && (type) <= AFFECT_PREMIUM_END))
#define IS_NO_CLEAR_ON_DEATH_AFFECT(type) ((type) == AFFECT_BLOCK_CHAT || ((type) >= 500 && (type) < 600))
void SendAffectRemovePacket(LPDESC d, DWORD pid, DWORD type, BYTE point)
{
TPacketGCAffectRemove ptoc;
ptoc.bHeader = HEADER_GC_AFFECT_REMOVE;
ptoc.dwType = type;
ptoc.bApplyOn = point;
d->Packet(&ptoc, sizeof(TPacketGCAffectRemove));
TPacketGDRemoveAffect ptod;
ptod.dwPID = pid;
ptod.dwType = type;
ptod.bApplyOn = point;
db_clientdesc->DBPacket(HEADER_GD_REMOVE_AFFECT, 0, &ptod, sizeof(ptod));
}
void SendAffectAddPacket(LPDESC d, CAffect * pkAff)
{
TPacketGCAffectAdd ptoc;
ptoc.bHeader = HEADER_GC_AFFECT_ADD;
ptoc.elem.dwType = pkAff->dwType;
ptoc.elem.bApplyOn = pkAff->bApplyOn;
ptoc.elem.lApplyValue = pkAff->lApplyValue;
ptoc.elem.dwFlag = pkAff->dwFlag;
ptoc.elem.lDuration = pkAff->lDuration;
ptoc.elem.lSPCost = pkAff->lSPCost;
d->Packet(&ptoc, sizeof(TPacketGCAffectAdd));
}
////////////////////////////////////////////////////////////////////
// Affect
CAffect * CHARACTER::FindAffect(DWORD dwType, BYTE bApply) const
{
itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
while (it != m_list_pkAffect.end())
{
CAffect * pkAffect = *it++;
if (pkAffect->dwType == dwType && (bApply == APPLY_NONE || bApply == pkAffect->bApplyOn))
return pkAffect;
}
return NULL;
}
EVENTFUNC(affect_event)
{
char_event_info* info = dynamic_cast<char_event_info*>( event->info );
if ( info == NULL )
{
sys_err( "affect_event> <Factor> Null pointer" );
return 0;
}
LPCHARACTER ch = info->ch;
if (ch == NULL) { // <Factor>
return 0;
}
if (!ch->UpdateAffect())
return 0;
else
return passes_per_sec; // 1초
}
bool CHARACTER::UpdateAffect()
{
// affect_event 에서 처리할 일은 아니지만, 1초짜리 이벤트에서 처리하는 것이
// 이것 뿐이라 여기서 물약 처리를 한다.
if (GetPoint(POINT_HP_RECOVERY) > 0)
{
if (GetMaxHP() <= GetHP())
{
PointChange(POINT_HP_RECOVERY, -GetPoint(POINT_HP_RECOVERY));
}
else
{
int iVal = 0;
if (LC_IsYMIR())
{
iVal = MIN(GetPoint(POINT_HP_RECOVERY), GetMaxHP() * 9 / 100);
}
else
{
iVal = MIN(GetPoint(POINT_HP_RECOVERY), GetMaxHP() * 7 / 100);
}
PointChange(POINT_HP, iVal);
PointChange(POINT_HP_RECOVERY, -iVal);
}
}
if (GetPoint(POINT_SP_RECOVERY) > 0)
{
if (GetMaxSP() <= GetSP())
PointChange(POINT_SP_RECOVERY, -GetPoint(POINT_SP_RECOVERY));
else
{
int iVal;
if (!g_iUseLocale)
iVal = MIN(GetPoint(POINT_SP_RECOVERY), GetMaxSP() * 7 / 100);
else
iVal = MIN(GetPoint(POINT_SP_RECOVERY), GetMaxSP() * 7 / 100);
PointChange(POINT_SP, iVal);
PointChange(POINT_SP_RECOVERY, -iVal);
}
}
if (GetPoint(POINT_HP_RECOVER_CONTINUE) > 0)
{
PointChange(POINT_HP, GetPoint(POINT_HP_RECOVER_CONTINUE));
}
if (GetPoint(POINT_SP_RECOVER_CONTINUE) > 0)
{
PointChange(POINT_SP, GetPoint(POINT_SP_RECOVER_CONTINUE));
}
AutoRecoveryItemProcess( AFFECT_AUTO_HP_RECOVERY );
AutoRecoveryItemProcess( AFFECT_AUTO_SP_RECOVERY );
// 스테미나 회복
if (GetMaxStamina() > GetStamina())
{
int iSec = (get_dword_time() - GetStopTime()) / 3000;
if (iSec)
PointChange(POINT_STAMINA, GetMaxStamina()/1);
}
// ProcessAffect는 affect가 없으면 true를 리턴한다.
if (ProcessAffect())
if (GetPoint(POINT_HP_RECOVERY) == 0 && GetPoint(POINT_SP_RECOVERY) == 0 && GetStamina() == GetMaxStamina())
{
m_pkAffectEvent = NULL;
return false;
}
return true;
}
void CHARACTER::StartAffectEvent()
{
if (m_pkAffectEvent)
return;
char_event_info* info = AllocEventInfo<char_event_info>();
info->ch = this;
m_pkAffectEvent = event_create(affect_event, info, passes_per_sec);
sys_log(1, "StartAffectEvent %s %p %p", GetName(), this, get_pointer(m_pkAffectEvent));
}
void CHARACTER::ClearAffect(bool bSave)
{
TAffectFlag afOld = m_afAffectFlag;
WORD wMovSpd = GetPoint(POINT_MOV_SPEED);
WORD wAttSpd = GetPoint(POINT_ATT_SPEED);
itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
while (it != m_list_pkAffect.end())
{
CAffect * pkAff = *it;
if (bSave)
{
if ( IS_NO_CLEAR_ON_DEATH_AFFECT(pkAff->dwType) || IS_NO_SAVE_AFFECT(pkAff->dwType) )
{
++it;
continue;
}
if (IsPC())
{
SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
}
}
ComputeAffect(pkAff, false);
it = m_list_pkAffect.erase(it);
CAffect::Release(pkAff);
}
if (afOld != m_afAffectFlag ||
wMovSpd != GetPoint(POINT_MOV_SPEED) ||
wAttSpd != GetPoint(POINT_ATT_SPEED))
UpdatePacket();
CheckMaximumPoints();
if (m_list_pkAffect.empty())
event_cancel(&m_pkAffectEvent);
}
int CHARACTER::ProcessAffect()
{
bool bDiff = false;
CAffect *pkAff = NULL;
//
// 프리미엄 처리
//
for (int i = 0; i <= PREMIUM_MAX_NUM; ++i)
{
int aff_idx = i + AFFECT_PREMIUM_START;
pkAff = FindAffect(aff_idx);
if (!pkAff)
continue;
int remain = GetPremiumRemainSeconds(i);
if (remain < 0)
{
RemoveAffect(aff_idx);
bDiff = true;
}
else
pkAff->lDuration = remain + 1;
}
////////// HAIR_AFFECT
pkAff = FindAffect(AFFECT_HAIR);
if (pkAff)
{
// IF HAIR_LIMIT_TIME() < CURRENT_TIME()
if ( this->GetQuestFlag("hair.limit_time") < get_global_time())
{
// SET HAIR NORMAL
this->SetPart(PART_HAIR, 0);
// REMOVE HAIR AFFECT
RemoveAffect(AFFECT_HAIR);
}
else
{
// INCREASE AFFECT DURATION
++(pkAff->lDuration);
}
}
////////// HAIR_AFFECT
//
CHorseNameManager::instance().Validate(this);
TAffectFlag afOld = m_afAffectFlag;
long lMovSpd = GetPoint(POINT_MOV_SPEED);
long lAttSpd = GetPoint(POINT_ATT_SPEED);
itertype(m_list_pkAffect) it;
it = m_list_pkAffect.begin();
while (it != m_list_pkAffect.end())
{
pkAff = *it;
bool bEnd = false;
if (pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END)
{
if (!GetGuild() || !GetGuild()->UnderAnyWar())
bEnd = true;
}
if (pkAff->lSPCost > 0)
{
if (GetSP() < pkAff->lSPCost)
bEnd = true;
else
PointChange(POINT_SP, -pkAff->lSPCost);
}
// AFFECT_DURATION_BUG_FIX
// 무한 효과 아이템도 시간을 줄인다.
// 시간을 매우 크게 잡기 때문에 상관 없을 것이라 생각됨.
if ( --pkAff->lDuration <= 0 )
{
bEnd = true;
}
// END_AFFECT_DURATION_BUG_FIX
if (bEnd)
{
it = m_list_pkAffect.erase(it);
ComputeAffect(pkAff, false);
bDiff = true;
if (IsPC())
{
SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
}
CAffect::Release(pkAff);
continue;
}
++it;
}
if (bDiff)
{
if (afOld != m_afAffectFlag ||
lMovSpd != GetPoint(POINT_MOV_SPEED) ||
lAttSpd != GetPoint(POINT_ATT_SPEED))
{
UpdatePacket();
}
CheckMaximumPoints();
}
if (m_list_pkAffect.empty())
return true;
return false;
}
void CHARACTER::SaveAffect()
{
TPacketGDAddAffect p;
itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
while (it != m_list_pkAffect.end())
{
CAffect * pkAff = *it++;
if (IS_NO_SAVE_AFFECT(pkAff->dwType))
continue;
sys_log(1, "AFFECT_SAVE: %u %u %d %d", pkAff->dwType, pkAff->bApplyOn, pkAff->lApplyValue, pkAff->lDuration);
p.dwPID = GetPlayerID();
p.elem.dwType = pkAff->dwType;
p.elem.bApplyOn = pkAff->bApplyOn;
p.elem.lApplyValue = pkAff->lApplyValue;
p.elem.dwFlag = pkAff->dwFlag;
p.elem.lDuration = pkAff->lDuration;
p.elem.lSPCost = pkAff->lSPCost;
db_clientdesc->DBPacket(HEADER_GD_ADD_AFFECT, 0, &p, sizeof(p));
}
}
EVENTINFO(load_affect_login_event_info)
{
DWORD pid;
DWORD count;
char* data;
load_affect_login_event_info()
: pid( 0 )
, count( 0 )
, data( 0 )
{
}
};
EVENTFUNC(load_affect_login_event)
{
load_affect_login_event_info* info = dynamic_cast<load_affect_login_event_info*>( event->info );
if ( info == NULL )
{
sys_err( "load_affect_login_event_info> <Factor> Null pointer" );
return 0;
}
DWORD dwPID = info->pid;
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(dwPID);
if (!ch)
{
M2_DELETE_ARRAY(info->data);
return 0;
}
LPDESC d = ch->GetDesc();
if (!d)
{
M2_DELETE_ARRAY(info->data);
return 0;
}
if (d->IsPhase(PHASE_HANDSHAKE) ||
d->IsPhase(PHASE_LOGIN) ||
d->IsPhase(PHASE_SELECT) ||
d->IsPhase(PHASE_DEAD) ||
d->IsPhase(PHASE_LOADING))
{
return PASSES_PER_SEC(1);
}
else if (d->IsPhase(PHASE_CLOSE))
{
M2_DELETE_ARRAY(info->data);
return 0;
}
else if (d->IsPhase(PHASE_GAME))
{
sys_log(1, "Affect Load by Event");
ch->LoadAffect(info->count, (TPacketAffectElement*)info->data);
M2_DELETE_ARRAY(info->data);
return 0;
}
else
{
sys_err("input_db.cpp:quest_login_event INVALID PHASE pid %d", ch->GetPlayerID());
M2_DELETE_ARRAY(info->data);
return 0;
}
}
void CHARACTER::LoadAffect(DWORD dwCount, TPacketAffectElement * pElements)
{
m_bIsLoadedAffect = false;
if (!GetDesc()->IsPhase(PHASE_GAME))
{
if (test_server)
sys_log(0, "LOAD_AFFECT: Creating Event", GetName(), dwCount);
load_affect_login_event_info* info = AllocEventInfo<load_affect_login_event_info>();
info->pid = GetPlayerID();
info->count = dwCount;
info->data = M2_NEW char[sizeof(TPacketAffectElement) * dwCount];
thecore_memcpy(info->data, pElements, sizeof(TPacketAffectElement) * dwCount);
event_create(load_affect_login_event, info, PASSES_PER_SEC(1));
return;
}
ClearAffect(true);
if (test_server)
sys_log(0, "LOAD_AFFECT: %s count %d", GetName(), dwCount);
TAffectFlag afOld = m_afAffectFlag;
long lMovSpd = GetPoint(POINT_MOV_SPEED);
long lAttSpd = GetPoint(POINT_ATT_SPEED);
for (DWORD i = 0; i < dwCount; ++i, ++pElements)
{
// 무영진은 로드하지않는다.
if (pElements->dwType == SKILL_MUYEONG)
continue;
if (AFFECT_AUTO_HP_RECOVERY == pElements->dwType || AFFECT_AUTO_SP_RECOVERY == pElements->dwType)
{
LPITEM item = FindItemByID( pElements->dwFlag );
if (NULL == item)
continue;
item->Lock(true);
}
if (pElements->bApplyOn >= POINT_MAX_NUM)
{
sys_err("invalid affect data %s ApplyOn %u ApplyValue %d",
GetName(), pElements->bApplyOn, pElements->lApplyValue);
continue;
}
if (test_server)
{
sys_log(0, "Load Affect : Affect %s %d %d", GetName(), pElements->dwType, pElements->bApplyOn );
}
CAffect* pkAff = CAffect::Acquire();
m_list_pkAffect.push_back(pkAff);
pkAff->dwType = pElements->dwType;
pkAff->bApplyOn = pElements->bApplyOn;
pkAff->lApplyValue = pElements->lApplyValue;
pkAff->dwFlag = pElements->dwFlag;
pkAff->lDuration = pElements->lDuration;
pkAff->lSPCost = pElements->lSPCost;
SendAffectAddPacket(GetDesc(), pkAff);
ComputeAffect(pkAff, true);
}
if ( CArenaManager::instance().IsArenaMap(GetMapIndex()) == true )
{
RemoveGoodAffect();
}
if (afOld != m_afAffectFlag || lMovSpd != GetPoint(POINT_MOV_SPEED) || lAttSpd != GetPoint(POINT_ATT_SPEED))
{
UpdatePacket();
}
StartAffectEvent();
m_bIsLoadedAffect = true;
// 용혼석 셋팅 로드 및 초기화
DragonSoul_Initialize();
}
bool CHARACTER::AddAffect(DWORD dwType, BYTE bApplyOn, long lApplyValue, DWORD dwFlag, long lDuration, long lSPCost, bool bOverride, bool IsCube )
{
// CHAT_BLOCK
if (dwType == AFFECT_BLOCK_CHAT && lDuration > 1)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("운영자 제제로 채팅이 금지 되었습니다."));
}
// END_OF_CHAT_BLOCK
if (lDuration == 0)
{
sys_err("Character::AddAffect lDuration == 0 type %d", lDuration, dwType);
lDuration = 1;
}
CAffect * pkAff = NULL;
if (IsCube)
pkAff = FindAffect(dwType,bApplyOn);
else
pkAff = FindAffect(dwType);
if (dwFlag == AFF_STUN)
{
if (m_posDest.x != GetX() || m_posDest.y != GetY())
{
m_posDest.x = m_posStart.x = GetX();
m_posDest.y = m_posStart.y = GetY();
battle_end(this);
SyncPacket();
}
}
// 이미 있는 효과를 덮어 쓰는 처리
if (pkAff && bOverride)
{
ComputeAffect(pkAff, false); // 일단 효과를 삭제하고
if (GetDesc())
SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
}
else
{
//
// 새 에펙를 추가
//
// NOTE: 따라서 같은 type 으로도 여러 에펙트를 붙을 수 있다.
//
pkAff = CAffect::Acquire();
m_list_pkAffect.push_back(pkAff);
}
sys_log(1, "AddAffect %s type %d apply %d %d flag %u duration %d", GetName(), dwType, bApplyOn, lApplyValue, dwFlag, lDuration);
sys_log(0, "AddAffect %s type %d apply %d %d flag %u duration %d", GetName(), dwType, bApplyOn, lApplyValue, dwFlag, lDuration);
pkAff->dwType = dwType;
pkAff->bApplyOn = bApplyOn;
pkAff->lApplyValue = lApplyValue;
pkAff->dwFlag = dwFlag;
pkAff->lDuration = lDuration;
pkAff->lSPCost = lSPCost;
WORD wMovSpd = GetPoint(POINT_MOV_SPEED);
WORD wAttSpd = GetPoint(POINT_ATT_SPEED);
ComputeAffect(pkAff, true);
if (pkAff->dwFlag || wMovSpd != GetPoint(POINT_MOV_SPEED) || wAttSpd != GetPoint(POINT_ATT_SPEED))
UpdatePacket();
StartAffectEvent();
if (IsPC())
{
SendAffectAddPacket(GetDesc(), pkAff);
if (IS_NO_SAVE_AFFECT(pkAff->dwType))
return true;
TPacketGDAddAffect p;
p.dwPID = GetPlayerID();
p.elem.dwType = pkAff->dwType;
p.elem.bApplyOn = pkAff->bApplyOn;
p.elem.lApplyValue = pkAff->lApplyValue;
p.elem.dwFlag = pkAff->dwFlag;
p.elem.lDuration = pkAff->lDuration;
p.elem.lSPCost = pkAff->lSPCost;
db_clientdesc->DBPacket(HEADER_GD_ADD_AFFECT, 0, &p, sizeof(p));
}
return true;
}
void CHARACTER::RefreshAffect()
{
itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
while (it != m_list_pkAffect.end())
{
CAffect * pkAff = *it++;
ComputeAffect(pkAff, true);
}
}
void CHARACTER::ComputeAffect(CAffect * pkAff, bool bAdd)
{
if (bAdd && pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END)
{
if (!GetGuild())
return;
if (!GetGuild()->UnderAnyWar())
return;
}
if (pkAff->dwFlag)
{
if (!bAdd)
m_afAffectFlag.Reset(pkAff->dwFlag);
else
m_afAffectFlag.Set(pkAff->dwFlag);
}
if (bAdd)
PointChange(pkAff->bApplyOn, pkAff->lApplyValue);
else
PointChange(pkAff->bApplyOn, -pkAff->lApplyValue);
if (pkAff->dwType == SKILL_MUYEONG)
{
if (bAdd)
StartMuyeongEvent();
else
StopMuyeongEvent();
}
}
bool CHARACTER::RemoveAffect(CAffect * pkAff)
{
if (!pkAff)
return false;
// AFFECT_BUF_FIX
m_list_pkAffect.remove(pkAff);
// END_OF_AFFECT_BUF_FIX
ComputeAffect(pkAff, false);
// 백기 버그 수정.
// 백기 버그는 버프 스킬 시전->둔갑->백기 사용(AFFECT_REVIVE_INVISIBLE) 후 바로 공격 할 경우에 발생한다.
// 원인은 둔갑을 시전하는 시점에, 버프 스킬 효과를 무시하고 둔갑 효과만 적용되게 되어있는데,
// 백기 사용 후 바로 공격하면 RemoveAffect가 불리게 되고, ComputePoints하면서 둔갑 효과 + 버프 스킬 효과가 된다.
// ComputePoints에서 둔갑 상태면 버프 스킬 효과 안 먹히도록 하면 되긴 하는데,
// ComputePoints는 광범위하게 사용되고 있어서 큰 변화를 주는 것이 꺼려진다.(어떤 side effect가 발생할지 알기 힘들다.)
// 따라서 AFFECT_REVIVE_INVISIBLE가 RemoveAffect로 삭제되는 경우만 수정한다.
// 시간이 다 되어 백기 효과가 풀리는 경우는 버그가 발생하지 않으므로 그와 똑같이 함.
// (ProcessAffect를 보면 시간이 다 되어서 Affect가 삭제되는 경우, ComputePoints를 부르지 않는다.)
if (AFFECT_REVIVE_INVISIBLE != pkAff->dwType)
{
ComputePoints();
}
else // Fix
{
UpdatePacket();
}
CheckMaximumPoints();
if (test_server)
sys_log(0, "AFFECT_REMOVE: %s (flag %u apply: %u)", GetName(), pkAff->dwFlag, pkAff->bApplyOn);
if (IsPC())
{
SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
}
CAffect::Release(pkAff);
return true;
}
bool CHARACTER::RemoveAffect(DWORD dwType)
{
// CHAT_BLOCK
if (dwType == AFFECT_BLOCK_CHAT)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("채팅 금지가 풀렸습니다."));
}
// END_OF_CHAT_BLOCK
bool flag = false;
CAffect * pkAff;
while ((pkAff = FindAffect(dwType)))
{
RemoveAffect(pkAff);
flag = true;
}
return flag;
}
bool CHARACTER::IsAffectFlag(DWORD dwAff) const
{
return m_afAffectFlag.IsSet(dwAff);
}
void CHARACTER::RemoveGoodAffect()
{
RemoveAffect(AFFECT_MOV_SPEED);
RemoveAffect(AFFECT_ATT_SPEED);
RemoveAffect(AFFECT_STR);
RemoveAffect(AFFECT_DEX);
RemoveAffect(AFFECT_INT);
RemoveAffect(AFFECT_CON);
RemoveAffect(AFFECT_CHINA_FIREWORK);
RemoveAffect(SKILL_JEONGWI);
RemoveAffect(SKILL_GEOMKYUNG);
RemoveAffect(SKILL_CHUNKEON);
RemoveAffect(SKILL_EUNHYUNG);
RemoveAffect(SKILL_GYEONGGONG);
RemoveAffect(SKILL_GWIGEOM);
RemoveAffect(SKILL_TERROR);
RemoveAffect(SKILL_JUMAGAP);
RemoveAffect(SKILL_MANASHILED);
RemoveAffect(SKILL_HOSIN);
RemoveAffect(SKILL_REFLECT);
RemoveAffect(SKILL_KWAESOK);
RemoveAffect(SKILL_JEUNGRYEOK);
RemoveAffect(SKILL_GICHEON);
}
bool CHARACTER::IsGoodAffect(BYTE bAffectType) const
{
switch (bAffectType)
{
case (AFFECT_MOV_SPEED):
case (AFFECT_ATT_SPEED):
case (AFFECT_STR):
case (AFFECT_DEX):
case (AFFECT_INT):
case (AFFECT_CON):
case (AFFECT_CHINA_FIREWORK):
case (SKILL_JEONGWI):
case (SKILL_GEOMKYUNG):
case (SKILL_CHUNKEON):
case (SKILL_EUNHYUNG):
case (SKILL_GYEONGGONG):
case (SKILL_GWIGEOM):
case (SKILL_TERROR):
case (SKILL_JUMAGAP):
case (SKILL_MANASHILED):
case (SKILL_HOSIN):
case (SKILL_REFLECT):
case (SKILL_KWAESOK):
case (SKILL_JEUNGRYEOK):
case (SKILL_GICHEON):
return true;
}
return false;
}
void CHARACTER::RemoveBadAffect()
{
sys_log(0, "RemoveBadAffect %s", GetName());
// 독
RemovePoison();
RemoveFire();
// 스턴 : Value%로 상대방을 5초간 머리 위에 별이 돌아간다. (때리면 1/2 확률로 풀림) AFF_STUN
RemoveAffect(AFFECT_STUN);
// 슬로우 : Value%로 상대방의 공속/이속 모두 느려진다. 수련도에 따라 달라짐 기술로 사용 한 경우에 AFF_SLOW
RemoveAffect(AFFECT_SLOW);
// 투속마령
RemoveAffect(SKILL_TUSOK);
// 저주
//RemoveAffect(SKILL_CURSE);
// 파법술
//RemoveAffect(SKILL_PABUP);
// 기절 : Value%로 상대방을 기절시킨다. 2초 AFF_FAINT
//RemoveAffect(AFFECT_FAINT);
// 다리묶임 : Value%로 상대방의 이동속도를 떨어트린다. 5초간 -40 AFF_WEB
//RemoveAffect(AFFECT_WEB);
// 잠들기 : Value%로 상대방을 10초간 잠재운다. (때리면 풀림) AFF_SLEEP
//RemoveAffect(AFFECT_SLEEP);
// 저주 : Value%로 상대방의 공등/방등 모두 떨어트린다. 수련도에 따라 달라짐 기술로 사용 한 경우에 AFF_CURSE
//RemoveAffect(AFFECT_CURSE);
// 마비 : Value%로 상대방을 4초간 마비시킨다. AFF_PARA
//RemoveAffect(AFFECT_PARALYZE);
// 부동박부 : 무당 기술
//RemoveAffect(SKILL_BUDONG);
}

3656
src/game/char_battle.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
#include "stdafx.h"
#include "config.h"
#include "char.h"
#include "char_manager.h"
#include "db.h"
#include "guild_manager.h"
#include "marriage.h"
/*
Return Value
0 : 알 수 없는 에러 or 쿼리 에러
1 : 동일한 제국으로 바꾸려고함
2 : 길드 가입한 캐릭터가 있음
3 : 결혼한 캐릭터가 있음
999 : 제국 이동 성공
*/
int CHARACTER::ChangeEmpire(BYTE empire)
{
if (GetEmpire() == empire)
return 1;
char szQuery[1024+1];
DWORD dwAID;
DWORD dwPID[4];
memset(dwPID, 0, sizeof(dwPID));
{
// 1. 내 계정의 모든 pid를 얻어 온다
snprintf(szQuery, sizeof(szQuery),
"SELECT id, pid1, pid2, pid3, pid4 FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());
std::unique_ptr<SQLMsg> msg(DBManager::instance().DirectQuery(szQuery));
if (msg->Get()->uiNumRows == 0)
{
return 0;
}
MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);
str_to_number(dwAID, row[0]);
str_to_number(dwPID[0], row[1]);
str_to_number(dwPID[1], row[2]);
str_to_number(dwPID[2], row[3]);
str_to_number(dwPID[3], row[4]);
}
const int loop = 4;
{
// 2. 각 캐릭터의 길드 정보를 얻어온다.
// 한 캐릭터라도 길드에 가입 되어 있다면, 제국 이동을 할 수 없다.
DWORD dwGuildID[4];
CGuild * pGuild[4];
SQLMsg * pMsg = NULL;
for (int i = 0; i < loop; ++i)
{
snprintf(szQuery, sizeof(szQuery), "SELECT guild_id FROM guild_member%s WHERE pid=%u", get_table_postfix(), dwPID[i]);
pMsg = DBManager::instance().DirectQuery(szQuery);
if (pMsg != NULL)
{
if (pMsg->Get()->uiNumRows > 0)
{
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
str_to_number(dwGuildID[i], row[0]);
pGuild[i] = CGuildManager::instance().FindGuild(dwGuildID[i]);
if (pGuild[i] != NULL)
{
M2_DELETE(pMsg);
return 2;
}
}
else
{
dwGuildID[i] = 0;
pGuild[i] = NULL;
}
M2_DELETE(pMsg);
}
}
}
{
// 3. 각 캐릭터의 결혼 정보를 얻어온다.
// 한 캐릭터라도 결혼 상태라면 제국 이동을 할 수 없다.
for (int i = 0; i < loop; ++i)
{
if (marriage::CManager::instance().IsEngagedOrMarried(dwPID[i]) == true)
return 3;
}
}
{
// 4. db의 제국 정보를 업데이트 한다.
snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());
std::unique_ptr<SQLMsg> msg(DBManager::instance().DirectQuery(szQuery));
if (msg->Get()->uiAffectedRows > 0)
{
// 5. 제국 변경 이력을 추가한다.
SetChangeEmpireCount();
return 999;
}
}
return 0;
}
int CHARACTER::GetChangeEmpireCount() const
{
char szQuery[1024+1];
DWORD dwAID = GetAID();
if (dwAID == 0)
return 0;
snprintf(szQuery, sizeof(szQuery), "SELECT change_count FROM change_empire WHERE account_id = %u", dwAID);
SQLMsg * pMsg = DBManager::instance().DirectQuery(szQuery);
if (pMsg != NULL)
{
if (pMsg->Get()->uiNumRows == 0)
{
M2_DELETE(pMsg);
return 0;
}
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
DWORD count = 0;
str_to_number(count, row[0]);
M2_DELETE(pMsg);
return count;
}
return 0;
}
void CHARACTER::SetChangeEmpireCount()
{
char szQuery[1024+1];
DWORD dwAID = GetAID();
if (dwAID == 0) return;
int count = GetChangeEmpireCount();
if (count == 0)
{
count++;
snprintf(szQuery, sizeof(szQuery), "INSERT INTO change_empire VALUES(%u, %d, NOW())", dwAID, count);
}
else
{
count++;
snprintf(szQuery, sizeof(szQuery), "UPDATE change_empire SET change_count=%d WHERE account_id=%u", count, dwAID);
}
std::unique_ptr<SQLMsg> pmsg(DBManager::instance().DirectQuery(szQuery));
}
DWORD CHARACTER::GetAID() const
{
char szQuery[1024+1];
DWORD dwAID = 0;
snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());
SQLMsg* pMsg = DBManager::instance().DirectQuery(szQuery);
if (pMsg != NULL)
{
if (pMsg->Get()->uiNumRows == 0)
{
M2_DELETE(pMsg);
return 0;
}
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
str_to_number(dwAID, row[0]);
M2_DELETE(pMsg);
return dwAID;
}
else
{
return 0;
}
}

View File

@@ -0,0 +1,145 @@
#include "stdafx.h"
#include "char.h"
#include "item.h"
#include "desc.h"
#include "DragonSoul.h"
#include "log.h"
// 용혼석 초기화
// 용혼석 on/off는 Affect로 저장되기 때문에,
// 용혼석 Affect가 있다면 덱에 있는 용혼석을 activate해야한다.
// 또한 용혼석 사용 자격은 QuestFlag로 저장해 놓았기 때문에,
// 퀘스트 Flag에서 용혼석 사용 자격을 읽어온다.
// 캐릭터의 affect, quest가 load 되기 전에 DragonSoul_Initialize를 호출하면 안된다.
// affect가 가장 마지막에 로드되어 LoadAffect에서 호출함.
void CHARACTER::DragonSoul_Initialize()
{
for (int i = INVENTORY_MAX_NUM + WEAR_MAX_NUM; i < DRAGON_SOUL_EQUIP_SLOT_END; i++)
{
LPITEM pItem = GetItem(TItemPos(INVENTORY, i));
if (NULL != pItem)
pItem->SetSocket(ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX, 0);
}
if (FindAffect(AFFECT_DRAGON_SOUL_DECK_0))
{
DragonSoul_ActivateDeck(DRAGON_SOUL_DECK_0);
}
else if (FindAffect(AFFECT_DRAGON_SOUL_DECK_1))
{
DragonSoul_ActivateDeck(DRAGON_SOUL_DECK_1);
}
}
int CHARACTER::DragonSoul_GetActiveDeck() const
{
return m_pointsInstant.iDragonSoulActiveDeck;
}
bool CHARACTER::DragonSoul_IsDeckActivated() const
{
return m_pointsInstant.iDragonSoulActiveDeck >= 0;
}
bool CHARACTER::DragonSoul_IsQualified() const
{
return FindAffect(AFFECT_DRAGON_SOUL_QUALIFIED) != NULL;
}
void CHARACTER::DragonSoul_GiveQualification()
{
if(NULL == FindAffect(AFFECT_DRAGON_SOUL_QUALIFIED))
{
LogManager::instance().CharLog(this, 0, "DS_QUALIFIED", "");
}
AddAffect(AFFECT_DRAGON_SOUL_QUALIFIED, APPLY_NONE, 0, AFF_NONE, INFINITE_AFFECT_DURATION, 0, false, false);
//SetQuestFlag("dragon_soul.is_qualified", 1);
//// 자격있다면 POINT_DRAGON_SOUL_IS_QUALIFIED는 무조건 1
//PointChange(POINT_DRAGON_SOUL_IS_QUALIFIED, 1 - GetPoint(POINT_DRAGON_SOUL_IS_QUALIFIED));
}
bool CHARACTER::DragonSoul_ActivateDeck(int deck_idx)
{
if (deck_idx < DRAGON_SOUL_DECK_0 || deck_idx >= DRAGON_SOUL_DECK_MAX_NUM)
{
return false;
}
if (DragonSoul_GetActiveDeck() == deck_idx)
return true;
DragonSoul_DeactivateAll();
if (!DragonSoul_IsQualified())
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("용혼석 상자가 활성화되지 않았습니다."));
return false;
}
AddAffect(AFFECT_DRAGON_SOUL_DECK_0 + deck_idx, APPLY_NONE, 0, 0, INFINITE_AFFECT_DURATION, 0, false);
m_pointsInstant.iDragonSoulActiveDeck = deck_idx;
for (int i = DRAGON_SOUL_EQUIP_SLOT_START + DS_SLOT_MAX * deck_idx;
i < DRAGON_SOUL_EQUIP_SLOT_START + DS_SLOT_MAX * (deck_idx + 1); i++)
{
LPITEM pItem = GetInventoryItem(i);
if (NULL != pItem)
DSManager::instance().ActivateDragonSoul(pItem);
}
return true; //fix
}
void CHARACTER::DragonSoul_DeactivateAll()
{
for (int i = DRAGON_SOUL_EQUIP_SLOT_START; i < DRAGON_SOUL_EQUIP_SLOT_END; i++)
{
DSManager::instance().DeactivateDragonSoul(GetInventoryItem(i), true);
}
m_pointsInstant.iDragonSoulActiveDeck = -1;
RemoveAffect(AFFECT_DRAGON_SOUL_DECK_0);
RemoveAffect(AFFECT_DRAGON_SOUL_DECK_1);
}
void CHARACTER::DragonSoul_CleanUp()
{
for (int i = DRAGON_SOUL_EQUIP_SLOT_START; i < DRAGON_SOUL_EQUIP_SLOT_END; i++)
{
DSManager::instance().DeactivateDragonSoul(GetInventoryItem(i), true);
}
}
bool CHARACTER::DragonSoul_RefineWindow_Open(LPENTITY pEntity)
{
if (NULL == m_pointsInstant.m_pDragonSoulRefineWindowOpener)
{
m_pointsInstant.m_pDragonSoulRefineWindowOpener = pEntity;
}
TPacketGCDragonSoulRefine PDS;
PDS.header = HEADER_GC_DRAGON_SOUL_REFINE;
PDS.bSubType = DS_SUB_HEADER_OPEN;
LPDESC d = GetDesc();
if (NULL == d)
{
sys_err ("User(%s)'s DESC is NULL POINT.", GetName());
return false;
}
d->Packet(&PDS, sizeof(PDS));
return true;
}
bool CHARACTER::DragonSoul_RefineWindow_Close()
{
m_pointsInstant.m_pDragonSoulRefineWindowOpener = NULL;
return true;
}
bool CHARACTER::DragonSoul_RefineWindow_CanRefine()
{
return NULL != m_pointsInstant.m_pDragonSoulRefineWindowOpener;
}

View File

@@ -0,0 +1,93 @@
#include "stdafx.h"
#include "char.h"
#include "config.h"
#include "event.h"
#include "HackShield.h"
#include "log.h"
#include "desc.h"
#include "packet.h"
EVENTINFO(hackshield_event_info)
{
DynamicCharacterPtr CharPtr;
};
EVENTFUNC(hackshield_event)
{
hackshield_event_info* info = dynamic_cast<hackshield_event_info*>( event->info );
if ( info == NULL )
{
sys_err( "hackshield_event> <Factor> Null pointer" );
return 0;
}
LPCHARACTER ch = info->CharPtr;
if (NULL == ch)
{
sys_err("HShield: character pointer is null");
return 0;
}
if (NULL == ch->GetDesc())
{
sys_err("HShield: character has no descriptor");
return 0;
}
if (false == ch->GetHackShieldCheckMode())
{
if (false == CHackShieldManager::instance().SendCheckPacket(ch))
{
return 0;
}
else
{
ch->SetHackShieldCheckMode(true);
return HackShield_CheckCycleTime;
}
}
sys_log(0, "HShield: no response from Player(%u)", ch->GetPlayerID());
LogManager::instance().HackShieldLog(0, ch);
ch->m_HackShieldCheckEvent = NULL;
ch->GetDesc()->SetPhase(PHASE_CLOSE);
return 0;
}
void CHARACTER::StartHackShieldCheckCycle(int seconds)
{
StopHackShieldCheckCycle();
if (false == isHackShieldEnable)
return;
hackshield_event_info* info = AllocEventInfo<hackshield_event_info>();
info->CharPtr = this;
m_HackShieldCheckEvent = event_create(hackshield_event, info, seconds);
sys_log(0, "HShield: StartHackShieldCheckCycle %d", seconds);
}
void CHARACTER::StopHackShieldCheckCycle()
{
if (NULL != m_HackShieldCheckEvent)
{
event_cancel(&m_HackShieldCheckEvent);
m_HackShieldCheckEvent = NULL;
sys_log(0, "HShield: StopHackShieldCheckCycle");
}
}

387
src/game/char_horse.cpp Normal file
View File

@@ -0,0 +1,387 @@
#include "stdafx.h"
#include "config.h"
#include "char.h"
#include "char_manager.h"
#include "packet.h"
#include "guild.h"
#include "vector.h"
#include "questmanager.h"
#include "item.h"
#include "horsename_manager.h"
#include "locale_service.h"
#include "arena.h"
#include "../../common/VnumHelper.h"
bool CHARACTER::StartRiding()
{
if (IsDead() == true)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("쓰러진 상태에서는 말에 탈 수 없습니다."));
return false;
}
if (IsPolymorphed())
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("변신 상태에서는 말에 탈 수 없습니다."));
return false;
}
// 턱시도 입은 상태의 말 타기 금지
LPITEM armor = GetWear(WEAR_BODY);
if (armor && (armor->GetVnum() >= 11901 && armor->GetVnum() <= 11904))
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("예복을 입은 상태에서 말을 탈 수 없습니다."));
return false;
}
if (LC_IsCanada() == true)
{
if (CArenaManager::instance().IsArenaMap(GetMapIndex()) == true)
return false;
}
DWORD dwMountVnum = m_chHorse ? m_chHorse->GetRaceNum() : GetMyHorseVnum();
if (false == CHorseRider::StartRiding())
{
if (GetHorseLevel() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 소유하고 있지 않습니다."));
else if (GetHorseHealth() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말이 죽어있는 상태 입니다."));
else if (GetHorseStamina() <= 0)
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말의 스테미너가 부족하여 말을 탈 수 없습니다."));
return false;
}
// 소환한 말 없애고
HorseSummon(false);
MountVnum(dwMountVnum);
if(test_server)
sys_log(0, "Ride Horse : %s ", GetName());
return true;
}
bool CHARACTER::StopRiding()
{
if (CHorseRider::StopRiding())
{
quest::CQuestManager::instance().Unmount(GetPlayerID());
if (!IsDead() && !IsStun())
{
DWORD dwOldVnum = GetMountVnum();
MountVnum(0);
// [NOTE] 말에서 내릴 땐 자기가 탔던걸 소환하도록 수정
HorseSummon(true, false, dwOldVnum);
}
else
{
m_dwMountVnum = 0;
ComputePoints();
UpdatePacket();
}
PointChange(POINT_ST, 0);
PointChange(POINT_DX, 0);
PointChange(POINT_HT, 0);
PointChange(POINT_IQ, 0);
return true;
}
return false;
}
EVENTFUNC(horse_dead_event)
{
char_event_info* info = dynamic_cast<char_event_info*>( event->info );
if ( info == NULL )
{
sys_err( "horse_dead_event> <Factor> Null pointer" );
return 0;
}
// <Factor>
LPCHARACTER ch = info->ch;
if (ch == NULL) {
return 0;
}
ch->HorseSummon(false);
return 0;
}
void CHARACTER::SetRider(LPCHARACTER ch)
{
if (m_chRider)
m_chRider->ClearHorseInfo();
m_chRider = ch;
if (m_chRider)
m_chRider->SendHorseInfo();
}
LPCHARACTER CHARACTER::GetRider() const
{
return m_chRider;
}
void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const char* pPetName)
{
if ( bSummon )
{
//NOTE : summon했는데 이미 horse가 있으면 아무것도 안한다.
if( m_chHorse != NULL )
return;
if (GetHorseLevel() <= 0)
return;
// 무언가를 타고 있다면 실패
if (IsRiding())
return;
sys_log(0, "HorseSummon : %s lv:%d bSummon:%d fromFar:%d", GetName(), GetLevel(), bSummon, bFromFar);
long x = GetX();
long y = GetY();
if (GetHorseHealth() <= 0)
bFromFar = false;
if (bFromFar)
{
x += (number(0, 1) * 2 - 1) * number(2000, 2500);
y += (number(0, 1) * 2 - 1) * number(2000, 2500);
}
else
{
x += number(-100, 100);
y += number(-100, 100);
}
m_chHorse = CHARACTER_MANAGER::instance().SpawnMob(
(0 == dwVnum) ? GetMyHorseVnum() : dwVnum,
GetMapIndex(),
x, y,
GetZ(), false, (int)(GetRotation()+180), false);
if (!m_chHorse)
{
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말 소환에 실패하였습니다."));
return;
}
if (GetHorseHealth() <= 0)
{
// 죽은거처럼 있게 하는 처리
m_chHorse->SetPosition(POS_DEAD);
// 일정시간있다 사라지게 하자.
char_event_info* info = AllocEventInfo<char_event_info>();
info->ch = this;
m_chHorse->m_pkDeadEvent = event_create(horse_dead_event, info, PASSES_PER_SEC(60));
}
m_chHorse->SetLevel(GetHorseLevel());
const char* pHorseName = CHorseNameManager::instance().GetHorseName(GetPlayerID());
if ( pHorseName != NULL && strlen(pHorseName) != 0 )
{
m_chHorse->m_stName = pHorseName;
}
else
{
m_chHorse->m_stName = GetName();
m_chHorse->m_stName += LC_TEXT("님의 말");
}
if (!m_chHorse->Show(GetMapIndex(), x, y, GetZ()))
{
M2_DESTROY_CHARACTER(m_chHorse);
sys_err("cannot show monster");
m_chHorse = NULL;
return;
}
if ((GetHorseHealth() <= 0))
{
TPacketGCDead pack;
pack.header = HEADER_GC_DEAD;
pack.vid = m_chHorse->GetVID();
PacketAround(&pack, sizeof(pack));
}
m_chHorse->SetRider(this);
}
else
{
if (!m_chHorse)
return;
LPCHARACTER chHorse = m_chHorse;
chHorse->SetRider(NULL); // m_chHorse assign to NULL
if (!bFromFar)
{
M2_DESTROY_CHARACTER(chHorse);
}
else
{
// 멀어지면서 사라지는 처리 하기
chHorse->SetNowWalking(false);
float fx, fy;
chHorse->SetRotation(GetDegreeFromPositionXY(chHorse->GetX(), chHorse->GetY(), GetX(), GetY())+180);
GetDeltaByDegree(chHorse->GetRotation(), 3500, &fx, &fy);
chHorse->Goto((long)(chHorse->GetX()+fx), (long) (chHorse->GetY()+fy));
chHorse->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
}
m_chHorse = NULL;
}
}
DWORD CHARACTER::GetMyHorseVnum() const
{
int delta = 0;
if (GetGuild())
{
++delta;
if (GetGuild()->GetMasterPID() == GetPlayerID())
++delta;
}
return c_aHorseStat[GetHorseLevel()].iNPCRace + delta;
}
void CHARACTER::HorseDie()
{
CHorseRider::HorseDie();
HorseSummon(false);
}
bool CHARACTER::ReviveHorse()
{
if (CHorseRider::ReviveHorse())
{
HorseSummon(false);
HorseSummon(true);
return true;
}
return false;
}
void CHARACTER::ClearHorseInfo()
{
if (!IsHorseRiding())
{
ChatPacket(CHAT_TYPE_COMMAND, "hide_horse_state");
m_bSendHorseLevel = 0;
m_bSendHorseHealthGrade = 0;
m_bSendHorseStaminaGrade = 0;
}
m_chHorse = NULL;
}
void CHARACTER::SendHorseInfo()
{
if (m_chHorse || IsHorseRiding())
{
int iHealthGrade;
int iStaminaGrade;
/*
HP
3: 70% < ~ <= 100%
2: 30% < ~ <= 70%
1: 0% < ~ <= 30%
0: 사망
STM
3: 71% < ~ <= 100%
2: 31% < ~ <= 70%
1: 10% < ~ <= 30%
0: ~ <= 10%
*/
if (GetHorseHealth() == 0)
iHealthGrade = 0;
else if (GetHorseHealth() * 10 <= GetHorseMaxHealth() * 3)
iHealthGrade = 1;
else if (GetHorseHealth() * 10 <= GetHorseMaxHealth() * 7)
iHealthGrade = 2;
else
iHealthGrade = 3;
if (GetHorseStamina() * 10 <= GetHorseMaxStamina())
iStaminaGrade = 0;
else if (GetHorseStamina() * 10 <= GetHorseMaxStamina() * 3)
iStaminaGrade = 1;
else if (GetHorseStamina() * 10 <= GetHorseMaxStamina() * 7)
iStaminaGrade = 2;
else
iStaminaGrade = 3;
if (m_bSendHorseLevel != GetHorseLevel() ||
m_bSendHorseHealthGrade != iHealthGrade ||
m_bSendHorseStaminaGrade != iStaminaGrade)
{
ChatPacket(CHAT_TYPE_COMMAND, "horse_state %d %d %d", GetHorseLevel(), iHealthGrade, iStaminaGrade);
// FIX : 클라이언트에 "말 상태 버프" 아이콘을 표시하지 않을 목적으로 함수 초입에 return함으로써 아래 코드를 무시한다면
// 말을 무한대로 소환하는 무시무시한 버그가 생김.. 정확한 원인은 파악 안해봐서 모름.
m_bSendHorseLevel = GetHorseLevel();
m_bSendHorseHealthGrade = iHealthGrade;
m_bSendHorseStaminaGrade = iStaminaGrade;
}
}
}
bool CHARACTER::CanUseHorseSkill()
{
if(IsRiding())
{
if (GetHorseGrade() == 3)
return true;
else
return false;
if(GetMountVnum())
{
if (GetMountVnum() >= 20209 && GetMountVnum() <= 20212)
return true;
//라마단 흑마
if (CMobVnumHelper::IsRamadanBlackHorse(GetMountVnum()))
return true;
}
else
return false;
}
return false;
}
void CHARACTER::SetHorseLevel(int iLevel)
{
CHorseRider::SetHorseLevel(iLevel);
SetSkillLevel(SKILL_HORSE, GetHorseLevel());
}

7584
src/game/char_item.cpp Normal file

File diff suppressed because it is too large Load Diff

1097
src/game/char_manager.cpp Normal file

File diff suppressed because it is too large Load Diff

175
src/game/char_manager.h Normal file
View File

@@ -0,0 +1,175 @@
#ifndef __INC_METIN_II_GAME_CHARACTER_MANAGER_H__
#define __INC_METIN_II_GAME_CHARACTER_MANAGER_H__
#ifdef M2_USE_POOL
#include "pool.h"
#endif
#include "../../common/stl.h"
#include "../../common/length.h"
#include "vid.h"
class CDungeon;
class CHARACTER;
class CharacterVectorInteractor;
class CHARACTER_MANAGER : public singleton<CHARACTER_MANAGER>
{
public:
typedef std::unordered_map<std::string, LPCHARACTER> NAME_MAP;
CHARACTER_MANAGER();
virtual ~CHARACTER_MANAGER();
void Destroy();
void GracefulShutdown(); // 정상적 셧다운할 때 사용. PC를 모두 저장시키고 Destroy 한다.
DWORD AllocVID();
LPCHARACTER CreateCharacter(const char * name, DWORD dwPID = 0);
#ifndef DEBUG_ALLOC
void DestroyCharacter(LPCHARACTER ch);
#else
void DestroyCharacter(LPCHARACTER ch, const char* file, size_t line);
#endif
void Update(int iPulse);
LPCHARACTER SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion = false, int iRot = -1, bool bShow = true);
LPCHARACTER SpawnMobRange(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, bool bIsException=false, bool bSpawnMotion = false , bool bAggressive = false);
LPCHARACTER SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL);
bool SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL);
bool SpawnMoveGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen = NULL, bool bAggressive_ = false);
LPCHARACTER SpawnMobRandomPosition(DWORD dwVnum, long lMapIndex);
void SelectStone(LPCHARACTER pkChrStone);
NAME_MAP & GetPCMap() { return m_map_pkPCChr; }
LPCHARACTER Find(DWORD dwVID);
LPCHARACTER Find(const VID & vid);
LPCHARACTER FindPC(const char * name);
LPCHARACTER FindByPID(DWORD dwPID);
bool AddToStateList(LPCHARACTER ch);
void RemoveFromStateList(LPCHARACTER ch);
// DelayedSave: 어떠한 루틴 내에서 저장을 해야 할 짓을 많이 하면 저장
// 쿼리가 너무 많아지므로 "저장을 한다" 라고 표시만 해두고 잠깐
// (예: 1 frame) 후에 저장시킨다.
void DelayedSave(LPCHARACTER ch);
bool FlushDelayedSave(LPCHARACTER ch); // Delayed 리스트에 있다면 지우고 저장한다. 끊김 처리시 사용 됨.
void ProcessDelayedSave();
template<class Func> Func for_each_pc(Func f);
void RegisterForMonsterLog(LPCHARACTER ch);
void UnregisterForMonsterLog(LPCHARACTER ch);
void PacketMonsterLog(LPCHARACTER ch, const void* buf, int size);
void KillLog(DWORD dwVnum);
void RegisterRaceNum(DWORD dwVnum);
void RegisterRaceNumMap(LPCHARACTER ch);
void UnregisterRaceNumMap(LPCHARACTER ch);
bool GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorInteractor & i);
LPCHARACTER FindSpecifyPC(unsigned int uiJobFlag, long lMapIndex, LPCHARACTER except=NULL, int iMinLevel = 1, int iMaxLevel = PLAYER_MAX_LEVEL_CONST);
void SetMobItemRate(int value) { m_iMobItemRate = value; }
void SetMobDamageRate(int value) { m_iMobDamageRate = value; }
void SetMobGoldAmountRate(int value) { m_iMobGoldAmountRate = value; }
void SetMobGoldDropRate(int value) { m_iMobGoldDropRate = value; }
void SetMobExpRate(int value) { m_iMobExpRate = value; }
void SetMobItemRatePremium(int value) { m_iMobItemRatePremium = value; }
void SetMobGoldAmountRatePremium(int value) { m_iMobGoldAmountRatePremium = value; }
void SetMobGoldDropRatePremium(int value) { m_iMobGoldDropRatePremium = value; }
void SetMobExpRatePremium(int value) { m_iMobExpRatePremium = value; }
void SetUserDamageRatePremium(int value) { m_iUserDamageRatePremium = value; }
void SetUserDamageRate(int value ) { m_iUserDamageRate = value; }
int GetMobItemRate(LPCHARACTER ch);
int GetMobDamageRate(LPCHARACTER ch);
int GetMobGoldAmountRate(LPCHARACTER ch);
int GetMobGoldDropRate(LPCHARACTER ch);
int GetMobExpRate(LPCHARACTER ch);
int GetUserDamageRate(LPCHARACTER ch);
void SendScriptToMap(long lMapIndex, const std::string & s);
bool BeginPendingDestroy();
void FlushPendingDestroy();
private:
int m_iMobItemRate;
int m_iMobDamageRate;
int m_iMobGoldAmountRate;
int m_iMobGoldDropRate;
int m_iMobExpRate;
int m_iMobItemRatePremium;
int m_iMobGoldAmountRatePremium;
int m_iMobGoldDropRatePremium;
int m_iMobExpRatePremium;
int m_iUserDamageRate;
int m_iUserDamageRatePremium;
int m_iVIDCount;
std::unordered_map<DWORD, LPCHARACTER> m_map_pkChrByVID;
std::unordered_map<DWORD, LPCHARACTER> m_map_pkChrByPID;
NAME_MAP m_map_pkPCChr;
char dummy1[1024]; // memory barrier
CHARACTER_SET m_set_pkChrState; // FSM이 돌아가고 있는 놈들
CHARACTER_SET m_set_pkChrForDelayedSave;
CHARACTER_SET m_set_pkChrMonsterLog;
LPCHARACTER m_pkChrSelectedStone;
std::map<DWORD, DWORD> m_map_dwMobKillCount;
std::set<DWORD> m_set_dwRegisteredRaceNum;
std::map<DWORD, CHARACTER_SET> m_map_pkChrByRaceNum;
bool m_bUsePendingDestroy;
CHARACTER_SET m_set_pkChrPendingDestroy;
#ifdef M2_USE_POOL
ObjectPool<CHARACTER> pool_;
#endif
};
template<class Func>
Func CHARACTER_MANAGER::for_each_pc(Func f)
{
std::unordered_map<DWORD, LPCHARACTER>::iterator it;
for (it = m_map_pkChrByPID.begin(); it != m_map_pkChrByPID.end(); ++it)
f(it->second);
return f;
}
class CharacterVectorInteractor : public CHARACTER_VECTOR
{
public:
CharacterVectorInteractor() : m_bMyBegin(false) { }
CharacterVectorInteractor(const CHARACTER_SET & r);
virtual ~CharacterVectorInteractor();
private:
bool m_bMyBegin;
};
#ifndef DEBUG_ALLOC
#define M2_DESTROY_CHARACTER(ptr) CHARACTER_MANAGER::instance().DestroyCharacter(ptr)
#else
#define M2_DESTROY_CHARACTER(ptr) CHARACTER_MANAGER::instance().DestroyCharacter(ptr, __FILE__, __LINE__)
#endif
#endif

156
src/game/char_quickslot.cpp Normal file
View File

@@ -0,0 +1,156 @@
#include "stdafx.h"
#include "constants.h"
#include "char.h"
#include "desc.h"
#include "desc_manager.h"
#include "packet.h"
#include "item.h"
/////////////////////////////////////////////////////////////////////////////
// QUICKSLOT HANDLING
/////////////////////////////////////////////////////////////////////////////
void CHARACTER::SyncQuickslot(BYTE bType, BYTE bOldPos, BYTE bNewPos) // bNewPos == 255 면 DELETE
{
if (bOldPos == bNewPos)
return;
for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i)
{
if (m_quickslot[i].type == bType && m_quickslot[i].pos == bOldPos)
{
if (bNewPos == 255)
DelQuickslot(i);
else
{
TQuickslot slot;
slot.type = bType;
slot.pos = bNewPos;
SetQuickslot(i, slot);
}
}
}
}
bool CHARACTER::GetQuickslot(BYTE pos, TQuickslot ** ppSlot)
{
if (pos >= QUICKSLOT_MAX_NUM)
return false;
*ppSlot = &m_quickslot[pos];
return true;
}
bool CHARACTER::SetQuickslot(BYTE pos, TQuickslot & rSlot)
{
struct packet_quickslot_add pack_quickslot_add;
if (pos >= QUICKSLOT_MAX_NUM)
return false;
if (rSlot.type >= QUICKSLOT_TYPE_MAX_NUM)
return false;
for (int i = 0; i < QUICKSLOT_MAX_NUM; ++i)
{
if (rSlot.type == 0)
continue;
else if (m_quickslot[i].type == rSlot.type && m_quickslot[i].pos == rSlot.pos)
DelQuickslot(i);
}
TItemPos srcCell(INVENTORY, rSlot.pos);
switch (rSlot.type)
{
case QUICKSLOT_TYPE_ITEM:
if (false == srcCell.IsDefaultInventoryPosition() && false == srcCell.IsBeltInventoryPosition())
return false;
break;
case QUICKSLOT_TYPE_SKILL:
if ((int) rSlot.pos >= SKILL_MAX_NUM)
return false;
break;
case QUICKSLOT_TYPE_COMMAND:
break;
default:
return false;
}
m_quickslot[pos] = rSlot;
if (GetDesc())
{
pack_quickslot_add.header = HEADER_GC_QUICKSLOT_ADD;
pack_quickslot_add.pos = pos;
pack_quickslot_add.slot = m_quickslot[pos];
GetDesc()->Packet(&pack_quickslot_add, sizeof(pack_quickslot_add));
}
return true;
}
bool CHARACTER::DelQuickslot(BYTE pos)
{
struct packet_quickslot_del pack_quickslot_del;
if (pos >= QUICKSLOT_MAX_NUM)
return false;
memset(&m_quickslot[pos], 0, sizeof(TQuickslot));
pack_quickslot_del.header = HEADER_GC_QUICKSLOT_DEL;
pack_quickslot_del.pos = pos;
GetDesc()->Packet(&pack_quickslot_del, sizeof(pack_quickslot_del));
return true;
}
bool CHARACTER::SwapQuickslot(BYTE a, BYTE b)
{
struct packet_quickslot_swap pack_quickslot_swap;
TQuickslot quickslot;
if (a >= QUICKSLOT_MAX_NUM || b >= QUICKSLOT_MAX_NUM)
return false;
// 퀵 슬롯 자리를 서로 바꾼다.
quickslot = m_quickslot[a];
m_quickslot[a] = m_quickslot[b];
m_quickslot[b] = quickslot;
pack_quickslot_swap.header = HEADER_GC_QUICKSLOT_SWAP;
pack_quickslot_swap.pos = a;
pack_quickslot_swap.pos_to = b;
GetDesc()->Packet(&pack_quickslot_swap, sizeof(pack_quickslot_swap));
return true;
}
void CHARACTER::ChainQuickslotItem(LPITEM pItem, BYTE bType, BYTE bOldPos)
{
if (pItem->IsDragonSoul())
return;
for ( int i=0; i < QUICKSLOT_MAX_NUM; ++i )
{
if ( m_quickslot[i].type == bType && m_quickslot[i].pos == bOldPos )
{
TQuickslot slot;
slot.type = bType;
slot.pos = pItem->GetCell();
SetQuickslot(i, slot);
break;
}
}
}

280
src/game/char_resist.cpp Normal file
View File

@@ -0,0 +1,280 @@
#include "stdafx.h"
#include "constants.h"
#include "config.h"
#include "char.h"
#include "char_manager.h"
#include "affect.h"
#include "locale_service.h"
// 독
const int poison_damage_rate[MOB_RANK_MAX_NUM] =
{
80, 50, 40, 30, 25, 1
};
int GetPoisonDamageRate(LPCHARACTER ch)
{
int iRate;
if (ch->IsPC())
{
if (LC_IsYMIR())
iRate = 40;
else
iRate = 50;
}
else
iRate = poison_damage_rate[ch->GetMobRank()];
iRate = MAX(0, iRate - ch->GetPoint(POINT_POISON_REDUCE));
return iRate;
}
EVENTINFO(TPoisonEventInfo)
{
DynamicCharacterPtr ch;
int count;
DWORD attacker_pid;
TPoisonEventInfo()
: ch()
, count(0)
, attacker_pid(0)
{
}
};
EVENTFUNC(poison_event)
{
TPoisonEventInfo * info = dynamic_cast<TPoisonEventInfo *>( event->info );
if ( info == NULL )
{
sys_err( "poison_event> <Factor> Null pointer" );
return 0;
}
LPCHARACTER ch = info->ch;
if (ch == NULL) { // <Factor>
return 0;
}
LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid);
int dam = ch->GetMaxHP() * GetPoisonDamageRate(ch) / 1000;
if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Poison Damage %d", dam);
if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_POISON))
{
ch->m_pkPoisonEvent = NULL;
return 0;
}
--info->count;
if (info->count)
return PASSES_PER_SEC(3);
else
{
ch->m_pkPoisonEvent = NULL;
return 0;
}
}
EVENTINFO(TFireEventInfo)
{
DynamicCharacterPtr ch;
int count;
int amount;
DWORD attacker_pid;
TFireEventInfo()
: ch()
, count(0)
, amount(0)
, attacker_pid(0)
{
}
};
EVENTFUNC(fire_event)
{
TFireEventInfo * info = dynamic_cast<TFireEventInfo *>( event->info );
if ( info == NULL )
{
sys_err( "fire_event> <Factor> Null pointer" );
return 0;
}
LPCHARACTER ch = info->ch;
if (ch == NULL) { // <Factor>
return 0;
}
LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid);
int dam = info->amount;
if (test_server) ch->ChatPacket(CHAT_TYPE_NOTICE, "Fire Damage %d", dam);
if (ch->Damage(pkAttacker, dam, DAMAGE_TYPE_FIRE))
{
ch->m_pkFireEvent = NULL;
return 0;
}
--info->count;
if (info->count)
return PASSES_PER_SEC(3);
else
{
ch->m_pkFireEvent = NULL;
return 0;
}
}
/*
LEVEL에 의한..
+8 0%
+7 5%
+6 10%
+5 30%
+4 50%
+3 70%
+2 80%
+1 90%
+0 100%
-1 100%
-2 100%
-3 100%
-4 100%
-5 100%
-6 100%
-7 100%
-8 100%
*/
static int poison_level_adjust[9] =
{
100, 90, 80, 70, 50, 30, 10, 5, 0
};
void CHARACTER::AttackedByFire(LPCHARACTER pkAttacker, int amount, int count)
{
if (m_pkFireEvent)
return;
AddAffect(AFFECT_FIRE, POINT_NONE, 0, AFF_FIRE, count*3+1, 0, true);
TFireEventInfo* info = AllocEventInfo<TFireEventInfo>();
info->ch = this;
info->count = count;
info->amount = amount;
info->attacker_pid = pkAttacker->GetPlayerID();
m_pkFireEvent = event_create(fire_event, info, 1);
}
void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker)
{
if (m_pkPoisonEvent)
return;
if (m_bHasPoisoned && !IsPC()) // 몬스터는 독이 한번만 걸린다.
return;
if (pkAttacker && pkAttacker->GetLevel() < GetLevel())
{
int delta = GetLevel() - pkAttacker->GetLevel();
if (delta > 8)
delta = 8;
if (number(1, 100) > poison_level_adjust[delta])
return;
}
/*if (IsImmune(IMMUNE_POISON))
return;*/
// 독 내성 굴림 실패, 독에 걸렸다!
m_bHasPoisoned = true;
AddAffect(AFFECT_POISON, POINT_NONE, 0, AFF_POISON, POISON_LENGTH + 1, 0, true);
TPoisonEventInfo* info = AllocEventInfo<TPoisonEventInfo>();
info->ch = this;
info->count = 10;
info->attacker_pid = pkAttacker?pkAttacker->GetPlayerID():0;
m_pkPoisonEvent = event_create(poison_event, info, 1);
if (test_server && pkAttacker)
{
char buf[256];
snprintf(buf, sizeof(buf), "POISON %s -> %s", pkAttacker->GetName(), GetName());
pkAttacker->ChatPacket(CHAT_TYPE_INFO, "%s", buf);
}
}
void CHARACTER::RemoveFire()
{
RemoveAffect(AFFECT_FIRE);
event_cancel(&m_pkFireEvent);
}
void CHARACTER::RemovePoison()
{
RemoveAffect(AFFECT_POISON);
event_cancel(&m_pkPoisonEvent);
}
void CHARACTER::ApplyMobAttribute(const TMobTable* table)
{
for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i)
{
if (table->cEnchants[i] != 0)
ApplyPoint(aiMobEnchantApplyIdx[i], table->cEnchants[i]);
}
for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i)
{
if (table->cResists[i] != 0)
ApplyPoint(aiMobResistsApplyIdx[i], table->cResists[i]);
}
}
bool CHARACTER::IsImmune(DWORD dwImmuneFlag)
{
if (IS_SET(m_pointsInstant.dwImmuneFlag, dwImmuneFlag))
{
int immune_pct = 90;
int percent = number(1, 100);
if (percent <= immune_pct) // 90% Immune
{
if (test_server && IsPC())
ChatPacket(CHAT_TYPE_PARTY, "<IMMUNE_SUCCESS> (%s)", GetName());
return true;
}
else
{
if (test_server && IsPC())
ChatPacket(CHAT_TYPE_PARTY, "<IMMUNE_FAIL> (%s)", GetName());
return false;
}
}
if (test_server && IsPC())
ChatPacket(CHAT_TYPE_PARTY, "<IMMUNE_FAIL> (%s) NO_IMMUNE_FLAG", GetName());
return false;
}

3600
src/game/char_skill.cpp Normal file

File diff suppressed because it is too large Load Diff

1230
src/game/char_state.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
#include "check_server.h"
std::vector<std::string> CheckServer::keys_;
bool CheckServer::fail_ = true;

48
src/game/check_server.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef _M2_CHECK_SERVER_KEY_H_
#define _M2_CHECK_SERVER_KEY_H_
#include <string>
#include <vector>
#include "CheckServerKey.h"
class CheckServer
{
public:
static FORCEINLINE void AddServerKey(const char* serverKey)
{
keys_.push_back(serverKey);
}
static FORCEINLINE bool CheckIp(const char* ip)
{
// 디파인 안걸리면 체크 안함
#ifndef _USE_SERVER_KEY_
fail_ = false;
return true;
#endif
for (int i = 0; i < keys_.size(); i++)
{
// 하나라도 맞는 서버키가 있으면 ok
std::string errorString;
if (CheckServerKey(keys_[i].c_str(), ip, "", errorString))
{
fail_ = false;
break;
}
}
return !IsFail();
}
static FORCEINLINE bool IsFail()
{
return fail_;
}
private:
static std::vector<std::string> keys_;
static bool fail_;
};
#endif // #ifndef _M2_CHECK_SERVER_KEY_H_

402
src/game/cipher.cpp Normal file
View File

@@ -0,0 +1,402 @@
#include "stdafx.h"
#include "cipher.h"
#ifdef _IMPROVED_PACKET_ENCRYPTION_
#include <cryptopp/modes.h>
#include <cryptopp/nbtheory.h>
#include <cryptopp/osrng.h>
// Diffie-Hellman key agreement
#include <cryptopp/dh.h>
#include <cryptopp/dh2.h>
// AES winner and candidates
//#include <cryptopp/aes.h>
#include <cryptopp/cast.h>
#include <cryptopp/rc6.h>
#include <cryptopp/mars.h>
#include <cryptopp/serpent.h>
#include <cryptopp/twofish.h>
// Other block ciphers
#include <cryptopp/blowfish.h>
#include <cryptopp/camellia.h>
#include <cryptopp/des.h>
#include <cryptopp/idea.h>
#include <cryptopp/rc5.h>
#include <cryptopp/seed.h>
#include <cryptopp/shacal2.h>
#include <cryptopp/skipjack.h>
#include <cryptopp/tea.h>
#include <cryptopp/cryptoppLibLink.h>
using namespace CryptoPP;
// Block cipher algorithm selector abstract base class.
struct BlockCipherAlgorithm {
enum {
kDefault, // to give more chances to default algorithm
// AES winner and candidates
// kAES, // Rijndael
kRC6,
kMARS,
kTwofish,
kSerpent,
kCAST256,
// Other block ciphers
kIDEA,
k3DES, // DES-EDE2
kCamellia,
kSEED,
kRC5,
kBlowfish,
kTEA,
// kSKIPJACK,
kSHACAL2,
// End sentinel
kMaxAlgorithms
};
BlockCipherAlgorithm() {}
virtual ~BlockCipherAlgorithm() {}
static BlockCipherAlgorithm* Pick(int hint);
virtual int GetBlockSize() const = 0;
virtual int GetDefaultKeyLength() const = 0;
virtual int GetIVLength() const = 0;
virtual SymmetricCipher* CreateEncoder(const byte* key, size_t keylen,
const byte* iv) const = 0;
virtual SymmetricCipher* CreateDecoder(const byte* key, size_t keylen,
const byte* iv) const = 0;
};
// Block cipher (with CTR mode) algorithm selector template class.
template<class T>
struct BlockCipherDetail : public BlockCipherAlgorithm {
BlockCipherDetail() {}
virtual ~BlockCipherDetail() {}
virtual int GetBlockSize() const { return T::BLOCKSIZE; }
virtual int GetDefaultKeyLength() const { return T::DEFAULT_KEYLENGTH; }
virtual int GetIVLength() const { return T::IV_LENGTH; }
virtual SymmetricCipher* CreateEncoder(const byte* key, size_t keylen,
const byte* iv) const {
return new typename CTR_Mode<T>::Encryption(key, keylen, iv);
}
virtual SymmetricCipher* CreateDecoder(const byte* key, size_t keylen,
const byte* iv) const {
return new typename CTR_Mode<T>::Decryption(key, keylen, iv);
}
};
// Key agreement scheme abstract class.
class KeyAgreement {
public:
KeyAgreement() {}
virtual ~KeyAgreement() {}
virtual size_t Prepare(void* buffer, size_t* length) = 0;
virtual bool Agree(size_t agreed_length, const void* buffer, size_t length) = 0;
const SecByteBlock& shared() const { return shared_; }
protected:
SecByteBlock shared_;
};
// Crypto++ Unified Diffie-Hellman key agreement scheme implementation.
class DH2KeyAgreement : public KeyAgreement {
public:
DH2KeyAgreement();
virtual ~DH2KeyAgreement();
virtual size_t Prepare(void* buffer, size_t* length);
virtual bool Agree(size_t agreed_length, const void* buffer, size_t length);
private:
DH dh_;
DH2 dh2_;
SecByteBlock spriv_key_;
SecByteBlock epriv_key_;
};
Cipher::Cipher()
: activated_(false), encoder_(NULL), decoder_(NULL), key_agreement_(NULL) {
}
Cipher::~Cipher() {
if (activated_) {
CleanUp();
}
}
void Cipher::CleanUp() {
if (encoder_ != NULL) {
delete encoder_;
encoder_ = NULL;
}
if (decoder_ != NULL) {
delete decoder_;
decoder_ = NULL;
}
if (key_agreement_ != NULL) {
delete key_agreement_;
key_agreement_ = NULL;
}
activated_ = false;
}
size_t Cipher::Prepare(void* buffer, size_t* length) {
assert(key_agreement_ == NULL);
key_agreement_ = new DH2KeyAgreement();
assert(key_agreement_ != NULL);
size_t agreed_length = key_agreement_->Prepare(buffer, length);
if (agreed_length == 0) {
delete key_agreement_;
key_agreement_ = NULL;
}
return agreed_length;
}
bool Cipher::Activate(bool polarity, size_t agreed_length,
const void* buffer, size_t length) {
assert(activated_ == false);
assert(key_agreement_ != NULL);
if (activated_ != false)
return false;
if (key_agreement_->Agree(agreed_length, buffer, length)) {
activated_ = SetUp(polarity);
}
delete key_agreement_;
key_agreement_ = NULL;
return activated_;
}
bool Cipher::SetUp(bool polarity) {
assert(key_agreement_ != NULL);
const SecByteBlock& shared = key_agreement_->shared();
// Pick a block cipher algorithm
if (shared.size() < 2) {
return false;
}
int hint_0 = shared.BytePtr()[*(shared.BytePtr()) % shared.size()];
int hint_1 = shared.BytePtr()[*(shared.BytePtr() + 1) % shared.size()];
BlockCipherAlgorithm* detail_0 = BlockCipherAlgorithm::Pick(hint_0);
BlockCipherAlgorithm* detail_1 = BlockCipherAlgorithm::Pick(hint_1);
assert(detail_0 != NULL);
assert(detail_1 != NULL);
std::unique_ptr<BlockCipherAlgorithm> algorithm_0(detail_0);
std::unique_ptr<BlockCipherAlgorithm> algorithm_1(detail_1);
const size_t key_length_0 = algorithm_0->GetDefaultKeyLength();
const size_t iv_length_0 = algorithm_0->GetBlockSize();
if (shared.size() < key_length_0 || shared.size() < iv_length_0) {
return false;
}
const size_t key_length_1 = algorithm_1->GetDefaultKeyLength();
const size_t iv_length_1 = algorithm_1->GetBlockSize();
if (shared.size() < key_length_1 || shared.size() < iv_length_1) {
return false;
}
// Pick encryption keys and initial vectors
SecByteBlock key_0(key_length_0), iv_0(iv_length_0);
SecByteBlock key_1(key_length_1), iv_1(iv_length_1);
size_t offset;
key_0.Assign(shared, key_length_0);
offset = key_length_0;
#ifdef __GNUC__
offset = std::min(key_length_0, shared.size() - key_length_1);
#else
offset = min(key_length_0, shared.size() - key_length_1);
#endif
key_1.Assign(shared.BytePtr() + offset, key_length_1);
offset = shared.size() - iv_length_0;
iv_0.Assign(shared.BytePtr() + offset, iv_length_0);
offset = (offset < iv_length_1 ? 0 : offset - iv_length_1);
iv_1.Assign(shared.BytePtr() + offset, iv_length_1);
// Create encryption/decryption objects
if (polarity) {
encoder_ = algorithm_1->CreateEncoder(key_1, key_1.size(), iv_1);
decoder_ = algorithm_0->CreateDecoder(key_0, key_0.size(), iv_0);
} else {
encoder_ = algorithm_0->CreateEncoder(key_0, key_0.size(), iv_0);
decoder_ = algorithm_1->CreateDecoder(key_1, key_1.size(), iv_1);
}
assert(encoder_ != NULL);
assert(decoder_ != NULL);
return true;
}
BlockCipherAlgorithm* BlockCipherAlgorithm::Pick(int hint) {
BlockCipherAlgorithm* detail;
int selector = hint % kMaxAlgorithms;
switch (selector) {
// case kAES:
// detail = new BlockCipherDetail<AES>();
break;
case kRC6:
detail = new BlockCipherDetail<RC6>();
break;
case kMARS:
detail = new BlockCipherDetail<MARS>();
break;
case kTwofish:
detail = new BlockCipherDetail<Twofish>();
break;
case kSerpent:
detail = new BlockCipherDetail<Serpent>();
break;
case kCAST256:
detail = new BlockCipherDetail<CAST256>();
break;
case kIDEA:
detail = new BlockCipherDetail<IDEA>();
break;
case k3DES:
detail = new BlockCipherDetail<DES_EDE2>();
break;
case kCamellia:
detail = new BlockCipherDetail<Camellia>();
break;
case kSEED:
detail = new BlockCipherDetail<SEED>();
break;
case kRC5:
detail = new BlockCipherDetail<RC5>();
break;
case kBlowfish:
detail = new BlockCipherDetail<Blowfish>();
break;
case kTEA:
detail = new BlockCipherDetail<TEA>();
break;
// case kSKIPJACK:
// detail = new BlockCipherDetail<SKIPJACK>();
// break;
case kSHACAL2:
detail = new BlockCipherDetail<SHACAL2>();
break;
case kDefault:
default:
detail = new BlockCipherDetail<Twofish>(); // default algorithm
break;
}
return detail;
}
DH2KeyAgreement::DH2KeyAgreement() : dh_(), dh2_(dh_) {
}
DH2KeyAgreement::~DH2KeyAgreement() {
}
size_t DH2KeyAgreement::Prepare(void* buffer, size_t* length) {
// RFC 5114, 1024-bit MODP Group with 160-bit Prime Order Subgroup
// http://tools.ietf.org/html/rfc5114#section-2.1
Integer p("0xB10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
"9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0"
"13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
"98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0"
"A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
"DF1FB2BC2E4A4371");
Integer g("0xA4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
"D6406CFF14266D31266FEA1E5C41564B777E690F5504F213"
"160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
"909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A"
"D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
"855E6EEB22B3B2E5");
Integer q("0xF518AA8781A8DF278ABA4E7D64B7CB9D49462353");
// Schnorr Group primes are of the form p = rq + 1, p and q prime. They
// provide a subgroup order. In the case of 1024-bit MODP Group, the
// security level is 80 bits (based on the 160-bit prime order subgroup).
// For a compare/contrast of using the maximum security level, see
// dh-unified.zip. Also see http://www.cryptopp.com/wiki/Diffie-Hellman
// and http://www.cryptopp.com/wiki/Security_level .
AutoSeededRandomPool rnd;
dh_.AccessGroupParameters().Initialize(p, q, g);
if(!dh_.GetGroupParameters().ValidateGroup(rnd, 3)) {
// Failed to validate prime and generator
return 0;
}
p = dh_.GetGroupParameters().GetModulus();
q = dh_.GetGroupParameters().GetSubgroupOrder();
g = dh_.GetGroupParameters().GetGenerator();
// http://groups.google.com/group/sci.crypt/browse_thread/thread/7dc7eeb04a09f0ce
Integer v = ModularExponentiation(g, q, p);
if(v != Integer::One()) {
// Failed to verify order of the subgroup
return 0;
}
//////////////////////////////////////////////////////////////
spriv_key_.New(dh2_.StaticPrivateKeyLength());
epriv_key_.New(dh2_.EphemeralPrivateKeyLength());
SecByteBlock spub_key(dh2_.StaticPublicKeyLength());
SecByteBlock epub_key(dh2_.EphemeralPublicKeyLength());
dh2_.GenerateStaticKeyPair(rnd, spriv_key_, spub_key);
dh2_.GenerateEphemeralKeyPair(rnd, epriv_key_, epub_key);
// Prepare key agreement data
const size_t spub_key_length = spub_key.size();
const size_t epub_key_length = epub_key.size();
const size_t data_length = spub_key_length + epub_key_length;
if (*length < data_length) {
// Not enough data buffer length
return 0;
}
*length = data_length;
byte* buf = (byte*)buffer;
memcpy(buf, spub_key.BytePtr(), spub_key_length);
memcpy(buf + spub_key_length, epub_key.BytePtr(), epub_key_length);
return dh2_.AgreedValueLength();
}
bool DH2KeyAgreement::Agree(size_t agreed_length, const void* buffer, size_t length) {
if (agreed_length != dh2_.AgreedValueLength()) {
// Shared secret size mismatch
return false;
}
const size_t spub_key_length = dh2_.StaticPublicKeyLength();
const size_t epub_key_length = dh2_.EphemeralPublicKeyLength();
if (length != (spub_key_length + epub_key_length)) {
// Wrong data length
return false;
}
shared_.New(dh2_.AgreedValueLength());
const byte* buf = (const byte*)buffer;
if (!dh2_.Agree(shared_, spriv_key_, epriv_key_, buf, buf + spub_key_length)) {
// Failed to reach shared secret
return false;
}
return true;
}
#endif // _IMPROVED_PACKET_ENCRYPTION_
// EOF cipher.cpp

60
src/game/cipher.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef __CIPHER_H__
#define __CIPHER_H__
#ifdef _IMPROVED_PACKET_ENCRYPTION_
#include <cryptopp/cryptlib.h>
using CryptoPP::byte;
// Forward declaration
class KeyAgreement;
// Communication channel encryption handler.
class Cipher {
public:
Cipher();
~Cipher();
void CleanUp();
// Returns agreed value length in bytes, or zero on failure.
size_t Prepare(void* buffer, size_t* length);
// Try to activate cipher algorithm with agreement data received from peer.
bool Activate(bool polarity, size_t agreed_length,
const void* buffer, size_t length);
// Encrypts the given block of data. (no padding required)
void Encrypt(void* buffer, size_t length) {
assert(activated_);
if (!activated_) {
return;
}
encoder_->ProcessData((byte*)buffer, (const byte*)buffer, length);
}
// Decrypts the given block of data. (no padding required)
void Decrypt(void* buffer, size_t length) {
assert(activated_);
if (!activated_) {
return;
}
decoder_->ProcessData((byte*)buffer, (const byte*)buffer, length);
}
bool activated() const { return activated_; }
void set_activated(bool value) { activated_ = value; }
bool IsKeyPrepared() { return key_agreement_ != NULL; }
private:
bool SetUp(bool polarity);
bool activated_;
CryptoPP::SymmetricCipher* encoder_;
CryptoPP::SymmetricCipher* decoder_;
KeyAgreement* key_agreement_;
};
#endif // _IMPROVED_PACKET_ENCRYPTION_
#endif // __CIPHER_H__

737
src/game/cmd.cpp Normal file
View File

@@ -0,0 +1,737 @@
#include "stdafx.h"
#include "utils.h"
#include "config.h"
#include "char.h"
#include "locale_service.h"
#include "log.h"
#include "desc.h"
ACMD(do_user_horse_ride);
ACMD(do_user_horse_back);
ACMD(do_user_horse_feed);
ACMD(do_pcbang_update);
ACMD(do_pcbang_check);
// ADD_COMMAND_SLOW_STUN
ACMD(do_slow);
ACMD(do_stun);
// END_OF_ADD_COMMAND_SLOW_STUN
ACMD(do_warp);
ACMD(do_goto);
ACMD(do_item);
ACMD(do_mob);
ACMD(do_mob_ld);
ACMD(do_mob_aggresive);
ACMD(do_mob_coward);
ACMD(do_mob_map);
ACMD(do_purge);
ACMD(do_weaken);
ACMD(do_item_purge);
ACMD(do_state);
ACMD(do_notice);
ACMD(do_map_notice);
ACMD(do_big_notice);
ACMD(do_who);
ACMD(do_user);
ACMD(do_disconnect);
ACMD(do_kill);
ACMD(do_emotion_allow);
ACMD(do_emotion);
ACMD(do_transfer);
ACMD(do_set);
ACMD(do_cmd);
ACMD(do_reset);
ACMD(do_greset);
ACMD(do_mount);
ACMD(do_fishing);
ACMD(do_refine_rod);
// REFINE_PICK
ACMD(do_max_pick);
ACMD(do_refine_pick);
// END_OF_REFINE_PICK
ACMD(do_fishing_simul);
ACMD(do_console);
ACMD(do_restart);
ACMD(do_advance);
ACMD(do_stat);
ACMD(do_respawn);
ACMD(do_skillup);
ACMD(do_guildskillup);
ACMD(do_pvp);
ACMD(do_point_reset);
ACMD(do_safebox_size);
ACMD(do_safebox_close);
ACMD(do_safebox_password);
ACMD(do_safebox_change_password);
ACMD(do_mall_password);
ACMD(do_mall_close);
ACMD(do_ungroup);
ACMD(do_makeguild);
ACMD(do_deleteguild);
ACMD(do_shutdown);
ACMD(do_group);
ACMD(do_group_random);
ACMD(do_invisibility);
ACMD(do_event_flag);
ACMD(do_get_event_flag);
ACMD(do_private);
ACMD(do_qf);
ACMD(do_clear_quest);
ACMD(do_book);
ACMD(do_reload);
ACMD(do_war);
ACMD(do_nowar);
ACMD(do_setskill);
ACMD(do_setskillother);
ACMD(do_level);
ACMD(do_polymorph);
ACMD(do_polymorph_item);
/*
ACMD(do_b1);
ACMD(do_b2);
ACMD(do_b3);
ACMD(do_b4);
ACMD(do_b5);
ACMD(do_b6);
ACMD(do_b7);
*/
ACMD(do_close_shop);
ACMD(do_set_walk_mode);
ACMD(do_set_run_mode);
ACMD(do_set_skill_group);
ACMD(do_set_skill_point);
ACMD(do_cooltime);
ACMD(do_detaillog);
ACMD(do_monsterlog);
ACMD(do_gwlist);
ACMD(do_stop_guild_war);
ACMD(do_cancel_guild_war);
ACMD(do_guild_state);
ACMD(do_pkmode);
ACMD(do_mobile);
ACMD(do_mobile_auth);
ACMD(do_messenger_auth);
ACMD(do_getqf);
ACMD(do_setqf);
ACMD(do_delqf);
ACMD(do_set_state);
ACMD(do_forgetme);
ACMD(do_aggregate);
ACMD(do_attract_ranger);
ACMD(do_pull_monster);
ACMD(do_setblockmode);
ACMD(do_priv_empire);
ACMD(do_priv_guild);
ACMD(do_mount_test);
ACMD(do_unmount);
ACMD(do_observer);
ACMD(do_observer_exit);
ACMD(do_socket_item);
ACMD(do_xmas);
ACMD(do_stat_minus);
ACMD(do_stat_reset);
ACMD(do_view_equip);
ACMD(do_block_chat);
ACMD(do_vote_block_chat);
// BLOCK_CHAT
ACMD(do_block_chat_list);
// END_OF_BLOCK_CHAT
ACMD(do_party_request);
ACMD(do_party_request_deny);
ACMD(do_party_request_accept);
ACMD(do_build);
ACMD(do_clear_land);
ACMD(do_horse_state);
ACMD(do_horse_level);
ACMD(do_horse_ride);
ACMD(do_horse_summon);
ACMD(do_horse_unsummon);
ACMD(do_horse_set_stat);
ACMD(do_save_attribute_to_image);
ACMD(do_affect_remove);
ACMD(do_change_attr);
ACMD(do_add_attr);
ACMD(do_add_socket);
ACMD(do_inputall)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("명령어를 모두 입력하세요."));
}
ACMD(do_show_arena_list);
ACMD(do_end_all_duel);
ACMD(do_end_duel);
ACMD(do_duel);
ACMD(do_stat_plus_amount);
ACMD(do_break_marriage);
ACMD(do_oxevent_show_quiz);
ACMD(do_oxevent_log);
ACMD(do_oxevent_get_attender);
ACMD(do_effect);
ACMD(do_threeway_war_info );
ACMD(do_threeway_war_myinfo );
//
//군주 전용기능
ACMD(do_monarch_warpto);
ACMD(do_monarch_transfer);
ACMD(do_monarch_info);
ACMD(do_elect);
ACMD(do_monarch_tax);
ACMD(do_monarch_mob);
ACMD(do_monarch_notice);
//군주 관리 기능
ACMD(do_rmcandidacy);
ACMD(do_setmonarch);
ACMD(do_rmmonarch);
ACMD(do_hair);
//gift notify quest command
ACMD(do_gift);
// 큐브관련
ACMD(do_inventory);
ACMD(do_cube);
// 공성전
ACMD(do_siege);
ACMD(do_temp);
ACMD(do_frog);
ACMD(do_check_monarch_money);
ACMD(do_reset_subskill );
ACMD(do_flush);
ACMD(do_eclipse);
ACMD(do_weeklyevent);
ACMD(do_event_helper);
ACMD(do_in_game_mall);
ACMD(do_get_mob_count);
ACMD(do_dice);
ACMD(do_special_item);
ACMD(do_click_mall);
ACMD(do_ride);
ACMD(do_get_item_id_list);
ACMD(do_set_socket);
#ifdef __AUCTION__
// temp_auction 임시
ACMD(do_get_auction_list);
ACMD (do_get_my_auction_list);
ACMD (do_get_my_purchase_list);
ACMD(do_get_item_id_list);
ACMD(do_enroll_auction);
ACMD (do_auction_bid);
ACMD (do_auction_impur);
ACMD (do_enroll_wish);
ACMD (do_enroll_sale);
ACMD (do_get_auctioned_item);
ACMD (do_buy_sold_item);
ACMD (do_cancel_auction);
ACMD (do_cancel_wish);
ACMD (do_cancel_sale);
ACMD (do_rebid);
ACMD (do_bid_cancel);
#endif
// 코스츔 상태보기 및 벗기
ACMD(do_costume);
ACMD(do_set_stat);
// 무적
ACMD (do_can_dead);
ACMD (do_full_set);
// 직군과 레벨에 따른 최고 아이템
ACMD (do_item_full_set);
// 직군에 따른 최고 옵션의 속성 셋팅
ACMD (do_attr_full_set);
// 모든 스킬 마스터
ACMD (do_all_skill_master);
// 아이템 착용. icon이 없어 클라에서 확인 할 수 없는 아이템 착용을 위해 만듦.
ACMD (do_use_item);
ACMD (do_dragon_soul);
ACMD (do_ds_list);
ACMD (do_clear_affect);
struct command_info cmd_info[] =
{
{ "!RESERVED!", NULL, 0, POS_DEAD, GM_IMPLEMENTOR }, /* 반드시 이 것이 처음이어야 한다. */
{ "who", do_who, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "war", do_war, 0, POS_DEAD, GM_PLAYER },
{ "warp", do_warp, 0, POS_DEAD, GM_LOW_WIZARD },
{ "user", do_user, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "notice", do_notice, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "notice_map", do_map_notice, 0, POS_DEAD, GM_LOW_WIZARD },
{ "big_notice", do_big_notice, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "nowar", do_nowar, 0, POS_DEAD, GM_PLAYER },
{ "purge", do_purge, 0, POS_DEAD, GM_WIZARD },
{ "weaken", do_weaken, 0, POS_DEAD, GM_GOD },
{ "dc", do_disconnect, 0, POS_DEAD, GM_LOW_WIZARD },
{ "transfer", do_transfer, 0, POS_DEAD, GM_LOW_WIZARD },
{ "goto", do_goto, 0, POS_DEAD, GM_LOW_WIZARD },
{ "level", do_level, 0, POS_DEAD, GM_LOW_WIZARD },
{ "eventflag", do_event_flag, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "geteventflag", do_get_event_flag, 0, POS_DEAD, GM_LOW_WIZARD },
{ "item", do_item, 0, POS_DEAD, GM_GOD },
{ "mob", do_mob, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "mob_ld", do_mob_ld, 0, POS_DEAD, GM_HIGH_WIZARD }, /* 몹의 위치와 방향을 설정해 소환 /mob_ld vnum x y dir */
{ "ma", do_mob_aggresive, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "mc", do_mob_coward, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "mm", do_mob_map, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "kill", do_kill, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "ipurge", do_item_purge, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "group", do_group, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "grrandom", do_group_random, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "set", do_set, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "reset", do_reset, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "greset", do_greset, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "advance", do_advance, 0, POS_DEAD, GM_GOD },
{ "book", do_book, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "console", do_console, 0, POS_DEAD, GM_LOW_WIZARD },
{ "shutdow", do_inputall, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "shutdown", do_shutdown, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "stat", do_stat, 0, POS_DEAD, GM_PLAYER },
{ "stat-", do_stat_minus, 0, POS_DEAD, GM_PLAYER },
{ "stat_reset", do_stat_reset, 0, POS_DEAD, GM_LOW_WIZARD },
{ "state", do_state, 0, POS_DEAD, GM_LOW_WIZARD },
// ADD_COMMAND_SLOW_STUN
{ "stun", do_stun, 0, POS_DEAD, GM_LOW_WIZARD },
{ "slow", do_slow, 0, POS_DEAD, GM_LOW_WIZARD },
// END_OF_ADD_COMMAND_SLOW_STUN
{ "respawn", do_respawn, 0, POS_DEAD, GM_WIZARD },
{ "makeguild", do_makeguild, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "deleteguild", do_deleteguild, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "mount", do_mount, 0, POS_MOUNTING, GM_PLAYER },
{ "restart_here", do_restart, SCMD_RESTART_HERE, POS_DEAD, GM_PLAYER },
{ "restart_town", do_restart, SCMD_RESTART_TOWN, POS_DEAD, GM_PLAYER },
{ "phase_selec", do_inputall, 0, POS_DEAD, GM_PLAYER },
{ "phase_select", do_cmd, SCMD_PHASE_SELECT, POS_DEAD, GM_PLAYER },
{ "qui", do_inputall, 0, POS_DEAD, GM_PLAYER },
{ "quit", do_cmd, SCMD_QUIT, POS_DEAD, GM_PLAYER },
{ "logou", do_inputall, 0, POS_DEAD, GM_PLAYER },
{ "logout", do_cmd, SCMD_LOGOUT, POS_DEAD, GM_PLAYER },
{ "skillup", do_skillup, 0, POS_DEAD, GM_PLAYER },
{ "gskillup", do_guildskillup, 0, POS_DEAD, GM_PLAYER },
{ "pvp", do_pvp, 0, POS_DEAD, GM_PLAYER },
{ "safebox", do_safebox_size, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "safebox_close", do_safebox_close, 0, POS_DEAD, GM_PLAYER },
{ "safebox_passwor",do_inputall, 0, POS_DEAD, GM_PLAYER },
{ "safebox_password",do_safebox_password, 0, POS_DEAD, GM_PLAYER },
{ "safebox_change_passwor", do_inputall, 0, POS_DEAD, GM_PLAYER },
{ "safebox_change_password", do_safebox_change_password, 0, POS_DEAD, GM_PLAYER },
{ "mall_passwor", do_inputall, 0, POS_DEAD, GM_PLAYER },
{ "mall_password", do_mall_password, 0, POS_DEAD, GM_PLAYER },
{ "mall_close", do_mall_close, 0, POS_DEAD, GM_PLAYER },
// Group Command
{ "ungroup", do_ungroup, 0, POS_DEAD, GM_PLAYER },
// REFINE_ROD_HACK_BUG_FIX
{ "refine_rod", do_refine_rod, 0, POS_DEAD, GM_IMPLEMENTOR },
// END_OF_REFINE_ROD_HACK_BUG_FIX
// REFINE_PICK
{ "refine_pick", do_refine_pick, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "max_pick", do_max_pick, 0, POS_DEAD, GM_IMPLEMENTOR },
// END_OF_REFINE_PICK
{ "fish_simul", do_fishing_simul, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "invisible", do_invisibility, 0, POS_DEAD, GM_LOW_WIZARD },
{ "qf", do_qf, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "clear_quest", do_clear_quest, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "close_shop", do_close_shop, 0, POS_DEAD, GM_PLAYER },
{ "set_walk_mode", do_set_walk_mode, 0, POS_DEAD, GM_PLAYER },
{ "set_run_mode", do_set_run_mode, 0, POS_DEAD, GM_PLAYER },
{ "setjob",do_set_skill_group, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "setskill", do_setskill, 0, POS_DEAD, GM_LOW_WIZARD },
{ "setskillother", do_setskillother, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "setskillpoint", do_set_skill_point, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "reload", do_reload, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "cooltime", do_cooltime, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "gwlist", do_gwlist, 0, POS_DEAD, GM_LOW_WIZARD },
{ "gwstop", do_stop_guild_war, 0, POS_DEAD, GM_LOW_WIZARD },
{ "gwcancel", do_cancel_guild_war, 0, POS_DEAD, GM_LOW_WIZARD },
{ "gstate", do_guild_state, 0, POS_DEAD, GM_LOW_WIZARD },
{ "pkmode", do_pkmode, 0, POS_DEAD, GM_PLAYER },
{ "messenger_auth", do_messenger_auth, 0, POS_DEAD, GM_PLAYER },
{ "getqf", do_getqf, 0, POS_DEAD, GM_LOW_WIZARD },
{ "setqf", do_setqf, 0, POS_DEAD, GM_LOW_WIZARD },
{ "delqf", do_delqf, 0, POS_DEAD, GM_LOW_WIZARD },
{ "set_state", do_set_state, 0, POS_DEAD, GM_LOW_WIZARD },
{ "로그를보여줘", do_detaillog, 0, POS_DEAD, GM_LOW_WIZARD },
{ "몬스터보여줘", do_monsterlog, 0, POS_DEAD, GM_LOW_WIZARD },
{ "detaillog", do_detaillog, 0, POS_DEAD, GM_LOW_WIZARD },
{ "monsterlog", do_monsterlog, 0, POS_DEAD, GM_LOW_WIZARD },
{ "forgetme", do_forgetme, 0, POS_DEAD, GM_LOW_WIZARD },
{ "aggregate", do_aggregate, 0, POS_DEAD, GM_LOW_WIZARD },
{ "attract_ranger", do_attract_ranger, 0, POS_DEAD, GM_LOW_WIZARD },
{ "pull_monster", do_pull_monster, 0, POS_DEAD, GM_LOW_WIZARD },
{ "setblockmode", do_setblockmode, 0, POS_DEAD, GM_PLAYER },
{ "polymorph", do_polymorph, 0, POS_DEAD, GM_LOW_WIZARD },
{ "polyitem", do_polymorph_item, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "priv_empire", do_priv_empire, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "priv_guild", do_priv_guild, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "mount_test", do_mount_test, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "unmount", do_unmount, 0, POS_DEAD, GM_PLAYER },
{ "private", do_private, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "party_request", do_party_request, 0, POS_DEAD, GM_PLAYER },
{ "party_request_accept", do_party_request_accept,0, POS_DEAD, GM_PLAYER },
{ "party_request_deny", do_party_request_deny,0, POS_DEAD, GM_PLAYER },
{ "observer", do_observer, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "observer_exit", do_observer_exit, 0, POS_DEAD, GM_PLAYER },
{ "socketitem", do_socket_item, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "saveati", do_save_attribute_to_image, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "xmas_boom", do_xmas, SCMD_XMAS_BOOM, POS_DEAD, GM_HIGH_WIZARD },
{ "xmas_snow", do_xmas, SCMD_XMAS_SNOW, POS_DEAD, GM_HIGH_WIZARD },
{ "xmas_santa", do_xmas, SCMD_XMAS_SANTA, POS_DEAD, GM_HIGH_WIZARD },
{ "view_equip", do_view_equip, 0, POS_DEAD, GM_PLAYER },
{ "jy", do_block_chat, 0, POS_DEAD, GM_HIGH_WIZARD },
// BLOCK_CHAT
{ "vote_block_chat", do_vote_block_chat, 0, POS_DEAD, GM_PLAYER },
{ "block_chat", do_block_chat, 0, POS_DEAD, GM_PLAYER },
{ "block_chat_list",do_block_chat_list, 0, POS_DEAD, GM_PLAYER },
// END_OF_BLOCK_CHAT
{ "build", do_build, 0, POS_DEAD, GM_PLAYER },
{ "clear_land", do_clear_land, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "affect_remove", do_affect_remove, 0, POS_DEAD, GM_LOW_WIZARD },
{ "horse_state", do_horse_state, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "horse_level", do_horse_level, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "horse_ride", do_horse_ride, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "horse_summon", do_horse_summon, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "horse_unsummon", do_horse_unsummon, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "horse_set_stat", do_horse_set_stat, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "pcbang_update", do_pcbang_update, 0, POS_DEAD, GM_LOW_WIZARD },
{ "pcbang_check", do_pcbang_check, 0, POS_DEAD, GM_LOW_WIZARD },
{ "emotion_allow", do_emotion_allow, 0, POS_FIGHTING, GM_PLAYER },
{ "kiss", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "slap", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "french_kiss", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "clap", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "cheer1", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "cheer2", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
// DANCE
{ "dance1", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "dance2", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "dance3", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "dance4", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "dance5", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "dance6", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
// END_OF_DANCE
{ "congratulation", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "forgive", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "angry", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "attractive", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "sad", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "shy", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "cheerup", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "banter", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "joy", do_emotion, 0, POS_FIGHTING, GM_PLAYER },
{ "change_attr", do_change_attr, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "add_attr", do_add_attr, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "add_socket", do_add_socket, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "user_horse_ride", do_user_horse_ride, 0, POS_FISHING, GM_PLAYER },
{ "user_horse_back", do_user_horse_back, 0, POS_FISHING, GM_PLAYER },
{ "user_horse_feed", do_user_horse_feed, 0, POS_FISHING, GM_PLAYER },
{ "show_arena_list", do_show_arena_list, 0, POS_DEAD, GM_LOW_WIZARD },
{ "end_all_duel", do_end_all_duel, 0, POS_DEAD, GM_LOW_WIZARD },
{ "end_duel", do_end_duel, 0, POS_DEAD, GM_LOW_WIZARD },
{ "duel", do_duel, 0, POS_DEAD, GM_LOW_WIZARD },
{ "con+", do_stat_plus_amount, POINT_HT, POS_DEAD, GM_LOW_WIZARD },
{ "int+", do_stat_plus_amount, POINT_IQ, POS_DEAD, GM_LOW_WIZARD },
{ "str+", do_stat_plus_amount, POINT_ST, POS_DEAD, GM_LOW_WIZARD },
{ "dex+", do_stat_plus_amount, POINT_DX, POS_DEAD, GM_LOW_WIZARD },
{ "break_marriage", do_break_marriage, 0, POS_DEAD, GM_LOW_WIZARD },
{ "show_quiz", do_oxevent_show_quiz, 0, POS_DEAD, GM_LOW_WIZARD },
{ "log_oxevent", do_oxevent_log, 0, POS_DEAD, GM_LOW_WIZARD },
{ "get_oxevent_att", do_oxevent_get_attender,0, POS_DEAD, GM_LOW_WIZARD },
{ "effect", do_effect, 0, POS_DEAD, GM_LOW_WIZARD },
{ "threeway_info", do_threeway_war_info, 0, POS_DEAD, GM_LOW_WIZARD},
{ "threeway_myinfo", do_threeway_war_myinfo, 0, POS_DEAD, GM_LOW_WIZARD},
{ "mto", do_monarch_warpto, 0, POS_DEAD, GM_PLAYER},
{ "mtr", do_monarch_transfer, 0, POS_DEAD, GM_PLAYER},
{ "minfo", do_monarch_info, 0, POS_DEAD, GM_PLAYER},
{ "mtax", do_monarch_tax, 0, POS_DEAD, GM_PLAYER},
{ "mmob", do_monarch_mob, 0, POS_DEAD, GM_PLAYER},
{ "elect", do_elect, 0, POS_DEAD, GM_HIGH_WIZARD},
{ "rmcandidacy", do_rmcandidacy, 0, POS_DEAD, GM_LOW_WIZARD},
{ "setmonarch", do_setmonarch, 0, POS_DEAD, GM_LOW_WIZARD},
{ "rmmonarch", do_rmmonarch, 0, POS_DEAD, GM_LOW_WIZARD},
{ "hair", do_hair, 0, POS_DEAD, GM_PLAYER },
{ "inventory", do_inventory, 0, POS_DEAD, GM_LOW_WIZARD },
{ "cube", do_cube, 0, POS_DEAD, GM_PLAYER },
{ "siege", do_siege, 0, POS_DEAD, GM_LOW_WIZARD },
{ "temp", do_temp, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "frog", do_frog, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "check_mmoney", do_check_monarch_money, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "reset_subskill", do_reset_subskill, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "flush", do_flush, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "gift", do_gift, 0, POS_DEAD, GM_PLAYER }, //gift
{ "mnotice", do_monarch_notice, 0, POS_DEAD, GM_PLAYER },
{ "eclipse", do_eclipse, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "weeklyevent", do_weeklyevent, 0, POS_DEAD, GM_LOW_WIZARD },
{ "eventhelper", do_event_helper, 0, POS_DEAD, GM_HIGH_WIZARD },
{ "in_game_mall", do_in_game_mall, 0, POS_DEAD, GM_PLAYER },
{ "get_mob_count", do_get_mob_count, 0, POS_DEAD, GM_LOW_WIZARD },
{ "dice", do_dice, 0, POS_DEAD, GM_PLAYER },
{ "주사위", do_dice, 0, POS_DEAD, GM_PLAYER },
{ "special_item", do_special_item, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "click_mall", do_click_mall, 0, POS_DEAD, GM_PLAYER },
{ "ride", do_ride, 0, POS_DEAD, GM_PLAYER },
{ "item_id_list", do_get_item_id_list, 0, POS_DEAD, GM_LOW_WIZARD },
{ "set_socket", do_set_socket, 0, POS_DEAD, GM_LOW_WIZARD },
#ifdef __AUCTION__
// auction 임시
{ "auction_list", do_get_auction_list, 0, POS_DEAD, GM_PLAYER },
{ "my_auction_list", do_get_my_auction_list, 0, POS_DEAD, GM_PLAYER },
{ "my_purchase_list", do_get_my_purchase_list, 0, POS_DEAD, GM_PLAYER },
{ "enroll_auction", do_enroll_auction, 0, POS_DEAD, GM_PLAYER },
{ "bid", do_auction_bid, 0, POS_DEAD, GM_PLAYER },
{ "impur", do_auction_impur, 0, POS_DEAD, GM_PLAYER },
{ "enroll_wish", do_enroll_wish, 0, POS_DEAD, GM_PLAYER },
{ "enroll_sale", do_enroll_sale, 0, POS_DEAD, GM_PLAYER },
{ "get_auctioned_item", do_get_auctioned_item, 0, POS_DEAD, GM_PLAYER },
{ "buy_sold_item", do_buy_sold_item, 0, POS_DEAD, GM_PLAYER },
{ "cancel_auction", do_cancel_auction, 0, POS_DEAD, GM_PLAYER },
{ "cancel_wish", do_cancel_wish, 0, POS_DEAD, GM_PLAYER },
{ "cancel_sale", do_cancel_sale, 0, POS_DEAD, GM_PLAYER },
{ "rebid", do_rebid, 0, POS_DEAD, GM_PLAYER },
{ "bid_cancel", do_bid_cancel, 0, POS_DEAD, GM_PLAYER },
#endif
{ "costume", do_costume, 0, POS_DEAD, GM_PLAYER },
{ "tcon", do_set_stat, POINT_HT, POS_DEAD, GM_LOW_WIZARD },
{ "tint", do_set_stat, POINT_IQ, POS_DEAD, GM_LOW_WIZARD },
{ "tstr", do_set_stat, POINT_ST, POS_DEAD, GM_LOW_WIZARD },
{ "tdex", do_set_stat, POINT_DX, POS_DEAD, GM_LOW_WIZARD },
{ "cannot_dead", do_can_dead, 1, POS_DEAD, GM_LOW_WIZARD},
{ "can_dead", do_can_dead, 0, POS_DEAD, GM_LOW_WIZARD},
{ "full_set", do_full_set, 0, POS_DEAD, GM_LOW_WIZARD},
{ "item_full_set", do_item_full_set, 0, POS_DEAD, GM_LOW_WIZARD},
{ "attr_full_set", do_attr_full_set, 0, POS_DEAD, GM_LOW_WIZARD},
{ "all_skill_master", do_all_skill_master, 0, POS_DEAD, GM_LOW_WIZARD},
{ "use_item", do_use_item, 0, POS_DEAD, GM_LOW_WIZARD},
{ "dragon_soul", do_dragon_soul, 0, POS_DEAD, GM_PLAYER },
{ "ds_list", do_ds_list, 0, POS_DEAD, GM_PLAYER },
{ "do_clear_affect", do_clear_affect, 0, POS_DEAD, GM_LOW_WIZARD},
{ "\n", NULL, 0, POS_DEAD, GM_IMPLEMENTOR } /* 반드시 이 것이 마지막이어야 한다. */
};
void interpreter_set_privilege(const char *cmd, int lvl)
{
int i;
for (i = 0; *cmd_info[i].command != '\n'; ++i)
{
if (!str_cmp(cmd, cmd_info[i].command))
{
cmd_info[i].gm_level = lvl;
sys_log(0, "Setting command privilege: %s -> %d", cmd, lvl);
break;
}
}
}
void double_dollar(const char *src, size_t src_len, char *dest, size_t dest_len)
{
const char * tmp = src;
size_t cur_len = 0;
// \0 넣을 자리 확보
dest_len -= 1;
while (src_len-- && *tmp)
{
if (*tmp == '$')
{
if (cur_len + 1 >= dest_len)
break;
*(dest++) = '$';
*(dest++) = *(tmp++);
cur_len += 2;
}
else
{
if (cur_len >= dest_len)
break;
*(dest++) = *(tmp++);
cur_len += 1;
}
}
*dest = '\0';
}
void interpret_command(LPCHARACTER ch, const char * argument, size_t len)
{
if (NULL == ch)
{
sys_err ("NULL CHRACTER");
return ;
}
char cmd[128 + 1]; // buffer overflow 문제가 생기지 않도록 일부러 길이를 짧게 잡음
char new_line[256 + 1];
const char * line;
int icmd;
if (len == 0 || !*argument)
return;
double_dollar(argument, len, new_line, sizeof(new_line));
size_t cmdlen;
line = first_cmd(new_line, cmd, sizeof(cmd), &cmdlen);
for (icmd = 1; *cmd_info[icmd].command != '\n'; ++icmd)
{
if (cmd_info[icmd].command_pointer == do_cmd)
{
if (!strcmp(cmd_info[icmd].command, cmd)) // do_cmd는 모든 명령어를 쳐야 할 수 있다.
break;
}
else if (!strncmp(cmd_info[icmd].command, cmd, cmdlen))
break;
}
if (ch->GetPosition() < cmd_info[icmd].minimum_position)
{
switch (ch->GetPosition())
{
case POS_MOUNTING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("탄 상태에서는 할 수 없습니다."));
break;
case POS_DEAD:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("쓰러진 상태에서는 할 수 없습니다."));
break;
case POS_SLEEPING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("꿈속에서 어떻게요?"));
break;
case POS_RESTING:
case POS_SITTING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("먼저 일어 나세요."));
break;
/*
case POS_FIGHTING:
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("목숨을 걸고 전투 중 입니다. 집중 하세요."));
break;
*/
default:
sys_err("unknown position %d", ch->GetPosition());
break;
}
return;
}
if (*cmd_info[icmd].command == '\n')
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다"));
return;
}
if (cmd_info[icmd].gm_level && cmd_info[icmd].gm_level > ch->GetGMLevel())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다"));
return;
}
if (strncmp("phase", cmd_info[icmd].command, 5) != 0) // 히든 명령어 처리
sys_log(0, "COMMAND: %s: %s", ch->GetName(), cmd_info[icmd].command);
((*cmd_info[icmd].command_pointer) (ch, line, icmd, cmd_info[icmd].subcmd));
if (ch->GetGMLevel() >= GM_LOW_WIZARD)
{
if (cmd_info[icmd].gm_level >= GM_LOW_WIZARD)
{
if (LC_IsEurope() == true || /*LC_IsNewCIBN() == true || */LC_IsCanada() == true || LC_IsBrazil() == true || LC_IsSingapore() == true )
{
char buf[1024];
snprintf( buf, sizeof(buf), "%s", argument );
LogManager::instance().GMCommandLog(ch->GetPlayerID(), ch->GetName(), ch->GetDesc()->GetHostName(), g_bChannel, buf);
}
}
}
}

69
src/game/cmd.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef __INC_METIN_II_GAME_CMD_H__
#define __INC_METIN_II_GAME_CMD_H__
#define ACMD(name) void (name)(LPCHARACTER ch, const char *argument, int cmd, int subcmd)
#define CMD_NAME(name) cmd_info[cmd].command
struct command_info
{
const char * command;
void (*command_pointer) (LPCHARACTER ch, const char *argument, int cmd, int subcmd);
int subcmd;
int minimum_position;
int gm_level;
};
extern struct command_info cmd_info[];
extern void interpret_command(LPCHARACTER ch, const char * argument, size_t len);
extern void interpreter_set_privilege(const char * cmd, int lvl);
enum SCMD_ACTION
{
SCMD_SLAP,
SCMD_KISS,
SCMD_FRENCH_KISS,
SCMD_HUG,
SCMD_LONG_HUG,
SCMD_SHOLDER,
SCMD_FOLD_ARM
};
enum SCMD_CMD
{
SCMD_LOGOUT,
SCMD_QUIT,
SCMD_PHASE_SELECT,
SCMD_SHUTDOWN,
};
enum SCMD_RESTART
{
SCMD_RESTART_TOWN,
SCMD_RESTART_HERE
};
enum SCMD_XMAS
{
SCMD_XMAS_BOOM,
SCMD_XMAS_SNOW,
SCMD_XMAS_SANTA,
};
extern void Shutdown(int iSec);
extern void SendNotice(const char * c_pszBuf); // 이 게임서버에만 공지
extern void SendLog(const char * c_pszBuf); // 운영자에게만 공지
extern void BroadcastNotice(const char * c_pszBuf); // 전 서버에 공지
extern void SendNoticeMap(const char* c_pszBuf, int nMapIndex, bool bBigFont); // 지정 맵에만 공지
extern void SendMonarchNotice(BYTE bEmpire, const char * c_pszBuf); // 같은 제국에게 공지
// LUA_ADD_BGM_INFO
void CHARACTER_SetBGMVolumeEnable();
void CHARACTER_AddBGMInfo(unsigned mapIndex, const char* name, float vol);
// END_OF_LUA_ADD_BGM_INFO
// LUA_ADD_GOTO_INFO
extern void CHARACTER_AddGotoInfo(const std::string& c_st_name, BYTE empire, int mapIndex, DWORD x, DWORD y);
// END_OF_LUA_ADD_GOTO_INFO
#endif

267
src/game/cmd_emotion.cpp Normal file
View File

@@ -0,0 +1,267 @@
#include "stdafx.h"
#include "utils.h"
#include "char.h"
#include "char_manager.h"
#include "motion.h"
#include "packet.h"
#include "buffer_manager.h"
#include "unique_item.h"
#include "wedding.h"
#define NEED_TARGET (1 << 0)
#define NEED_PC (1 << 1)
#define WOMAN_ONLY (1 << 2)
#define OTHER_SEX_ONLY (1 << 3)
#define SELF_DISARM (1 << 4)
#define TARGET_DISARM (1 << 5)
#define BOTH_DISARM (SELF_DISARM | TARGET_DISARM)
struct emotion_type_s
{
const char * command;
const char * command_to_client;
long flag;
float extra_delay;
} emotion_types[] = {
{ "키스", "french_kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 2.0f },
{ "뽀뽀", "kiss", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, 1.5f },
{ "따귀", "slap", NEED_PC | SELF_DISARM, 1.5f },
{ "박수", "clap", 0, 1.0f },
{ "", "cheer1", 0, 1.0f },
{ "만세", "cheer2", 0, 1.0f },
// DANCE
{ "댄스1", "dance1", 0, 1.0f },
{ "댄스2", "dance2", 0, 1.0f },
{ "댄스3", "dance3", 0, 1.0f },
{ "댄스4", "dance4", 0, 1.0f },
{ "댄스5", "dance5", 0, 1.0f },
{ "댄스6", "dance6", 0, 1.0f },
// END_OF_DANCE
{ "축하", "congratulation", 0, 1.0f },
{ "용서", "forgive", 0, 1.0f },
{ "화남", "angry", 0, 1.0f },
{ "유혹", "attractive", 0, 1.0f },
{ "슬픔", "sad", 0, 1.0f },
{ "브끄", "shy", 0, 1.0f },
{ "응원", "cheerup", 0, 1.0f },
{ "질투", "banter", 0, 1.0f },
{ "기쁨", "joy", 0, 1.0f },
{ "\n", "\n", 0, 0.0f },
/*
//{ "키스", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_FRENCH_KISS, 1.0f },
{ "뽀뽀", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_KISS, 1.0f },
{ "껴안기", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_SHORT_HUG, 1.0f },
{ "포옹", NEED_PC | OTHER_SEX_ONLY | BOTH_DISARM, MOTION_ACTION_LONG_HUG, 1.0f },
{ "어깨동무", NEED_PC | SELF_DISARM, MOTION_ACTION_PUT_ARMS_SHOULDER, 0.0f },
{ "팔짱", NEED_PC | WOMAN_ONLY | SELF_DISARM, MOTION_ACTION_FOLD_ARM, 0.0f },
{ "따귀", NEED_PC | SELF_DISARM, MOTION_ACTION_SLAP, 1.5f },
{ "휘파람", 0, MOTION_ACTION_CHEER_01, 0.0f },
{ "만세", 0, MOTION_ACTION_CHEER_02, 0.0f },
{ "박수", 0, MOTION_ACTION_CHEER_03, 0.0f },
{ "호호", 0, MOTION_ACTION_LAUGH_01, 0.0f },
{ "킥킥", 0, MOTION_ACTION_LAUGH_02, 0.0f },
{ "우하하", 0, MOTION_ACTION_LAUGH_03, 0.0f },
{ "엉엉", 0, MOTION_ACTION_CRY_01, 0.0f },
{ "흑흑", 0, MOTION_ACTION_CRY_02, 0.0f },
{ "인사", 0, MOTION_ACTION_GREETING_01, 0.0f },
{ "바이", 0, MOTION_ACTION_GREETING_02, 0.0f },
{ "정중인사", 0, MOTION_ACTION_GREETING_03, 0.0f },
{ "비난", 0, MOTION_ACTION_INSULT_01, 0.0f },
{ "모욕", SELF_DISARM, MOTION_ACTION_INSULT_02, 0.0f },
{ "우웩", 0, MOTION_ACTION_INSULT_03, 0.0f },
{ "갸우뚱", 0, MOTION_ACTION_ETC_01, 0.0f },
{ "끄덕끄덕", 0, MOTION_ACTION_ETC_02, 0.0f },
{ "도리도리", 0, MOTION_ACTION_ETC_03, 0.0f },
{ "긁적긁적", 0, MOTION_ACTION_ETC_04, 0.0f },
{ "퉤", 0, MOTION_ACTION_ETC_05, 0.0f },
{ "뿡", 0, MOTION_ACTION_ETC_06, 0.0f },
*/
};
std::set<std::pair<DWORD, DWORD> > s_emotion_set;
ACMD(do_emotion_allow)
{
if ( ch->GetArena() )
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("대련장에서 사용하실 수 없습니다."));
return;
}
char arg1[256];
one_argument(argument, arg1, sizeof(arg1));
if (!*arg1)
return;
DWORD val = 0; str_to_number(val, arg1);
s_emotion_set.insert(std::make_pair(ch->GetVID(), val));
}
bool CHARACTER_CanEmotion(CHARACTER& rch)
{
// 결혼식 맵에서는 사용할 수 있다.
if (marriage::WeddingManager::instance().IsWeddingMap(rch.GetMapIndex()))
return true;
// 열정의 가면 착용시 사용할 수 있다.
if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK))
return true;
if (rch.IsEquipUniqueItem(UNIQUE_ITEM_EMOTION_MASK2))
return true;
return false;
}
ACMD(do_emotion)
{
int i;
{
if (ch->IsRiding())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 탄 상태에서 감정표현을 할 수 없습니다."));
return;
}
}
for (i = 0; *emotion_types[i].command != '\n'; ++i)
{
if (!strcmp(cmd_info[cmd].command, emotion_types[i].command))
break;
if (!strcmp(cmd_info[cmd].command, emotion_types[i].command_to_client))
break;
}
if (*emotion_types[i].command == '\n')
{
sys_err("cannot find emotion");
return;
}
if (!CHARACTER_CanEmotion(*ch))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("열정의 가면을 착용시에만 할 수 있습니다."));
return;
}
if (IS_SET(emotion_types[i].flag, WOMAN_ONLY) && SEX_MALE==GET_SEX(ch))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("여자만 할 수 있습니다."));
return;
}
char arg1[256];
one_argument(argument, arg1, sizeof(arg1));
LPCHARACTER victim = NULL;
if (*arg1)
victim = ch->FindCharacterInView(arg1, IS_SET(emotion_types[i].flag, NEED_PC));
if (IS_SET(emotion_types[i].flag, NEED_TARGET | NEED_PC))
{
if (!victim)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 사람이 없습니다."));
return;
}
}
if (victim)
{
if (!victim->IsPC() || victim == ch)
return;
if (victim->IsRiding())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("말을 탄 상대와 감정표현을 할 수 없습니다."));
return;
}
long distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY());
if (distance < 10)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("너무 가까이 있습니다."));
return;
}
if (distance > 500)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("너무 멀리 있습니다"));
return;
}
if (IS_SET(emotion_types[i].flag, OTHER_SEX_ONLY))
{
if (GET_SEX(ch)==GET_SEX(victim))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이성간에만 할 수 있습니다."));
return;
}
}
if (IS_SET(emotion_types[i].flag, NEED_PC))
{
if (s_emotion_set.find(std::make_pair(victim->GetVID(), ch->GetVID())) == s_emotion_set.end())
{
if (true == marriage::CManager::instance().IsMarried( ch->GetPlayerID() ))
{
const marriage::TMarriage* marriageInfo = marriage::CManager::instance().Get( ch->GetPlayerID() );
const DWORD other = marriageInfo->GetOther( ch->GetPlayerID() );
if (0 == other || other != victim->GetPlayerID())
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 행동은 상호동의 하에 가능 합니다."));
return;
}
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("이 행동은 상호동의 하에 가능 합니다."));
return;
}
}
s_emotion_set.insert(std::make_pair(ch->GetVID(), victim->GetVID()));
}
}
char chatbuf[256+1];
int len = snprintf(chatbuf, sizeof(chatbuf), "%s %u %u",
emotion_types[i].command_to_client,
(DWORD) ch->GetVID(), victim ? (DWORD) victim->GetVID() : 0);
if (len < 0 || len >= (int) sizeof(chatbuf))
len = sizeof(chatbuf) - 1;
++len; // \0 문자 포함
TPacketGCChat pack_chat;
pack_chat.header = HEADER_GC_CHAT;
pack_chat.size = sizeof(TPacketGCChat) + len;
pack_chat.type = CHAT_TYPE_COMMAND;
pack_chat.id = 0;
TEMP_BUFFER buf;
buf.write(&pack_chat, sizeof(TPacketGCChat));
buf.write(chatbuf, len);
ch->PacketAround(buf.read_peek(), buf.size());
if (victim)
sys_log(1, "ACTION: %s TO %s", emotion_types[i].command, victim->GetName());
else
sys_log(1, "ACTION: %s", emotion_types[i].command);
}

2684
src/game/cmd_general.cpp Normal file

File diff suppressed because it is too large Load Diff

4411
src/game/cmd_gm.cpp Normal file

File diff suppressed because it is too large Load Diff

34
src/game/cmd_oxevent.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "stdafx.h"
#include "utils.h"
#include "char.h"
#include "OXEvent.h"
#include "questmanager.h"
#include "questlua.h"
#include "config.h"
#include "locale_service.h"
#include "cmd.h"
ACMD(do_oxevent_show_quiz)
{
ch->ChatPacket(CHAT_TYPE_INFO, "===== OX QUIZ LIST =====");
COXEventManager::instance().ShowQuizList(ch);
ch->ChatPacket(CHAT_TYPE_INFO, "===== OX QUIZ LIST END =====");
}
ACMD(do_oxevent_log)
{
if ( COXEventManager::instance().LogWinner() == false )
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("OX이벤트의 나머지 인원을 기록하였습니다."));
}
else
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("OX이벤트의 나머지 인원 기록을 실패했습니다."));
}
}
ACMD(do_oxevent_get_attender)
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("현재 남은 참가자수 : %d"), COXEventManager::instance().GetAttenderCount());
}

1372
src/game/config.cpp Normal file

File diff suppressed because it is too large Load Diff

124
src/game/config.h Normal file
View File

@@ -0,0 +1,124 @@
#ifndef __INC_METIN_II_GAME_CONFIG_H__
#define __INC_METIN_II_GAME_CONFIG_H__
enum
{
ADDRESS_MAX_LEN = 15
};
void config_init(const std::string& st_localeServiceName); // default "" is CONFIG
extern char sql_addr[256];
extern WORD mother_port;
extern WORD p2p_port;
extern char db_addr[ADDRESS_MAX_LEN + 1];
extern WORD db_port;
extern char teen_addr[ADDRESS_MAX_LEN + 1];
extern WORD teen_port;
extern char passpod_addr[ADDRESS_MAX_LEN + 1];
extern WORD passpod_port;
extern int passes_per_sec;
extern int save_event_second_cycle;
extern int ping_event_second_cycle;
extern int test_server;
extern bool guild_mark_server;
extern BYTE guild_mark_min_level;
extern bool distribution_test_server;
extern bool china_event_server;
extern bool g_bNoMoreClient;
extern bool g_bNoRegen;
extern bool g_bTrafficProfileOn; ///< true 이면 TrafficProfiler 를 켠다.
extern BYTE g_bChannel;
extern bool map_allow_find(int index);
extern void map_allow_copy(long * pl, int size);
extern bool no_wander;
extern int g_iUserLimit;
extern time_t g_global_time;
const char * get_table_postfix();
extern std::string g_stHostname;
extern std::string g_stLocale;
extern std::string g_stLocaleFilename;
extern char g_szPublicIP[16];
extern char g_szInternalIP[16];
#ifdef ENABLE_PROXY_IP
extern std::string g_stProxyIP;
#endif
extern int (*is_twobyte) (const char * str);
extern int (*check_name) (const char * str);
extern bool g_bSkillDisable;
extern int g_iFullUserCount;
extern int g_iBusyUserCount;
extern void LoadStateUserCount();
extern bool g_bEmpireWhisper;
extern BYTE g_bAuthServer;
extern BYTE g_bBilling;
extern BYTE PK_PROTECT_LEVEL;
extern void LoadValidCRCList();
extern bool IsValidProcessCRC(DWORD dwCRC);
extern bool IsValidFileCRC(DWORD dwCRC);
extern std::string g_stAuthMasterIP;
extern WORD g_wAuthMasterPort;
extern std::string g_stClientVersion;
extern bool g_bCheckClientVersion;
extern void CheckClientVersion();
extern std::string g_stQuestDir;
//extern std::string g_stQuestObjectDir;
extern std::set<std::string> g_setQuestObjectDir;
extern std::vector<std::string> g_stAdminPageIP;
extern std::string g_stAdminPagePassword;
extern int SPEEDHACK_LIMIT_COUNT;
extern int SPEEDHACK_LIMIT_BONUS;
extern int g_iSyncHackLimitCount;
extern int g_server_id;
extern std::string g_strWebMallURL;
extern int VIEW_RANGE;
extern int VIEW_BONUS_RANGE;
extern bool g_bCheckMultiHack;
extern bool g_protectNormalPlayer; // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
extern bool g_noticeBattleZone; // 중립지대에 입장하면 안내메세지를 알려줌
extern DWORD g_GoldDropTimeLimitValue;
// extern bool isHackShieldEnable;
// extern int HackShield_FirstCheckWaitTime;
// extern int HackShield_CheckCycleTime;
// extern bool bXTrapEnabled;
extern int gPlayerMaxLevel;
extern bool g_BlockCharCreation;
#endif /* __INC_METIN_II_GAME_CONFIG_H__ */

1297
src/game/constants.cpp Normal file

File diff suppressed because it is too large Load Diff

190
src/game/constants.h Normal file
View File

@@ -0,0 +1,190 @@
#ifndef __INC_METIN_II_GAME_CONSTANTS_H__
#define __INC_METIN_II_GAME_CONSTANTS_H__
#include "../../common/tables.h"
enum EMonsterChatState
{
MONSTER_CHAT_WAIT,
MONSTER_CHAT_ATTACK,
MONSTER_CHAT_CHASE,
MONSTER_CHAT_ATTACKED,
};
typedef struct SMobRankStat
{
int iGoldPercent; // µ·ÀÌ ³ª¿Ã È®·ü
} TMobRankStat;
typedef struct SMobStat
{
BYTE byLevel;
WORD HP;
DWORD dwExp;
WORD wDefGrade;
} TMobStat;
typedef struct SBattleTypeStat
{
int AttGradeBias;
int DefGradeBias;
int MagicAttGradeBias;
int MagicDefGradeBias;
} TBattleTypeStat;
typedef struct SJobInitialPoints
{
int st, ht, dx, iq;
int max_hp, max_sp;
int hp_per_ht, sp_per_iq;
int hp_per_lv_begin, hp_per_lv_end;
int sp_per_lv_begin, sp_per_lv_end;
int max_stamina;
int stamina_per_con;
int stamina_per_lv_begin, stamina_per_lv_end;
} TJobInitialPoints;
typedef struct __coord
{
int x, y;
} Coord;
typedef struct SApplyInfo
{
BYTE bPointType; // APPLY -> POINT
} TApplyInfo;
enum {
FORTUNE_BIG_LUCK,
FORTUNE_LUCK,
FORTUNE_SMALL_LUCK,
FORTUNE_NORMAL,
FORTUNE_SMALL_BAD_LUCK,
FORTUNE_BAD_LUCK,
FORTUNE_BIG_BAD_LUCK,
FORTUNE_MAX_NUM,
};
const int STONE_INFO_MAX_NUM = 10;
const int STONE_LEVEL_MAX_NUM = 4;
struct SStoneDropInfo
{
DWORD dwMobVnum;
int iDropPct;
int iLevelPct[STONE_LEVEL_MAX_NUM+1];
};
inline bool operator < (const SStoneDropInfo& l, DWORD r)
{
return l.dwMobVnum < r;
}
inline bool operator < (DWORD l, const SStoneDropInfo& r)
{
return l < r.dwMobVnum;
}
inline bool operator < (const SStoneDropInfo& l, const SStoneDropInfo& r)
{
return l.dwMobVnum < r.dwMobVnum;
}
extern const TApplyInfo aApplyInfo[MAX_APPLY_NUM];
extern const TMobRankStat MobRankStats[MOB_RANK_MAX_NUM];
extern TBattleTypeStat BattleTypeStats[BATTLE_TYPE_MAX_NUM];
extern const DWORD party_exp_distribute_table[PLAYER_MAX_LEVEL_CONST + 1];
extern const DWORD exp_table_euckr[PLAYER_EXP_TABLE_MAX + 1];
extern const DWORD exp_table_common[PLAYER_EXP_TABLE_MAX + 1];
extern const DWORD exp_table_newcibn[PLAYER_EXP_TABLE_MAX + 1];
extern const DWORD* exp_table;
extern const DWORD guild_exp_table[GUILD_MAX_LEVEL + 1];
extern const DWORD guild_exp_table2[GUILD_MAX_LEVEL + 1];
#define MAX_EXP_DELTA_OF_LEV 31
#define PERCENT_LVDELTA(me, victim) aiPercentByDeltaLev[MINMAX(0, (victim + 15) - me, MAX_EXP_DELTA_OF_LEV - 1)]
#define PERCENT_LVDELTA_BOSS(me, victim) aiPercentByDeltaLevForBoss[MINMAX(0, (victim + 15) - me, MAX_EXP_DELTA_OF_LEV - 1)]
#define CALCULATE_VALUE_LVDELTA(me, victim, val) ((val * PERCENT_LVDELTA(me, victim)) / 100)
extern const int aiPercentByDeltaLev_euckr[MAX_EXP_DELTA_OF_LEV];
extern const int aiPercentByDeltaLevForBoss_euckr[MAX_EXP_DELTA_OF_LEV];
extern const int * aiPercentByDeltaLev;
extern const int * aiPercentByDeltaLevForBoss;
#define ARROUND_COORD_MAX_NUM 161
extern Coord aArroundCoords[ARROUND_COORD_MAX_NUM];
extern TJobInitialPoints JobInitialPoints[JOB_MAX_NUM];
extern const int aiMobEnchantApplyIdx[MOB_ENCHANTS_MAX_NUM];
extern const int aiMobResistsApplyIdx[MOB_RESISTS_MAX_NUM];
extern const int aSkillAttackAffectProbByRank[MOB_RANK_MAX_NUM];
extern const int aiItemMagicAttributePercentHigh[ITEM_ATTRIBUTE_MAX_LEVEL]; // 1°³±îÁö
extern const int aiItemMagicAttributePercentLow[ITEM_ATTRIBUTE_MAX_LEVEL];
extern const int aiItemAttributeAddPercent[ITEM_ATTRIBUTE_MAX_NUM];
extern const int aiWeaponSocketQty[WEAPON_NUM_TYPES];
extern const int aiArmorSocketQty[ARMOR_NUM_TYPES];
extern const int aiSocketPercentByQty[5][4];
extern const int aiExpLossPercents[PLAYER_EXP_TABLE_MAX + 1];
extern const int * aiSkillPowerByLevel;
extern const int aiSkillPowerByLevel_euckr[SKILL_MAX_LEVEL + 1];
extern const int aiPolymorphPowerByLevel[SKILL_MAX_LEVEL + 1];
extern const int aiSkillBookCountForLevelUp[10];
extern const int aiGrandMasterSkillBookCountForLevelUp[10];
extern const int aiGrandMasterSkillBookMinCount[10];
extern const int aiGrandMasterSkillBookMaxCount[10];
extern const int CHN_aiPartyBonusExpPercentByMemberCount[9];
extern const int KOR_aiPartyBonusExpPercentByMemberCount[9];
extern const int KOR_aiUniqueItemPartyBonusExpPercentByMemberCount[9];
typedef std::map<DWORD, TItemAttrTable> TItemAttrMap;
extern TItemAttrMap g_map_itemAttr;
extern TItemAttrMap g_map_itemRare;
extern const int * aiChainLightningCountBySkillLevel;
extern const int aiChainLightningCountBySkillLevel_euckr[SKILL_MAX_LEVEL + 1];
extern const char * c_apszEmpireNames[EMPIRE_MAX_NUM];
extern const char * c_apszPrivNames[MAX_PRIV_NUM];
extern const SStoneDropInfo aStoneDrop[STONE_INFO_MAX_NUM];
typedef struct
{
long lMapIndex;
int iWarPrice;
int iWinnerPotionRewardPctToWinner;
int iLoserPotionRewardPctToWinner;
int iInitialScore;
int iEndScore;
} TGuildWarInfo;
extern TGuildWarInfo KOR_aGuildWarInfo[GUILD_WAR_TYPE_MAX_NUM];
// ACCESSORY_REFINE
enum
{
ITEM_ACCESSORY_SOCKET_MAX_NUM = 3
};
extern const int aiAccessorySocketAddPct[ITEM_ACCESSORY_SOCKET_MAX_NUM];
extern const int aiAccessorySocketEffectivePct[ITEM_ACCESSORY_SOCKET_MAX_NUM + 1];
extern const int aiAccessorySocketDegradeTime[ITEM_ACCESSORY_SOCKET_MAX_NUM + 1];
extern const int aiAccessorySocketPutPct[ITEM_ACCESSORY_SOCKET_MAX_NUM + 1];
long FN_get_apply_type(const char *apply_type_string);
// END_OF_ACCESSORY_REFINE
long FN_get_apply_type(const char *apply_type_string);
#endif

136
src/game/crc32.cpp Normal file
View File

@@ -0,0 +1,136 @@
#include "stdafx.h"
#include "crc32.h"
static unsigned long CRCTable[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
#define DO1(buf, i) crc = CRCTable[(crc ^ buf[i]) & 0xff] ^ (crc >> 8)
#define DO2(buf, i) DO1(buf, i); DO1(buf, i + 1);
#define DO4(buf, i) DO2(buf, i); DO2(buf, i + 2);
#define DO8(buf, i) DO4(buf, i); DO4(buf, i + 4);
#define DO16(buf, i) DO8(buf, i); DO8(buf, i + 8);
crc_t GetCRC32(const char * buf, size_t len)
{
crc_t crc = 0xffffffff;
if (16 <= len)
{
do
{
DO16(buf, 0);
buf += 16;
len -= 16;
} while (len >= 16);
}
if (0 != len)
{
do
{
DO1(buf, 0);
++buf;
--len;
} while (len > 0);
}
crc ^= 0xffffffff;
return crc;
}
#define DO1CI(buf, i) crc = CRCTable[(crc ^ UPPER(buf[i])) & 0xff] ^ (crc >> 8)
#define DO2CI(buf, i) DO1CI(buf, i); DO1CI(buf, i + 1);
#define DO4CI(buf, i) DO2CI(buf, i); DO2CI(buf, i + 2);
#define DO8CI(buf, i) DO4CI(buf, i); DO4CI(buf, i + 4);
#define DO16CI(buf, i) DO8CI(buf, i); DO8CI(buf, i + 8);
crc_t GetCaseCRC32(const char * buf, size_t len)
{
crc_t crc = 0xffffffff;
if (16 <= len)
{
do
{
DO16CI(buf, 0);
buf += 16;
len -= 16;
} while (len >= 16);
}
if (0 != len)
{
do
{
DO1CI(buf, 0);
++buf;
--len;
} while (len > 0);
}
crc ^= 0xffffffff;
return crc;
}
crc_t GetFastHash(const char * key, size_t len)
{
const char * end = key + len;
unsigned long h = 0;
while (key < end)
{
h *= 16777619;
h ^= *(key++);
}
return (h);
}

13
src/game/crc32.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef __INC_CRC32_H__
#define __INC_CRC32_H__
typedef unsigned long crc_t;
crc_t GetCRC32(const char * buffer, size_t count);
crc_t GetCaseCRC32(const char * buffer, size_t count);
crc_t GetFastHash(const char * key, size_t len);
/*#define CRC32(buf) GetCRC32(buf, strlen(buf))
#define CRC32CASE(buf) GetCaseCRC32(buf, strlen(buf))*/
#endif

Some files were not shown because too many files have changed in this diff Show More