Files
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

623 lines
15 KiB
C++

#include "stdafx.h"
#include <Skill/SkillMgr.h>
#include <Creature/AggresiveCreature.h>
#include "Spell.h"
#include "Affected.h"
#include "SpellKind.h"
bool CAffectedSpell::Add(CSpell* pSpell, unsigned short& wError)
{
if (true == RemoveOverlappedSpell(pSpell))
{
switch (pSpell->GetSkillType())
{
case Skill::Type::CHANT:
{
if (m_cChantNum < MAX_CHANT)
{
m_pChant[m_cChantNum] = pSpell;
++m_cChantNum;
return true;
}
} break;
case Skill::Type::ENCHANT:
{
if (Skill::SpellTarget::ENEMY_TARGET_ENCHANT == pSpell->GetSpellTarget())
{
RemoveEnchantBySpellType(Skill::SpellID::Stealth);
}
if (Skill::SpellID::Stealth == pSpell->GetSpellID() &&
true == IsSpellThisTargetType(Skill::SpellTarget::ENEMY_TARGET_ENCHANT))
{
ERRLOG1(g_Log, "CID:0x%08x 적대 스킬이 풀리지 않은 상태에서 스텔스를 걸려고 합니다.", m_pOwner->GetCID());
wError = CSpell::ENCHANT_FAIL_BY_ENEMY_ENCHANT;
return false;
}
if (m_cEnchantNum < MAX_ENCHANT)
{
/*
// edith 2008.10.16 오브 삭제시 로그 남김.
if(pSpell->GetSpellID() == Skill::SpellID::ExpOrb)
{
unsigned long time = pSpell->GetDurationSec();
if(m_pOwner)
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Add 경험치의 오브 생성 : Type:%d : %d Tick남음", m_pOwner->GetUID(), m_pOwner->GetCID(), pSpell->GetSpellType(), time);
else
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Add 경험치의 오브 생성 : Type:%d : %d Tick남음", 0, 0, pSpell->GetSpellType(), time);
}
else if(pSpell->GetSpellID() == Skill::SpellID::LuckyOrb)
{
unsigned long time = pSpell->GetDurationSec();
if(m_pOwner)
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Add 행운의 오브 생성 : Type:%d : %d Tick남음", m_pOwner->GetUID(), m_pOwner->GetCID(), pSpell->GetSpellType(), time);
else
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Add 행운의 오브 생성 : Type:%d : %d Tick남음", 0, 0, pSpell->GetSpellType(), time);
}
*/
m_pEnchant[m_cEnchantNum] = pSpell;
++m_cEnchantNum;
return true;
}
} break;
}
}
wError = CSpell::ENCHNAT_FAIL_ALREADY_AFFECTED;
return false;
}
bool CAffectedSpell::Remove(CSpell* pSpell)
{
CSpell** ppBegin = NULL;
CSpell** ppPastEnd = NULL;
switch (pSpell->GetSkillType())
{
case Skill::Type::CHANT:
{
for (ppBegin = m_pChant, ppPastEnd = m_pChant + m_cChantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
if (pSpell == *ppBegin)
{
std::copy(ppBegin + 1, ppPastEnd, ppBegin);
--m_cChantNum;
m_pChant[m_cChantNum] = 0;
return true;
}
}
} break;
case Skill::Type::ENCHANT:
{
for (ppBegin = m_pEnchant, ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
if (pSpell == *ppBegin)
{
// edith 2008.10.16 오브 삭제시 로그 남김.
if(pSpell->GetSpellType() == Skill::SpellType::PAYBUFF_SPELL)
{
unsigned long time = pSpell->GetDurationSec();
if(m_pOwner)
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Remove : Type:%d : %d Tick남음", m_pOwner->GetUID(), m_pOwner->GetCID(), pSpell->GetSpellType(), time);
else
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Remove : Type:%d : %d Tick남음", 0, 0, pSpell->GetSpellType(), time);
}
/*
// edith 2008.10.16 오브 삭제시 로그 남김.
if(pSpell->GetSpellID() == Skill::SpellID::ExpOrb)
{
unsigned long time = pSpell->GetDurationSec();
if(m_pOwner)
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Remove 경험치의 오브 삭제 : Type:%d : %d Tick남음", m_pOwner->GetUID(), m_pOwner->GetCID(), pSpell->GetSpellType(), time);
else
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Remove 경험치의 오브 삭제 : Type:%d : %d Tick남음", 0, 0, pSpell->GetSpellType(), time);
}
else if(pSpell->GetSpellID() == Skill::SpellID::LuckyOrb)
{
unsigned long time = pSpell->GetDurationSec();
if(m_pOwner)
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Remove 행운의 오브 삭제 : Type:%d : %d Tick남음", m_pOwner->GetUID(), m_pOwner->GetCID(), pSpell->GetSpellType(), time);
else
SERLOG4(g_SkillLog, "UID:%d/CID:0x%08x CAffectedSpell::Remove 행운의 오브 삭제 : Type:%d : %d Tick남음", 0, 0, pSpell->GetSpellType(), time);
}
*/
std::copy(ppBegin + 1, ppPastEnd, ppBegin);
--m_cEnchantNum;
m_pEnchant[m_cEnchantNum] = 0;
return true;
}
}
} break;
}
return false;
}
void CAffectedSpell::ClearChant()
{
CSpell** pprBegin = m_pChant + m_cChantNum - 1;
CSpell** pprPastEnd = m_pChant - 1;
for (;pprBegin != pprPastEnd; --pprBegin)
{
(*pprBegin)->RemoveAffected(m_pOwner);
}
}
void CAffectedSpell::ClearEnchant()
{
CSpell** pprBegin = m_pEnchant + m_cEnchantNum - 1;
CSpell** pprPastEnd = m_pEnchant - 1;
for (;pprBegin != pprPastEnd; --pprBegin)
{
(*pprBegin)->RemoveAffected(m_pOwner);
}
}
void CAffectedSpell::EnableChant(unsigned long dwOperateFlag)
{
CSpell** pprBegin = m_pChant + m_cChantNum - 1;
CSpell** pprPastEnd = m_pChant - 1;
for (;pprBegin != pprPastEnd; --pprBegin)
{
(*pprBegin)->Enable(dwOperateFlag);
}
}
void CAffectedSpell::EnableEnchant(unsigned long dwOperateFlag)
{
CSpell** pprBegin = m_pEnchant + m_cEnchantNum - 1;
CSpell** pprPastEnd = m_pEnchant - 1;
for (;pprBegin != pprPastEnd; --pprBegin)
{
(*pprBegin)->Enable(dwOperateFlag);
}
}
void CAffectedSpell::DisableChant(unsigned long dwOperateFlag)
{
CSpell** pprBegin = m_pChant + m_cChantNum - 1;
CSpell** pprPastEnd = m_pChant - 1;
for (;pprBegin != pprPastEnd; --pprBegin)
{
(*pprBegin)->Disable(dwOperateFlag);
}
}
void CAffectedSpell::DisableEnchant(unsigned long dwOperateFlag)
{
CSpell** pprBegin = m_pEnchant + m_cEnchantNum - 1;
CSpell** pprPastEnd = m_pEnchant - 1;
for (;pprBegin != pprPastEnd; --pprBegin)
{
(*pprBegin)->Disable(dwOperateFlag);
}
}
CSpell* CAffectedSpell::GetEnchant(int Index)
{
if(Index < 0)
return NULL;
if(Index >= MAX_ENCHANT)
return NULL;
return m_pEnchant[Index];
}
CSpell* CAffectedSpell::GetSpell(unsigned short usSpellID)
{
CSpell** ppBegin = NULL;
CSpell** ppPastEnd = NULL;
// 챈트
ppBegin = m_pChant;
ppPastEnd = m_pChant + m_cChantNum;
for (;ppBegin != ppPastEnd; ++ppBegin)
{
if ((*ppBegin)->GetSpellID() == usSpellID && true == (*ppBegin)->IsActivate(m_pOwner))
{
return *ppBegin;
}
}
// 인챈트
ppBegin = m_pEnchant;
ppPastEnd = m_pEnchant + m_cEnchantNum;
for (;ppBegin != ppPastEnd; ++ppBegin)
{
if ((*ppBegin)->GetSpellID() == usSpellID)
{
return *ppBegin;
}
}
return NULL;
}
SPELL CAffectedSpell::GetSpellInfo(BOOL bDead)
{
SPELL spell = SPELL::SPELL();
/*
CSpell** ppBegin = NULL;
CSpell** ppPastEnd = NULL;
// 인챈트
ppBegin = m_pEnchant;
ppPastEnd = m_pEnchant + MAX_ENCHANT;
unsigned char cIndex = 0;
for (;ppBegin != ppPastEnd; ++ppBegin)
{
switch((*ppBegin)->GetSpellType())
{
case Skill::SpellType::BUFF_SPELL:
if(bDead == TRUE)
continue;
// edith 2008.06.03 석상전 버프를 저장한다.
case Skill::SpellType::STATUE_SPELL:
case Skill::SpellType::PAYBUFF_SPELL:
case Skill::SpellType::ETERNAL_SPELL:
spell.Spells[cIndex] = SPELLINSTANCE((*ppBegin)->GetDurationSec(), (*ppBegin)->GetSpellID(), (*ppBegin)->GetSpellLevel(), (*ppBegin)->GetSpellType());
++cIndex;
break;
}
if(cIndex >= SPELL::MAX_SPELL_NUM)
break;
}
*/
unsigned char cIndex = 0;
for (int i = 0; i < MAX_ENCHANT; ++i)
{
if(!m_pEnchant[i])
continue;
switch(m_pEnchant[i]->GetSpellType())
{
case Skill::SpellType::BUFF_SPELL:
if(bDead == TRUE)
break;
// edith 2008.06.03 석상전 버프를 저장한다.
case Skill::SpellType::STATUE_SPELL:
case Skill::SpellType::PAYBUFF_SPELL:
case Skill::SpellType::ETERNAL_SPELL:
spell.Spells[cIndex] = SPELLINSTANCE(m_pEnchant[i]->GetDurationSec(), m_pEnchant[i]->GetSpellID(), m_pEnchant[i]->GetSpellLevel(), m_pEnchant[i]->GetSpellType());
++cIndex;
break;
}
if(cIndex >= SPELL::MAX_SPELL_NUM)
break;
}
return spell;
}
void CAffectedSpell::ApplyPartyChant(CAggresiveCreature* pAffected)
{
unsigned short wError = CSpell::NO_ENCHANT_ERROR;
for (CSpell** ppBegin = m_pChant, **ppPastEnd = m_pChant + m_cChantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
if ((*ppBegin)->IsPartySkill())
{
(*ppBegin)->AddAffected(pAffected, wError);
}
}
}
void CAffectedSpell::ApplyEnchant(CAggresiveCreature* pAffected)
{
unsigned short wError = CSpell::NO_ENCHANT_ERROR;
for (CSpell** ppBegin = m_pEnchant, **ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
(*ppBegin)->AddAffected(pAffected, wError);
}
}
bool CAffectedSpell::RemoveOverlappedSpell(CSpell* pSpell)
{
CSkillMgr& SkillMgr = CSkillMgr::GetInstance();
unsigned short usSkill_ID = pSpell->GetSpellID();
CSpell** ppBegin = NULL;
CSpell** ppPastEnd = NULL;
if (Skill::Type::ENCHANT == pSpell->GetSkillType())
{
ppBegin = m_pEnchant;
ppPastEnd = m_pEnchant + m_cEnchantNum;
}
else
{
return true;
}
for (; ppBegin != ppPastEnd; ++ppBegin)
{
CSpell* pOverlappedSpell = (*ppBegin);
if (NULL != pOverlappedSpell)
{
if (usSkill_ID == pOverlappedSpell->GetSpellID())
{
bool bRemoveAffected = false;
// 여기서 스킬을 비교한다. 스킬레벨이 더 높은걸로 돌게 비교
if (pSpell->GetSpellLevel() > pOverlappedSpell->GetSpellLevel())
{
bRemoveAffected = true;
}
else if (pSpell->GetSpellLevel() == pOverlappedSpell->GetSpellLevel())
{
if (pSpell->GetDurationSec() >= pOverlappedSpell->GetDurationSec())
{
bRemoveAffected = true;
}
}
if (true == bRemoveAffected)
{
pOverlappedSpell->ClearAll();
}
else
{
return false;
}
}
}
}
return true;
}
void CAffectedSpell::RemoveChantByCaster(CAggresiveCreature* pCaster)
{
for (CSpell** ppBegin = m_pChant, **ppPastEnd = m_pChant + m_cChantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
CSpell* lpSpell = (*ppBegin);
if (0 != lpSpell && pCaster == lpSpell->GetCaster())
{
lpSpell->RemoveAffected(m_pOwner);
}
}
}
void CAffectedSpell::RemoveEnchantByCaster(CAggresiveCreature* pCaster)
{
for (CSpell** ppBegin = m_pEnchant, **ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
CSpell* lpSpell = (*ppBegin);
if (0 != lpSpell && pCaster == lpSpell->GetCaster())
{
lpSpell->RemoveAffected(m_pOwner);
}
}
}
void CAffectedSpell::RemoveChantBySpellType(unsigned char cSpellType)
{
for (CSpell** ppBegin = m_pChant, **ppPastEnd = m_pChant + m_cChantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
CSpell* lpSpell = (*ppBegin);
if (0 != lpSpell && lpSpell->GetSpellID() == cSpellType)
{
lpSpell->RemoveAffected(m_pOwner);
}
}
}
bool CAffectedSpell::RemoveEnchantBySpellType(unsigned char cSpellType)
{
for (CSpell** ppBegin = m_pEnchant, **ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
CSpell* lpSpell = (*ppBegin);
if (0 != lpSpell && lpSpell->GetSpellID() == cSpellType)
{
lpSpell->RemoveAffected(m_pOwner);
return true;
}
}
return false;
}
bool CAffectedSpell::IsSpellOfEnemyCharacter(void)
{
for (CSpell** ppBegin = m_pEnchant, **ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
CAggresiveCreature* lpCaster = (*ppBegin)->GetCaster();
Creature::CreatureType eCreatureType = Creature::GetCreatureType(lpCaster->GetCID());
if (Creature::CT_MONSTER == eCreatureType ||
Creature::CT_SUMMON == eCreatureType ||
Creature::CT_STRUCT == eCreatureType)
{
continue;
}
if (EnemyCheck::EC_ENEMY == m_pOwner->IsEnemy(lpCaster))
{
return true;
}
}
return false;
}
bool CAffectedSpell::IsSpellThisTargetType(Skill::SpellTarget::Type eTargetType)
{
for (CSpell** ppBegin = m_pEnchant, **ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
if ((*ppBegin)->GetSpellTarget() == eTargetType)
{
return true;
}
}
return false;
}
// --------------------------------------------------------------------------------------------------
// CAffectedSpell::Disenchant
//
// 인자 : 주문 종류(마법형/물리형/월드웨폰/월드웨폰제외/모두), 타겟(긍정적/부정적/모두), 파괴 기준(레벨/시간/모두), 주문 레벨, 갯수
// 리턴 : 파괴한 인챈트 갯수
// --------------------------------------------------------------------------------------------------
unsigned char CAffectedSpell::Disenchant(Skill::SpellType::Type eSpellType, Skill::SpellTarget::Type eTargetType,
Skill::Disenchant::Type eDisenchantType, unsigned short usSkillLevel, unsigned char cNum)
{
CSpell *pTargetSpell = NULL;
unsigned short usMaxLevel = 0;
unsigned long dwMaxDuration = 0;
unsigned char cResult = 0;
if (Skill::Disenchant::INFINITE_NUM == cNum)
{
cNum = m_cEnchantNum;
}
for (unsigned char nIndex = 0; nIndex < cNum; nIndex++)
{
for (CSpell** ppBegin = m_pEnchant, **ppPastEnd = m_pEnchant + m_cEnchantNum;
ppBegin != ppPastEnd; ++ppBegin)
{
if (Skill::SpellTarget::ALL_ENCHANT != eTargetType && (*ppBegin)->GetSpellTarget() != eTargetType)
{
// 긍정적? 부정적?
continue;
}
// 죽었을때 없애는 인챈트이면
if (Skill::Disenchant::DEAD == eDisenchantType)
{
// 버프스펠보다 낮은거면 다 지워라
if(Skill::SpellType::BUFF_SPELL >= (*ppBegin)->GetSpellType())
{
pTargetSpell = *ppBegin;
break;
}
}
// edith 경험치의 오브이면 해제하면 안된다.
if((*ppBegin)->GetSpellID() == Skill::SpellID::ExpOrb)
continue;
if((*ppBegin)->GetSpellID() == Skill::SpellID::LuckyOrb)
continue;
// 유료면 디스펠에서 디스펠 되면 안된다.
if(Skill::SpellType::PAYBUFF_SPELL == (*ppBegin)->GetSpellType())
continue;
if (Skill::SpellType::NONE != eSpellType && (*ppBegin)->GetSpellType() != eSpellType)
{
// 마법형? 물리형?
continue;
}
else if (Skill::SpellType::ETERNAL_SPELL != eSpellType &&
Skill::SpellType::ETERNAL_SPELL == (*ppBegin)->GetSpellType())
{
// 영구적으로 유지되야 하는 스펠이면 제외
continue;
}
// 모두 파괴
if (Skill::Disenchant::NONE == eDisenchantType)
{
pTargetSpell = *ppBegin;
break;
}
switch (eDisenchantType)
{
// 일정 레벨보다 낮은 것들 중 가장 높은 레벨의 것을 파괴
case Skill::Disenchant::LEVEL:
{
if ((*ppBegin)->GetSpellLevel() < usSkillLevel && (*ppBegin)->GetSpellLevel() > usMaxLevel)
{
pTargetSpell = *ppBegin;
usMaxLevel = (*ppBegin)->GetSpellLevel();
}
} break;
// 가장 긴 시간이 남은 것을 파괴
case Skill::Disenchant::DURATION:
{
if (CSpell::INFINITE_DURATION != (*ppBegin)->GetDurationSec() && (*ppBegin)->GetDurationSec() > dwMaxDuration)
{
pTargetSpell = *ppBegin;
dwMaxDuration = (*ppBegin)->GetDurationSec();
}
} break;
}
}
if (NULL == pTargetSpell)
{
return cResult;
}
pTargetSpell->RemoveAffected(m_pOwner);
pTargetSpell = NULL;
++cResult;
}
return cResult;
}