Files
Client/Server/RylServerProject/RylGameLibrary/Creature/Character/CharacterStatus.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

1150 lines
35 KiB
C++

#include "stdafx.h"
#include <Log/GameLog.h>
#include <Log/CharacterLog.h>
#include <Log/ItemLog.h>
#include <Log/LogStruct.h>
#include <Item/Item.h>
#include <Item/ItemFactory.h>
#include <Item/Container/ItemContainer.h>
#include <Item/Container/EquipmentsContainer.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharLevelUp.h>
#include <Network/Dispatch/GameClient/SendCharEtc.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Packet/PacketStruct/CharStatuspacket.h>
#include <Network/Packet/PacketCommand.h>
#include <Utility/Setup/ServerSetup.h>
#include <Utility/Math/Math.h>
#include <Community/Guild/GuildMgr.h>
#include <Community/Guild/Guild.h>
#include <Creature/Siege/SiegeConstants.h>
#include <Creature/Monster/Monster.h>
#include <Creature/Monster/AwardTable.h>
#include <Castle/CastleMgr.h>
#include <Skill/SkillTable.h>
#include <Map/FieldMap/Cell.h>
#include <GameTime/GameTimeMgr.h>
#include "ExpTable.h"
#include "Character.h"
bool CCharacter::CheckRenameWarrant(Item::ItemPos InvenPos, bool bItemAccept)
{
// 계명허가서 확인
Item::CItem* lpItem = NULL;
if(InvenPos.m_cPos != TakeType::TS_ADMIN)
{
// 망각의돌 확인
lpItem = m_Inventory.GetItem(InvenPos);
if (NULL == lpItem)
{
ERRLOG3(g_Log, "CID:0x%08x 이름변경 오류 : 요청한 위치에 아이템이 없습니다. Pos:%d, Index:%d", m_dwCID, InvenPos.m_cPos, InvenPos.m_cIndex);
return false;
}
if (Item::EtcItemID::RENAME_WARRANT != lpItem->GetPrototypeID())
{
ERRLOG2(g_Log, "CID:0x%08x 이름변경 오류 : 사용하려는 아이템이 계명허가서가 아닙니다. ItemID:%d", m_dwCID, lpItem->GetPrototypeID());
return false;
}
// 계명허가서의 개수가 이상한지 확인
if(lpItem->GetNumOrDurability() <= 0)
{
ERRLOG3(g_Log, "CID:0x%08x 이름변경 오류 : 계명허가서의 갯수가 이상합니다. Pos:%d, Index:%d", m_dwCID, InvenPos.m_cPos, InvenPos.m_cIndex);
return false;
}
}
if(bItemAccept == true)
{
// 에러없이 성공했으면 계명허가서 감소
if(InvenPos.m_cPos != TakeType::TS_ADMIN)
{
// 계명허가서 개수 1개 감소
lpItem->SetNumOrDurability(lpItem->GetNumOrDurability() - 1);
if (0 == lpItem->GetNumOrDurability())
{
if (RemoveItem(InvenPos))
{
DELETE_ITEM(lpItem);
}
else
{
ERRLOG3(g_Log, "CID:0x%08x 스킬삭제 오류 : 계명허가서를 제거할 수 없습니다. Pos:%d, Index:%d", m_dwCID, InvenPos.m_cPos, InvenPos.m_cIndex);
m_Inventory.DumpItemInfo();
return false;
}
}
}
}
return true;
}
bool CCharacter::CalculateAbility(const SKILL& skill)
{
memset(m_AbilityValue, 0, sizeof(m_AbilityValue));
// 어빌리티 스킬 효과
for (unsigned char cSlotIndex = 0; cSlotIndex < skill.wSlotNum; ++cSlotIndex)
{
const SKILLSLOT& SkillSlot = skill.SSlot[cSlotIndex];
unsigned short wSkillID = SkillSlot.SKILLINFO.wSkill;
// 앨터너티브 스킬의 경우
const Skill::ProtoType* lpProtoType = CSkillMgr::GetInstance().GetSkillProtoType(wSkillID);
if (NULL == lpProtoType)
continue;
if(lpProtoType->m_eSkillType != Skill::Type::ABILITY)
continue;
int cLock = SkillSlot.SKILLINFO.cLockCount;
int id = wSkillID-0x1001;
m_AbilityValue[Skill::Type::AB_ANIMAL_TALK+id] = static_cast<unsigned short>(lpProtoType[cLock].m_fMaxRange);
/*
AB_ANIMAL_TALK = 0, // 동물NPC와 대화
AB_SOCIALEX = 1, // 확장 소셜모션 사용가능
AB_NOFIRSTATK = 2, // 선공몹 공격 안당함
AB_MOUNT_SPEEDUP = 3, // 탈것 이속 증가
AB_TEMPINVEN_TIMEUP = 4, // 임시인벤토리 시간증가
AB_GETUP_GOLD = 5, // 골드 획득량 증가
AB_RESPAWN_EX = 6, // 확장 리스폰 사용
AB_FOOD_UP = 7, // 음식류 능력치 증가
AB_SHOP_MEMBERSHIP = 8, // 상점 멤버십(구매가격 하락, 판매가격 상승)
AB_CHEMICAL = 9, // 잡템을 회복템으로 변경
AB_ENDUR_SHILD = 10, // 내구도 하락 감소
AB_RECOVERY_UP = 11, // HP,MP 회복도 증가
AB_AWARD_UP = 12, // 퀘스트 보상 증가
AB_MYEQUIP_UP = 13, // 자신의 장비 획득율 증가
AB_LUCK_UP = 14, // 럭찬 증가
AB_EQUIP_LEVELDOWN = 15, // 장비의 제한 레벨을 다운시킨다.
*/
}
return true;
}
bool CCharacter::CalculateStatusData(bool bFullHPandMP)
{
// 기본 스탯
m_CharacterStatus.Init(m_DBData.m_Info);
m_CreatureStatus.m_StatusInfo.m_cCalculateState = FightStatus::CS_NONE;
const unsigned char cLevel = static_cast<unsigned char>(m_DBData.m_Info.Level);
const unsigned char cClass = static_cast<unsigned char>(m_DBData.m_Info.Class);
m_CreatureStatus.m_StatusInfo.CalculateBaseInfo(m_CharacterStatus, cLevel, cClass);
// 어빌리티를 계산해준다
CalculateAbility(m_DBData.m_Skill);
// 리젠값을 증가시켜준다.
if(m_AbilityValue[Skill::Type::AB_RECOVERY_UP])
{
int Regen = m_AbilityValue[Skill::Type::AB_RECOVERY_UP];
m_CreatureStatus.m_StatusInfo.m_wHPRegen += Regen;
m_CreatureStatus.m_StatusInfo.m_wMPRegen += Regen;
}
// 장비에 의한 추가 스탯
Item::CEquipment* aryEquipList[Item::EquipmentPos::MAX_EQUPMENT_POS];
m_Equipments.GetEquipList(aryEquipList);
bool bFirstWeaponSlot = (m_cHandPos == 1) ? false : true;
bool bRidesSlot = (m_cRidePos == 0) ? false : true;
// 기본적으로 사용되는 장비 스탯
m_CreatureStatus.m_StatusInfo.CalculateEquipInfo(const_cast<const Item::CEquipment** >(aryEquipList),
bFirstWeaponSlot, true, bRidesSlot, m_DBData.m_Skill, cLevel, m_AbilityValue[Skill::Type::AB_MOUNT_SPEEDUP], m_AbilityValue[Skill::Type::AB_LUCK_UP], m_AbilityValue[Skill::Type::AB_EQUIP_LEVELDOWN], m_CharacterStatus, cClass, m_EquipStatus);
// 기타 특수한 경우에 사용되는 스탯들
m_CreatureStatus.m_StatusInfo.CalculateEquipInfo(const_cast<const Item::CEquipment** >(aryEquipList),
bFirstWeaponSlot, false, bRidesSlot, m_DBData.m_Skill, cLevel, m_AbilityValue[Skill::Type::AB_MOUNT_SPEEDUP], m_AbilityValue[Skill::Type::AB_LUCK_UP], m_AbilityValue[Skill::Type::AB_EQUIP_LEVELDOWN], m_CharacterStatus, cClass, m_EtcTypeStatus[Creature::LEFT_PASSIVE_TYPE]);
m_CreatureStatus.m_StatusInfo.CalculateEquipInfo(const_cast<const Item::CEquipment** >(aryEquipList),
bFirstWeaponSlot, true, bRidesSlot, SKILL(), cLevel, m_AbilityValue[Skill::Type::AB_MOUNT_SPEEDUP], m_AbilityValue[Skill::Type::AB_LUCK_UP], m_AbilityValue[Skill::Type::AB_EQUIP_LEVELDOWN], m_CharacterStatus, cClass, m_EtcTypeStatus[Creature::RIGHT_NON_PASSIVE_TYPE]);
m_CreatureStatus.m_StatusInfo.CalculateEquipInfo(const_cast<const Item::CEquipment** >(aryEquipList),
bFirstWeaponSlot, false, bRidesSlot, SKILL(), cLevel, m_AbilityValue[Skill::Type::AB_MOUNT_SPEEDUP], m_AbilityValue[Skill::Type::AB_LUCK_UP], m_AbilityValue[Skill::Type::AB_EQUIP_LEVELDOWN], m_CharacterStatus, cClass, m_EtcTypeStatus[Creature::LEFT_NON_PASSIVE_TYPE]);
// 퀘스트에 의해 받는 영향을 계산
CalculateStatusByQuest();
// 인챈트에 의한 추가 스탯
CalculateEnchantStatus();
if (true == bFullHPandMP)
{
m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
m_CreatureStatus.m_nNowMP = m_CreatureStatus.m_StatusInfo.m_nMaxMP;
}
// 최대값보다 현재값이 더 큰 경우 최대값으로 설정해준다.
m_CreatureStatus.m_nNowHP = std::min(m_CreatureStatus.m_nNowHP, m_CreatureStatus.m_StatusInfo.m_nMaxHP);
m_CreatureStatus.m_nNowMP = std::min(m_CreatureStatus.m_nNowMP, m_CreatureStatus.m_StatusInfo.m_nMaxMP);
return true;
}
// edith 2008.02.13 전직 패킷.
bool CCharacter::ChangeClass(unsigned char cClassType)
{
unsigned short usError = 0;
if (cClassType >= CClass::MAX_CLASS)
{
ERRLOG3(g_Log, "CID:0x%08x 클래스 체인지 실패. 현재 캐릭터의 클래스는 %d, 바꾸고자 하는 클래스는 %d입니다.",
m_dwCID, m_DBData.m_Info.Class, cClassType);
usError = 1;
}
m_DBData.m_Info.Exp = m_CreatureStatus.m_nExp;
if (false == ClassTable[cClassType].JobChange(&m_DBData, cClassType))
{
ERRLOG3(g_Log, "CID:0x%08x 클래스 체인지 실패. 현재 캐릭터의 클래스는 %d, 바꾸고자 하는 클래스는 %d입니다.",
m_dwCID, m_DBData.m_Info.Class, cClassType);
usError = 1;
}
m_CreatureStatus.Init(m_DBData.m_Info);
// 길드원 정보 업데이트
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (0 != lpGuild)
{
lpGuild->UpdateMemberInfo(m_dwCID, cClassType, Guild::TYPE_CLASS);
}
// 파티에 클래스가 변경되었음을 보냄
if (0 != m_pParty)
{
CCharacterParty* lpParty = static_cast<CCharacterParty*>(m_pParty);
lpParty->SendPartyMemberDataToDBAgent(m_dwCID, 0, cClassType, 0, 0,
GetCharacterName(), PktDD::SCmdChangeClassPartyMem);
}
// 친구 리스트에 클레스 변경 메세지를 보낸다 //
FriendInfoUpdate(GetUID(), GetCID(), GetGID(), GetClass(), GetLevel(), CServerSetup::GetInstance().GetServerID());
if (0 != m_lpGameClientDispatch &&
!GameClientSendPacket::SendCharClassUpgrade(
m_lpGameClientDispatch->GetSendStream(), m_dwCID, m_DBData, usError) &&
0 != usError)
{
return false;
}
return CalculateStatusData(true);
}
// Desc : Admin 명령으로 레벨을 다운 시킬때 사용하는 함수
// 레벨 1의 초기 클래스로 만들어 버린다.
bool CCharacter::InitLevel1Char(unsigned char cClassType)
{
if (cClassType >= CClass::MAX_CLASS)
{
ERRLOG3(g_Log, "CID:0x%08x 클래스 체인지 실패. 현재 캐릭터의 클래스는 %d, 바꾸고자 하는 클래스는 %d입니다.",
m_dwCID, m_DBData.m_Info.Class, cClassType);
return false;
}
m_DBData.m_Info.Exp = m_CreatureStatus.m_nExp;
if (false == ClassTable[cClassType].InitializeClass(&m_DBData, cClassType))
{
ERRLOG3(g_Log, "CID:0x%08x 클래스 초기화 실패. 현재 캐릭터의 클래스는 %d, 바꾸고자 하는 클래스는 %d입니다.",
m_dwCID, m_DBData.m_Info.Class, cClassType);
return false;
}
m_CreatureStatus.Init(m_DBData.m_Info);
if (NULL != m_lpGameClientDispatch)
{
if (false == GameClientSendPacket::SendCharClassUpgrade(m_lpGameClientDispatch->GetSendStream(),
m_dwCID, m_DBData, PktBase::NO_SERVER_ERR))
{
return false;
}
}
return CalculateStatusData(true);
}
bool CCharacter::GetHuntingExp(CAggresiveCreature* lpDeadCreature, unsigned long dwExp, unsigned char cMemberNum)
{
// edith 파티경험치
// 파티에 의한 경험점 캡 보너스
static const float fExpCapPartyBonus[PARTY::MAX_MEM] = {
// 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f
1.0f, 1.2f, 1.4f, 1.6f, 1.8f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f
};
float fPartyExpCap = fExpCapPartyBonus[cMemberNum -1];
// edith 2008.06.30 파티를 맺었을때 보너스 경험치를 받는다.
dwExp = static_cast<unsigned long>(dwExp * fPartyExpCap);
// 경험점 캡 적용
const float fExpCapRevision = EXP::ExpCapTable[m_CreatureStatus.m_nLevel - 1] *
(CServerSetup::GetInstance().GetExpDefault() / 100.0f);
/*
// edith 2008.06.30
// 파티 몬스터를 잡았을 경우에는 경험치 보상을 준다.
if (NULL != lpDeadCreature->GetParty())
{
// 그룹(파티)의 난이도에 대한 보상
CMonsterParty* lpParty = reinterpret_cast<CMonsterParty* >(lpDeadCreature->GetParty());
switch (lpParty->GetMemberTypeNum())
{
case 1: dwExp += static_cast<unsigned long>(dwExp * 0.06f); break;
case 2: dwExp += static_cast<unsigned long>(dwExp * 0.12f); break;
case 3: dwExp += static_cast<unsigned long>(dwExp * 0.18f); break;
case 4: dwExp += static_cast<unsigned long>(dwExp * 0.24f); break;
case 5: dwExp += static_cast<unsigned long>(dwExp * 0.3f); break;
}
}
*/
dwExp = std::max(dwExp, static_cast<unsigned long>(1));
dwExp = static_cast<unsigned long>(dwExp * (CServerSetup::GetInstance().GetExpDefault() / 100.0f));
if (dwExp > fExpCapRevision)
{
/*
RULLOG9(g_Log, "CID:0x%08x 획득 경험치가 경험점 캡을 초과하였습니다. 멤버수:%d "
"배율 : %d%%, 획득 경험치 : %ld, 경험점 캡(레벨) : %.1f (%d), 클래스 : %d, 살해된 녀석(레벨) : 0x%08x (%d)",
m_dwCID, cMemberNum, CServerSetup::GetInstance().GetExpDefault(),
dwExp, fExpCapRevision, m_CreatureStatus.m_nLevel, m_DBData.m_Info.Class,
lpDeadCreature->GetCID(), lpDeadCreature->GetStatus().m_nLevel);
*/
dwExp = static_cast<unsigned long>(fExpCapRevision);
}
// 서버 엘리트 보너스에 의한 추가 경험점 (캡의 영향을 받지 않는다)
char eliteBonus = GetEliteBonus();
if (0 < eliteBonus)
{
dwExp = static_cast<unsigned long>(dwExp * EliteBonus::usBonusExp[eliteBonus - 1] / 100.0f);
}
// 소환수가 있는 경우 90%의 경험치만을 받는다.
if (NULL != m_lpSummonee)
{
dwExp = static_cast<unsigned long>(dwExp * 0.9f);
}
unsigned long dwAddExp = 0;
float fPt = 0.0f;
// '경험의 오브' 사용시
if (true == GetEnchantInfo().GetFlag(Skill::SpellID::ExpOrb))
{
// 현재 50% 계산
fPt += 0.3f;
}
// 프리미엄 서비스시 경험치 추가로직
if(CheckPremiumTime())
{
fPt += GetPremiumPt();
}
// '경험의 석상' 사용시.
if (true == GetEnchantInfo().GetFlag(Skill::SpellID::ExpStatue))
{
unsigned short wLevel = GetEnchantLevel(Skill::SpellID::ExpStatue);
fPt += (wLevel*0.01f);
}
// 공성전 보상으로 성의 반경 xxx m 내에서 사냥시 경험치 10%씩 더 획득
if (true == CServerSetup::GetInstance().UseContents(GameRYL::SIEGE))
{
// 공선전 보상이기 때문에..
// 석성전, 공성전 시간이 아닐때만,
if (!CGameTimeMgr::GetInstance().IsSiegeWarTime())
{
int iMyCastleNum = Castle::CCastleMgr::GetInstance().GetCastleNum(
GetNation(), CServerSetup::GetInstance().GetServerZone(), GetCurrentPos() );
if (iMyCastleNum > 0)
{
fPt += (iMyCastleNum*0.01f);
}
}
}
// edith 2010.01.02 길드원 수와 길드가입자의 수에 의한 보너스 포인트 추가
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (0 != lpGuild)
{
float fD = lpGuild->GetGuildBonus(GetRealmPoint());
fPt += fD;
}
if(fPt > 0.0f)
dwAddExp = static_cast<unsigned long>((float)dwExp*fPt);
return IncrementExp(dwExp + dwAddExp);
}
bool CCharacter::IncrementExp(unsigned long dwExp)
{
// 중국이면
float fPt = GetAwardPer();
dwExp = (unsigned long)((float)dwExp*fPt);
// 현재 캐릭터 레벨 값이 제대로 된 값인지 검사.
if (m_CreatureStatus.m_nLevel > EXP::GetUsingMaxLevel() - 1|| m_CreatureStatus.m_nLevel <= 0)
{
if (m_CreatureStatus.m_nLevel == EXP::GetUsingMaxLevel())
{
return true;
}
ERRLOG2(g_Log, "CID:0x%08x 레벨업을 할 수 없습니다 : 현재 캐릭터의 레벨 값이 이상합니다."
" 레벨 값은 %d입니다.", m_dwCID, m_CreatureStatus.m_nLevel);
return false;
}
// 현재 클래스가 정상인지 확인.
if (m_DBData.m_Info.Class >= CClass::MAX_CLASS)
{
ERRLOG2(g_Log, "CID:0x%08x 레벨업을 할 수 없습니다 : 현재 캐릭터의 클래스가 이상합니다."
" 클래스 값은 %d입니다.", m_dwCID, m_DBData.m_Info.Class);
return false;
}
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharAward(m_lpGameClientDispatch->GetSendStream(),
m_dwCID, dwExp);
}
// 현재 Exp로 레벨업 할 수 있는지를 검사.
m_CreatureStatus.m_nExp += dwExp;
if (m_CreatureStatus.m_nExp < EXP::ExpTable[m_CreatureStatus.m_nLevel - 1])
{
// 아직 경험치가 모자라 레벨업을 할 수 없다.
return true;
}
// 레벨업을 한번에 두개 이상 하려는지 확인.
if (m_CreatureStatus.m_nExp - EXP::ExpTable[m_CreatureStatus.m_nLevel - 1] >= EXP::ExpTable[m_CreatureStatus.m_nLevel])
{
RULLOG2(g_Log, "CID:0x%08x 한번에 2레벨 이상 레벨업을 하려 합니다. 렙업전의 레벨:%d", m_dwCID, m_CreatureStatus.m_nLevel);
m_DBData.m_Info.Exp = m_CreatureStatus.m_nExp = EXP::ExpTable[m_CreatureStatus.m_nLevel] - 1;
}
else
{
m_DBData.m_Info.Exp = m_CreatureStatus.m_nExp = m_CreatureStatus.m_nExp - EXP::ExpTable[m_CreatureStatus.m_nLevel - 1];
}
++m_DBData.m_Info.Level;
++m_CreatureStatus.m_nLevel;
// 클래스에 따른 스테이터스 자동 증가. IP수 2개 올려 줌.
ClassTable[m_DBData.m_Info.Class].LevelUp(&m_DBData.m_Info);
m_DBData.m_Info.HP = m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxMP;
m_DBData.m_Info.MP = m_CreatureStatus.m_nNowMP = m_CreatureStatus.m_StatusInfo.m_nMaxMP;
// 길드원 정보 업데이트
CGuild* lpGuild = Guild::CGuildMgr::GetInstance().GetGuild(GetGID());
if (0 != lpGuild)
{
lpGuild->UpdateMemberInfo(m_dwCID, m_CreatureStatus.m_nLevel, Guild::TYPE_LEVEL);
}
// 파티에 레벨업했음을 보냄
if (0 != m_pParty)
{
CCharacterParty* lpParty = static_cast<CCharacterParty*>(m_pParty);
lpParty->SendPartyMemberDataToDBAgent(m_dwCID, 0, 0, 0, m_CreatureStatus.m_nLevel,
GetCharacterName(), PktDD::SCmdLevelUpPartyMem);
}
// 친구 리스트에 레벨업 메세지를 보낸다 //
FriendInfoUpdate(GetUID(), GetCID(), GetGID(), GetClass(), GetLevel(), CServerSetup::GetInstance().GetServerID());
// 레벨업 이벤트
// edith 2009.09.05 신규 이벤트 제작부분
if (CServerSetup::GetInstance().GetLevelUpEvent() && 0 != m_CellPos.m_lpCell)
{
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 == lpDBAgentDispatch)
{
ERRLOG0(g_Log, "이벤트로그 : 에이전트 얻기 실패.");
}
else if(m_DBData.m_Info.Level == 30) // 이부분 이벤트는 30레벨업 이벤트
{
// 레벨업에 관련된 이벤트 전송
GameClientSendPacket::SendExtraEvent(lpDBAgentDispatch->GetSendStream(), GetUID(),
GetCID(), MAKEWPARAM(1,30), 0, 0, 0);
}
else if(m_DBData.m_Info.Level == 60) // 이부분 이벤트는 30레벨업 이벤트
{
// 레벨업에 관련된 이벤트 전송
GameClientSendPacket::SendExtraEvent(lpDBAgentDispatch->GetSendStream(), GetUID(),
GetCID(), MAKEWPARAM(1,60), 0, 0, 0);
}
else if(m_DBData.m_Info.Level == 90) // 이부분 이벤트는 30레벨업 이벤트
{
// 레벨업에 관련된 이벤트 전송
GameClientSendPacket::SendExtraEvent(lpDBAgentDispatch->GetSendStream(), GetUID(),
GetCID(), MAKEWPARAM(1,90), 0, 0, 0);
}
}
/*
if (CServerSetup::GetInstance().GetLevelUpEvent() && 0 != m_CellPos.m_lpCell)
{
unsigned short wItemKind = AwardTable::CAward::GetInstance().GetAwardEquipment(
Item::EquipType::STANDARD_OPTION, m_DBData.m_Info.Level, this, true);
Item::CItem* lpItem = Item::CItemFactory::GetInstance().CreateItem(wItemKind);
Item::CEquipment* lpEquip = Item::CEquipment::DowncastToEquipment(lpItem);
if (NULL != lpEquip)
{
lpEquip->AddRandomOption(Item::EquipType::F_GRADE, 0);
lpEquip->SetNewEquip();
}
GiveItem(lpItem);
// GievItem 으로 스택된 경우
if (lpItem->IsSet(Item::DetailData::STACKABLE) && 0 == lpItem->GetNumOrDurability())
{
DELETE_ITEM(lpItem);
}
}
*/
// 캐릭터 레벨 업 로그를 남긴다.
GAMELOG::LogCharLevelUp(*this, m_DBData.m_Info.IP, m_DBData.m_Info.Level, 0);
if (0 != m_lpGameClientDispatch)
{
// 레벨업하는 사람에게 레벨업 이벤트를 보낸다.
GameClientSendPacket::SendCharLevelUp(
m_lpGameClientDispatch->GetSendStream(), m_dwCID, m_DBData);
}
// 주변 인물들에게 레벨업 이벤트 정보를 보낸다.
// 받으면 적당히 이펙트를 뿌려 준다.
if(0 != m_CellPos.m_lpCell)
{
PktLUInfo pktLUInfo;
memset(&pktLUInfo, 0, sizeof(PktLUInfo));
pktLUInfo.m_dwCharID = m_dwCID;
pktLUInfo.m_cLevel = m_DBData.m_Info.Level;
m_CellPos.m_lpCell->SendAllNearCellCharacter(
&pktLUInfo, sizeof(PktLUInfo), CmdCharLevelUpInfo);
}
/*
// edith 100랩 달성 이팩트
if (true == CServerSetup::GetInstance().UseContents(GameRYL::LEVEL_LIMIT_100))
{
if(100 == m_DBData.m_Info.Level)
{
Skill::CAddSpell<CHundredLevelSpell>(CSpell::Spell_Info(Skill::CProcessTable::ProcessInfo::m_NullProtoType, NULL,
Skill::SpellType::ETERNAL_SPELL, Skill::SpellID::HundredLevel, 1, CSpell::INFINITE_DURATION))(this);
}
}
*/
DBUpdateForce(DBUpdateData::UPDATE);
return CalculateStatusData(true);
}
bool CCharacter::RegenHPAndMP(unsigned short usAdditionalHP, unsigned short usAdditionalMP, bool bAddDefaultRegenValue)
{
if (false == CAggresiveCreature::RegenHPAndMP(usAdditionalHP, usAdditionalMP, bAddDefaultRegenValue)) {
return false;
}
if (NULL != m_lpGameClientDispatch)
{
return GameClientSendPacket::SendCharHPRegen(m_lpGameClientDispatch->GetSendStream(),
m_dwCID, m_CreatureStatus.m_nNowHP, m_CreatureStatus.m_nNowMP);
}
return true;
}
unsigned short CCharacter::AddState(unsigned char Type_In, ChState& ChState_Out)
{
unsigned char cDecrementIP = CClass::GetRequiredIP(m_DBData.m_Info.Class, Type_In);
if (m_DBData.m_Info.IP < cDecrementIP)
{
return 2;
}
switch(Type_In)
{
case CClass::STR: ++m_DBData.m_Info.STR; break;
case CClass::DEX: ++m_DBData.m_Info.DEX; break;
case CClass::CON: ++m_DBData.m_Info.CON; break;
case CClass::INT: ++m_DBData.m_Info.INT; break;
case CClass::WIS: ++m_DBData.m_Info.WIS; break;
}
m_DBData.m_Info.IP -= cDecrementIP;
ChState_Out.m_wIP = m_DBData.m_Info.IP;
ChState_Out.m_wSTR = m_DBData.m_Info.STR;
ChState_Out.m_wDEX = m_DBData.m_Info.DEX;
ChState_Out.m_wCON = m_DBData.m_Info.CON;
ChState_Out.m_wINT = m_DBData.m_Info.INT;
ChState_Out.m_wWIS = m_DBData.m_Info.WIS;
CalculateStatusData(false);
return 0;
}
bool CCharacter::StateRedistribution(ChState& State)
{
unsigned short usError = PktSRAck::NO_SERVER_ERR;
/*
if (0 == m_DBData.m_Info.Chance)
{
usError = PktSRAck::FAIL_NO_CHANCE;
}
else
*/ {
int CheckStat = State.m_wIP+State.m_wSTR+State.m_wDEX+State.m_wCON+State.m_wINT+State.m_wWIS;
if(CheckStat >= 10000)
{
ERRLOG9(g_Log, "CID:0x%08x 재분배된 스탯이 올바르지 않습니다. "
"클래스:%d, 레벨:%d, STR:%d, DEX:%d, CON:%d, INT:%d, WIS:%d IP:%d",
m_dwCID, m_DBData.m_Info.Class, m_DBData.m_Info.Level,
State.m_wSTR, State.m_wDEX, State.m_wCON, State.m_wINT, State.m_wWIS, State.m_wIP);
return false;
}
if (false == ClassTable[m_DBData.m_Info.Class].CheckState(State, m_DBData.m_Info.Level))
{
ERRLOG9(g_Log, "CID:0x%08x 재분배된 스탯이 올바르지 않습니다. "
"클래스:%d, 레벨:%d, STR:%d, DEX:%d, CON:%d, INT:%d, WIS:%d IP:%d",
m_dwCID, m_DBData.m_Info.Class, m_DBData.m_Info.Level,
State.m_wSTR, State.m_wDEX, State.m_wCON, State.m_wINT, State.m_wWIS, State.m_wIP);
return false;
}
const CHAR_INFOST tempInfoSt = m_DBData.m_Info;
m_DBData.m_Info.IP = State.m_wIP;
m_DBData.m_Info.STR = State.m_wSTR;
m_DBData.m_Info.DEX = State.m_wDEX;
m_DBData.m_Info.CON = State.m_wCON;
m_DBData.m_Info.INT = State.m_wINT;
m_DBData.m_Info.WIS = State.m_wWIS;
// 재분배된 스탯에 따라 스킬 재정리
if (false == RedistributionSkill())
{
ERRLOG1(g_Log, "CID:0x%08x 스킬 재분배에 실패하였습니다.", m_dwCID);
m_DBData.m_Info = tempInfoSt;
return false;
}
// 퀵슬롯 초기화
m_DBData.m_Quick = QUICK::QUICK();
// m_DBData.m_Info.Chance--;
m_DBData.m_Info.Chance = 1;
DETLOG2(g_Log, "CID:%u 의 캐릭터가 스탯 재분배 기회를 사용하였습니다. (사용후 남은 Chance: %d)",
m_dwCID, m_DBData.m_Info.Chance);
}
if (NULL != m_lpGameClientDispatch)
{
if (false == GameClientSendPacket::SendCharStateRedistribution(m_lpGameClientDispatch->GetSendStream(),
m_dwCID, GetState(), GetSkill(), usError))
{
return false;
}
}
return CalculateStatusData(true);
}
// 스탯을 일정량 돌려받는 처리를 하는 함수 (망각의 돌 사용)
bool CCharacter::StatusRetrain(ChState& State, Item::ItemPos InvenPos)
{
unsigned short usError = PktSRTAck::NO_SERVER_ERR;
if (State.m_wIP - m_DBData.m_Info.IP > PktSRTAck::MAX_STATUS_RETRAIN)
{
usError = PktSRTAck::FAIL_INVALID_IP;
goto SEND_PACKET;
}
// 부활의 돌
Item::CItem* lpItem = m_Inventory.GetItem(InvenPos);
if (NULL == lpItem)
{
ERRLOG2(g_Log, "스테이터스 재훈련 오류 : 요청한 위치에 아이템이 없습니다. Pos:%d, Index:%d",
InvenPos.m_cPos, InvenPos.m_cIndex);
usError = PktSRTAck::FAIL_NO_ITEM;
goto SEND_PACKET;
}
if (Item::EtcItemID::REBIRTH_STONE != lpItem->GetPrototypeID())
{
ERRLOG1(g_Log, "스테이터스 재훈련 오류 : 사용하려는 아이템이 부활의 돌이 아닙니다. ItemID:%d",
lpItem->GetPrototypeID());
usError = PktSRTAck::FAIL_INVALID_ITEM;
goto SEND_PACKET;
}
/*
unsigned long dwReduceGold = m_CreatureStatus.m_nLevel * 10000;
if (dwReduceGold > m_DBData.m_Info.Gold)
{
ERRLOG2(g_Log, "스테이터스 재훈련 오류 : 돈이 부족합니다. 요구금액:%ld, 소지금:%ld",
dwReduceGold, m_DBData.m_Info.Gold);
usError = PktSRTAck::FAIL_NOT_ENOUGH_GOLD;
goto SEND_PACKET;
}
*/
int CheckStat = State.m_wIP+State.m_wSTR+State.m_wDEX+State.m_wCON+State.m_wINT+State.m_wWIS;
if(CheckStat >= 10000)
{
ERRLOG9(g_Log, "CID:0x%08x 재분배된 스탯이 올바르지 않습니다. "
"클래스:%d, 레벨:%d, STR:%d, DEX:%d, CON:%d, INT:%d, WIS:%d IP:%d",
m_dwCID, m_DBData.m_Info.Class, m_DBData.m_Info.Level,
State.m_wSTR, State.m_wDEX, State.m_wCON, State.m_wINT, State.m_wWIS, State.m_wIP);
usError = PktSRTAck::FAIL_INVALID_STATUS;
goto SEND_PACKET;
}
// IP 총 수치가 올바른가 체크
if (false == ClassTable[m_DBData.m_Info.Class].CheckState(State, m_DBData.m_Info.Level))
{
ERRLOG9(g_Log, "CID:0x%08x 재분배된 스탯이 올바르지 않습니다. "
"클래스:%d, 레벨:%d, STR:%d, DEX:%d, CON:%d, INT:%d, WIS:%d IP:%d",
m_dwCID, m_DBData.m_Info.Class, m_DBData.m_Info.Level,
State.m_wSTR, State.m_wDEX, State.m_wCON, State.m_wINT, State.m_wWIS, State.m_wIP);
usError = PktSRTAck::FAIL_INVALID_STATUS;
goto SEND_PACKET;
}
// 최소 스탯 체크
if (false == ClassTable[m_DBData.m_Info.Class].CheckMinState(State, m_DBData.m_Info.Level))
{
ERRLOG9(g_Log, "CID:0x%08x 재분배된 스탯이 올바르지 않습니다. "
"클래스:%d, 레벨:%d, STR:%d, DEX:%d, CON:%d, INT:%d, WIS:%d IP:%d",
m_dwCID, m_DBData.m_Info.Class, m_DBData.m_Info.Level,
State.m_wSTR, State.m_wDEX, State.m_wCON, State.m_wINT, State.m_wWIS, State.m_wIP);
usError = PktSRTAck::FAIL_INVALID_STATUS;
goto SEND_PACKET;
}
if (usError == PktSRTAck::NO_SERVER_ERR)
{
const CHAR_INFOST tempInfoSt = m_DBData.m_Info;
// 스테이터스 재훈련 성공시 아이템 삭제 및 돈 감소
if (0 < lpItem->GetNumOrDurability())
{
lpItem->SetNumOrDurability(lpItem->GetNumOrDurability() - 1);
}
else
{
ERRLOG2(g_Log, "CID:0x%08x 스테이터스 재훈련 오류 : 부활의 돌의 갯수가 이상합니다. Pos:%d, Index:%d",
InvenPos.m_cPos, InvenPos.m_cIndex);
usError = PktSRTAck::FAIL_NO_ITEM;
goto SEND_PACKET;
}
if (0 == lpItem->GetNumOrDurability())
{
if (RemoveItem(InvenPos))
{
DELETE_ITEM(lpItem);
}
else
{
ERRLOG2(g_Log, "CID:0x%08x 스테이터스 재훈련 오류 : 부활의 돌을 제거할 수 없습니다. Pos:%d, Index:%d",
InvenPos.m_cPos, InvenPos.m_cIndex);
m_Inventory.DumpItemInfo();
usError = PktSRTAck::FAIL_NO_ITEM;
goto SEND_PACKET;
}
}
// unsigned long dwSrcGold = GetGold();
// DeductGold(dwReduceGold, false);
// GAMELOG::LogTakeGold(*this, dwSrcGold, GetGold(), dwReduceGold,
// TakeType::TS_INVEN, TakeType::TS_INVEN, GAMELOG::sTakeGoldLogV2::STATE_REDISTRIBUTE, 0);
// 원래 스텟 검사
int OriStat = tempInfoSt.IP+tempInfoSt.STR+tempInfoSt.DEX+tempInfoSt.CON+tempInfoSt.INT+tempInfoSt.WIS;
int NewStat = State.m_wIP+State.m_wSTR+State.m_wDEX+State.m_wCON+State.m_wINT+State.m_wWIS;
// 바뀐 스탯을 적용
m_DBData.m_Info.IP = State.m_wIP;
m_DBData.m_Info.STR = State.m_wSTR;
m_DBData.m_Info.DEX = State.m_wDEX;
m_DBData.m_Info.CON = State.m_wCON;
m_DBData.m_Info.INT = State.m_wINT;
m_DBData.m_Info.WIS = State.m_wWIS;
// 재분배된 스탯에 따라 스킬 재정리
if (false == RedistributionSkill())
{
ERRLOG1(g_Log, "CID:0x%08x 스킬 재분배에 실패하였습니다.", m_dwCID);
m_DBData.m_Info = tempInfoSt;
return false;
}
DETLOG7(g_Log, "CID:%u 의 캐릭터가 스탯 재분배를 했습니다.(오리지널:IP:%d,STR:%d,DEX:%d,CON:%d,INT:%d,WIS:%d)", m_dwCID, tempInfoSt.IP, tempInfoSt.STR, tempInfoSt.DEX, tempInfoSt.CON, tempInfoSt.INT, tempInfoSt.WIS);
DETLOG7(g_Log, "CID:%u 의 캐릭터가 스탯 재분배를 했습니다.(IP:%d,STR:%d,DEX:%d,CON:%d,INT:%d,WIS:%d)", m_dwCID, State.m_wIP, State.m_wSTR, State.m_wDEX, State.m_wCON, State.m_wINT, State.m_wWIS);
// 퀵슬롯 초기화
m_DBData.m_Quick = QUICK::QUICK();
}
SEND_PACKET:
if (NULL != m_lpGameClientDispatch)
{
if (false == GameClientSendPacket::SendCharStatusRetrain(m_lpGameClientDispatch->GetSendStream(),
m_dwCID, m_DBData, InvenPos, m_DBData.m_Info.Gold, usError))
{
return false;
}
else
{
if (false == GameClientSendPacket::SendCharStateRedistribution(m_lpGameClientDispatch->GetSendStream(),
m_dwCID, GetState(), GetSkill(), usError))
{
return false;
}
}
}
if (usError == PktSRTAck::NO_SERVER_ERR)
{
return CalculateStatusData(true);
}
// usError 가 Error 값을 가지고 있는 패킷을 보냈기 때문에
// true 를 리턴해서 클라이언트의 접속을 끊지 않는다.
return true;
}
unsigned char CCharacter::GetCurrentDurability(unsigned char cEquipmentIndex)
{
if (cEquipmentIndex >= Item::EquipmentPos::MAX_EQUPMENT_POS) {
return 0;
}
Item::ItemPos itemPos(TakeType::TS_EQUIP, cEquipmentIndex);
Item::CItem* lpItem = m_Equipments.GetItem(itemPos);
if (NULL == lpItem) {
return 0;
}
return lpItem->GetNumOrDurability();
}
bool CCharacter::ChangeWeaponAndShield(unsigned char cSelect)
{
if (cSelect < 0 || cSelect > 1)
{
ERRLOG2(g_Log, "CID:0x%08x 잘못된 위치의 무기로 교체하려 합니다. Select: %d", m_dwCID, cSelect);
return false;
}
if ((cSelect == 0 && Item::EquipmentPos::WEAPON_HAND1 == m_Equipments.GetRightHandIndex()) ||
(cSelect == 1 && Item::EquipmentPos::WEAPON_HAND2 == m_Equipments.GetRightHandIndex()))
{
ERRLOG2(g_Log, "CID:0x%08x 이미 사용하고 있는 위치의 무기로 교체하려 합니다. Select: %d", m_dwCID, cSelect);
return false;
}
m_cHandPos = cSelect;
/*
if (cSelect <= 0 || cSelect > 2)
{
ERRLOG2(g_Log, "CID:0x%08x 잘못된 위치의 무기로 교체하려 합니다. Select: %d", m_dwCID, cSelect);
return false;
}
if ((cSelect == 1 && Item::EquipmentPos::WEAPON_HAND1 == m_Equipments.GetRightHandIndex()) ||
(cSelect == 2 && Item::EquipmentPos::WEAPON_HAND2 == m_Equipments.GetRightHandIndex()))
{
ERRLOG2(g_Log, "CID:0x%08x 이미 사용하고 있는 위치의 무기로 교체하려 합니다. Select: %d", m_dwCID, cSelect);
return false;
}
m_cHandPos = cSelect - 1;
*/
m_Equipments.ChangeWeaponAndShield();
CalculateStatusData(false);
return true;
}
bool CCharacter::ChangeRide(unsigned char cSelect) // 말 타기 내리기
{
if (cSelect < 0 || cSelect > 1)
{
ERRLOG2(g_Log, "CID:0x%08x 잘못된 위치의 말로 교체하려 합니다. Select: %d", m_dwCID, cSelect);
return false;
}
if (m_cRidePos == cSelect)
{
ERRLOG2(g_Log, "CID:0x%08x 이미 같은 상태로 말을 사용하고 있습니다. Select: %d", m_dwCID, cSelect);
return false;
}
m_cRidePos = cSelect;
m_Equipments.ChangeRide(m_cRidePos);
CalculateStatusData(false);
return true;
}
bool CCharacter::CalculateEquipDurability(unsigned short wAttackType)
{
Item::CItem* lpEquipment = NULL;
unsigned char cEquipIndex = Item::EquipmentPos::MAX_EQUPMENT_POS;
unsigned char cEquipValue = 0;
int AbValue = m_AbilityValue[Skill::Type::AB_ENDUR_SHILD]; // 퍼센트로 들어가있다. (100 이면 100프로만큼 보너스를 받는다)
int AddPer = 0;
switch (wAttackType)
{
// 블럭
case AtType::GUARD:
{
AddPer = SHIELD_DURABILITY_DECRESE_PERCENTAGE*AbValue/100;
if (0 == Math::Random::ComplexRandom(SHIELD_DURABILITY_DECRESE_PERCENTAGE+AddPer))
{
lpEquipment = GetEquipments().GetLeftHand();
if (NULL == lpEquipment)
{
// 왼손 장비가 없으면 오른손으로 블럭
lpEquipment = GetEquipments().GetRightHand();
if (NULL == lpEquipment)
{
ERRLOG1(g_Log, "CID:0x%08x 블럭할 장비가 없는 캐릭터가 블럭하였습니다.", m_dwCID);
return false;
}
}
}
break;
}
// 방어
case AtType::DEFENCE:
{
AddPer = ARMOUR_DURABILITY_DECRESE_PERCENTAGE*AbValue/100;
if (0 == Math::Random::ComplexRandom(ARMOUR_DURABILITY_DECRESE_PERCENTAGE+AddPer))
{
const unsigned char MAX_ARMOUR_INDEX_NUM = 4;
static unsigned char aryArmourShuffleList[MAX_ARMOUR_INDEX_NUM] = {
Item::EquipmentPos::HELM,
Item::EquipmentPos::BOOTS,
Item::EquipmentPos::GLOVE,
Item::EquipmentPos::ARMOUR
};
std::random_shuffle(aryArmourShuffleList, aryArmourShuffleList + MAX_ARMOUR_INDEX_NUM);
lpEquipment = GetEquipments().GetItem(Item::ItemPos(TakeType::TS_EQUIP, aryArmourShuffleList[0]));
}
break;
}
// 왼손 공격
case AtType::LEFT_MELEE:
{
AddPer = WEAPON_DURABILITY_DECRESE_PERCENTAGE*AbValue/100;
if (0 == Math::Random::ComplexRandom(WEAPON_DURABILITY_DECRESE_PERCENTAGE+AddPer))
{
lpEquipment = GetEquipments().GetLeftHand();
}
break;
}
// 일반 공격
default:
{
AddPer = WEAPON_DURABILITY_DECRESE_PERCENTAGE*AbValue/100;
if (0 == Math::Random::ComplexRandom(WEAPON_DURABILITY_DECRESE_PERCENTAGE+AddPer))
{
lpEquipment = GetEquipments().GetRightHand();
}
break;
}
}
if (NULL != lpEquipment)
{
if (0 < lpEquipment->GetNumOrDurability())
{
lpEquipment->SetNumOrDurability(lpEquipment->GetNumOrDurability() - 1);
}
else if (0 == lpEquipment->GetNumOrDurability())
{
// edith 2008.01.16 아이템 최대내구도 감소
int idown = STARTAXDURABILITY+Math::Random::ComplexRandom(DOWNMAXDURABILITY);
int curDur = lpEquipment->GetMaxNumOrDurability() - idown;
if(curDur < 0)
curDur = 1;
lpEquipment->SetMaxNumOrDurability(curDur);
lpEquipment->SetNumOrDurability(0);
CalculateStatusData(false);
}
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharEquipDurability(m_lpGameClientDispatch->GetSendStream(), m_dwCID,
lpEquipment->GetPos().m_cIndex, lpEquipment->GetNumOrDurability(), lpEquipment->GetMaxNumOrDurability(), PktBase::NO_SERVER_ERR);
}
}
return true;
}
bool CCharacter::CalculateAllEquipDurability(unsigned char cDecreasePersent)
{
for (char cIndex = 0; cIndex < Item::EquipmentPos::MAX_EQUPMENT_POS; ++cIndex)
{
// 내구도 감소가 없는 아이템은 제외
if (Item::EquipmentPos::SHIRT == cIndex ||
Item::EquipmentPos::TUNIC == cIndex ||
Item::EquipmentPos::NECKLACE == cIndex ||
Item::EquipmentPos::RINGR == cIndex ||
Item::EquipmentPos::RINGL == cIndex ||
Item::EquipmentPos::AVATA == cIndex )
{
continue;
}
Item::ItemPos itemPos(TakeType::TS_EQUIP, cIndex);
Item::CItem* lpItem = m_Equipments.GetItem(itemPos);
if (NULL == lpItem)
{
continue;
}
if (lpItem->GetEnableRepair())
{
if (0 < lpItem->GetNumOrDurability())
{
const unsigned char cDecreasePoint =
static_cast<unsigned char>(lpItem->GetMaxNumOrDurability() * cDecreasePersent / 100.0f);
if (lpItem->GetNumOrDurability() <= cDecreasePoint)
{
// edith 2008.01.16 아이템 최대내구도 감소
int idown = STARTAXDURABILITY+Math::Random::ComplexRandom(DOWNMAXDURABILITY)/2;
int curDur = lpItem->GetMaxNumOrDurability() - idown;
if(curDur < 0)
curDur = 1;
lpItem->SetMaxNumOrDurability(curDur);
lpItem->SetNumOrDurability(0);
}
else
{
lpItem->SetNumOrDurability(lpItem->GetNumOrDurability() - cDecreasePoint);
}
}
else if (0 == lpItem->GetNumOrDurability())
{
// edith 2008.01.16 아이템 최대내구도 감소
int idown = STARTAXDURABILITY+Math::Random::ComplexRandom(DOWNMAXDURABILITY);
int curDur = lpItem->GetMaxNumOrDurability() - idown;
if(curDur < 0)
curDur = 1;
lpItem->SetMaxNumOrDurability(curDur);
lpItem->SetNumOrDurability(0);
}
if (0 == lpItem->GetNumOrDurability())
{
CalculateStatusData(false);
}
if (NULL != m_lpGameClientDispatch)
{
GameClientSendPacket::SendCharEquipDurability(m_lpGameClientDispatch->GetSendStream(), m_dwCID,
lpItem->GetPos().m_cIndex, lpItem->GetNumOrDurability(), lpItem->GetMaxNumOrDurability(), PktBase::NO_SERVER_ERR);
}
}
}
return true;
}
void CCharacter::UpgradeRespawnSpeedByEmblem(unsigned char cUpgradeType, unsigned char cUpgradeStep)
{
int nKID = Siege::GetKID(Siege::EMBLEM, Siege::COMPLETE, cUpgradeType, cUpgradeStep);
const CMonsterMgr::MonsterProtoType* lpProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(nKID);
if (lpProtoType)
{
m_dwRespawnSpeed -= static_cast<unsigned long>(m_dwRespawnSpeed * lpProtoType->m_MonsterInfo.m_fRespawnSpeedUp);
}
}
void CCharacter::DegradeRespawnSpeedByEmblem()
{
m_dwRespawnSpeed = CCharacter::RESPAWN_PULSE;
}