3695 lines
93 KiB
C++
3695 lines
93 KiB
C++
#include "stdafx.h"
|
||
#include <sstream>
|
||
|
||
#include "utils.h"
|
||
#include "config.h"
|
||
#include "vector.h"
|
||
#include "char.h"
|
||
#include "char_manager.h"
|
||
#include "battle.h"
|
||
#include "desc.h"
|
||
#include "desc_manager.h"
|
||
#include "packet.h"
|
||
#include "affect.h"
|
||
#include "item.h"
|
||
#include "sectree_manager.h"
|
||
#include "mob_manager.h"
|
||
#include "start_position.h"
|
||
#include "party.h"
|
||
#include "buffer_manager.h"
|
||
#include "guild.h"
|
||
#include "log.h"
|
||
#include "unique_item.h"
|
||
#include "questmanager.h"
|
||
#include "../../common/CommonDefines.h"
|
||
|
||
#define ENABLE_FORCE2MASTERSKILL
|
||
// #define ENABLE_NULLIFYAFFECT_LIMIT
|
||
|
||
static const DWORD s_adwSubSkillVnums[] =
|
||
{
|
||
SKILL_LEADERSHIP,
|
||
SKILL_COMBO,
|
||
SKILL_MINING,
|
||
SKILL_LANGUAGE1,
|
||
SKILL_LANGUAGE2,
|
||
SKILL_LANGUAGE3,
|
||
SKILL_POLYMORPH,
|
||
SKILL_HORSE,
|
||
SKILL_HORSE_SUMMON,
|
||
SKILL_HORSE_WILDATTACK,
|
||
SKILL_HORSE_CHARGE,
|
||
SKILL_HORSE_ESCAPE,
|
||
SKILL_HORSE_WILDATTACK_RANGE,
|
||
SKILL_ADD_HP,
|
||
SKILL_RESIST_PENETRATE
|
||
};
|
||
|
||
time_t CHARACTER::GetSkillNextReadTime(DWORD dwVnum) const
|
||
{
|
||
if (dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("vnum overflow (vnum: %u)", dwVnum);
|
||
return 0;
|
||
}
|
||
|
||
return m_pSkillLevels ? m_pSkillLevels[dwVnum].tNextRead : 0;
|
||
}
|
||
|
||
void CHARACTER::SetSkillNextReadTime(DWORD dwVnum, time_t time)
|
||
{
|
||
if (m_pSkillLevels && dwVnum < SKILL_MAX_NUM)
|
||
m_pSkillLevels[dwVnum].tNextRead = time;
|
||
}
|
||
|
||
bool TSkillUseInfo::HitOnce(DWORD dwVnum)
|
||
{
|
||
if (!bUsed)
|
||
return false;
|
||
|
||
sys_log(1, "__HitOnce NextUse %u current %u count %d scount %d", dwNextSkillUsableTime, get_dword_time(), iHitCount, iSplashCount);
|
||
|
||
if (dwNextSkillUsableTime && dwNextSkillUsableTime<get_dword_time() && dwVnum != SKILL_MUYEONG && dwVnum != SKILL_HORSE_WILDATTACK)
|
||
{
|
||
sys_log(1, "__HitOnce can't hit");
|
||
|
||
return false;
|
||
}
|
||
|
||
if (iHitCount == -1)
|
||
{
|
||
sys_log(1, "__HitOnce OK %d %d %d", dwNextSkillUsableTime, get_dword_time(), iHitCount);
|
||
return true;
|
||
}
|
||
|
||
if (iHitCount)
|
||
{
|
||
sys_log(1, "__HitOnce OK %d %d %d", dwNextSkillUsableTime, get_dword_time(), iHitCount);
|
||
iHitCount--;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool TSkillUseInfo::UseSkill(bool isGrandMaster, DWORD vid, DWORD dwCooltime, int splashcount, int hitcount, int range)
|
||
{
|
||
this->isGrandMaster = isGrandMaster;
|
||
DWORD dwCur = get_dword_time();
|
||
|
||
if (bUsed && dwNextSkillUsableTime > dwCur)
|
||
{
|
||
sys_log(0, "cooltime is not over delta %u", dwNextSkillUsableTime - dwCur);
|
||
iHitCount = 0;
|
||
return false;
|
||
}
|
||
|
||
bUsed = true;
|
||
#ifdef ENABLE_SKILL_COOLDOWN_CHECK
|
||
this->cooldown = true;
|
||
this->skillCount = 0;
|
||
#endif
|
||
|
||
if (dwCooltime)
|
||
dwNextSkillUsableTime = dwCur + dwCooltime;
|
||
else
|
||
dwNextSkillUsableTime = 0;
|
||
|
||
iRange = range;
|
||
iMaxHitCount = iHitCount = hitcount;
|
||
|
||
if (test_server)
|
||
sys_log(0, "UseSkill NextUse %u current %u cooltime %d hitcount %d/%d", dwNextSkillUsableTime, dwCur, dwCooltime, iHitCount, iMaxHitCount);
|
||
|
||
dwVID = vid;
|
||
iSplashCount = splashcount;
|
||
return true;
|
||
}
|
||
|
||
#ifdef ENABLE_SKILL_COOLDOWN_CHECK
|
||
bool TSkillUseInfo::IsOnCooldown(int vnum)
|
||
{
|
||
int maxSkillCount = 0;
|
||
switch (vnum)
|
||
{
|
||
case SKILL_KWANKYEOK:
|
||
maxSkillCount = 8;
|
||
case SKILL_HORSE_WILDATTACK_RANGE:
|
||
maxSkillCount = 5;
|
||
case SKILL_HORSE_ESCAPE:
|
||
maxSkillCount = 10;
|
||
}
|
||
|
||
this->skillCount++;
|
||
if (maxSkillCount)
|
||
return this->skillCount > maxSkillCount;
|
||
|
||
auto ret = !this->cooldown;
|
||
this->cooldown = false;
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
int CHARACTER::GetChainLightningMaxCount() const
|
||
{
|
||
return aiChainLightningCountBySkillLevel[MIN(SKILL_MAX_LEVEL, GetSkillLevel(SKILL_CHAIN))];
|
||
}
|
||
|
||
void CHARACTER::SetAffectedEunhyung()
|
||
{
|
||
m_dwAffectedEunhyungLevel = GetSkillPower(SKILL_EUNHYUNG);
|
||
}
|
||
|
||
void CHARACTER::SetSkillGroup(BYTE bSkillGroup)
|
||
{
|
||
if (bSkillGroup > 2)
|
||
return;
|
||
|
||
if (GetLevel() < 5)
|
||
return;
|
||
|
||
m_points.skill_group = bSkillGroup;
|
||
|
||
TPacketGCChangeSkillGroup p;
|
||
p.header = HEADER_GC_SKILL_GROUP;
|
||
p.skill_group = m_points.skill_group;
|
||
|
||
GetDesc()->Packet(&p, sizeof(TPacketGCChangeSkillGroup));
|
||
}
|
||
|
||
int CHARACTER::ComputeCooltime(int time)
|
||
{
|
||
return CalculateDuration(GetPoint(POINT_CASTING_SPEED), time);
|
||
}
|
||
|
||
void CHARACTER::SkillLevelPacket()
|
||
{
|
||
if (!GetDesc())
|
||
return;
|
||
|
||
TPacketGCSkillLevel pack;
|
||
|
||
pack.bHeader = HEADER_GC_SKILL_LEVEL;
|
||
thecore_memcpy(&pack.skills, m_pSkillLevels, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
|
||
GetDesc()->Packet(&pack, sizeof(TPacketGCSkillLevel));
|
||
}
|
||
|
||
void CHARACTER::SetSkillLevel(DWORD dwVnum, BYTE bLev)
|
||
{
|
||
if (NULL == m_pSkillLevels)
|
||
return;
|
||
|
||
if (dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("vnum overflow (vnum %u)", dwVnum);
|
||
return;
|
||
}
|
||
|
||
m_pSkillLevels[dwVnum].bLevel = MIN(40, bLev);
|
||
|
||
if (bLev >= 40)
|
||
m_pSkillLevels[dwVnum].bMasterType = SKILL_PERFECT_MASTER;
|
||
else if (bLev >= 30)
|
||
m_pSkillLevels[dwVnum].bMasterType = SKILL_GRAND_MASTER;
|
||
else if (bLev >= 20)
|
||
m_pSkillLevels[dwVnum].bMasterType = SKILL_MASTER;
|
||
else
|
||
m_pSkillLevels[dwVnum].bMasterType = SKILL_NORMAL;
|
||
}
|
||
|
||
bool CHARACTER::IsLearnableSkill(DWORD dwSkillVnum) const
|
||
{
|
||
const CSkillProto * pkSkill = CSkillManager::instance().Get(dwSkillVnum);
|
||
|
||
if (!pkSkill)
|
||
return false;
|
||
|
||
if (GetSkillLevel(dwSkillVnum) >= SKILL_MAX_LEVEL)
|
||
return false;
|
||
|
||
if (pkSkill->dwType == 0)
|
||
{
|
||
if (GetSkillLevel(dwSkillVnum) >= pkSkill->bMaxLevel)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
if (pkSkill->dwType == 5)
|
||
{
|
||
if (dwSkillVnum == SKILL_HORSE_WILDATTACK_RANGE && GetJob() != JOB_ASSASSIN)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
if (GetSkillGroup() == 0)
|
||
return false;
|
||
|
||
if (pkSkill->dwType - 1 == GetJob())
|
||
return true;
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
|
||
if (7 == pkSkill->dwType && JOB_WOLFMAN == GetJob())
|
||
return true;
|
||
#endif
|
||
if (6 == pkSkill->dwType)
|
||
{
|
||
if (SKILL_7_A_ANTI_TANHWAN <= dwSkillVnum && dwSkillVnum <= SKILL_7_D_ANTI_YONGBI)
|
||
{
|
||
for (int i=0 ; i < 4 ; i++)
|
||
{
|
||
if (unsigned(SKILL_7_A_ANTI_TANHWAN + i) != dwSkillVnum)
|
||
{
|
||
if (0 != GetSkillLevel(SKILL_7_A_ANTI_TANHWAN + i))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
if (SKILL_8_A_ANTI_GIGONGCHAM <= dwSkillVnum && dwSkillVnum <= SKILL_8_D_ANTI_BYEURAK)
|
||
{
|
||
for (int i=0 ; i < 4 ; i++)
|
||
{
|
||
if (unsigned(SKILL_8_A_ANTI_GIGONGCHAM + i) != dwSkillVnum)
|
||
{
|
||
if (0 != GetSkillLevel(SKILL_8_A_ANTI_GIGONGCHAM + i))
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
bool CHARACTER::LearnGrandMasterSkill(DWORD dwSkillVnum)
|
||
{
|
||
CSkillProto * pkSk = CSkillManager::instance().Get(dwSkillVnum);
|
||
|
||
if (!pkSk)
|
||
return false;
|
||
|
||
if (!IsLearnableSkill(dwSkillVnum))
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><>ų<EFBFBD>Դϴ<D4B4>."));
|
||
return false;
|
||
}
|
||
|
||
sys_log(0, "learn grand master skill[%d] cur %d, next %d", dwSkillVnum, get_global_time(), GetSkillNextReadTime(dwSkillVnum));
|
||
|
||
if (pkSk->dwType == 0)
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><> <20><><EFBFBD><EFBFBD> <20><>ų<EFBFBD>Դϴ<D4B4>."));
|
||
return false;
|
||
}
|
||
|
||
if (GetSkillMasterType(dwSkillVnum) != SKILL_GRAND_MASTER)
|
||
{
|
||
if (GetSkillMasterType(dwSkillVnum) > SKILL_GRAND_MASTER)
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ <20><><EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD> <20><>ų<EFBFBD>Դϴ<D4B4>. <20><> <20>̻<EFBFBD> <20><><EFBFBD><EFBFBD> <20><> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
else
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD> <20><>ų<EFBFBD><C5B3> <20><><EFBFBD><EFBFBD> <20><EFBFBD><D7B7><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20≯<EFBFBD><CCB8><EFBFBD> <20>ʾҽ<CABE><D2BD>ϴ<EFBFBD>."));
|
||
return false;
|
||
}
|
||
|
||
std::string strTrainSkill;
|
||
{
|
||
std::ostringstream os;
|
||
os << "training_grandmaster_skill.skill" << dwSkillVnum;
|
||
strTrainSkill = os.str();
|
||
}
|
||
|
||
BYTE bLastLevel = GetSkillLevel(dwSkillVnum);
|
||
|
||
int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 30);
|
||
|
||
sys_log(0, "LearnGrandMasterSkill %s table idx %d value %d", GetName(), idx, aiGrandMasterSkillBookCountForLevelUp[idx]);
|
||
|
||
int iTotalReadCount = GetQuestFlag(strTrainSkill) + 1;
|
||
SetQuestFlag(strTrainSkill, iTotalReadCount);
|
||
|
||
int iMinReadCount = aiGrandMasterSkillBookMinCount[idx];
|
||
int iMaxReadCount = aiGrandMasterSkillBookMaxCount[idx];
|
||
|
||
int iBookCount = aiGrandMasterSkillBookCountForLevelUp[idx];
|
||
|
||
if (FindAffect(AFFECT_SKILL_BOOK_BONUS))
|
||
{
|
||
if (iBookCount&1)
|
||
iBookCount = iBookCount / 2 + 1;
|
||
else
|
||
iBookCount = iBookCount / 2;
|
||
|
||
RemoveAffect(AFFECT_SKILL_BOOK_BONUS);
|
||
}
|
||
|
||
int n = number(1, iBookCount);
|
||
sys_log(0, "Number(%d)", n);
|
||
|
||
DWORD nextTime = get_global_time() + number(g_dwSkillBookNextReadMin, g_dwSkillBookNextReadMax);
|
||
|
||
sys_log(0, "GrandMaster SkillBookCount min %d cur %d max %d (next_time=%d)", iMinReadCount, iTotalReadCount, iMaxReadCount, nextTime);
|
||
|
||
bool bSuccess = n == 2;
|
||
|
||
if (iTotalReadCount < iMinReadCount)
|
||
bSuccess = false;
|
||
if (iTotalReadCount > iMaxReadCount)
|
||
bSuccess = true;
|
||
|
||
if (bSuccess)
|
||
{
|
||
SkillLevelUp(dwSkillVnum, SKILL_UP_BY_QUEST);
|
||
}
|
||
|
||
SetSkillNextReadTime(dwSkillVnum, nextTime);
|
||
|
||
if (bLastLevel == GetSkillLevel(dwSkillVnum))
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ũ<EFBFBD><EFBFBD>, <20>Ⱑ <20><><EFBFBD><EFBFBD><EFBFBD>ϰ<EFBFBD> <20>־<EFBFBD>! <20>̰<EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ȭ<EFBFBD>Ը<EFBFBD><D4B8>ΰ<EFBFBD>!? <20><><EFBFBD><EFBFBD>!"));
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>з<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>. <20>ٽ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֽñ<D6BD> <20>ٶ<EFBFBD><D9B6>ϴ<EFBFBD>."));
|
||
LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_FAIL", "");
|
||
return false;
|
||
}
|
||
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>̾<EFBFBD>!"));
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD>߰ſ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ġ<EFBFBD><C4A1> <20>־<EFBFBD>! <20>̰<EFBFBD>, <20>̰<EFBFBD><CCB0><EFBFBD>!"));
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>̽<EFBFBD><CCBD>ϴ<EFBFBD>."));
|
||
LogManager::instance().CharLog(this, dwSkillVnum, "GM_READ_SUCCESS", "");
|
||
return true;
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
static bool FN_should_check_exp(LPCHARACTER ch)
|
||
{
|
||
// @warme005
|
||
return ch->GetLevel() < gPlayerMaxLevel;
|
||
}
|
||
|
||
// #define ENABLE_MASTER_SKILLBOOK_NO_STEPS
|
||
bool CHARACTER::LearnSkillByBook(DWORD dwSkillVnum, BYTE bProb)
|
||
{
|
||
const CSkillProto* pkSk = CSkillManager::instance().Get(dwSkillVnum);
|
||
|
||
if (!pkSk)
|
||
return false;
|
||
|
||
if (!IsLearnableSkill(dwSkillVnum))
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><>ų<EFBFBD>Դϴ<D4B4>."));
|
||
return false;
|
||
}
|
||
|
||
DWORD need_exp = 0;
|
||
|
||
if (FN_should_check_exp(this))
|
||
{
|
||
need_exp = 20000;
|
||
|
||
if ( GetExp() < need_exp )
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ġ<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD> å<><C3A5> <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (pkSk->dwType != 0)
|
||
{
|
||
if (GetSkillMasterType(dwSkillVnum) != SKILL_MASTER)
|
||
{
|
||
if (GetSkillMasterType(dwSkillVnum) > SKILL_MASTER)
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD> <20><>ų<EFBFBD><C5B3> å<><C3A5><EFBFBD><EFBFBD> <20><><EFBFBD>̻<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
else
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD> <20><>ų<EFBFBD><C5B3> <20><><EFBFBD><EFBFBD> å<><C3A5><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20≯<EFBFBD><CCB8><EFBFBD> <20>ʾҽ<CABE><D2BD>ϴ<EFBFBD>."));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (get_global_time() < GetSkillNextReadTime(dwSkillVnum))
|
||
{
|
||
if (!(test_server && quest::CQuestManager::instance().GetEventFlag("no_read_delay")))
|
||
{
|
||
if (FindAffect(AFFECT_SKILL_NO_BOOK_DELAY))
|
||
{
|
||
RemoveAffect(AFFECT_SKILL_NO_BOOK_DELAY);
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD>־ȼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ȭ<EFBFBD>Ը<EFBFBD><D4B8><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Խ<EFBFBD><D4BD>ϴ<EFBFBD>."));
|
||
}
|
||
else
|
||
{
|
||
SkillLearnWaitMoreTimeMessage(GetSkillNextReadTime(dwSkillVnum) - get_global_time());
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
BYTE bLastLevel = GetSkillLevel(dwSkillVnum);
|
||
|
||
if (bProb != 0)
|
||
{
|
||
// SKILL_BOOK_BONUS
|
||
if (FindAffect(AFFECT_SKILL_BOOK_BONUS))
|
||
{
|
||
bProb += bProb / 2;
|
||
RemoveAffect(AFFECT_SKILL_BOOK_BONUS);
|
||
}
|
||
// END_OF_SKILL_BOOK_BONUS
|
||
|
||
sys_log(0, "LearnSkillByBook Pct %u prob %d", dwSkillVnum, bProb);
|
||
|
||
if (number(1, 100) <= bProb)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "LearnSkillByBook %u SUCC", dwSkillVnum);
|
||
|
||
SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK);
|
||
}
|
||
else
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "LearnSkillByBook %u FAIL", dwSkillVnum);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int idx = MIN(9, GetSkillLevel(dwSkillVnum) - 20);
|
||
|
||
sys_log(0, "LearnSkillByBook %s table idx %d value %d", GetName(), idx, aiSkillBookCountForLevelUp[idx]);
|
||
|
||
{
|
||
int need_bookcount = GetSkillLevel(dwSkillVnum) - 20;
|
||
|
||
PointChange(POINT_EXP, -need_exp);
|
||
|
||
quest::CQuestManager& q = quest::CQuestManager::instance();
|
||
quest::PC* pPC = q.GetPC(GetPlayerID());
|
||
|
||
if (pPC)
|
||
{
|
||
char flag[128+1];
|
||
memset(flag, 0, sizeof(flag));
|
||
snprintf(flag, sizeof(flag), "traning_master_skill.%u.read_count", dwSkillVnum);
|
||
|
||
int read_count = pPC->GetFlag(flag);
|
||
int percent = 65;
|
||
|
||
if (FindAffect(AFFECT_SKILL_BOOK_BONUS))
|
||
{
|
||
percent = 0;
|
||
RemoveAffect(AFFECT_SKILL_BOOK_BONUS);
|
||
}
|
||
|
||
if (number(1, 100) > percent)
|
||
{
|
||
#ifdef ENABLE_MASTER_SKILLBOOK_NO_STEPS
|
||
if (true)
|
||
#else
|
||
if (read_count >= need_bookcount)
|
||
#endif
|
||
{
|
||
SkillLevelUp(dwSkillVnum, SKILL_UP_BY_BOOK);
|
||
pPC->SetFlag(flag, 0);
|
||
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("å<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>̽<EFBFBD><CCBD>ϴ<EFBFBD>."));
|
||
LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", "");
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
pPC->SetFlag(flag, read_count + 1);
|
||
|
||
switch (number(1, 3))
|
||
{
|
||
case 1:
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>ذ<EFBFBD> <20>Ǿ<EFBFBD><C7BE><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ѵ<EFBFBD> <20>ѵ<EFBFBD>.."));
|
||
break;
|
||
|
||
case 2:
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>̴<EFBFBD> <20>ǰ<EFBFBD>... <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϱⰡ <20>ʹ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.."));
|
||
break;
|
||
|
||
case 3:
|
||
default:
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ϴ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><CDB8><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ִ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>̴<EFBFBD>.."));
|
||
break;
|
||
}
|
||
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%d <20><><EFBFBD><EFBFBD> <20><> <20>о<EFBFBD><D0BE><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>Ϸ<EFBFBD> <20><> <20><> <20>ֽ<EFBFBD><D6BD>ϴ<EFBFBD>."), need_bookcount - read_count);
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
}
|
||
}
|
||
}
|
||
|
||
if (bLastLevel != GetSkillLevel(dwSkillVnum))
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>̾<EFBFBD>!"));
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("<EFBFBD>߰ſ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ġ<EFBFBD><C4A1> <20>־<EFBFBD>! <20>̰<EFBFBD>, <20>̰<EFBFBD><CCB0><EFBFBD>!"));
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("å<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>̽<EFBFBD><CCBD>ϴ<EFBFBD>."));
|
||
LogManager::instance().CharLog(this, dwSkillVnum, "READ_SUCCESS", "");
|
||
}
|
||
else
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, LC_TEXT("ũ<EFBFBD><EFBFBD>, <20>Ⱑ <20><><EFBFBD><EFBFBD><EFBFBD>ϰ<EFBFBD> <20>־<EFBFBD>! <20>̰<EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ȭ<EFBFBD>Ը<EFBFBD><D4B8>ΰ<EFBFBD>!? <20><><EFBFBD><EFBFBD>!"));
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>з<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>. <20>ٽ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֽñ<D6BD> <20>ٶ<EFBFBD><D9B6>ϴ<EFBFBD>."));
|
||
LogManager::instance().CharLog(this, dwSkillVnum, "READ_FAIL", "");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CHARACTER::SkillLevelDown(DWORD dwVnum)
|
||
{
|
||
if (NULL == m_pSkillLevels)
|
||
return false;
|
||
|
||
if (g_bSkillDisable)
|
||
return false;
|
||
|
||
if (IsPolymorphed())
|
||
return false;
|
||
|
||
CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum);
|
||
|
||
if (!pkSk)
|
||
{
|
||
sys_err("There is no such skill by number %u", dwVnum);
|
||
return false;
|
||
}
|
||
|
||
if (!IsLearnableSkill(dwVnum))
|
||
return false;
|
||
|
||
if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL)
|
||
return false;
|
||
|
||
if (!GetSkillGroup())
|
||
return false;
|
||
|
||
if (pkSk->dwVnum >= SKILL_MAX_NUM)
|
||
return false;
|
||
|
||
if (m_pSkillLevels[pkSk->dwVnum].bLevel == 0)
|
||
return false;
|
||
|
||
int idx = POINT_SKILL;
|
||
switch (pkSk->dwType)
|
||
{
|
||
case 0:
|
||
idx = POINT_SUB_SKILL;
|
||
break;
|
||
case 1:
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
case 6:
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
case 7:
|
||
#endif
|
||
idx = POINT_SKILL;
|
||
break;
|
||
case 5:
|
||
idx = POINT_HORSE_SKILL;
|
||
break;
|
||
default:
|
||
sys_err("Wrong skill type %d skill vnum %d", pkSk->dwType, pkSk->dwVnum);
|
||
return false;
|
||
|
||
}
|
||
|
||
PointChange(idx, +1);
|
||
SetSkillLevel(pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bLevel - 1);
|
||
|
||
sys_log(0, "SkillDown: %s %u %u %u type %u", GetName(), pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bMasterType, m_pSkillLevels[pkSk->dwVnum].bLevel, pkSk->dwType);
|
||
Save();
|
||
|
||
ComputePoints();
|
||
SkillLevelPacket();
|
||
return true;
|
||
}
|
||
|
||
void CHARACTER::SkillLevelUp(DWORD dwVnum, BYTE bMethod)
|
||
{
|
||
if (NULL == m_pSkillLevels)
|
||
return;
|
||
|
||
if (g_bSkillDisable)
|
||
return;
|
||
|
||
if (IsPolymorphed())
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD>а<EFBFBD> <20>߿<EFBFBD><DFBF><EFBFBD> <20>ɷ<EFBFBD><C9B7><EFBFBD> <20>ø<EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
return;
|
||
}
|
||
|
||
if (SKILL_7_A_ANTI_TANHWAN <= dwVnum && dwVnum <= SKILL_8_D_ANTI_BYEURAK)
|
||
{
|
||
if (0 == GetSkillLevel(dwVnum))
|
||
return;
|
||
}
|
||
|
||
const CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum);
|
||
|
||
if (!pkSk)
|
||
{
|
||
sys_err("There is no such skill by number (vnum %u)", dwVnum);
|
||
return;
|
||
}
|
||
|
||
if (pkSk->dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("Skill Vnum overflow (vnum %u)", dwVnum);
|
||
return;
|
||
}
|
||
|
||
if (!IsLearnableSkill(dwVnum))
|
||
return;
|
||
|
||
if (pkSk->dwType != 0)
|
||
{
|
||
switch (GetSkillMasterType(pkSk->dwVnum))
|
||
{
|
||
case SKILL_GRAND_MASTER:
|
||
if (bMethod != SKILL_UP_BY_QUEST)
|
||
return;
|
||
break;
|
||
|
||
case SKILL_PERFECT_MASTER:
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (bMethod == SKILL_UP_BY_POINT)
|
||
{
|
||
if (GetSkillMasterType(pkSk->dwVnum) != SKILL_NORMAL)
|
||
return;
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_DISABLE_BY_POINT_UP))
|
||
return;
|
||
}
|
||
else if (bMethod == SKILL_UP_BY_BOOK)
|
||
{
|
||
if (pkSk->dwType != 0)
|
||
if (GetSkillMasterType(pkSk->dwVnum) != SKILL_MASTER)
|
||
return;
|
||
}
|
||
|
||
if (GetLevel() < pkSk->bLevelLimit)
|
||
return;
|
||
|
||
if (pkSk->preSkillVnum)
|
||
if (GetSkillMasterType(pkSk->preSkillVnum) == SKILL_NORMAL &&
|
||
GetSkillLevel(pkSk->preSkillVnum) < pkSk->preSkillLevel)
|
||
return;
|
||
|
||
if (!GetSkillGroup())
|
||
return;
|
||
|
||
if (bMethod == SKILL_UP_BY_POINT)
|
||
{
|
||
int idx;
|
||
|
||
switch (pkSk->dwType)
|
||
{
|
||
case 0:
|
||
idx = POINT_SUB_SKILL;
|
||
break;
|
||
|
||
case 1:
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
case 6:
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
case 7:
|
||
#endif
|
||
idx = POINT_SKILL;
|
||
break;
|
||
|
||
case 5:
|
||
idx = POINT_HORSE_SKILL;
|
||
break;
|
||
|
||
default:
|
||
sys_err("Wrong skill type %d skill vnum %d", pkSk->dwType, pkSk->dwVnum);
|
||
return;
|
||
}
|
||
|
||
if (GetPoint(idx) < 1)
|
||
return;
|
||
|
||
PointChange(idx, -1);
|
||
}
|
||
|
||
int SkillPointBefore = GetSkillLevel(pkSk->dwVnum);
|
||
SetSkillLevel(pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bLevel + 1);
|
||
|
||
if (pkSk->dwType != 0)
|
||
{
|
||
switch (GetSkillMasterType(pkSk->dwVnum))
|
||
{
|
||
case SKILL_NORMAL:
|
||
|
||
if (GetSkillLevel(pkSk->dwVnum) >= 17)
|
||
{
|
||
#ifdef ENABLE_FORCE2MASTERSKILL
|
||
SetSkillLevel(pkSk->dwVnum, 20);
|
||
#else
|
||
if (GetQuestFlag("reset_scroll.force_to_master_skill") > 0)
|
||
{
|
||
SetSkillLevel(pkSk->dwVnum, 20);
|
||
SetQuestFlag("reset_scroll.force_to_master_skill", 0);
|
||
}
|
||
else
|
||
{
|
||
if (number(1, 21 - MIN(20, GetSkillLevel(pkSk->dwVnum))) == 1)
|
||
SetSkillLevel(pkSk->dwVnum, 20);
|
||
}
|
||
#endif
|
||
}
|
||
break;
|
||
|
||
case SKILL_MASTER:
|
||
if (GetSkillLevel(pkSk->dwVnum) >= 30)
|
||
{
|
||
if (number(1, 31 - MIN(30, GetSkillLevel(pkSk->dwVnum))) == 1)
|
||
SetSkillLevel(pkSk->dwVnum, 30);
|
||
}
|
||
break;
|
||
|
||
case SKILL_GRAND_MASTER:
|
||
if (GetSkillLevel(pkSk->dwVnum) >= 40)
|
||
{
|
||
SetSkillLevel(pkSk->dwVnum, 40);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
char szSkillUp[1024];
|
||
|
||
snprintf(szSkillUp, sizeof(szSkillUp), "SkillUp: %s %u %d %d[Before:%d] type %u",
|
||
GetName(), pkSk->dwVnum, m_pSkillLevels[pkSk->dwVnum].bMasterType, m_pSkillLevels[pkSk->dwVnum].bLevel, SkillPointBefore, pkSk->dwType);
|
||
|
||
sys_log(0, "%s", szSkillUp);
|
||
|
||
LogManager::instance().CharLog(this, pkSk->dwVnum, "SKILLUP", szSkillUp);
|
||
Save();
|
||
|
||
ComputePoints();
|
||
SkillLevelPacket();
|
||
}
|
||
|
||
void CHARACTER::ComputeSkillPoints()
|
||
{
|
||
if (g_bSkillDisable)
|
||
return;
|
||
}
|
||
|
||
void CHARACTER::ResetSkill()
|
||
{
|
||
if (NULL == m_pSkillLevels)
|
||
return;
|
||
|
||
std::vector<std::pair<DWORD, TPlayerSkill> > vec;
|
||
size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]);
|
||
|
||
for (size_t i = 0; i < count; ++i)
|
||
{
|
||
if (s_adwSubSkillVnums[i] >= SKILL_MAX_NUM)
|
||
continue;
|
||
|
||
vec.emplace_back(s_adwSubSkillVnums[i], m_pSkillLevels[s_adwSubSkillVnums[i]]);
|
||
}
|
||
|
||
memset(m_pSkillLevels, 0, sizeof(TPlayerSkill) * SKILL_MAX_NUM);
|
||
|
||
std::vector<std::pair<DWORD, TPlayerSkill> >::const_iterator iter = vec.begin();
|
||
|
||
while (iter != vec.end())
|
||
{
|
||
const std::pair<DWORD, TPlayerSkill>& pair = *(iter++);
|
||
m_pSkillLevels[pair.first] = pair.second;
|
||
}
|
||
|
||
ComputePoints();
|
||
SkillLevelPacket();
|
||
}
|
||
|
||
void CHARACTER::ComputePassiveSkill(DWORD dwVnum)
|
||
{
|
||
if (g_bSkillDisable)
|
||
return;
|
||
|
||
if (GetSkillLevel(dwVnum) == 0)
|
||
return;
|
||
|
||
CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum);
|
||
pkSk->SetPointVar("k", GetSkillLevel(dwVnum));
|
||
int iAmount = (int) pkSk->kPointPoly.Eval();
|
||
|
||
sys_log(2, "%s passive #%d on %d amount %d", GetName(), dwVnum, pkSk->bPointOn, iAmount);
|
||
PointChange(pkSk->bPointOn, iAmount);
|
||
}
|
||
|
||
struct FFindNearVictim
|
||
{
|
||
FFindNearVictim(LPCHARACTER center, LPCHARACTER attacker, const CHARACTER_SET& excepts_set = empty_set_)
|
||
: m_pkChrCenter(center),
|
||
m_pkChrNextTarget(NULL),
|
||
m_pkChrAttacker(attacker),
|
||
m_count(0),
|
||
m_excepts_set(excepts_set)
|
||
{
|
||
}
|
||
|
||
void operator ()(LPENTITY ent)
|
||
{
|
||
if (!ent->IsType(ENTITY_CHARACTER))
|
||
return;
|
||
|
||
LPCHARACTER pkChr = (LPCHARACTER) ent;
|
||
|
||
if (!m_excepts_set.empty()) {
|
||
if (m_excepts_set.find(pkChr) != m_excepts_set.end())
|
||
return;
|
||
}
|
||
|
||
if (m_pkChrCenter == pkChr)
|
||
return;
|
||
|
||
if (!battle_is_attackable(m_pkChrAttacker, pkChr))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (abs(m_pkChrCenter->GetX() - pkChr->GetX()) > 1000 || abs(m_pkChrCenter->GetY() - pkChr->GetY()) > 1000)
|
||
return;
|
||
|
||
float fDist = DISTANCE_APPROX(m_pkChrCenter->GetX() - pkChr->GetX(), m_pkChrCenter->GetY() - pkChr->GetY());
|
||
|
||
if (fDist < 1000)
|
||
{
|
||
++m_count;
|
||
|
||
if ((m_count == 1) || number(1, m_count) == 1)
|
||
m_pkChrNextTarget = pkChr;
|
||
}
|
||
}
|
||
|
||
LPCHARACTER GetVictim()
|
||
{
|
||
return m_pkChrNextTarget;
|
||
}
|
||
|
||
LPCHARACTER m_pkChrCenter;
|
||
LPCHARACTER m_pkChrNextTarget;
|
||
LPCHARACTER m_pkChrAttacker;
|
||
int m_count;
|
||
const CHARACTER_SET & m_excepts_set;
|
||
private:
|
||
static CHARACTER_SET empty_set_;
|
||
};
|
||
|
||
CHARACTER_SET FFindNearVictim::empty_set_;
|
||
|
||
EVENTINFO(chain_lightning_event_info)
|
||
{
|
||
DWORD dwVictim;
|
||
DWORD dwChr;
|
||
|
||
chain_lightning_event_info()
|
||
: dwVictim(0)
|
||
, dwChr(0)
|
||
{
|
||
}
|
||
};
|
||
|
||
EVENTFUNC(ChainLightningEvent)
|
||
{
|
||
chain_lightning_event_info * info = dynamic_cast<chain_lightning_event_info *>( event->info );
|
||
|
||
LPCHARACTER pkChrVictim = CHARACTER_MANAGER::instance().Find(info->dwVictim);
|
||
LPCHARACTER pkChr = CHARACTER_MANAGER::instance().Find(info->dwChr);
|
||
LPCHARACTER pkTarget = NULL;
|
||
|
||
if (!pkChr || !pkChrVictim)
|
||
{
|
||
sys_log(1, "use chainlighting, but no character");
|
||
return 0;
|
||
}
|
||
|
||
sys_log(1, "chainlighting event %s", pkChr->GetName());
|
||
|
||
if (pkChrVictim->GetParty())
|
||
{
|
||
pkTarget = pkChrVictim->GetParty()->GetNextOwnership(NULL, pkChrVictim->GetX(), pkChrVictim->GetY());
|
||
if (pkTarget == pkChrVictim || !number(0, 2) || pkChr->GetChainLightingExcept().find(pkTarget) != pkChr->GetChainLightingExcept().end())
|
||
pkTarget = NULL;
|
||
}
|
||
|
||
if (!pkTarget)
|
||
{
|
||
// 1. Find Next victim
|
||
FFindNearVictim f(pkChrVictim, pkChr, pkChr->GetChainLightingExcept());
|
||
|
||
if (pkChrVictim->GetSectree())
|
||
{
|
||
pkChrVictim->GetSectree()->ForEachAround(f);
|
||
// 2. If exist, compute it again
|
||
pkTarget = f.GetVictim();
|
||
}
|
||
}
|
||
|
||
if (pkTarget)
|
||
{
|
||
pkChrVictim->CreateFly(FLY_CHAIN_LIGHTNING, pkTarget);
|
||
pkChr->ComputeSkill(SKILL_CHAIN, pkTarget);
|
||
pkChr->AddChainLightningExcept(pkTarget);
|
||
}
|
||
else
|
||
{
|
||
sys_log(1, "%s use chainlighting, but find victim failed near %s", pkChr->GetName(), pkChrVictim->GetName());
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void SetPolyVarForAttack(LPCHARACTER ch, CSkillProto * pkSk, LPITEM pkWeapon)
|
||
{
|
||
if (ch->IsPC())
|
||
{
|
||
if (pkWeapon && pkWeapon->GetType() == ITEM_WEAPON)
|
||
{
|
||
int iWep = number(pkWeapon->GetValue(3), pkWeapon->GetValue(4));
|
||
iWep += pkWeapon->GetValue(5);
|
||
|
||
int iMtk = number(pkWeapon->GetValue(1), pkWeapon->GetValue(2));
|
||
iMtk += pkWeapon->GetValue(5);
|
||
|
||
pkSk->SetPointVar("wep", iWep);
|
||
pkSk->SetPointVar("mtk", iMtk);
|
||
pkSk->SetPointVar("mwep", iMtk);
|
||
}
|
||
else
|
||
{
|
||
pkSk->SetPointVar("wep", 0);
|
||
pkSk->SetPointVar("mtk", 0);
|
||
pkSk->SetPointVar("mwep", 0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int iWep = number(ch->GetMobDamageMin(), ch->GetMobDamageMax());
|
||
pkSk->SetPointVar("wep", iWep);
|
||
pkSk->SetPointVar("mwep", iWep);
|
||
pkSk->SetPointVar("mtk", iWep);
|
||
}
|
||
}
|
||
|
||
struct FuncSplashDamage
|
||
{
|
||
FuncSplashDamage(int x, int y, CSkillProto * pkSk, LPCHARACTER pkChr, int iAmount, int iAG, int iMaxHit, LPITEM pkWeapon, bool bDisableCooltime, TSkillUseInfo* pInfo, BYTE bUseSkillPower)
|
||
:
|
||
m_x(x), m_y(y), m_pkSk(pkSk), m_pkChr(pkChr), m_iAmount(iAmount), m_iAG(iAG), m_iCount(0), m_iMaxHit(iMaxHit), m_pkWeapon(pkWeapon), m_bDisableCooltime(bDisableCooltime), m_pInfo(pInfo), m_bUseSkillPower(bUseSkillPower)
|
||
{
|
||
}
|
||
|
||
void operator () (LPENTITY ent)
|
||
{
|
||
if (!ent->IsType(ENTITY_CHARACTER))
|
||
{
|
||
//if (m_pkSk->dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN target not character %s", m_pkChr->GetName());
|
||
return;
|
||
}
|
||
|
||
LPCHARACTER pkChrVictim = (LPCHARACTER) ent;
|
||
|
||
if (DISTANCE_APPROX(m_x - pkChrVictim->GetX(), m_y - pkChrVictim->GetY()) > m_pkSk->iSplashRange)
|
||
{
|
||
if(test_server)
|
||
sys_log(0, "XXX target too far %s", m_pkChr->GetName());
|
||
return;
|
||
}
|
||
|
||
if (!battle_is_attackable(m_pkChr, pkChrVictim))
|
||
{
|
||
if(test_server)
|
||
sys_log(0, "XXX target not attackable %s", m_pkChr->GetName());
|
||
return;
|
||
}
|
||
|
||
if (m_pkChr->IsPC())
|
||
|
||
if (!(m_pkSk->dwVnum >= GUILD_SKILL_START && m_pkSk->dwVnum <= GUILD_SKILL_END))
|
||
if (!m_bDisableCooltime && m_pInfo && !m_pInfo->HitOnce(m_pkSk->dwVnum) && m_pkSk->dwVnum != SKILL_MUYEONG)
|
||
{
|
||
if(test_server)
|
||
sys_log(0, "check guild skill %s", m_pkChr->GetName());
|
||
return;
|
||
}
|
||
|
||
++m_iCount;
|
||
|
||
int iDam;
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
//float k = 1.0f * m_pkChr->GetSkillPower(m_pkSk->dwVnum) * m_pkSk->bMaxLevel / 100;
|
||
//m_pkSk->kPointPoly2.SetVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100);
|
||
m_pkSk->SetPointVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100);
|
||
m_pkSk->SetPointVar("lv", m_pkChr->GetLevel());
|
||
m_pkSk->SetPointVar("iq", m_pkChr->GetPoint(POINT_IQ));
|
||
m_pkSk->SetPointVar("str", m_pkChr->GetPoint(POINT_ST));
|
||
m_pkSk->SetPointVar("dex", m_pkChr->GetPoint(POINT_DX));
|
||
m_pkSk->SetPointVar("con", m_pkChr->GetPoint(POINT_HT));
|
||
m_pkSk->SetPointVar("def", m_pkChr->GetPoint(POINT_DEF_GRADE));
|
||
m_pkSk->SetPointVar("odef", m_pkChr->GetPoint(POINT_DEF_GRADE) - m_pkChr->GetPoint(POINT_DEF_GRADE_BONUS));
|
||
m_pkSk->SetPointVar("horse_level", m_pkChr->GetHorseLevel());
|
||
|
||
//int iPenetratePct = (int)(1 + k*4);
|
||
bool bIgnoreDefense = false;
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_PENETRATE))
|
||
{
|
||
int iPenetratePct = (int) m_pkSk->kPointPoly2.Eval();
|
||
|
||
if (number(1, 100) <= iPenetratePct)
|
||
bIgnoreDefense = true;
|
||
}
|
||
|
||
bool bIgnoreTargetRating = false;
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_IGNORE_TARGET_RATING))
|
||
{
|
||
int iPct = (int) m_pkSk->kPointPoly2.Eval();
|
||
|
||
if (number(1, 100) <= iPct)
|
||
bIgnoreTargetRating = true;
|
||
}
|
||
|
||
m_pkSk->SetPointVar("ar", CalcAttackRating(m_pkChr, pkChrVictim, bIgnoreTargetRating));
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE))
|
||
m_pkSk->SetPointVar("atk", CalcMeleeDamage(m_pkChr, pkChrVictim, true, bIgnoreTargetRating));
|
||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE))
|
||
{
|
||
LPITEM pkBow, pkArrow;
|
||
|
||
if (1 == m_pkChr->GetArrowAndBow(&pkBow, &pkArrow, 1))
|
||
m_pkSk->SetPointVar("atk", CalcArrowDamage(m_pkChr, pkChrVictim, pkBow, pkArrow, true));
|
||
else
|
||
m_pkSk->SetPointVar("atk", 0);
|
||
}
|
||
|
||
if (m_pkSk->bPointOn == POINT_MOV_SPEED)
|
||
m_pkSk->kPointPoly.SetVar("maxv", pkChrVictim->GetLimitPoint(POINT_MOV_SPEED));
|
||
|
||
m_pkSk->SetPointVar("maxhp", pkChrVictim->GetMaxHP());
|
||
m_pkSk->SetPointVar("maxsp", pkChrVictim->GetMaxSP());
|
||
|
||
m_pkSk->SetPointVar("chain", m_pkChr->GetChainLightningIndex());
|
||
m_pkChr->IncChainLightningIndex();
|
||
|
||
bool bUnderEunhyung = m_pkChr->GetAffectedEunhyung() > 0;
|
||
|
||
m_pkSk->SetPointVar("ek", m_pkChr->GetAffectedEunhyung()*1./100);
|
||
//m_pkChr->ClearAffectedEunhyung();
|
||
SetPolyVarForAttack(m_pkChr, m_pkSk, m_pkWeapon);
|
||
|
||
int iAmount = 0;
|
||
|
||
if (m_pkChr->GetUsedSkillMasterType(m_pkSk->dwVnum) >= SKILL_GRAND_MASTER)
|
||
{
|
||
iAmount = (int) m_pkSk->kMasterBonusPoly.Eval();
|
||
}
|
||
else
|
||
{
|
||
iAmount = (int) m_pkSk->kPointPoly.Eval();
|
||
}
|
||
|
||
if (test_server && iAmount == 0 && m_pkSk->bPointOn != POINT_NONE)
|
||
{
|
||
m_pkChr->ChatPacket(CHAT_TYPE_INFO, "ȿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>. <20><>ų <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Ȯ<><C8AE><EFBFBD>ϼ<EFBFBD><CFBC><EFBFBD>");
|
||
}
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
iAmount = -iAmount;
|
||
|
||
if (m_pkSk->dwVnum == SKILL_AMSEOP)
|
||
{
|
||
float fDelta = GetDegreeDelta(m_pkChr->GetRotation(), pkChrVictim->GetRotation());
|
||
float adjust;
|
||
|
||
if (fDelta < 35.0f)
|
||
{
|
||
adjust = 1.5f;
|
||
|
||
if (bUnderEunhyung)
|
||
adjust += 0.5f;
|
||
|
||
if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER)
|
||
{
|
||
adjust += 0.5f;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
adjust = 1.0f;
|
||
|
||
if (bUnderEunhyung)
|
||
adjust += 0.5f;
|
||
|
||
if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER)
|
||
adjust += 0.5f;
|
||
}
|
||
|
||
iAmount = (int) (iAmount * adjust);
|
||
}
|
||
else if (m_pkSk->dwVnum == SKILL_GUNGSIN)
|
||
{
|
||
float adjust = 1.0;
|
||
|
||
if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_DAGGER)
|
||
{
|
||
adjust = 1.35f;
|
||
}
|
||
|
||
iAmount = (int) (iAmount * adjust);
|
||
}
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
else if (m_pkSk->dwVnum == SKILL_GONGDAB)
|
||
{
|
||
float adjust = 1.0;
|
||
|
||
if (m_pkChr->GetWear(WEAR_WEAPON) && m_pkChr->GetWear(WEAR_WEAPON)->GetSubType() == WEAPON_CLAW)
|
||
{
|
||
adjust = 1.35f;
|
||
}
|
||
|
||
iAmount = (int)(iAmount * adjust);
|
||
}
|
||
#endif
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
//sys_log(0, "name: %s skill: %s amount %d to %s", m_pkChr->GetName(), m_pkSk->szName, iAmount, pkChrVictim->GetName());
|
||
|
||
iDam = CalcBattleDamage(iAmount, m_pkChr->GetLevel(), pkChrVictim->GetLevel());
|
||
|
||
if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() != (DWORD) pkChrVictim->GetVID())
|
||
{
|
||
iDam = (int) (iDam * m_pkSk->kSplashAroundDamageAdjustPoly.Eval());
|
||
}
|
||
|
||
EDamageType dt = DAMAGE_TYPE_NONE;
|
||
|
||
switch (m_pkSk->bSkillAttrType)
|
||
{
|
||
case SKILL_ATTR_TYPE_NORMAL:
|
||
break;
|
||
|
||
case SKILL_ATTR_TYPE_MELEE:
|
||
{
|
||
dt = DAMAGE_TYPE_MELEE;
|
||
|
||
iDam = CalcDamBonus(m_pkChr, pkChrVictim, iDam);
|
||
|
||
if (!bIgnoreDefense)
|
||
iDam -= pkChrVictim->GetPoint(POINT_DEF_GRADE);
|
||
}
|
||
break;
|
||
|
||
case SKILL_ATTR_TYPE_RANGE:
|
||
dt = DAMAGE_TYPE_RANGE;
|
||
|
||
//iDam -= pkChrVictim->GetPoint(POINT_DEF_GRADE);
|
||
iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_BOW)) / 100;
|
||
break;
|
||
|
||
case SKILL_ATTR_TYPE_MAGIC:
|
||
dt = DAMAGE_TYPE_MAGIC;
|
||
iDam = CalcAttBonus(m_pkChr, pkChrVictim, iDam);
|
||
|
||
//iDam -= pkChrVictim->GetPoint(POINT_MAGIC_DEF_GRADE);
|
||
#ifdef ENABLE_MAGIC_REDUCTION_SYSTEM
|
||
{
|
||
const int resist_magic = MINMAX(0, pkChrVictim->GetPoint(POINT_RESIST_MAGIC), 100);
|
||
const int resist_magic_reduction = MINMAX(0, (m_pkChr->GetJob()==JOB_SURA) ? m_pkChr->GetPoint(POINT_RESIST_MAGIC_REDUCTION)/2 : m_pkChr->GetPoint(POINT_RESIST_MAGIC_REDUCTION), 50);
|
||
const int total_res_magic = MINMAX(0, resist_magic - resist_magic_reduction, 100);
|
||
iDam = iDam * (100 - total_res_magic) / 100;
|
||
}
|
||
#else
|
||
iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_MAGIC)) / 100;
|
||
#endif
|
||
break;
|
||
|
||
default:
|
||
sys_err("Unknown skill attr type %u vnum %u", m_pkSk->bSkillAttrType, m_pkSk->dwVnum);
|
||
break;
|
||
}
|
||
|
||
if (pkChrVictim->IsNPC())
|
||
{
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_WIND))
|
||
{
|
||
iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_WIND)) / 100;
|
||
}
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_ELEC))
|
||
{
|
||
iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_ELEC)) / 100;
|
||
}
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE))
|
||
{
|
||
iDam = iDam * (100 - pkChrVictim->GetPoint(POINT_RESIST_FIRE)) / 100;
|
||
}
|
||
}
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_COMPUTE_MAGIC_DAMAGE))
|
||
dt = DAMAGE_TYPE_MAGIC;
|
||
|
||
if (pkChrVictim->CanBeginFight())
|
||
pkChrVictim->BeginFight(m_pkChr);
|
||
|
||
if (m_pkSk->dwVnum == SKILL_CHAIN)
|
||
sys_log(0, "%s CHAIN INDEX %d DAM %d DT %d", m_pkChr->GetName(), m_pkChr->GetChainLightningIndex() - 1, iDam, dt);
|
||
|
||
{
|
||
BYTE AntiSkillID = 0;
|
||
|
||
switch (m_pkSk->dwVnum)
|
||
{
|
||
case SKILL_TANHWAN: AntiSkillID = SKILL_7_A_ANTI_TANHWAN; break;
|
||
case SKILL_AMSEOP: AntiSkillID = SKILL_7_B_ANTI_AMSEOP; break;
|
||
case SKILL_SWAERYUNG: AntiSkillID = SKILL_7_C_ANTI_SWAERYUNG; break;
|
||
case SKILL_YONGBI: AntiSkillID = SKILL_7_D_ANTI_YONGBI; break;
|
||
case SKILL_GIGONGCHAM: AntiSkillID = SKILL_8_A_ANTI_GIGONGCHAM; break;
|
||
case SKILL_YEONSA: AntiSkillID = SKILL_8_B_ANTI_YEONSA; break;
|
||
case SKILL_MAHWAN: AntiSkillID = SKILL_8_C_ANTI_MAHWAN; break;
|
||
case SKILL_BYEURAK: AntiSkillID = SKILL_8_D_ANTI_BYEURAK; break;
|
||
}
|
||
|
||
if (0 != AntiSkillID)
|
||
{
|
||
BYTE AntiSkillLevel = pkChrVictim->GetSkillLevel(AntiSkillID);
|
||
|
||
if (0 != AntiSkillLevel)
|
||
{
|
||
CSkillProto* pkSk = CSkillManager::instance().Get(AntiSkillID);
|
||
if (!pkSk)
|
||
{
|
||
sys_err ("There is no anti skill(%d) in skill proto", AntiSkillID);
|
||
}
|
||
else
|
||
{
|
||
pkSk->SetPointVar("k", 1.0f * pkChrVictim->GetSkillPower(AntiSkillID) * pkSk->bMaxLevel / 100);
|
||
|
||
double ResistAmount = pkSk->kPointPoly.Eval();
|
||
|
||
sys_log(0, "ANTI_SKILL: Resist(%lf) Orig(%d) Reduce(%d)", ResistAmount, iDam, int(iDam * (ResistAmount/100.0)));
|
||
|
||
iDam -= iDam * (ResistAmount/100.0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!pkChrVictim->Damage(m_pkChr, iDam, dt) && !pkChrVictim->IsStun() && !pkChrVictim->IsDead()) // @fixme312 IsDead
|
||
{
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_REMOVE_GOOD_AFFECT))
|
||
{
|
||
#ifdef ENABLE_NULLIFYAFFECT_LIMIT
|
||
int iLevel = m_pkChr->GetLevel();
|
||
int yLevel = pkChrVictim->GetLevel();
|
||
// const float k = 1.0 * m_pkChr->GetSkillPower(m_pkSk->dwVnum, bSkillLevel) * m_pkSk->bMaxLevel / 100;
|
||
int iDifLev = 9;
|
||
if ((iLevel-iDifLev <= yLevel) && (iLevel+iDifLev >= yLevel))
|
||
#endif
|
||
{
|
||
int iAmount2 = (int) m_pkSk->kPointPoly2.Eval();
|
||
int iDur2 = (int) m_pkSk->kDurationPoly2.Eval();
|
||
iDur2 += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (number(1, 100) <= iAmount2)
|
||
{
|
||
pkChrVictim->RemoveGoodAffect();
|
||
pkChrVictim->AddAffect(m_pkSk->dwVnum, POINT_NONE, 0, AFF_PABEOP, iDur2, 0, true);
|
||
}
|
||
}
|
||
}
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW | SKILL_FLAG_STUN | SKILL_FLAG_FIRE_CONT | SKILL_FLAG_POISON | SKILL_FLAG_BLEEDING))
|
||
#else
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW | SKILL_FLAG_STUN | SKILL_FLAG_FIRE_CONT | SKILL_FLAG_POISON))
|
||
#endif
|
||
{
|
||
int iPct = (int) m_pkSk->kPointPoly2.Eval();
|
||
int iDur = (int) m_pkSk->kDurationPoly2.Eval();
|
||
|
||
iDur += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_STUN))
|
||
{
|
||
SkillAttackAffect(pkChrVictim, iPct, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iDur, m_pkSk->szName);
|
||
}
|
||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW))
|
||
{
|
||
SkillAttackAffect(pkChrVictim, iPct, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, iDur, m_pkSk->szName);
|
||
}
|
||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE_CONT))
|
||
{
|
||
m_pkSk->SetDurationVar("k", 1.0 * m_bUseSkillPower * m_pkSk->bMaxLevel / 100);
|
||
m_pkSk->SetDurationVar("iq", m_pkChr->GetPoint(POINT_IQ));
|
||
|
||
iDur = (int)m_pkSk->kDurationPoly2.Eval();
|
||
int bonus = m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (bonus != 0)
|
||
{
|
||
iDur += bonus / 2;
|
||
}
|
||
|
||
if (number(1, 100) <= iDur)
|
||
{
|
||
pkChrVictim->AttackedByFire(m_pkChr, iPct, 5);
|
||
}
|
||
}
|
||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_POISON))
|
||
{
|
||
if (number(1, 100) <= iPct)
|
||
pkChrVictim->AttackedByPoison(m_pkChr);
|
||
}
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_BLEEDING))
|
||
{
|
||
if (number(1, 100) <= iPct)
|
||
pkChrVictim->AttackedByBleeding(m_pkChr);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) &&
|
||
!IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE))
|
||
{
|
||
float fCrushSlidingLength = 200;
|
||
|
||
if (m_pkChr->IsNPC())
|
||
fCrushSlidingLength = 400;
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH_LONG))
|
||
fCrushSlidingLength *= 2;
|
||
|
||
float fx, fy;
|
||
float degree = GetDegreeFromPositionXY(m_pkChr->GetX(), m_pkChr->GetY(), pkChrVictim->GetX(), pkChrVictim->GetY());
|
||
|
||
if (m_pkSk->dwVnum == SKILL_HORSE_WILDATTACK)
|
||
{
|
||
degree -= m_pkChr->GetRotation();
|
||
degree = fmod(degree, 360.0f) - 180.0f;
|
||
|
||
if (degree > 0)
|
||
degree = m_pkChr->GetRotation() + 90.0f;
|
||
else
|
||
degree = m_pkChr->GetRotation() - 90.0f;
|
||
}
|
||
|
||
GetDeltaByDegree(degree, fCrushSlidingLength, &fx, &fy);
|
||
sys_log(0, "CRUSH! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy));
|
||
long tx = (long)(pkChrVictim->GetX()+fx);
|
||
long ty = (long)(pkChrVictim->GetY()+fy);
|
||
|
||
pkChrVictim->Sync(tx, ty);
|
||
pkChrVictim->Goto(tx, ty);
|
||
pkChrVictim->CalculateMoveDuration();
|
||
|
||
if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID())
|
||
{
|
||
SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName);
|
||
}
|
||
else
|
||
{
|
||
pkChrVictim->SyncPacket();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_HP_ABSORB))
|
||
{
|
||
int iPct = (int) m_pkSk->kPointPoly2.Eval();
|
||
m_pkChr->PointChange(POINT_HP, iDam * iPct / 100);
|
||
}
|
||
|
||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SP_ABSORB))
|
||
{
|
||
int iPct = (int) m_pkSk->kPointPoly2.Eval();
|
||
m_pkChr->PointChange(POINT_SP, iDam * iPct / 100);
|
||
}
|
||
|
||
if (m_pkSk->dwVnum == SKILL_CHAIN && m_pkChr->GetChainLightningIndex() < m_pkChr->GetChainLightningMaxCount())
|
||
{
|
||
chain_lightning_event_info* info = AllocEventInfo<chain_lightning_event_info>();
|
||
|
||
info->dwVictim = pkChrVictim->GetVID();
|
||
info->dwChr = m_pkChr->GetVID();
|
||
|
||
event_create(ChainLightningEvent, info, passes_per_sec / 5);
|
||
}
|
||
if(test_server)
|
||
sys_log(0, "FuncSplashDamage End :%s ", m_pkChr->GetName());
|
||
}
|
||
|
||
int m_x;
|
||
int m_y;
|
||
CSkillProto * m_pkSk;
|
||
LPCHARACTER m_pkChr;
|
||
int m_iAmount;
|
||
int m_iAG;
|
||
int m_iCount;
|
||
int m_iMaxHit;
|
||
LPITEM m_pkWeapon;
|
||
bool m_bDisableCooltime;
|
||
TSkillUseInfo* m_pInfo;
|
||
BYTE m_bUseSkillPower;
|
||
};
|
||
|
||
struct FuncSplashAffect
|
||
{
|
||
FuncSplashAffect(LPCHARACTER ch, int x, int y, int iDist, DWORD dwVnum, BYTE bPointOn, int iAmount, DWORD dwAffectFlag, int iDuration, int iSPCost, bool bOverride, int iMaxHit)
|
||
{
|
||
m_x = x;
|
||
m_y = y;
|
||
m_iDist = iDist;
|
||
m_dwVnum = dwVnum;
|
||
m_bPointOn = bPointOn;
|
||
m_iAmount = iAmount;
|
||
m_dwAffectFlag = dwAffectFlag;
|
||
m_iDuration = iDuration;
|
||
m_iSPCost = iSPCost;
|
||
m_bOverride = bOverride;
|
||
m_pkChrAttacker = ch;
|
||
m_iMaxHit = iMaxHit;
|
||
m_iCount = 0;
|
||
}
|
||
|
||
void operator () (LPENTITY ent)
|
||
{
|
||
if (m_iMaxHit && m_iMaxHit <= m_iCount)
|
||
return;
|
||
|
||
if (ent->IsType(ENTITY_CHARACTER))
|
||
{
|
||
LPCHARACTER pkChr = (LPCHARACTER) ent;
|
||
|
||
if (test_server)
|
||
sys_log(0, "FuncSplashAffect step 1 : name:%s vnum:%d iDur:%d", pkChr->GetName(), m_dwVnum, m_iDuration);
|
||
if (DISTANCE_APPROX(m_x - pkChr->GetX(), m_y - pkChr->GetY()) < m_iDist)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "FuncSplashAffect step 2 : name:%s vnum:%d iDur:%d", pkChr->GetName(), m_dwVnum, m_iDuration);
|
||
if (m_dwVnum == SKILL_TUSOK)
|
||
if (pkChr->CanBeginFight())
|
||
pkChr->BeginFight(m_pkChrAttacker);
|
||
|
||
if (pkChr->IsPC() && m_dwVnum == SKILL_TUSOK)
|
||
pkChr->AddAffect(m_dwVnum, m_bPointOn, m_iAmount, m_dwAffectFlag, m_iDuration/3, m_iSPCost, m_bOverride);
|
||
else
|
||
pkChr->AddAffect(m_dwVnum, m_bPointOn, m_iAmount, m_dwAffectFlag, m_iDuration, m_iSPCost, m_bOverride);
|
||
|
||
m_iCount ++;
|
||
}
|
||
}
|
||
}
|
||
|
||
LPCHARACTER m_pkChrAttacker;
|
||
int m_x;
|
||
int m_y;
|
||
int m_iDist;
|
||
DWORD m_dwVnum;
|
||
BYTE m_bPointOn;
|
||
int m_iAmount;
|
||
DWORD m_dwAffectFlag;
|
||
int m_iDuration;
|
||
int m_iSPCost;
|
||
bool m_bOverride;
|
||
int m_iMaxHit;
|
||
int m_iCount;
|
||
};
|
||
|
||
EVENTINFO(skill_gwihwan_info)
|
||
{
|
||
DWORD pid;
|
||
BYTE bsklv;
|
||
|
||
skill_gwihwan_info()
|
||
: pid( 0 )
|
||
, bsklv( 0 )
|
||
{
|
||
}
|
||
};
|
||
|
||
EVENTFUNC(skill_gwihwan_event)
|
||
{
|
||
skill_gwihwan_info* info = dynamic_cast<skill_gwihwan_info*>( event->info );
|
||
|
||
if ( info == NULL )
|
||
{
|
||
sys_err( "skill_gwihwan_event> <Factor> Null pointer" );
|
||
return 0;
|
||
}
|
||
|
||
DWORD pid = info->pid;
|
||
BYTE sklv= info->bsklv;
|
||
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pid);
|
||
|
||
if (!ch)
|
||
return 0;
|
||
|
||
int percent = 20 * sklv - 1;
|
||
|
||
if (number(1, 100) <= percent)
|
||
{
|
||
PIXEL_POSITION pos;
|
||
|
||
if (SECTREE_MANAGER::instance().GetRecallPositionByEmpire(ch->GetMapIndex(), ch->GetEmpire(), pos))
|
||
{
|
||
sys_log(1, "Recall: %s %d %d -> %d %d", ch->GetName(), ch->GetX(), ch->GetY(), pos.x, pos.y);
|
||
ch->WarpSet(pos.x, pos.y);
|
||
}
|
||
else
|
||
{
|
||
sys_err("CHARACTER::UseItem : cannot find spawn position (name %s, %d x %d)", ch->GetName(), ch->GetX(), ch->GetY());
|
||
ch->WarpSet(EMPIRE_START_X(ch->GetEmpire()), EMPIRE_START_Y(ch->GetEmpire()));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD>ȯ<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD><CFBF><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int CHARACTER::ComputeSkillAtPosition(DWORD dwVnum, const PIXEL_POSITION& posTarget, BYTE bSkillLevel)
|
||
{
|
||
if (GetMountVnum())
|
||
return BATTLE_NONE;
|
||
|
||
if (IsPolymorphed())
|
||
return BATTLE_NONE;
|
||
|
||
if (g_bSkillDisable)
|
||
return BATTLE_NONE;
|
||
|
||
CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum);
|
||
|
||
if (!pkSk)
|
||
return BATTLE_NONE;
|
||
|
||
if (test_server)
|
||
{
|
||
sys_log(0, "ComputeSkillAtPosition %s vnum %d x %d y %d level %d",
|
||
GetName(), dwVnum, posTarget.x, posTarget.y, bSkillLevel);
|
||
}
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
return BATTLE_NONE;
|
||
|
||
if (0 == bSkillLevel)
|
||
{
|
||
if ((bSkillLevel = GetSkillLevel(pkSk->dwVnum)) == 0)
|
||
{
|
||
return BATTLE_NONE;
|
||
}
|
||
}
|
||
|
||
const float k = 1.0 * GetSkillPower(pkSk->dwVnum, bSkillLevel) * pkSk->bMaxLevel / 100;
|
||
|
||
pkSk->SetPointVar("k", k);
|
||
pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k);
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcMeleeDamage(this, this, true, false));
|
||
}
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MAGIC_DAMAGE))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcMagicDamage(this, this));
|
||
}
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE))
|
||
{
|
||
LPITEM pkBow, pkArrow;
|
||
if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcArrowDamage(this, this, pkBow, pkArrow, true));
|
||
}
|
||
else
|
||
{
|
||
pkSk->SetPointVar("atk", 0);
|
||
}
|
||
}
|
||
|
||
if (pkSk->bPointOn == POINT_MOV_SPEED)
|
||
{
|
||
pkSk->SetPointVar("maxv", this->GetLimitPoint(POINT_MOV_SPEED));
|
||
}
|
||
|
||
pkSk->SetPointVar("lv", GetLevel());
|
||
pkSk->SetPointVar("iq", GetPoint(POINT_IQ));
|
||
pkSk->SetPointVar("str", GetPoint(POINT_ST));
|
||
pkSk->SetPointVar("dex", GetPoint(POINT_DX));
|
||
pkSk->SetPointVar("con", GetPoint(POINT_HT));
|
||
pkSk->SetPointVar("maxhp", this->GetMaxHP());
|
||
pkSk->SetPointVar("maxsp", this->GetMaxSP());
|
||
pkSk->SetPointVar("chain", 0);
|
||
pkSk->SetPointVar("ar", CalcAttackRating(this, this));
|
||
pkSk->SetPointVar("def", GetPoint(POINT_DEF_GRADE));
|
||
pkSk->SetPointVar("odef", GetPoint(POINT_DEF_GRADE) - GetPoint(POINT_DEF_GRADE_BONUS));
|
||
pkSk->SetPointVar("horse_level", GetHorseLevel());
|
||
|
||
if (pkSk->bSkillAttrType != SKILL_ATTR_TYPE_NORMAL)
|
||
OnMove(true);
|
||
|
||
LPITEM pkWeapon = GetWear(WEAR_WEAPON);
|
||
|
||
SetPolyVarForAttack(this, pkSk, pkWeapon);
|
||
|
||
pkSk->SetDurationVar("k", k/*bSkillLevel*/);
|
||
|
||
int iAmount = (int) pkSk->kPointPoly.Eval();
|
||
int iAmount2 = (int) pkSk->kPointPoly2.Eval();
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
int iAmount3 = (int) pkSk->kPointPoly3.Eval();
|
||
|
||
if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER)
|
||
{
|
||
/*
|
||
if (iAmount >= 0)
|
||
iAmount += (int) m_pkSk->kMasterBonusPoly.Eval();
|
||
else
|
||
iAmount -= (int) m_pkSk->kMasterBonusPoly.Eval();
|
||
*/
|
||
iAmount = (int) pkSk->kMasterBonusPoly.Eval();
|
||
}
|
||
|
||
if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE)
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, "ȿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>. <20><>ų <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Ȯ<><C8AE><EFBFBD>ϼ<EFBFBD><CFBC><EFBFBD>");
|
||
}
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT))
|
||
{
|
||
if (number(1, 100) <= iAmount2)
|
||
{
|
||
RemoveBadAffect();
|
||
}
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE))
|
||
{
|
||
bool bAdded = false;
|
||
|
||
if (pkSk->bPointOn == POINT_HP && iAmount < 0)
|
||
{
|
||
int iAG = 0;
|
||
|
||
FuncSplashDamage f(posTarget.x, posTarget.y, pkSk, this, iAmount, iAG, pkSk->lMaxHit, pkWeapon, m_bDisableCooltime, IsPC()?&m_SkillUseInfo[dwVnum]:NULL, GetSkillPower(dwVnum, bSkillLevel));
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
{
|
||
if (GetSectree())
|
||
GetSectree()->ForEachAround(f);
|
||
}
|
||
else
|
||
{
|
||
//if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill call FuncSplashDamage %s", GetName());
|
||
f(this);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill no damage %d %s", iAmount, GetName());
|
||
int iDur = (int) pkSk->kDurationPoly.Eval();
|
||
|
||
if (IsPC())
|
||
if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END))
|
||
if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG)
|
||
{
|
||
//if (dwVnum == SKILL_CHAIN) sys_log(0, "CHAIN skill cannot hit %s", GetName());
|
||
return BATTLE_NONE;
|
||
}
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
AddAffect(pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true);
|
||
else
|
||
{
|
||
if (GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true, pkSk->lMaxHit);
|
||
GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
bAdded = true;
|
||
}
|
||
}
|
||
|
||
if (pkSk->bPointOn2 != POINT_NONE)
|
||
{
|
||
int iDur = (int) pkSk->kDurationPoly2.Eval();
|
||
|
||
sys_log(1, "try second %u %d %d", pkSk->dwVnum, pkSk->bPointOn2, iDur);
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded);
|
||
else
|
||
{
|
||
if (GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded, pkSk->lMaxHit);
|
||
GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
PointChange(pkSk->bPointOn2, iAmount2);
|
||
}
|
||
}
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER && pkSk->bPointOn3 != POINT_NONE)
|
||
{
|
||
int iDur = (int) pkSk->kDurationPoly3.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded);
|
||
else
|
||
{
|
||
if (GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, posTarget.x, posTarget.y, pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded, pkSk->lMaxHit);
|
||
GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PointChange(pkSk->bPointOn3, iAmount3);
|
||
}
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
return BATTLE_DAMAGE;
|
||
}
|
||
else
|
||
{
|
||
bool bAdded = false;
|
||
int iDur = (int) pkSk->kDurationPoly.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/);
|
||
|
||
AddAffect(pkSk->dwVnum,
|
||
pkSk->bPointOn,
|
||
iAmount,
|
||
pkSk->dwAffectFlag,
|
||
iDur,
|
||
(long) pkSk->kDurationSPCostPoly.Eval(),
|
||
!bAdded);
|
||
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
PointChange(pkSk->bPointOn, iAmount);
|
||
}
|
||
|
||
if (pkSk->bPointOn2 != POINT_NONE)
|
||
{
|
||
int iDur = (int) pkSk->kDurationPoly2.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded);
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
PointChange(pkSk->bPointOn2, iAmount2);
|
||
}
|
||
}
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER && pkSk->bPointOn3 != POINT_NONE)
|
||
{
|
||
int iDur = (int) pkSk->kDurationPoly3.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, 0 /*pkSk->dwAffectFlag3*/, iDur, 0, !bAdded);
|
||
}
|
||
else
|
||
{
|
||
PointChange(pkSk->bPointOn3, iAmount3);
|
||
}
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
return BATTLE_NONE;
|
||
}
|
||
}
|
||
|
||
#ifdef ENABLE_SKILL_FLAG_PARTY
|
||
struct FComputeSkillParty
|
||
{
|
||
FComputeSkillParty(DWORD dwVnum, LPCHARACTER pkAttacker, BYTE bSkillLevel = 0)
|
||
: m_dwVnum(dwVnum), m_pkAttacker(pkAttacker), m_bSkillLevel(bSkillLevel)
|
||
{
|
||
}
|
||
|
||
void operator () (LPCHARACTER ch)
|
||
{
|
||
m_pkAttacker->ComputeSkill(m_dwVnum, ch, m_bSkillLevel);
|
||
}
|
||
|
||
DWORD m_dwVnum;
|
||
LPCHARACTER m_pkAttacker;
|
||
BYTE m_bSkillLevel;
|
||
};
|
||
|
||
int CHARACTER::ComputeSkillParty(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel)
|
||
{
|
||
FComputeSkillParty f(dwVnum, pkVictim, bSkillLevel);
|
||
if (GetParty() && GetParty()->GetNearMemberCount())
|
||
GetParty()->ForEachNearMember(f);
|
||
else
|
||
f(this);
|
||
|
||
return BATTLE_NONE;
|
||
}
|
||
#endif
|
||
|
||
int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel)
|
||
{
|
||
const bool bCanUseHorseSkill = CanUseHorseSkill();
|
||
|
||
if (false == bCanUseHorseSkill && true == IsRiding())
|
||
return BATTLE_NONE;
|
||
|
||
if (IsPolymorphed())
|
||
return BATTLE_NONE;
|
||
|
||
if (g_bSkillDisable)
|
||
return BATTLE_NONE;
|
||
|
||
CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum);
|
||
|
||
if (!pkSk)
|
||
return BATTLE_NONE;
|
||
|
||
if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE)
|
||
return BATTLE_NONE;
|
||
|
||
if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE)
|
||
return BATTLE_NONE;
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY))
|
||
pkVictim = this;
|
||
|
||
if (!pkVictim)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "ComputeSkill: %s Victim == null, skill %d", GetName(), dwVnum);
|
||
|
||
return BATTLE_NONE;
|
||
}
|
||
|
||
if (pkSk->dwTargetRange && DISTANCE_SQRT(GetX() - pkVictim->GetX(), GetY() - pkVictim->GetY()) >= pkSk->dwTargetRange + 50)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "ComputeSkill: Victim too far, skill %d : %s to %s (distance %u limit %u)",
|
||
dwVnum,
|
||
GetName(),
|
||
pkVictim->GetName(),
|
||
(long)DISTANCE_SQRT(GetX() - pkVictim->GetX(), GetY() - pkVictim->GetY()),
|
||
pkSk->dwTargetRange);
|
||
|
||
return BATTLE_NONE;
|
||
}
|
||
|
||
if (0 == bSkillLevel)
|
||
{
|
||
if ((bSkillLevel = GetSkillLevel(pkSk->dwVnum)) == 0)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "ComputeSkill : name:%s vnum:%d skillLevelBySkill : %d ", GetName(), pkSk->dwVnum, bSkillLevel);
|
||
return BATTLE_NONE;
|
||
}
|
||
}
|
||
|
||
if (pkVictim->IsAffectFlag(AFF_PABEOP) && pkVictim->IsGoodAffect(dwVnum))
|
||
{
|
||
return BATTLE_NONE;
|
||
}
|
||
|
||
const float k = 1.0 * GetSkillPower(pkSk->dwVnum, bSkillLevel) * pkSk->bMaxLevel / 100;
|
||
|
||
pkSk->SetPointVar("k", k);
|
||
pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k);
|
||
|
||
if (pkSk->dwType == SKILL_TYPE_HORSE)
|
||
{
|
||
LPITEM pkBow, pkArrow;
|
||
if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcArrowDamage(this, pkVictim, pkBow, pkArrow, true));
|
||
}
|
||
else
|
||
{
|
||
pkSk->SetPointVar("atk", CalcMeleeDamage(this, pkVictim, true, false));
|
||
}
|
||
}
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MELEE_DAMAGE))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcMeleeDamage(this, pkVictim, true, false));
|
||
}
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_MAGIC_DAMAGE))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcMagicDamage(this, pkVictim));
|
||
}
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_ARROW_DAMAGE))
|
||
{
|
||
LPITEM pkBow, pkArrow;
|
||
if (1 == GetArrowAndBow(&pkBow, &pkArrow, 1))
|
||
{
|
||
pkSk->SetPointVar("atk", CalcArrowDamage(this, pkVictim, pkBow, pkArrow, true));
|
||
}
|
||
else
|
||
{
|
||
pkSk->SetPointVar("atk", 0);
|
||
}
|
||
}
|
||
|
||
if (pkSk->bPointOn == POINT_MOV_SPEED)
|
||
{
|
||
pkSk->SetPointVar("maxv", pkVictim->GetLimitPoint(POINT_MOV_SPEED));
|
||
}
|
||
|
||
pkSk->SetPointVar("lv", GetLevel());
|
||
pkSk->SetPointVar("iq", GetPoint(POINT_IQ));
|
||
pkSk->SetPointVar("str", GetPoint(POINT_ST));
|
||
pkSk->SetPointVar("dex", GetPoint(POINT_DX));
|
||
pkSk->SetPointVar("con", GetPoint(POINT_HT));
|
||
pkSk->SetPointVar("maxhp", pkVictim->GetMaxHP());
|
||
pkSk->SetPointVar("maxsp", pkVictim->GetMaxSP());
|
||
pkSk->SetPointVar("chain", 0);
|
||
pkSk->SetPointVar("ar", CalcAttackRating(this, pkVictim));
|
||
pkSk->SetPointVar("def", GetPoint(POINT_DEF_GRADE));
|
||
pkSk->SetPointVar("odef", GetPoint(POINT_DEF_GRADE) - GetPoint(POINT_DEF_GRADE_BONUS));
|
||
pkSk->SetPointVar("horse_level", GetHorseLevel());
|
||
|
||
if (pkSk->bSkillAttrType != SKILL_ATTR_TYPE_NORMAL)
|
||
OnMove(true);
|
||
|
||
LPITEM pkWeapon = GetWear(WEAR_WEAPON);
|
||
|
||
SetPolyVarForAttack(this, pkSk, pkWeapon);
|
||
|
||
pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/);
|
||
pkSk->kDurationPoly2.SetVar("k", k/*bSkillLevel*/);
|
||
|
||
int iAmount = (int) pkSk->kPointPoly.Eval();
|
||
int iAmount2 = (int) pkSk->kPointPoly2.Eval();
|
||
int iAmount3 = (int) pkSk->kPointPoly3.Eval();
|
||
|
||
if (test_server && IsPC())
|
||
sys_log(0, "iAmount: %d %d %d , atk:%f skLevel:%f k:%f GetSkillPower(%d) MaxLevel:%d Per:%f",
|
||
iAmount, iAmount2, iAmount3,
|
||
pkSk->kPointPoly.GetVar("atk"),
|
||
pkSk->kPointPoly.GetVar("k"),
|
||
k,
|
||
GetSkillPower(pkSk->dwVnum, bSkillLevel),
|
||
pkSk->bMaxLevel,
|
||
pkSk->bMaxLevel/100
|
||
);
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if (GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER)
|
||
{
|
||
iAmount = (int) pkSk->kMasterBonusPoly.Eval();
|
||
}
|
||
|
||
if (test_server && iAmount == 0 && pkSk->bPointOn != POINT_NONE)
|
||
{
|
||
ChatPacket(CHAT_TYPE_INFO, "ȿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>. <20><>ų <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Ȯ<><C8AE><EFBFBD>ϼ<EFBFBD><CFBC><EFBFBD>");
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
//sys_log(0, "XXX SKILL Calc %d Amount %d", dwVnum, iAmount);
|
||
|
||
// REMOVE_BAD_AFFECT_BUG_FIX
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_REMOVE_BAD_AFFECT))
|
||
{
|
||
if (number(1, 100) <= iAmount2)
|
||
{
|
||
pkVictim->RemoveBadAffect();
|
||
}
|
||
}
|
||
// END_OF_REMOVE_BAD_AFFECT_BUG_FIX
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK | SKILL_FLAG_USE_MELEE_DAMAGE | SKILL_FLAG_USE_MAGIC_DAMAGE) &&
|
||
!(pkSk->dwVnum == SKILL_MUYEONG && pkVictim == this) && !(pkSk->IsChargeSkill() && pkVictim == this))
|
||
{
|
||
bool bAdded = false;
|
||
|
||
if (pkSk->bPointOn == POINT_HP && iAmount < 0)
|
||
{
|
||
int iAG = 0;
|
||
|
||
FuncSplashDamage f(pkVictim->GetX(), pkVictim->GetY(), pkSk, this, iAmount, iAG, pkSk->lMaxHit, pkWeapon, m_bDisableCooltime, IsPC()?&m_SkillUseInfo[dwVnum]:NULL, GetSkillPower(dwVnum, bSkillLevel));
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
{
|
||
if (pkVictim->GetSectree())
|
||
pkVictim->GetSectree()->ForEachAround(f);
|
||
}
|
||
else
|
||
{
|
||
f(pkVictim);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/);
|
||
int iDur = (int) pkSk->kDurationPoly.Eval();
|
||
|
||
if (IsPC())
|
||
if (!(dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END))
|
||
if (!m_bDisableCooltime && !m_SkillUseInfo[dwVnum].HitOnce(dwVnum) && dwVnum != SKILL_MUYEONG)
|
||
{
|
||
return BATTLE_NONE;
|
||
}
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true);
|
||
else
|
||
{
|
||
if (pkVictim->GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn, iAmount, pkSk->dwAffectFlag, iDur, 0, true, pkSk->lMaxHit);
|
||
pkVictim->GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
bAdded = true;
|
||
}
|
||
}
|
||
|
||
if (pkSk->bPointOn2 != POINT_NONE && !pkSk->IsChargeSkill())
|
||
{
|
||
pkSk->kDurationPoly2.SetVar("k", k/*bSkillLevel*/);
|
||
int iDur = (int) pkSk->kDurationPoly2.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded);
|
||
else
|
||
{
|
||
if (pkVictim->GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur, 0, !bAdded, pkSk->lMaxHit);
|
||
pkVictim->GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
pkVictim->PointChange(pkSk->bPointOn2, iAmount2);
|
||
}
|
||
}
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if (pkSk->bPointOn3 != POINT_NONE && !pkSk->IsChargeSkill() && GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER)
|
||
{
|
||
pkSk->kDurationPoly3.SetVar("k", k/*bSkillLevel*/);
|
||
int iDur = (int) pkSk->kDurationPoly3.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded);
|
||
else
|
||
{
|
||
if (pkVictim->GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded, pkSk->lMaxHit);
|
||
pkVictim->GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
pkVictim->PointChange(pkSk->bPointOn3, iAmount3);
|
||
}
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
return BATTLE_DAMAGE;
|
||
}
|
||
else
|
||
{
|
||
if (dwVnum == SKILL_MUYEONG)
|
||
{
|
||
pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/);
|
||
pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/);
|
||
|
||
int iDur = (long) pkSk->kDurationPoly.Eval();
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (pkVictim == this)
|
||
AddAffect(dwVnum,
|
||
POINT_NONE, 0,
|
||
AFF_MUYEONG,
|
||
iDur,
|
||
(long) pkSk->kDurationSPCostPoly.Eval(),
|
||
true);
|
||
|
||
return BATTLE_NONE;
|
||
}
|
||
|
||
bool bAdded = false;
|
||
pkSk->kDurationPoly.SetVar("k", k/*bSkillLevel*/);
|
||
int iDur = (int) pkSk->kDurationPoly.Eval();
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
pkSk->kDurationSPCostPoly.SetVar("k", k/*bSkillLevel*/);
|
||
|
||
if (pkSk->bPointOn2 != POINT_NONE)
|
||
{
|
||
pkVictim->RemoveAffect(pkSk->dwVnum);
|
||
|
||
int iDur2 = (int) pkSk->kDurationPoly2.Eval();
|
||
|
||
if (iDur2 > 0)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "SKILL_AFFECT: %s %s Dur:%d To:%d Amount:%d",
|
||
GetName(),
|
||
pkSk->szName,
|
||
iDur2,
|
||
pkSk->bPointOn2,
|
||
iAmount2);
|
||
|
||
iDur2 += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur2, 0, false);
|
||
}
|
||
else
|
||
{
|
||
pkVictim->PointChange(pkSk->bPointOn2, iAmount2);
|
||
}
|
||
|
||
DWORD affact_flag = pkSk->dwAffectFlag;
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if ((pkSk->dwVnum == SKILL_CHUNKEON && GetUsedSkillMasterType(pkSk->dwVnum) < SKILL_GRAND_MASTER))
|
||
affact_flag = AFF_CHEONGEUN_WITH_FALL;
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
pkVictim->AddAffect(pkSk->dwVnum,
|
||
pkSk->bPointOn,
|
||
iAmount,
|
||
affact_flag,
|
||
iDur,
|
||
(long) pkSk->kDurationSPCostPoly.Eval(),
|
||
false);
|
||
}
|
||
else
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "SKILL_AFFECT: %s %s Dur:%d To:%d Amount:%d",
|
||
GetName(),
|
||
pkSk->szName,
|
||
iDur,
|
||
pkSk->bPointOn,
|
||
iAmount);
|
||
|
||
pkVictim->AddAffect(pkSk->dwVnum,
|
||
pkSk->bPointOn,
|
||
iAmount,
|
||
pkSk->dwAffectFlag,
|
||
iDur,
|
||
(long) pkSk->kDurationSPCostPoly.Eval(),
|
||
// ADD_GRANDMASTER_SKILL
|
||
!bAdded);
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
}
|
||
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
if (!pkSk->IsChargeSkill())
|
||
pkVictim->PointChange(pkSk->bPointOn, iAmount);
|
||
|
||
if (pkSk->bPointOn2 != POINT_NONE)
|
||
{
|
||
pkVictim->RemoveAffect(pkSk->dwVnum);
|
||
|
||
int iDur2 = (int) pkSk->kDurationPoly2.Eval();
|
||
|
||
if (iDur2 > 0)
|
||
{
|
||
iDur2 += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (pkSk->IsChargeSkill())
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, AFF_TANHWAN_DASH, iDur2, 0, false);
|
||
else
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn2, iAmount2, pkSk->dwAffectFlag2, iDur2, 0, false);
|
||
}
|
||
else
|
||
{
|
||
pkVictim->PointChange(pkSk->bPointOn2, iAmount2);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if (pkSk->bPointOn3 != POINT_NONE && !pkSk->IsChargeSkill() && GetUsedSkillMasterType(pkSk->dwVnum) >= SKILL_GRAND_MASTER)
|
||
{
|
||
pkSk->kDurationPoly3.SetVar("k", k/*bSkillLevel*/);
|
||
int iDur = (int) pkSk->kDurationPoly3.Eval();
|
||
|
||
sys_log(0, "try third %u %d %d %d 1894", pkSk->dwVnum, pkSk->bPointOn3, iDur, iAmount3);
|
||
|
||
if (iDur > 0)
|
||
{
|
||
iDur += GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||
|
||
if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_SPLASH))
|
||
pkVictim->AddAffect(pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded);
|
||
else
|
||
{
|
||
if (pkVictim->GetSectree())
|
||
{
|
||
FuncSplashAffect f(this, pkVictim->GetX(), pkVictim->GetY(), pkSk->iSplashRange, pkSk->dwVnum, pkSk->bPointOn3, iAmount3, /*pkSk->dwAffectFlag3*/ 0, iDur, 0, !bAdded, pkSk->lMaxHit);
|
||
pkVictim->GetSectree()->ForEachAround(f);
|
||
}
|
||
}
|
||
|
||
bAdded = true;
|
||
}
|
||
else
|
||
{
|
||
pkVictim->PointChange(pkSk->bPointOn3, iAmount3);
|
||
}
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
return BATTLE_NONE;
|
||
}
|
||
}
|
||
|
||
bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster)
|
||
{
|
||
if (false == CanUseSkill(dwVnum))
|
||
return false;
|
||
|
||
// NO_GRANDMASTER
|
||
if (test_server)
|
||
{
|
||
if (quest::CQuestManager::instance().GetEventFlag("no_grand_master"))
|
||
{
|
||
bUseGrandMaster = false;
|
||
}
|
||
}
|
||
// END_OF_NO_GRANDMASTER
|
||
|
||
if (g_bSkillDisable)
|
||
return false;
|
||
|
||
if (IsObserverMode())
|
||
return false;
|
||
|
||
if (!CanMove())
|
||
return false;
|
||
|
||
if (IsPolymorphed())
|
||
return false;
|
||
|
||
const bool bCanUseHorseSkill = CanUseHorseSkill();
|
||
|
||
if (dwVnum == SKILL_HORSE_SUMMON)
|
||
{
|
||
if (GetSkillLevel(dwVnum) == 0)
|
||
return false;
|
||
|
||
if (GetHorseLevel() <= 0)
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>. <20><><EFBFBD>°<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ã<>ư<EFBFBD><C6B0><EFBFBD><EFBFBD><EFBFBD>."));
|
||
else
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD> <20><>ȯ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϼ<EFBFBD><CFBC><EFBFBD>."));
|
||
|
||
return true;
|
||
}
|
||
|
||
if (false == bCanUseHorseSkill && true == IsRiding())
|
||
return false;
|
||
|
||
CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum);
|
||
sys_log(0, "%s: USE_SKILL: %d pkVictim %p", GetName(), dwVnum, get_pointer(pkVictim));
|
||
|
||
if (!pkSk)
|
||
return false;
|
||
|
||
if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE)
|
||
return BATTLE_NONE;
|
||
|
||
if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE)
|
||
return BATTLE_NONE;
|
||
|
||
if (GetSkillLevel(dwVnum) == 0)
|
||
return false;
|
||
|
||
// NO_GRANDMASTER
|
||
if (GetSkillMasterType(dwVnum) < SKILL_GRAND_MASTER)
|
||
bUseGrandMaster = false;
|
||
// END_OF_NO_GRANDMASTER
|
||
|
||
// MINING
|
||
if (GetWear(WEAR_WEAPON) && (GetWear(WEAR_WEAPON)->GetType() == ITEM_ROD || GetWear(WEAR_WEAPON)->GetType() == ITEM_PICK))
|
||
return false;
|
||
// END_OF_MINING
|
||
|
||
m_SkillUseInfo[dwVnum].TargetVIDMap.clear();
|
||
|
||
if (pkSk->IsChargeSkill())
|
||
{
|
||
if ((IsAffectFlag(AFF_TANHWAN_DASH)) || (pkVictim && (pkVictim != this)))
|
||
{
|
||
if (!pkVictim)
|
||
return false;
|
||
|
||
if (!IsAffectFlag(AFF_TANHWAN_DASH))
|
||
{
|
||
if (!UseSkill(dwVnum, this))
|
||
return false;
|
||
}
|
||
|
||
m_SkillUseInfo[dwVnum].SetMainTargetVID(pkVictim->GetVID());
|
||
|
||
ComputeSkill(dwVnum, pkVictim);
|
||
RemoveAffect(dwVnum);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
if (dwVnum == SKILL_COMBO)
|
||
{
|
||
if (m_bComboIndex)
|
||
m_bComboIndex = 0;
|
||
else
|
||
m_bComboIndex = GetSkillLevel(SKILL_COMBO);
|
||
|
||
ChatPacket(CHAT_TYPE_COMMAND, "combo %d", m_bComboIndex);
|
||
return true;
|
||
}
|
||
|
||
if ((0 != pkSk->dwAffectFlag || pkSk->dwVnum == SKILL_MUYEONG) && (pkSk->dwFlag & SKILL_FLAG_TOGGLE) && RemoveAffect(pkSk->dwVnum))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (IsAffectFlag(AFF_REVIVE_INVISIBLE))
|
||
RemoveAffect(AFFECT_REVIVE_INVISIBLE);
|
||
|
||
const float k = 1.0 * GetSkillPower(pkSk->dwVnum) * pkSk->bMaxLevel / 100;
|
||
|
||
pkSk->SetPointVar("k", k);
|
||
pkSk->kSplashAroundDamageAdjustPoly.SetVar("k", k);
|
||
|
||
pkSk->kCooldownPoly.SetVar("k", k);
|
||
int iCooltime = (int) pkSk->kCooldownPoly.Eval();
|
||
int lMaxHit = pkSk->lMaxHit ? pkSk->lMaxHit : -1;
|
||
|
||
pkSk->SetSPCostVar("k", k);
|
||
|
||
DWORD dwCur = get_dword_time();
|
||
|
||
if (dwVnum == SKILL_TERROR && m_SkillUseInfo[dwVnum].bUsed && m_SkillUseInfo[dwVnum].dwNextSkillUsableTime > dwCur )
|
||
{
|
||
sys_log(0, " SKILL_TERROR's Cooltime is not delta over %u", m_SkillUseInfo[dwVnum].dwNextSkillUsableTime - dwCur );
|
||
return false;
|
||
}
|
||
|
||
int iNeededSP = 0;
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_USE_HP_AS_COST))
|
||
{
|
||
pkSk->SetSPCostVar("maxhp", GetMaxHP());
|
||
pkSk->SetSPCostVar("v", GetHP());
|
||
iNeededSP = (int) pkSk->kSPCostPoly.Eval();
|
||
|
||
// ADD_GRANDMASTER_SKILL
|
||
if (GetSkillMasterType(dwVnum) >= SKILL_GRAND_MASTER && bUseGrandMaster)
|
||
{
|
||
iNeededSP = (int) pkSk->kGrandMasterAddSPCostPoly.Eval();
|
||
}
|
||
// END_OF_ADD_GRANDMASTER_SKILL
|
||
|
||
if (GetHP() < iNeededSP)
|
||
return false;
|
||
|
||
PointChange(POINT_HP, -iNeededSP);
|
||
}
|
||
else
|
||
{
|
||
// SKILL_FOMULA_REFACTORING
|
||
pkSk->SetSPCostVar("maxhp", GetMaxHP());
|
||
pkSk->SetSPCostVar("maxv", GetMaxSP());
|
||
pkSk->SetSPCostVar("v", GetSP());
|
||
|
||
iNeededSP = (int) pkSk->kSPCostPoly.Eval();
|
||
|
||
if (GetSkillMasterType(dwVnum) >= SKILL_GRAND_MASTER && bUseGrandMaster)
|
||
{
|
||
iNeededSP = (int) pkSk->kGrandMasterAddSPCostPoly.Eval();
|
||
}
|
||
// END_OF_SKILL_FOMULA_REFACTORING
|
||
|
||
if (GetSP() < iNeededSP)
|
||
return false;
|
||
|
||
if (test_server)
|
||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s SP<53>Ҹ<EFBFBD>: %d"), pkSk->szName, iNeededSP);
|
||
|
||
PointChange(POINT_SP, -iNeededSP);
|
||
}
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY))
|
||
pkVictim = this;
|
||
#ifdef ENABLE_SKILL_FLAG_PARTY
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_PARTY))
|
||
pkVictim = this;
|
||
#endif
|
||
|
||
if ((pkSk->dwVnum == SKILL_MUYEONG) || (pkSk->IsChargeSkill() && !IsAffectFlag(AFF_TANHWAN_DASH) && !pkVictim))
|
||
{
|
||
pkVictim = this;
|
||
}
|
||
|
||
int iSplashCount = 1;
|
||
|
||
if (false == m_bDisableCooltime)
|
||
{
|
||
if (false ==
|
||
m_SkillUseInfo[dwVnum].UseSkill(
|
||
bUseGrandMaster,
|
||
(NULL != pkVictim && SKILL_HORSE_WILDATTACK != dwVnum) ? pkVictim->GetVID() : VID(),
|
||
ComputeCooltime(iCooltime * 1000),
|
||
iSplashCount,
|
||
lMaxHit))
|
||
{
|
||
if (test_server)
|
||
ChatPacket(CHAT_TYPE_NOTICE, "cooltime not finished %s %d", pkSk->szName, iCooltime);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (dwVnum == SKILL_CHAIN)
|
||
{
|
||
ResetChainLightningIndex();
|
||
AddChainLightningExcept(pkVictim);
|
||
}
|
||
|
||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY))
|
||
ComputeSkill(dwVnum, this);
|
||
#ifdef ENABLE_SKILL_FLAG_PARTY
|
||
else if (IS_SET(pkSk->dwFlag, SKILL_FLAG_PARTY))
|
||
ComputeSkillParty(dwVnum, this);
|
||
#endif
|
||
else if (!IS_SET(pkSk->dwFlag, SKILL_FLAG_ATTACK))
|
||
ComputeSkill(dwVnum, pkVictim);
|
||
else if (dwVnum == SKILL_BYEURAK)
|
||
ComputeSkill(dwVnum, pkVictim);
|
||
else if (dwVnum == SKILL_MUYEONG || pkSk->IsChargeSkill())
|
||
ComputeSkill(dwVnum, pkVictim);
|
||
|
||
m_dwLastSkillTime = get_dword_time();
|
||
|
||
return true;
|
||
}
|
||
|
||
int CHARACTER::GetUsedSkillMasterType(DWORD dwVnum)
|
||
{
|
||
const TSkillUseInfo& rInfo = m_SkillUseInfo[dwVnum];
|
||
|
||
if (GetSkillMasterType(dwVnum) < SKILL_GRAND_MASTER)
|
||
return GetSkillMasterType(dwVnum);
|
||
|
||
if (rInfo.isGrandMaster)
|
||
return GetSkillMasterType(dwVnum);
|
||
|
||
return MIN(GetSkillMasterType(dwVnum), SKILL_MASTER);
|
||
}
|
||
|
||
int CHARACTER::GetSkillMasterType(DWORD dwVnum) const
|
||
{
|
||
if (!IsPC())
|
||
return 0;
|
||
|
||
if (dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("%s skill vnum overflow %u", GetName(), dwVnum);
|
||
return 0;
|
||
}
|
||
|
||
return m_pSkillLevels ? m_pSkillLevels[dwVnum].bMasterType:SKILL_NORMAL;
|
||
}
|
||
|
||
int CHARACTER::GetSkillPower(DWORD dwVnum, BYTE bLevel) const
|
||
{
|
||
if (dwVnum >= SKILL_LANGUAGE1 && dwVnum <= SKILL_LANGUAGE3 && IsEquipUniqueGroup(UNIQUE_GROUP_RING_OF_LANGUAGE))
|
||
{
|
||
return 100;
|
||
}
|
||
|
||
if (dwVnum >= GUILD_SKILL_START && dwVnum <= GUILD_SKILL_END)
|
||
{
|
||
if (GetGuild())
|
||
return 100 * GetGuild()->GetSkillLevel(dwVnum) / 7 / 7;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
if (bLevel)
|
||
{
|
||
//SKILL_POWER_BY_LEVEL
|
||
return GetSkillPowerByLevel(bLevel, true);
|
||
//END_SKILL_POWER_BY_LEVEL;
|
||
}
|
||
|
||
if (dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("%s skill vnum overflow %u", GetName(), dwVnum);
|
||
return 0;
|
||
}
|
||
|
||
//SKILL_POWER_BY_LEVEL
|
||
return GetSkillPowerByLevel(GetSkillLevel(dwVnum));
|
||
//SKILL_POWER_BY_LEVEL
|
||
}
|
||
|
||
int CHARACTER::GetSkillLevel(DWORD dwVnum) const
|
||
{
|
||
if (dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("%s skill vnum overflow %u", GetName(), dwVnum);
|
||
sys_log(0, "%s skill vnum overflow %u", GetName(), dwVnum);
|
||
return 0;
|
||
}
|
||
|
||
return MIN(SKILL_MAX_LEVEL, m_pSkillLevels ? m_pSkillLevels[dwVnum].bLevel : 0);
|
||
}
|
||
|
||
EVENTFUNC(skill_muyoung_event)
|
||
{
|
||
char_event_info* info = dynamic_cast<char_event_info*>( event->info );
|
||
|
||
if ( info == NULL )
|
||
{
|
||
sys_err( "skill_muyoung_event> <Factor> Null pointer" );
|
||
return 0;
|
||
}
|
||
|
||
LPCHARACTER ch = info->ch;
|
||
|
||
if (ch == NULL) { // <Factor>
|
||
return 0;
|
||
}
|
||
|
||
if (!ch->IsAffectFlag(AFF_MUYEONG))
|
||
{
|
||
ch->StopMuyeongEvent();
|
||
return 0;
|
||
}
|
||
|
||
// 1. Find Victim
|
||
FFindNearVictim f(ch, ch);
|
||
if (ch->GetSectree())
|
||
{
|
||
ch->GetSectree()->ForEachAround(f);
|
||
// 2. Shoot!
|
||
if (f.GetVictim())
|
||
{
|
||
ch->CreateFly(FLY_SKILL_MUYEONG, f.GetVictim());
|
||
ch->ComputeSkill(SKILL_MUYEONG, f.GetVictim());
|
||
}
|
||
}
|
||
|
||
return PASSES_PER_SEC(3);
|
||
}
|
||
|
||
void CHARACTER::StartMuyeongEvent()
|
||
{
|
||
if (m_pkMuyeongEvent)
|
||
return;
|
||
|
||
char_event_info* info = AllocEventInfo<char_event_info>();
|
||
|
||
info->ch = this;
|
||
m_pkMuyeongEvent = event_create(skill_muyoung_event, info, PASSES_PER_SEC(1));
|
||
}
|
||
|
||
void CHARACTER::StopMuyeongEvent()
|
||
{
|
||
event_cancel(&m_pkMuyeongEvent);
|
||
}
|
||
|
||
void CHARACTER::SkillLearnWaitMoreTimeMessage(DWORD ms)
|
||
{
|
||
//const char* str = "";
|
||
|
||
if (ms < 3 * 60)
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>̱߰<DFB0>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20>̴<EFBFBD><CCB4><EFBFBD> <20>⸦ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ű<EFBFBD><C5B0>."));
|
||
else if (ms < 5 * 60)
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD>, õõ<C3B5><C3B5>. <20><><EFBFBD><EFBFBD> õõ<C3B5><C3B5>, <20><EFBFBD><D7B7><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>!"));
|
||
else if (ms < 10 * 60)
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD>, <20><> <20><><EFBFBD><EFBFBD><EFBFBD>̾<EFBFBD>. ü<><C3BC><EFBFBD><EFBFBD> <20>Ⱑ <20><><EFBFBD><EFBFBD> <20>游<EFBFBD><E6B8B8>."));
|
||
else if (ms < 30 * 60)
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD> <20>о<EFBFBD><D0BE><EFBFBD>! <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>ſ<EFBFBD> <20>⸦ <20><><EFBFBD><EFBFBD><EFBFBD>⸸ <20>ϸ<EFBFBD>,"));
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD>װ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ž<EFBFBD>!"));
|
||
}
|
||
else if (ms < 1 * 3600)
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD> å<><C3A5> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>̾<EFBFBD>! <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>̰<EFBFBD> <20>־<EFBFBD>!"));
|
||
else if (ms < 2 * 3600)
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD>Ҿ<EFBFBD>! <20><><EFBFBD>ݸ<EFBFBD> <20><>!"));
|
||
else if (ms < 3 * 3600)
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD>Ҿ<EFBFBD>! <20><><EFBFBD>ݸ<EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>̴<EFBFBD>!"));
|
||
else if (ms < 6 * 3600)
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("å<EFBFBD>嵵 <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ʾұ<CABE>."));
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20>ȿ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>."));
|
||
}
|
||
else if (ms < 12 * 3600)
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>."));
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><> <20>⼼<EFBFBD><E2BCBC> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>!"));
|
||
}
|
||
else if (ms < 18 * 3600)
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD>ƴ<EFBFBD> <20><EFBFBD><EEB6BB> <20><> <20><> <20><><EFBFBD><EFBFBD> <20>о <20>Ӹ<EFBFBD><D3B8><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>."));
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD> <20>Ⱦ<EFBFBD><C8BE><EFBFBD><EFBFBD><EFBFBD>."));
|
||
}
|
||
else //if (ms < 2 * 86400)
|
||
{
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ŭ <20>бⰡ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ʱ<EFBFBD>. <20><><EFBFBD>ص<EFBFBD> <20><><EFBFBD>ư<EFBFBD> <20><><EFBFBD>뵵 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>."));
|
||
ChatPacket(CHAT_TYPE_TALKING, "%s", LC_TEXT("<EFBFBD>̷<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>ΰ<EFBFBD> <20>ȵȴٱ<C8B4>."));
|
||
}
|
||
/*
|
||
str = "30%";
|
||
else if (ms < 3 * 86400)
|
||
str = "10%";
|
||
else if (ms < 4 * 86400)
|
||
str = "5%";
|
||
else
|
||
str = "0%";*/
|
||
|
||
//ChatPacket(CHAT_TYPE_TALKING, "%s", str);
|
||
}
|
||
|
||
void CHARACTER::DisableCooltime()
|
||
{
|
||
m_bDisableCooltime = true;
|
||
}
|
||
|
||
bool CHARACTER::HasMobSkill() const
|
||
{
|
||
return CountMobSkill() > 0;
|
||
}
|
||
|
||
size_t CHARACTER::CountMobSkill() const
|
||
{
|
||
if (!m_pkMobData)
|
||
return 0;
|
||
|
||
size_t c = 0;
|
||
|
||
for (size_t i = 0; i < MOB_SKILL_MAX_NUM; ++i)
|
||
if (m_pkMobData->m_table.Skills[i].dwVnum)
|
||
++c;
|
||
|
||
return c;
|
||
}
|
||
|
||
const TMobSkillInfo* CHARACTER::GetMobSkill(unsigned int idx) const
|
||
{
|
||
if (idx >= MOB_SKILL_MAX_NUM)
|
||
return NULL;
|
||
|
||
if (!m_pkMobData)
|
||
return NULL;
|
||
|
||
if (0 == m_pkMobData->m_table.Skills[idx].dwVnum)
|
||
return NULL;
|
||
|
||
return &m_pkMobData->m_mobSkillInfo[idx];
|
||
}
|
||
|
||
bool CHARACTER::CanUseMobSkill(unsigned int idx) const
|
||
{
|
||
const TMobSkillInfo* pInfo = GetMobSkill(idx);
|
||
|
||
if (!pInfo)
|
||
return false;
|
||
|
||
if (m_adwMobSkillCooltime[idx] > get_dword_time())
|
||
return false;
|
||
|
||
if (number(0, 1))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
EVENTINFO(mob_skill_event_info)
|
||
{
|
||
DynamicCharacterPtr ch;
|
||
PIXEL_POSITION pos;
|
||
DWORD vnum;
|
||
int index;
|
||
BYTE level;
|
||
|
||
mob_skill_event_info()
|
||
: ch()
|
||
, pos()
|
||
, vnum(0)
|
||
, index(0)
|
||
, level(0)
|
||
{
|
||
}
|
||
};
|
||
|
||
EVENTFUNC(mob_skill_hit_event)
|
||
{
|
||
mob_skill_event_info * info = dynamic_cast<mob_skill_event_info *>( event->info );
|
||
|
||
if ( info == NULL )
|
||
{
|
||
sys_err( "mob_skill_event_info> <Factor> Null pointer" );
|
||
return 0;
|
||
}
|
||
|
||
// <Factor>
|
||
LPCHARACTER ch = info->ch;
|
||
if (ch == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
ch->ComputeSkillAtPosition(info->vnum, info->pos, info->level);
|
||
ch->m_mapMobSkillEvent.erase(info->index);
|
||
|
||
return 0;
|
||
}
|
||
|
||
bool CHARACTER::UseMobSkill(unsigned int idx)
|
||
{
|
||
if (IsPC())
|
||
return false;
|
||
|
||
const TMobSkillInfo* pInfo = GetMobSkill(idx);
|
||
|
||
if (!pInfo)
|
||
return false;
|
||
|
||
DWORD dwVnum = pInfo->dwSkillVnum;
|
||
CSkillProto * pkSk = CSkillManager::instance().Get(dwVnum);
|
||
|
||
if (!pkSk)
|
||
return false;
|
||
|
||
const float k = 1.0 * GetSkillPower(pkSk->dwVnum, pInfo->bSkillLevel) * pkSk->bMaxLevel / 100;
|
||
|
||
pkSk->kCooldownPoly.SetVar("k", k);
|
||
int iCooltime = (int) (pkSk->kCooldownPoly.Eval() * 1000);
|
||
|
||
m_adwMobSkillCooltime[idx] = get_dword_time() + iCooltime;
|
||
|
||
sys_log(0, "USE_MOB_SKILL: %s idx %d vnum %u cooltime %d", GetName(), idx, dwVnum, iCooltime);
|
||
|
||
if (m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack.empty())
|
||
{
|
||
sys_err("No skill hit data for mob %s index %d", GetName(), idx);
|
||
return false;
|
||
}
|
||
|
||
for (size_t i = 0; i < m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack.size(); i++)
|
||
{
|
||
PIXEL_POSITION pos = GetXYZ();
|
||
const TMobSplashAttackInfo& rInfo = m_pkMobData->m_mobSkillInfo[idx].vecSplashAttack[i];
|
||
|
||
if (rInfo.dwHitDistance)
|
||
{
|
||
float fx, fy;
|
||
GetDeltaByDegree(GetRotation(), rInfo.dwHitDistance, &fx, &fy);
|
||
pos.x += (long) fx;
|
||
pos.y += (long) fy;
|
||
}
|
||
|
||
if (rInfo.dwTiming)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, " timing %ums", rInfo.dwTiming);
|
||
|
||
mob_skill_event_info* info = AllocEventInfo<mob_skill_event_info>();
|
||
|
||
info->ch = this;
|
||
info->pos = pos;
|
||
info->level = pInfo->bSkillLevel;
|
||
info->vnum = dwVnum;
|
||
info->index = i;
|
||
|
||
// <Factor> Cancel existing event first
|
||
itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.find(i);
|
||
if (it != m_mapMobSkillEvent.end()) {
|
||
LPEVENT existing = it->second;
|
||
event_cancel(&existing);
|
||
m_mapMobSkillEvent.erase(it);
|
||
}
|
||
|
||
m_mapMobSkillEvent.emplace(i, event_create(mob_skill_hit_event, info, PASSES_PER_SEC(rInfo.dwTiming) / 1000));
|
||
}
|
||
else
|
||
{
|
||
ComputeSkillAtPosition(dwVnum, pos, pInfo->bSkillLevel);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void CHARACTER::ResetMobSkillCooltime()
|
||
{
|
||
memset(m_adwMobSkillCooltime, 0, sizeof(m_adwMobSkillCooltime));
|
||
}
|
||
|
||
bool CHARACTER::IsUsableSkillMotion(DWORD dwMotionIndex) const
|
||
{
|
||
DWORD selfJobGroup = (GetJob()+1) * 10 + GetSkillGroup();
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
const DWORD SKILL_NUM = 176;
|
||
#else
|
||
const DWORD SKILL_NUM = 158;
|
||
#endif
|
||
static DWORD s_anSkill2JobGroup[SKILL_NUM] = {
|
||
0, // common_skill 0
|
||
11, // job_skill 1
|
||
11, // job_skill 2
|
||
11, // job_skill 3
|
||
11, // job_skill 4
|
||
11, // job_skill 5
|
||
11, // job_skill 6
|
||
0, // common_skill 7
|
||
0, // common_skill 8
|
||
0, // common_skill 9
|
||
0, // common_skill 10
|
||
0, // common_skill 11
|
||
0, // common_skill 12
|
||
0, // common_skill 13
|
||
0, // common_skill 14
|
||
0, // common_skill 15
|
||
12, // job_skill 16
|
||
12, // job_skill 17
|
||
12, // job_skill 18
|
||
12, // job_skill 19
|
||
12, // job_skill 20
|
||
12, // job_skill 21
|
||
0, // common_skill 22
|
||
0, // common_skill 23
|
||
0, // common_skill 24
|
||
0, // common_skill 25
|
||
0, // common_skill 26
|
||
0, // common_skill 27
|
||
0, // common_skill 28
|
||
0, // common_skill 29
|
||
0, // common_skill 30
|
||
21, // job_skill 31
|
||
21, // job_skill 32
|
||
21, // job_skill 33
|
||
21, // job_skill 34
|
||
21, // job_skill 35
|
||
21, // job_skill 36
|
||
0, // common_skill 37
|
||
0, // common_skill 38
|
||
0, // common_skill 39
|
||
0, // common_skill 40
|
||
0, // common_skill 41
|
||
0, // common_skill 42
|
||
0, // common_skill 43
|
||
0, // common_skill 44
|
||
0, // common_skill 45
|
||
22, // job_skill 46
|
||
22, // job_skill 47
|
||
22, // job_skill 48
|
||
22, // job_skill 49
|
||
22, // job_skill 50
|
||
22, // job_skill 51
|
||
0, // common_skill 52
|
||
0, // common_skill 53
|
||
0, // common_skill 54
|
||
0, // common_skill 55
|
||
0, // common_skill 56
|
||
0, // common_skill 57
|
||
0, // common_skill 58
|
||
0, // common_skill 59
|
||
0, // common_skill 60
|
||
31, // job_skill 61
|
||
31, // job_skill 62
|
||
31, // job_skill 63
|
||
31, // job_skill 64
|
||
31, // job_skill 65
|
||
31, // job_skill 66
|
||
0, // common_skill 67
|
||
0, // common_skill 68
|
||
0, // common_skill 69
|
||
0, // common_skill 70
|
||
0, // common_skill 71
|
||
0, // common_skill 72
|
||
0, // common_skill 73
|
||
0, // common_skill 74
|
||
0, // common_skill 75
|
||
32, // job_skill 76
|
||
32, // job_skill 77
|
||
32, // job_skill 78
|
||
32, // job_skill 79
|
||
32, // job_skill 80
|
||
32, // job_skill 81
|
||
0, // common_skill 82
|
||
0, // common_skill 83
|
||
0, // common_skill 84
|
||
0, // common_skill 85
|
||
0, // common_skill 86
|
||
0, // common_skill 87
|
||
0, // common_skill 88
|
||
0, // common_skill 89
|
||
0, // common_skill 90
|
||
41, // job_skill 91
|
||
41, // job_skill 92
|
||
41, // job_skill 93
|
||
41, // job_skill 94
|
||
41, // job_skill 95
|
||
41, // job_skill 96
|
||
0, // common_skill 97
|
||
0, // common_skill 98
|
||
0, // common_skill 99
|
||
0, // common_skill 100
|
||
0, // common_skill 101
|
||
0, // common_skill 102
|
||
0, // common_skill 103
|
||
0, // common_skill 104
|
||
0, // common_skill 105
|
||
42, // job_skill 106
|
||
42, // job_skill 107
|
||
42, // job_skill 108
|
||
42, // job_skill 109
|
||
42, // job_skill 110
|
||
42, // job_skill 111
|
||
0, // common_skill 112
|
||
0, // common_skill 113
|
||
0, // common_skill 114
|
||
0, // common_skill 115
|
||
0, // common_skill 116
|
||
0, // common_skill 117
|
||
0, // common_skill 118
|
||
0, // common_skill 119
|
||
0, // common_skill 120
|
||
0, // common_skill 121
|
||
0, // common_skill 122
|
||
0, // common_skill 123
|
||
0, // common_skill 124
|
||
0, // common_skill 125
|
||
0, // common_skill 126
|
||
0, // common_skill 127
|
||
0, // common_skill 128
|
||
0, // common_skill 129
|
||
0, // common_skill 130
|
||
0, // common_skill 131
|
||
0, // common_skill 132
|
||
0, // common_skill 133
|
||
0, // common_skill 134
|
||
0, // common_skill 135
|
||
0, // common_skill 136
|
||
0, // job_skill 137
|
||
0, // job_skill 138
|
||
0, // job_skill 139
|
||
0, // job_skill 140
|
||
0, // common_skill 141
|
||
0, // common_skill 142
|
||
0, // common_skill 143
|
||
0, // common_skill 144
|
||
0, // common_skill 145
|
||
0, // common_skill 146
|
||
0, // common_skill 147
|
||
0, // common_skill 148
|
||
0, // common_skill 149
|
||
0, // common_skill 150
|
||
0, // common_skill 151
|
||
0, // job_skill 152
|
||
0, // job_skill 153
|
||
0, // job_skill 154
|
||
0, // job_skill 155
|
||
0, // job_skill 156
|
||
0, // job_skill 157
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
0, // empty(reserved) 158
|
||
0, // empty(reserved) 159
|
||
0, // empty(reserved) 160
|
||
0, // empty(reserved) 161
|
||
0, // empty(reserved) 162
|
||
0, // empty(reserved) 163
|
||
0, // empty(reserved) 164
|
||
0, // empty(reserved) 165
|
||
0, // empty(reserved) 166
|
||
0, // empty(reserved) 167
|
||
0, // empty(reserved) 168
|
||
0, // empty(reserved) 169
|
||
51, // job_skill(WOLFMAN SKILL) 170
|
||
51, // job_skill(WOLFMAN SKILL) 171
|
||
51, // job_skill(WOLFMAN SKILL) 172
|
||
51, // job_skill(WOLFMAN SKILL) 173
|
||
51, // job_skill(WOLFMAN SKILL) 174
|
||
51, // job_skill(WOLFMAN SKILL) 175
|
||
#endif
|
||
}; // s_anSkill2JobGroup
|
||
|
||
const DWORD MOTION_MAX_NUM = 124;
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
const DWORD SKILL_LIST_MAX_COUNT = 6;
|
||
#else
|
||
const DWORD SKILL_LIST_MAX_COUNT = 5;
|
||
#endif
|
||
static DWORD s_anMotion2SkillVnumList[MOTION_MAX_NUM][SKILL_LIST_MAX_COUNT] =
|
||
{
|
||
{ 0, 0, 0, 0, 0 }, // 0
|
||
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
{ 5, 1, 31, 61, 91, 170 }, // 1
|
||
{ 5, 2, 32, 62, 92, 171 }, // 2
|
||
{ 5, 3, 33, 63, 93, 172 }, // 3
|
||
{ 5, 4, 34, 64, 94, 173 }, // 4
|
||
{ 5, 5, 35, 65, 95, 174 }, // 5
|
||
{ 5, 6, 36, 66, 96, 175 }, // 6
|
||
#else
|
||
{ 4, 1, 31, 61, 91 }, // 1
|
||
{ 4, 2, 32, 62, 92 }, // 2
|
||
{ 4, 3, 33, 63, 93 }, // 3
|
||
{ 4, 4, 34, 64, 94 }, // 4
|
||
{ 4, 5, 35, 65, 95 }, // 5
|
||
{ 4, 6, 36, 66, 96 }, // 6
|
||
#endif
|
||
{ 0, 0, 0, 0, 0 }, // 7
|
||
{ 0, 0, 0, 0, 0 }, // 8
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 9
|
||
{ 0, 0, 0, 0, 0 }, // 10
|
||
{ 0, 0, 0, 0, 0 }, // 11
|
||
{ 0, 0, 0, 0, 0 }, // 12
|
||
{ 0, 0, 0, 0, 0 }, // 13
|
||
{ 0, 0, 0, 0, 0 }, // 14
|
||
{ 0, 0, 0, 0, 0 }, // 15
|
||
|
||
{ 4, 16, 46, 76, 106 }, // 16
|
||
{ 4, 17, 47, 77, 107 }, // 17
|
||
{ 4, 18, 48, 78, 108 }, // 18
|
||
{ 4, 19, 49, 79, 109 }, // 19
|
||
{ 4, 20, 50, 80, 110 }, // 20
|
||
{ 4, 21, 51, 81, 111 }, // 21
|
||
{ 0, 0, 0, 0, 0 }, // 22
|
||
{ 0, 0, 0, 0, 0 }, // 23
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 24
|
||
{ 0, 0, 0, 0, 0 }, // 25
|
||
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
{ 5, 1, 31, 61, 91, 170 }, // 26
|
||
{ 5, 2, 32, 62, 92, 171 }, // 27
|
||
{ 5, 3, 33, 63, 93, 172 }, // 28
|
||
{ 5, 4, 34, 64, 94, 173 }, // 29
|
||
{ 5, 5, 35, 65, 95, 174 }, // 30
|
||
{ 5, 6, 36, 66, 96, 175 }, // 31
|
||
#else
|
||
{ 4, 1, 31, 61, 91 }, // 26
|
||
{ 4, 2, 32, 62, 92 }, // 27
|
||
{ 4, 3, 33, 63, 93 }, // 28
|
||
{ 4, 4, 34, 64, 94 }, // 29
|
||
{ 4, 5, 35, 65, 95 }, // 30
|
||
{ 4, 6, 36, 66, 96 }, // 31
|
||
#endif
|
||
{ 0, 0, 0, 0, 0 }, // 32
|
||
{ 0, 0, 0, 0, 0 }, // 33
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 34
|
||
{ 0, 0, 0, 0, 0 }, // 35
|
||
{ 0, 0, 0, 0, 0 }, // 36
|
||
{ 0, 0, 0, 0, 0 }, // 37
|
||
{ 0, 0, 0, 0, 0 }, // 38
|
||
{ 0, 0, 0, 0, 0 }, // 39
|
||
{ 0, 0, 0, 0, 0 }, // 40
|
||
|
||
{ 4, 16, 46, 76, 106 }, // 41
|
||
{ 4, 17, 47, 77, 107 }, // 42
|
||
{ 4, 18, 48, 78, 108 }, // 43
|
||
{ 4, 19, 49, 79, 109 }, // 44
|
||
{ 4, 20, 50, 80, 110 }, // 45
|
||
{ 4, 21, 51, 81, 111 }, // 46
|
||
{ 0, 0, 0, 0, 0 }, // 47
|
||
{ 0, 0, 0, 0, 0 }, // 48
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 49
|
||
{ 0, 0, 0, 0, 0 }, // 50
|
||
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
{ 5, 1, 31, 61, 91, 170 }, // 51
|
||
{ 5, 2, 32, 62, 92, 171 }, // 52
|
||
{ 5, 3, 33, 63, 93, 172 }, // 53
|
||
{ 5, 4, 34, 64, 94, 173 }, // 54
|
||
{ 5, 5, 35, 65, 95, 174 }, // 55
|
||
{ 5, 6, 36, 66, 96, 175 }, // 56
|
||
#else
|
||
{ 4, 1, 31, 61, 91 }, // 51
|
||
{ 4, 2, 32, 62, 92 }, // 52
|
||
{ 4, 3, 33, 63, 93 }, // 53
|
||
{ 4, 4, 34, 64, 94 }, // 54
|
||
{ 4, 5, 35, 65, 95 }, // 55
|
||
{ 4, 6, 36, 66, 96 }, // 56
|
||
#endif
|
||
{ 0, 0, 0, 0, 0 }, // 57
|
||
{ 0, 0, 0, 0, 0 }, // 58
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 59
|
||
{ 0, 0, 0, 0, 0 }, // 60
|
||
{ 0, 0, 0, 0, 0 }, // 61
|
||
{ 0, 0, 0, 0, 0 }, // 62
|
||
{ 0, 0, 0, 0, 0 }, // 63
|
||
{ 0, 0, 0, 0, 0 }, // 64
|
||
{ 0, 0, 0, 0, 0 }, // 65
|
||
|
||
{ 4, 16, 46, 76, 106 }, // 66
|
||
{ 4, 17, 47, 77, 107 }, // 67
|
||
{ 4, 18, 48, 78, 108 }, // 68
|
||
{ 4, 19, 49, 79, 109 }, // 69
|
||
{ 4, 20, 50, 80, 110 }, // 70
|
||
{ 4, 21, 51, 81, 111 }, // 71
|
||
{ 0, 0, 0, 0, 0 }, // 72
|
||
{ 0, 0, 0, 0, 0 }, // 73
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 74
|
||
{ 0, 0, 0, 0, 0 }, // 75
|
||
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
{ 5, 1, 31, 61, 91, 170 }, // 76
|
||
{ 5, 2, 32, 62, 92, 171 }, // 77
|
||
{ 5, 3, 33, 63, 93, 172 }, // 78
|
||
{ 5, 4, 34, 64, 94, 173 }, // 79
|
||
{ 5, 5, 35, 65, 95, 174 }, // 80
|
||
{ 5, 6, 36, 66, 96, 175 }, // 81
|
||
#else
|
||
{ 4, 1, 31, 61, 91 }, // 76
|
||
{ 4, 2, 32, 62, 92 }, // 77
|
||
{ 4, 3, 33, 63, 93 }, // 78
|
||
{ 4, 4, 34, 64, 94 }, // 79
|
||
{ 4, 5, 35, 65, 95 }, // 80
|
||
{ 4, 6, 36, 66, 96 }, // 81
|
||
#endif
|
||
{ 0, 0, 0, 0, 0 }, // 82
|
||
{ 0, 0, 0, 0, 0 }, // 83
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 84
|
||
{ 0, 0, 0, 0, 0 }, // 85
|
||
{ 0, 0, 0, 0, 0 }, // 86
|
||
{ 0, 0, 0, 0, 0 }, // 87
|
||
{ 0, 0, 0, 0, 0 }, // 88
|
||
{ 0, 0, 0, 0, 0 }, // 89
|
||
{ 0, 0, 0, 0, 0 }, // 90
|
||
|
||
{ 4, 16, 46, 76, 106 }, // 91
|
||
{ 4, 17, 47, 77, 107 }, // 92
|
||
{ 4, 18, 48, 78, 108 }, // 93
|
||
{ 4, 19, 49, 79, 109 }, // 94
|
||
{ 4, 20, 50, 80, 110 }, // 95
|
||
{ 4, 21, 51, 81, 111 }, // 96
|
||
{ 0, 0, 0, 0, 0 }, // 97
|
||
{ 0, 0, 0, 0, 0 }, // 98
|
||
|
||
{ 0, 0, 0, 0, 0 }, // 99
|
||
{ 0, 0, 0, 0, 0 }, // 100
|
||
|
||
{ 1, 152, 0, 0, 0}, // 101
|
||
{ 1, 153, 0, 0, 0}, // 102
|
||
{ 1, 154, 0, 0, 0}, // 103
|
||
{ 1, 155, 0, 0, 0}, // 104
|
||
{ 1, 156, 0, 0, 0}, // 105
|
||
{ 1, 157, 0, 0, 0}, // 106
|
||
|
||
{ 0, 0, 0, 0, 0}, // 107
|
||
{ 0, 0, 0, 0, 0}, // 108
|
||
{ 0, 0, 0, 0, 0}, // 109
|
||
{ 0, 0, 0, 0, 0}, // 110
|
||
{ 0, 0, 0, 0, 0}, // 111
|
||
{ 0, 0, 0, 0, 0}, // 112
|
||
{ 0, 0, 0, 0, 0}, // 113
|
||
{ 0, 0, 0, 0, 0}, // 114
|
||
{ 0, 0, 0, 0, 0}, // 115
|
||
{ 0, 0, 0, 0, 0}, // 116
|
||
{ 0, 0, 0, 0, 0}, // 117
|
||
{ 0, 0, 0, 0, 0}, // 118
|
||
{ 0, 0, 0, 0, 0}, // 119
|
||
{ 0, 0, 0, 0, 0}, // 120
|
||
|
||
{ 2, 137, 140, 0, 0}, // 121
|
||
{ 1, 138, 0, 0, 0}, // 122
|
||
{ 1, 139, 0, 0, 0}, // 123
|
||
|
||
};
|
||
|
||
if (dwMotionIndex >= MOTION_MAX_NUM)
|
||
{
|
||
sys_err("OUT_OF_MOTION_VNUM: name=%s, motion=%d/%d", GetName(), dwMotionIndex, MOTION_MAX_NUM);
|
||
return false;
|
||
}
|
||
|
||
DWORD* skillVNums = s_anMotion2SkillVnumList[dwMotionIndex];
|
||
|
||
DWORD skillCount = *skillVNums++;
|
||
if (skillCount >= SKILL_LIST_MAX_COUNT)
|
||
{
|
||
sys_err("OUT_OF_SKILL_LIST: name=%s, count=%d/%d", GetName(), skillCount, SKILL_LIST_MAX_COUNT);
|
||
return false;
|
||
}
|
||
|
||
for (DWORD skillIndex = 0; skillIndex != skillCount; ++skillIndex)
|
||
{
|
||
if (skillIndex >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("OUT_OF_SKILL_VNUM: name=%s, skill=%d/%d", GetName(), skillIndex, SKILL_MAX_NUM);
|
||
return false;
|
||
}
|
||
|
||
DWORD eachSkillVNum = skillVNums[skillIndex];
|
||
if ( eachSkillVNum != 0 )
|
||
{
|
||
DWORD eachJobGroup = s_anSkill2JobGroup[eachSkillVNum];
|
||
|
||
if (0 == eachJobGroup || eachJobGroup == selfJobGroup)
|
||
{
|
||
// GUILDSKILL_BUG_FIX
|
||
DWORD eachSkillLevel = 0;
|
||
|
||
if (eachSkillVNum >= GUILD_SKILL_START && eachSkillVNum <= GUILD_SKILL_END)
|
||
{
|
||
if (GetGuild())
|
||
eachSkillLevel = GetGuild()->GetSkillLevel(eachSkillVNum);
|
||
else
|
||
eachSkillLevel = 0;
|
||
}
|
||
else
|
||
{
|
||
eachSkillLevel = GetSkillLevel(eachSkillVNum);
|
||
}
|
||
|
||
if (eachSkillLevel > 0)
|
||
{
|
||
return true;
|
||
}
|
||
// END_OF_GUILDSKILL_BUG_FIX
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void CHARACTER::ClearSkill()
|
||
{
|
||
PointChange(POINT_SKILL, 4 + (GetLevel() - 5) - GetPoint(POINT_SKILL));
|
||
RemoveSkillAffect(); // @fixme305
|
||
ResetSkill();
|
||
}
|
||
|
||
void CHARACTER::ClearSubSkill()
|
||
{
|
||
PointChange(POINT_SUB_SKILL, GetLevel() < 10 ? 0 : (GetLevel() - 9) - GetPoint(POINT_SUB_SKILL));
|
||
|
||
if (m_pSkillLevels == NULL)
|
||
{
|
||
sys_err("m_pSkillLevels nil (name: %s)", GetName());
|
||
return;
|
||
}
|
||
|
||
TPlayerSkill CleanSkill;
|
||
memset(&CleanSkill, 0, sizeof(TPlayerSkill));
|
||
|
||
size_t count = sizeof(s_adwSubSkillVnums) / sizeof(s_adwSubSkillVnums[0]);
|
||
|
||
for (size_t i = 0; i < count; ++i)
|
||
{
|
||
if (s_adwSubSkillVnums[i] >= SKILL_MAX_NUM)
|
||
continue;
|
||
|
||
m_pSkillLevels[s_adwSubSkillVnums[i]] = CleanSkill;
|
||
}
|
||
|
||
ComputePoints();
|
||
SkillLevelPacket();
|
||
}
|
||
|
||
bool CHARACTER::ResetOneSkill(DWORD dwVnum)
|
||
{
|
||
if (NULL == m_pSkillLevels)
|
||
{
|
||
sys_err("m_pSkillLevels nil (name %s, vnum %u)", GetName(), dwVnum);
|
||
return false;
|
||
}
|
||
|
||
if (dwVnum >= SKILL_MAX_NUM)
|
||
{
|
||
sys_err("vnum overflow (name %s, vnum %u)", GetName(), dwVnum);
|
||
return false;
|
||
}
|
||
|
||
BYTE level = m_pSkillLevels[dwVnum].bLevel;
|
||
|
||
m_pSkillLevels[dwVnum].bLevel = 0;
|
||
m_pSkillLevels[dwVnum].bMasterType = 0;
|
||
m_pSkillLevels[dwVnum].tNextRead = 0;
|
||
|
||
if (level > 17)
|
||
level = 17;
|
||
|
||
PointChange(POINT_SKILL, level);
|
||
|
||
LogManager::instance().CharLog(this, dwVnum, "ONE_SKILL_RESET_BY_SCROLL", "");
|
||
|
||
ComputePoints();
|
||
SkillLevelPacket();
|
||
|
||
return true;
|
||
}
|
||
|
||
eMountType GetMountLevelByVnum(DWORD dwMountVnum, bool IsNew) // updated to 2014/12/10
|
||
{
|
||
if (!dwMountVnum)
|
||
return MOUNT_TYPE_NONE;
|
||
|
||
switch (dwMountVnum)
|
||
{
|
||
// ### YES SKILL
|
||
// @fixme116 begin
|
||
case 20107: // normal military horse (no guild)
|
||
case 20108: // normal military horse (guild member)
|
||
case 20109: // normal military horse (guild master)
|
||
if (IsNew)
|
||
return MOUNT_TYPE_NONE;
|
||
// @fixme116 end
|
||
// Classic
|
||
case 20110: // Classic Boar
|
||
case 20111: // Classic Wolf
|
||
case 20112: // Classic Tiger
|
||
case 20113: // Classic Lion
|
||
case 20114: // White Lion
|
||
// Special Lv2
|
||
case 20115: // Wild Battle Boar
|
||
case 20116: // Fight Wolf
|
||
case 20117: // Storm Tiger
|
||
case 20118: // Battle Lion (bugged)
|
||
case 20205: // Wild Battle Boar (alternative)
|
||
case 20206: // Fight Wolf (alternative)
|
||
case 20207: // Storm Tiger (alternative)
|
||
case 20208: // Battle Lion (bugged) (alternative)
|
||
// Royal Tigers
|
||
case 20120: // blue
|
||
case 20121: // dark red
|
||
case 20122: // gold
|
||
case 20123: // green
|
||
case 20124: // pied
|
||
case 20125: // white
|
||
// Royal mounts (Special Lv3)
|
||
case 20209: // Royal Boar
|
||
case 20210: // Royal Wolf
|
||
case 20211: // Royal Tiger
|
||
case 20212: // Royal Lion
|
||
|
||
case 20215: // Rudolph m Lv3 (yes skill, yes atk)
|
||
case 20218: // Rudolph f Lv3 (yes skill, yes atk)
|
||
case 20225: // Dyno Lv3 (yes skill, yes atk)
|
||
case 20230: // Turkey Lv3 (yes skill, yes atk)
|
||
return MOUNT_TYPE_MILITARY;
|
||
break;
|
||
// ### NO SKILL YES ATK
|
||
// @fixme116 begin
|
||
case 20104: // normal combat horse (no guild)
|
||
case 20105: // normal combat horse (guild member)
|
||
case 20106: // normal combat horse (guild master)
|
||
if (IsNew)
|
||
return MOUNT_TYPE_NONE;
|
||
// @fixme116 end
|
||
case 20119: // Black Horse (no skill, yes atk)
|
||
case 20214: // Rudolph m Lv2 (no skill, yes atk)
|
||
case 20217: // Rudolph f Lv2 (no skill, yes atk)
|
||
case 20219: // Equus Porphyreus (no skill, yes atk)
|
||
case 20220: // Comet (no skill, yes atk)
|
||
case 20221: // Polar Predator (no skill, yes atk)
|
||
case 20222: // Armoured Panda (no skill, yes atk)
|
||
case 20224: // Dyno Lv2 (no skill, yes atk)
|
||
case 20226: // Nightmare (no skill, yes atk)
|
||
case 20227: // Unicorn (no skill, yes atk)
|
||
case 20229: // Turkey Lv2 (no skill, yes atk)
|
||
case 20231: // Leopard (no skill, yes atk)
|
||
case 20232: // Black Panther (no skill, yes atk)
|
||
return MOUNT_TYPE_COMBAT;
|
||
break;
|
||
// ### NO SKILL NO ATK
|
||
// @fixme116 begin
|
||
case 20101: // normal beginner horse (no guild)
|
||
case 20102: // normal beginner horse (guild member)
|
||
case 20103: // normal beginner horse (guild master)
|
||
if (IsNew)
|
||
return MOUNT_TYPE_NONE;
|
||
// @fixme116 end
|
||
case 20213: // Rudolph m Lv1 (no skill, no atk)
|
||
case 20216: // Rudolph f Lv1 (no skill, no atk)
|
||
// Special Lv1
|
||
case 20201: // Boar Lv1 (no skill, no atk)
|
||
case 20202: // Wolf Lv1 (no skill, no atk)
|
||
case 20203: // Tiger Lv1 (no skill, no atk)
|
||
case 20204: // Lion Lv1 (no skill, no atk)
|
||
|
||
case 20223: // Dyno Lv1 (no skill, no atk)
|
||
case 20228: // Turkey Lv1 (no skill, no atk)
|
||
return MOUNT_TYPE_NORMAL;
|
||
break;
|
||
default:
|
||
return MOUNT_TYPE_NONE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
const int SKILL_COUNT = 6;
|
||
static const DWORD SkillList[JOB_MAX_NUM][SKILL_GROUP_MAX_NUM][SKILL_COUNT] =
|
||
{
|
||
{ { 1, 2, 3, 4, 5, 6 }, { 16, 17, 18, 19, 20, 21 } },
|
||
{ { 31, 32, 33, 34, 35, 36 }, { 46, 47, 48, 49, 50, 51 } },
|
||
{ { 61, 62, 63, 64, 65, 66 }, { 76, 77, 78, 79, 80, 81 } },
|
||
{ { 91, 92, 93, 94, 95, 96 }, { 106,107,108,109,110,111 } },
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
{ { 170,171,172,173,174,175 }, { 0, 0, 0, 0, 0, 0 } },
|
||
#endif
|
||
};
|
||
|
||
const DWORD GetRandomSkillVnum(BYTE bJob)
|
||
{
|
||
// the chosen skill
|
||
DWORD dwSkillVnum = 0;
|
||
do
|
||
{
|
||
// tmp stuff
|
||
DWORD tmpJob = (bJob != JOB_MAX_NUM)?MINMAX(0, bJob, JOB_MAX_NUM-1):number(0, JOB_MAX_NUM-1);
|
||
DWORD tmpSkillGroup = number(0, SKILL_GROUP_MAX_NUM-1);
|
||
DWORD tmpSkillCount = number(0, SKILL_COUNT-1);
|
||
// set skill
|
||
dwSkillVnum = SkillList[tmpJob][tmpSkillGroup][tmpSkillCount];
|
||
|
||
#if defined(ENABLE_WOLFMAN_CHARACTER) && !defined(USE_WOLFMAN_BOOKS)
|
||
if (tmpJob==JOB_WOLFMAN)
|
||
continue;
|
||
#endif
|
||
|
||
if (dwSkillVnum != 0 && NULL != CSkillManager::instance().Get(dwSkillVnum))
|
||
break;
|
||
} while (true);
|
||
return dwSkillVnum;
|
||
}
|
||
|
||
bool CHARACTER::CanUseSkill(DWORD dwSkillVnum) const
|
||
{
|
||
if (0 == dwSkillVnum) return false;
|
||
|
||
if (0 < GetSkillGroup())
|
||
{
|
||
const DWORD* pSkill = SkillList[ GetJob() ][ GetSkillGroup()-1 ];
|
||
|
||
for (int i=0 ; i < SKILL_COUNT ; ++i)
|
||
{
|
||
if (pSkill[i] == dwSkillVnum) return true;
|
||
}
|
||
}
|
||
|
||
//if (true == IsHorseRiding())
|
||
|
||
if (true == IsRiding())
|
||
{
|
||
#ifndef ENABLE_NO_MOUNT_CHECK
|
||
eMountType eIsMount = GetMountLevelByVnum(GetMountVnum(), false);
|
||
if (eIsMount != MOUNT_TYPE_MILITARY)
|
||
{
|
||
if (test_server)
|
||
sys_log(0, "CanUseSkill: Mount can't skill. vnum(%u) type(%d)", GetMountVnum(), static_cast<int>(eIsMount));
|
||
return false;
|
||
}
|
||
#endif
|
||
switch(dwSkillVnum)
|
||
{
|
||
case SKILL_HORSE_WILDATTACK:
|
||
case SKILL_HORSE_CHARGE:
|
||
case SKILL_HORSE_ESCAPE:
|
||
case SKILL_HORSE_WILDATTACK_RANGE:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
switch( dwSkillVnum )
|
||
{
|
||
case 121: case 122: case 124: case 126: case 127: case 128: case 129: case 130:
|
||
case 131:
|
||
case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159:
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool CHARACTER::CheckSkillHitCount(const BYTE SkillID, const VID TargetVID)
|
||
{
|
||
std::map<int, TSkillUseInfo>::iterator iter = m_SkillUseInfo.find(SkillID);
|
||
|
||
if (iter == m_SkillUseInfo.end())
|
||
{
|
||
sys_log(0, "SkillHack: Skill(%u) is not in container", SkillID);
|
||
return false;
|
||
}
|
||
|
||
TSkillUseInfo& rSkillUseInfo = iter->second;
|
||
|
||
if (false == rSkillUseInfo.bUsed)
|
||
{
|
||
sys_log(0, "SkillHack: not used skill(%u)", SkillID);
|
||
return false;
|
||
}
|
||
|
||
switch (SkillID)
|
||
{
|
||
case SKILL_YONGKWON:
|
||
case SKILL_HWAYEOMPOK:
|
||
case SKILL_DAEJINGAK:
|
||
case SKILL_PAERYONG:
|
||
sys_log(0, "SkillHack: cannot use attack packet for skill(%u)", SkillID);
|
||
return false;
|
||
}
|
||
|
||
target_map::iterator iterTargetMap = rSkillUseInfo.TargetVIDMap.find(TargetVID);
|
||
|
||
if (rSkillUseInfo.TargetVIDMap.end() != iterTargetMap)
|
||
{
|
||
size_t MaxAttackCountPerTarget = 1;
|
||
|
||
switch (SkillID)
|
||
{
|
||
case SKILL_SAMYEON:
|
||
case SKILL_CHARYUN:
|
||
#ifdef ENABLE_WOLFMAN_CHARACTER
|
||
case SKILL_CHAYEOL:
|
||
#endif
|
||
MaxAttackCountPerTarget = 3;
|
||
break;
|
||
|
||
case SKILL_HORSE_WILDATTACK_RANGE:
|
||
MaxAttackCountPerTarget = 5;
|
||
break;
|
||
|
||
case SKILL_YEONSA:
|
||
MaxAttackCountPerTarget = 7;
|
||
break;
|
||
|
||
case SKILL_HORSE_ESCAPE:
|
||
MaxAttackCountPerTarget = 10;
|
||
break;
|
||
}
|
||
|
||
if (iterTargetMap->second >= MaxAttackCountPerTarget)
|
||
{
|
||
sys_log(0, "SkillHack: Too Many Hit count from SkillID(%u) count(%u)", SkillID, iterTargetMap->second);
|
||
return false;
|
||
}
|
||
|
||
iterTargetMap->second++;
|
||
}
|
||
else
|
||
{
|
||
rSkillUseInfo.TargetVIDMap.emplace(TargetVID, 1);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
//martysama0134's 623a0779c74cb7565145d45548376308
|