/////////////////////////////////////////////////////////////////////////////////// // // Purpose : ¸ó½ºÅÍ Á¤º¸¸¦ ÀúÀå & °ü¸®Çϴ Ŭ·¡½º // /////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AwardTable.h" #include "Monster.h" #include "MonsterShout.h" LONG CMonster::ms_NormalBehaviorSendCount = 0; LONG CMonster::ms_AttackBehaviorSendCount = 0; LONG CMonster::ms_ReturnBehaviorSendCount = 0; LONG CMonster::ms_EscapeBehaviorSendCount = 0; LONG CMonster::ms_DeadBehaviorSendCount = 0; /////////////////////////////////////////////////////////////////////////////////// // Construction/Destruction /////////////////////////////////////////////////////////////////////////////////// CMonster::CMonster() : m_lpTarget(NULL), m_dwLastBehaviorTick(0), m_lCurrentFrame(0), m_bAttacking(false), m_nCurrentState(0), m_wSearchRange(0), m_nNormalMovingDelay(0), m_nLeaveMovingNum(0), m_bAvoid(false), m_bLongRangeAttacked(false), m_bAdminCmdSummon(false), m_bScout(false), m_nMovingPattern(0), m_OriginalPosition(), CAggresiveCreature(0), m_dwPID(0), m_wRespawnArea(0) { m_wDefaultSearchRange = MONSTER_SEARCH_RANGE; } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::CMonster // // Description : »ç¿ëÀÚ »ý¼ºÀÚ // // Inputs : MonsterCreate - ¸ó½ºÅÍ »ý¼º Á¤º¸ // // Outputs : None. // // Returns : None. /////////////////////////////////////////////////////////////////////////////////// CMonster::CMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon) : m_lpTarget(NULL), m_dwLastBehaviorTick(0), m_lCurrentFrame(0), m_bAttacking(false), m_nCurrentState(0), m_wSearchRange(0), m_nNormalMovingDelay(0), m_nLeaveMovingNum(0), m_bAvoid(false), m_bLongRangeAttacked(false), m_bAdminCmdSummon(bAdminCmdSummon), m_bScout(MonsterCreate.m_bScout), m_nMovingPattern(MonsterCreate.m_nMovingPattern), m_wRespawnArea(MonsterCreate.m_wRespawnArea), m_OriginalPosition(MonsterCreate.m_Pos), CAggresiveCreature(MonsterCreate.m_dwCID), m_dwPID(MonsterCreate.m_dwPID) { const CMonsterMgr::MonsterProtoType* pProtoType = CMonsterMgr::GetInstance().GetMonsterProtoType(MonsterCreate.m_nKID); if (NULL == pProtoType) { ERRLOG1(g_Log, "¾Ë¸ÂÀº ÇÁ·ÎÅäŸÀÔÀÌ ¾ø½À´Ï´Ù. MonsterProtoType.txt¸¦ È®ÀÎÇØÁֽʽÿÀ. KID:%d", MonsterCreate.m_nKID); return; } m_CreatureStatus = pProtoType->m_CreatureStatus; m_MonsterInfo = pProtoType->m_MonsterInfo; m_CreatureStatus.m_StatusInfo.CalculateSubStatus(); // °ÔÀÓÁß¿¡ æƮ È¿°ú °è»êÀ» À§ÇØ Á¸ÀçÇÕ´Ï´Ù. m_EquipStatus = pProtoType->m_CreatureStatus.m_StatusInfo; m_EquipStatus.m_cCalculateState = FightStatus::CS_EQUIP_INFO; m_wDefaultSearchRange = MONSTER_SEARCH_RANGE; } CMonster::~CMonster() { m_SpellMgr.GetAffectedInfo().ClearAll(); } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::InitMonster // // Description : ¸ó½ºÅÍ ÃʱâÈ­ // // Inputs : Pos - ¸ó½ºÅÍÀÇ À§Ä¡ // bDead - óÀ½ ·Î±ä ¶§´Â true, ¸®½ºÆù ½Ã¿£ false // // Outputs : None. // // Returns : ¼º°ø ¿©ºÎ. /////////////////////////////////////////////////////////////////////////////////// bool CMonster::InitMonster(Position &Pos, bool bDead) { m_CellPos.MoveTo(Pos); if (NULL == m_CellPos.m_lpCell) { ERRLOG4(g_Log, "CID:0x%08x ¹üÀ§¸¦ ¹þ¾î³­ ¼¿¿¡¼­ ¸ó½ºÅͰ¡ ·Î±äÇÏ¿´½À´Ï´Ù. X:%.1f, Y:%.1f, Z:%.1f", m_dwCID, Pos.m_fPointX, Pos.m_fPointY, Pos.m_fPointZ); return false; } m_CurrentPos = Pos; m_wSearchRange = m_wDefaultSearchRange; m_lpTarget = NULL; m_lCurrentFrame = 0; m_bAttacking = false; m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick(); ZeroMemory(&m_MotionInfo, sizeof(m_MotionInfo)); if (bDead) { m_nCurrentState = STATE_ID_DIE; m_CreatureStatus.m_nNowHP = 0; m_CreatureStatus.m_nNowMP = 0; } else { m_CellPos.m_lpCell->SetCreature(m_dwCID, this); m_nCurrentState = STATE_ID_NORMAL; m_CreatureStatus.m_nNowHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP; m_CreatureStatus.m_nNowMP = m_CreatureStatus.m_StatusInfo.m_nMaxMP; } return true; } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::GetMotion // // Description : ¸ð¼Ç Á¤º¸¸¦ ¾òÀ½ // // Inputs : MotionID - ¸ð¼Ç ID // // Outputs : Motion - ¸ð¼Ç Á¤º¸ // // Returns : bool - Á¤º¸°¡ ¾ø´Â ID¸é false. /////////////////////////////////////////////////////////////////////////////////// bool CMonster::GetMotion(unsigned long MotionID, MotionInfo &Motion) { int nIndex = 0; switch (MotionID) { case MonsterInfo::Z3D_CA_WALK: nIndex = 0; break; case MonsterInfo::Z3D_CA_RUN: nIndex = 1; break; case MonsterInfo::Z3D_CA_ATTACK: nIndex = 2; break; case MonsterInfo::Z3D_CA_CASTING: nIndex = 3; break; default: return false; } // ¹æÇâÀº º¹»çÇÏ¸é ¾È µÈ´Ù. Motion.m_wAction = m_MonsterInfo.m_MonsterMotions[nIndex].m_wAction; Motion.m_dwFrame = m_MonsterInfo.m_MonsterMotions[nIndex].m_dwFrame; Motion.m_fVelocity = m_MonsterInfo.m_MonsterMotions[nIndex].m_fVelocity; return true; } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::IsOverlap // // Description : ¸ó½ºÅͰ¡ °ãÃÄÁ® Àִ°¡? // // Inputs : None. // // Outputs : None. // // Returns : bool - À§ Áú¹®¿¡ ´ëÇÑ Yes/No /////////////////////////////////////////////////////////////////////////////////// bool CMonster::IsOverlap(void) { for (int nCellCount = 0; nCellCount < CCell::CONNECT_NUM; ++nCellCount) { CCell* pCell = m_CellPos.m_lpCell->GetConnectCell(nCellCount); if (NULL == pCell || false == pCell->IsMonster()) { continue; } CMonster* lpTempMonster = pCell->GetFirstMonster(); while (NULL != lpTempMonster) { if (this != lpTempMonster) { const float fDX = lpTempMonster->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX; const float fDZ = lpTempMonster->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ; if (fDX * fDX + fDZ * fDZ <= 1) { return true; } } lpTempMonster = pCell->GetNextMonster(); } } return false; } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::UpdateBehavior // // Description : ¸ó½ºÅÍÀÇ Çൿ ¼³Á¤ // // Inputs : dwTick - ÇöÀç Æ½Ä«¿îÆ® // // Outputs : None. // // Returns : None. /////////////////////////////////////////////////////////////////////////////////// void CMonster::UpdateBehavior(unsigned long dwTick) { // ³×ÀÓµå ¸ó½ºÅÍ´Â ½ºÅÏ/¼®È­¿¡ °É¸®Áö ¾Ê´Â´Ù. if (MonsterInfo::PATTERN_NAMED == m_MonsterInfo.m_cSkillPattern) { m_SpellMgr.GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::Stun); m_SpellMgr.GetAffectedInfo().RemoveEnchantBySpellType(Skill::SpellID::StoneForm); } // ½ºÅÏ/¼®È­½Ã¿£ ¾Æ¹« Çൿµµ ÇÏÁö ¾Ê´Â´Ù. if (GetEnchantInfo().GetFlag(Skill::SpellID::Stun) || GetEnchantInfo().GetFlag(Skill::SpellID::StoneForm)) { m_lCurrentFrame = FPS; return; } // »óÅ¿¡ µû¸¥ ¸ó½ºÅÍ Çൿ 󸮸¦ ÇÑ´Ù. switch (m_nCurrentState) { case STATE_ID_NORMAL: NormalBehavior(dwTick); break; // º¸Åë »óÅ case STATE_ID_ATTACK: AttackBehavior(dwTick); break; // °ø°Ý »óÅ (°Å¸®°¡ ¸Ö¾îÁö¸é ÂѾư¡°í...) case STATE_ID_RETURN: ReturnBehavior(dwTick); break; // ¸®ÅÏ »óÅ (Á¦ À§Ä¡·Î µ¹¾Æ°¡´Â...) case STATE_ID_ESCAPE: EscapeBehavior(); break; // µµ¸Á »óÅ case STATE_ID_DIE: DeadBehavior(dwTick); break; // Á×Àº »óÅ (¸®½ºÆù ½Ã°£ÀÌ Áö³ª¸é ¸®½ºÆù ÇÏÁö~) } /* // ¸ó½ºÅÍ Á¤º¸ ·Î±× char logString[MAX_PATH]; switch (m_nCurrentState) { case STATE_ID_NORMAL: strcpy(logString, "Normal"); break; case STATE_ID_ATTACK: strcpy(logString, "Attack"); break; case STATE_ID_RETURN: strcpy(logString, "Return"); break; case STATE_ID_ESCAPE: strcpy(logString, "Escape"); break; case STATE_ID_DIE: strcpy(logString, "Die"); break; } DETLOG3(g_Log, "¸ó½ºÅÍ Á¤º¸ ·Î±× - * State : %s\t* fX : %f\t* fZ : %f", logString, GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ); */ } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::Process // // Description : ¸ó½ºÅÍ ÇÁ·Î¼¼½Ì // // Inputs : None. // // Outputs : None. // // Returns : bool - ¸ð¼Ç Ãâ·Â Áß¿£ false /////////////////////////////////////////////////////////////////////////////////// bool CMonster::Process() { const unsigned long dwTick = CPulse::GetInstance().GetLastTick(); if (dwTick - m_dwLastBehaviorTick < FPS) { return false; } if ((GetSerialNumber() % 3) != (CPulse::GetInstance().GetCurrentPulse() % 3)) { return false; } const unsigned long dwFrame = ((dwTick - m_dwLastBehaviorTick) / FPS); // °ø°Ý ¸ð¼ÇÀº µû·Î ó¸®ÇÕ´Ï´Ù. (À̵¿Çϸ鼭 °ø°Ý °¡´É) if (m_lCurrentFrame <= 0 || m_bAttacking == true) { if (m_lCurrentFrame <= 0 && m_bAttacking == true) { m_bAttacking = false; } UpdateBehavior(dwTick); } // ƽ °»½Å m_dwLastBehaviorTick = dwTick; unsigned long dwSlowlyRate = (true == GetEnchantInfo().GetFlag(Skill::SpellID::Frozen)) ? 2 : 1; m_lCurrentFrame -= (dwFrame / dwSlowlyRate); return true; } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::MultiAttack // // Description : ¸ó½ºÅÍ ¹üÀ§ °ø°Ý ½Ã ¹üÀ§ ³» Ÿ°ÙÀ» °Ë»ö // // Inputs : None. // // Outputs : None. // // Returns : None. /////////////////////////////////////////////////////////////////////////////////// bool CMonster::MultiAttack(void) { unsigned char cDefenderNum = 1; CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM]; unsigned char nDefenserJudges[AtNode::MAX_DEFENDER_NUM]; ppAggresiveCreature[0] = m_lpTarget; // TODO : °ø°ÝÇÏ´Â ¹æÇâÀ» ¼³Á¤ÇսôÙ. (ÇöÀç´Â ¹«Á¶°Ç Á¤¸é) short nMaxDefenderNum = AtNode::MAX_DEFENDER_NUM; std::fill_n(&nDefenserJudges[0], nMaxDefenderNum, ClientConstants::Judge_Front); float fDir = CalcDir2D(GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ, m_lpTarget->GetCurrentPos().m_fPointX, m_lpTarget->GetCurrentPos().m_fPointZ); AtType attackType; attackType.m_wType = AtType::RIGHT_MELEE; char cTargetType = Skill::Target::ENEMY; return CAggresiveCreature::MultiAttack(attackType, cDefenderNum, ppAggresiveCreature, nDefenserJudges, GetCurrentPos(), fDir, m_MonsterInfo.m_wAttackRange / 100.0f, m_MonsterInfo.m_fAttackAngle, cTargetType); } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::CancelTarget // // Description : Ÿ°ÙÀÇ Ãë¼Ò // // Inputs : bool bSaveThreat - ¸ÂÀº ¾²·¹Æ® °ªÀ» À¯ÁöÇϴ°¡? // // Outputs : None. // // Returns : None. /////////////////////////////////////////////////////////////////////////////////// void CMonster::CancelTarget(bool bSaveThreat) { if (NULL != m_lpTarget) { if (!bSaveThreat) { m_lpTarget->GetThreat().DeleteThreatened(this); m_Threat.DeleteThreat(m_lpTarget); } // ÆÄƼ°¡ ÀÖ¾ú´Ù¸é.. ÆÄƼÀÇ Å¸°Ù¿¡¼­ Á¦°ÅÇÑ´Ù. CMonsterParty* lpParty = reinterpret_cast(GetParty()); if (NULL != lpParty) { CMonsterParty::PartyTargetSet::iterator itr = lpParty->GetPartyTargetSet().find(m_lpTarget->GetCID()); if (itr != lpParty->GetPartyTargetSet().end()) { lpParty->GetPartyTargetSet().erase(itr); } } } m_lpTarget = NULL; m_bAttacking = false; m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_LEAVE_PLAYER); } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::Dead // // Description : ¸ó½ºÅÍÀÇ »ç¸Á ó¸® // // Inputs : None. // // Outputs : None. // // Returns : ¼º°ø ¿©ºÎ. (false¸é Ÿ°Ùµµ ¾ø´Â ÁÖÁ¦¿¡ Á×Àº °æ¿ì) /////////////////////////////////////////////////////////////////////////////////// bool CMonster::Dead(CAggresiveCreature* pOffencer) { m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick(); m_lCurrentFrame = FPS; m_bAttacking = false; m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP); // ¾î¿öµå ó¸® unsigned long aryItemID[AwardTable::MAX_DROP_ITEM + EliteBonus::MAX_BONUS_DROP_ITEM] = { 0, }; Item::CItem* aryItem[AwardTable::MAX_DROP_ITEM + EliteBonus::MAX_BONUS_DROP_ITEM] = { 0, }; unsigned long dwOwnerID = 0; unsigned char cItemNum = m_Threat.GetAward(aryItemID, aryItem, &dwOwnerID); Position SetPosition; CCell::ItemInfo itemInfo; CCell* lpDropCell = GetCellPos().m_lpCell; if (NULL != lpDropCell) { for (unsigned char cDropIndex = 0; cDropIndex < cItemNum; ++cDropIndex) { if (0 != aryItemID[cDropIndex]) { SetPosition = GetCurrentPos(); SetPosition.m_fPointX += cDropIndex; if (cItemNum / 2 <= cDropIndex) { SetPosition.m_fPointX -= cItemNum / 2; SetPosition.m_fPointZ += 1.0f; } unsigned long dwGold = (NULL == aryItem[cDropIndex]) ? (aryItemID[cDropIndex] & ~CCell::TYPE_CHECK_BIT) : 0; if(pOffencer->GetStatus().m_nLevel<=GOLD_INC_LEVEL_LIMIT) { dwGold = static_cast( dwGold * 1.5f ); } lpDropCell->SetItem(SetPosition, aryItem[cDropIndex], dwGold, dwOwnerID, CCell::PARTY, itemInfo); } } } // °æÇèÄ¡¸¦ ºÐ¹èÇÑ´Ù. const long lMaxThreat = m_Threat.GetMaxThreatAmount(); m_Threat.DivisionExp(); // ¼¿¿¡¼­ ¸ó½ºÅ͸¦ ÀÏ´Ü Á¦°ÅÇÑ´Ù. m_CellPos.m_lpCell->DeleteCreature(m_dwCID); CAggresiveCreature *pCreature = m_Threat.GetMaxThreatCreature(); if (NULL != pCreature) { if (m_CreatureStatus.m_nLevel - pCreature->GetStatus().m_nLevel >= 3 && lMaxThreat > m_CreatureStatus.m_StatusInfo.m_nMaxHP * 0.8f) { // ·¹º§Â÷°¡ 3 ÀÌ»ó ³ª´Â ¸ó½ºÅ͸¦ Àâ¾ÒÀ» °æ¿ì ·Î±×¸¦ ÂïÀ½ RULLOG5(g_Log, "¸ó½ºÅͲ²¼­ µ¹¾Æ°¡¼Ì½À´Ï´Ù... Monster : 0x%08x (%d), Player : 0x%08x (%d), ȹµæ °æÇèÄ¡ ºñÀ² : %.1f%%", m_dwCID, m_CreatureStatus.m_nLevel, m_Threat.GetMaxThreatCreature()->GetCID(), m_Threat.GetMaxThreatCreature()->GetStatus().m_nLevel, lMaxThreat * 100.0f / m_CreatureStatus.m_StatusInfo.m_nMaxHP); } CCharacter* lpOffencerCharacter = (Creature::CT_PC == Creature::GetCreatureType(pCreature->GetCID())) ? static_cast(pCreature) : 0; if (0 != lpOffencerCharacter) { // Äù½ºÆ® Æ®¸®°Å ¹ßµ¿ lpOffencerCharacter->CheckTrigger(Quest::TRIGGER_KILL, m_MonsterInfo.m_dwKID, m_CurrentPos, 1); // ¸ó½ºÅÍ »ìÇØ ·Î±×¸¦ ³²±ä´Ù. ³²±â´Â Ç׸ñÀº ¸ó½ºÅÍ ·¹º§/¶³±º ¾ÆÀÌÅÛID ¹× µî±Þ, °³¼öµîÀÌ´Ù GAMELOG::LogMonsterDead(*lpOffencerCharacter, GetCID(), m_CreatureStatus.m_nLevel, aryItemID, cItemNum); } } // ÆÄƼ¿¡¼­ Á¦°Å CParty* lpParty = GetParty(); if (NULL != lpParty) { lpParty->Leave(GetCID(), 0, GetMapIndex()); } return CAggresiveCreature::Dead(pOffencer); } EnemyCheck::EnemyType CMonster::IsEnemy(CCreature* lpTarget, unsigned char* cResult) { if (NULL != lpTarget) { switch (Creature::GetCreatureType(lpTarget->GetCID())) { case Creature::CT_PC: case Creature::CT_SUMMON: case Creature::CT_SIEGE_OBJECT: { return lpTarget->IsEnemy(this); } case Creature::CT_NPC: case Creature::CT_MONSTER: case Creature::CT_STRUCT: { if (GetNation() == lpTarget->GetNation()) { return EnemyCheck::EC_FRIEND; } return EnemyCheck::EC_ENEMY; } } } ERRLOG1(g_Log, "CID:0x%08x ÇǾƽĺ°ÇÒ Å¸°ÙÀÌ ¾ø½À´Ï´Ù.", m_dwCID); return EnemyCheck::EC_NEUTRAL; } void CMonster::Respawn(unsigned long dwTick) { Position RespawnPos(m_OriginalPosition.m_fPointX, m_OriginalPosition.m_fPointY, m_OriginalPosition.m_fPointZ); if (m_nMovingPattern != FIXED && m_wRespawnArea > 0) { RespawnPos.m_fPointX += static_cast(Math::Random::SimpleRandom(dwTick, m_wRespawnArea*2) - m_wRespawnArea); RespawnPos.m_fPointY += m_OriginalPosition.m_fPointY; RespawnPos.m_fPointZ += static_cast(Math::Random::SimpleRandom(dwTick, m_wRespawnArea*2) - m_wRespawnArea); } InitMonster(RespawnPos); m_nCurrentState = STATE_ID_NORMAL; // ÆÄƼ¿¡ Ãß°¡ CParty* lpParty = CPartyMgr::GetInstance().GetParty(GetPID()); if (NULL != lpParty) { SetParty(lpParty); lpParty->Join(GetCID(), 0, NULL, GetMapIndex()); } } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::SendMove // // Description : ¸ó½ºÅÍ À̵¿ ÆÐŶ º¸³»±â // // Inputs : None. // // Outputs : Send¸¦ º¸³½ Ƚ¼ö. // // Returns : None. /////////////////////////////////////////////////////////////////////////////////// LONG CMonster::SendMove(unsigned short nAniNum) { // ±æµå ¿ä»õ¿Í »ó¡¹°Àº MonMove ¸¦ º¸³»Áö ¾Ê´Â´Ù. (°ø°Ý½Ã ¹æÇâ ÀüȯÀ» ÇÏÁö¾Êµµ·Ï...) if (Creature::CT_SIEGE_OBJECT == Creature::GetCreatureType(m_dwCID)) { CSiegeObject* lpSiegeObject = reinterpret_cast(this); if (lpSiegeObject && (lpSiegeObject->IsCamp() || lpSiegeObject->IsEmblem())) { return 0; } } LONG nSendCount = 0; if (true == GetEnchantInfo().GetFlag(Skill::SpellID::Hold) || true == GetEnchantInfo().GetFlag(Skill::SpellID::Stun) || true == GetEnchantInfo().GetFlag(Skill::SpellID::StoneForm)) { m_MotionInfo.m_fVelocity = 0; } if (NULL == m_CellPos.m_lpCell) { ERRLOG0(g_Log, "¸ó½ºÅÍ À̵¿ ÆÐŶ º¸³»±â ½ÇÆÐ : ¸ó½ºÅͰ¡ ¼¿À» ¹þ¾î³ª ÀÖ½À´Ï´Ù."); return nSendCount; } PktMM pktMM; memset(&pktMM, 0, sizeof(PktMM)); pktMM.m_dwMonID = m_dwCID; pktMM.m_NetworkPos = CNetworkPos(m_CurrentPos.m_fPointX, m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ, m_MotionInfo.m_fDirection, (0 == m_MotionInfo.m_dwFrame) ? 0.0f : m_MotionInfo.m_fVelocity / m_MotionInfo.m_dwFrame); pktMM.m_cAct = static_cast(m_MotionInfo.m_wAction); pktMM.m_cAniNum = static_cast(nAniNum); // BroadCasting ÇÑ ¹øÀ» ±âÁØÀ¸·Î Ä«¿îÆ® if (0 != m_CellPos.m_lpCell) { ++nSendCount; m_CellPos.m_lpCell->SendAllNearCellCharacter(&pktMM, sizeof(PktMM), CmdMonMove); } else { ERRLOG4(g_Log, "CID:0x%08x ÀÌ»óÇÑ À§Ä¡¿¡ ¸ó½ºÅͰ¡ ÀÖ½À´Ï´Ù. (%f,%f,%f)", m_dwCID, m_CurrentPos.m_fPointX, m_CurrentPos.m_fPointY, m_CurrentPos.m_fPointZ); } m_nLeaveMovingNum = nAniNum; return nSendCount; } const int CMonster::CalculateFixLevelGap(CAggresiveCreature *pDefender) { // << °íÁ¤ ·¹º§ °¸ ÀåÄ¡ >> // - ij¸¯ÅÍ¿Í ¸ó½ºÅÍÀÇ ·¹º§¿¡ »ó°ü¾øÀÌ ½ºÅ©¸³Æ®¿¡ Á¤ÀÇµÈ ¼öÄ¡¸¸Å­ ¸ó½ºÅÍÀÇ ·¹º§ÀÌ ³ôÀº °É·Î Ãë±ÞÇÑ´Ù. // - º¸½º¸÷ µî¿¡ ÀÌ¿ëÇÑ´Ù. if (true == m_MonsterInfo.m_bFixLevelGap) { const unsigned char cFixLevelGap = m_CreatureStatus.m_nLevel - m_MonsterInfo.m_cFixLevelGap; if (cFixLevelGap < pDefender->GetStatus().m_nLevel) { return m_MonsterInfo.m_cFixLevelGap; } } return CAggresiveCreature::CalculateLevelGap(pDefender); } /////////////////////////////////////////////////////////////////////////////////// // Function : CMonster::Attack // // Description : ¸ó½ºÅÍ °ø°Ý ÆÐŶ º¸³»±â // // Inputs : pDefender - ¹æ¾îÀÚÀÇ Æ÷ÀÎÅÍ // // Outputs : None. // // Returns : None. /////////////////////////////////////////////////////////////////////////////////// bool CMonster::Attack(AtType attackType, unsigned char cDefenderNum, CAggresiveCreature** ppDefenders, unsigned char* cDefenderJudges, unsigned short* wDefenderMPHeal) { if (m_CreatureStatus.m_nNowHP == 0) { ERRLOG1(g_Log, "CID:0x%08x Á×Àº ¸ó½ºÅͰ¡ °ø°ÝÇÏ·Á°í ÇÏ¿´½À´Ï´Ù.", m_dwCID); return false; } if (cDefenderNum > AtNode::MAX_DEFENDER_NUM) { ERRLOG2(g_Log, "CID:0x%08x ¸ó½ºÅͰ¡ °ø°ÝÇÒ ¶§, ¹æ¾îÀÚÀÇ ¼ýÀÚ°¡ ÃÖ´ë ¹æ¾îÀÚ ¼ýÀÚ¸¦ ³Ñ¾ú½À´Ï´Ù. ¹æ¾îÀÚ¼ö : %d", m_dwCID, cDefenderNum); cDefenderNum = AtNode::MAX_DEFENDER_NUM; } // MON_TODO : by Vincent - 2004 : 2 : 25 DefenserNode Defenser[AtNode::MAX_DEFENDER_NUM] = {0, }; int nDefenserCount = 0; // ------------ for (unsigned char cDefender = 0; cDefender < cDefenderNum; ++cDefender) { if (NULL == ppDefenders[cDefender]) { continue; } CCharacter* lpCharacter = NULL; CMonster* lpSummonee = NULL; // ŸÄÏÀÌ Ä³¸¯ÅÍÀÎ °æ¿ì if (Creature::CT_PC == Creature::GetCreatureType(ppDefenders[cDefender]->GetCID())) { lpCharacter = reinterpret_cast(ppDefenders[cDefender]); lpSummonee = lpCharacter->GetSummonee(); } else { // Ÿ°ÙÀÌ ¼Òȯ¼öÀÎ °æ¿ì if (Creature::IsSummonMonster(ppDefenders[cDefender]->GetCID())) { lpCharacter = reinterpret_cast(ppDefenders[cDefender])->GetMaster(); } } if (NULL == lpCharacter) { continue; } unsigned char cOffencerJudge = 0; unsigned short wOffencerMPHeal = 0; unsigned short wError = PktBase::NO_SERVER_ERR; const unsigned short wDamage = ppDefenders[cDefender]->ApplyDamage(attackType, this, cOffencerJudge, cDefenderJudges[cDefender], wOffencerMPHeal, wDefenderMPHeal[cDefender], wError); if (NULL != lpSummonee) { lpSummonee->GuardMe(this, wDamage); } lpCharacter->CalculateEquipDurability((ClientConstants::Judge_Guard == cDefenderJudges[cDefender]) ? AtType::GUARD : AtType::DEFENCE); CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher(); if (NULL != lpDispatch) { GameClientSendPacket::SendCharAttacked(lpDispatch->GetSendStream(), this, ppDefenders[cDefender], attackType, m_MotionInfo.m_fDirection, wDamage, cDefenderJudges[cDefender], wDefenderMPHeal[cDefender], wError); } Defenser[nDefenserCount].m_cJudge = cDefenderJudges[nDefenserCount]; Defenser[nDefenserCount].m_dwCharID = ppDefenders[nDefenserCount]->GetCID(); Defenser[nDefenserCount].m_wMaxHP = ppDefenders[nDefenserCount]->GetStatus().m_StatusInfo.m_nMaxHP; Defenser[nDefenserCount].m_wMaxMP = ppDefenders[nDefenserCount]->GetStatus().m_StatusInfo.m_nMaxMP; Defenser[nDefenserCount].m_sCurrHP = ppDefenders[nDefenserCount]->GetStatus().m_nNowHP; Defenser[nDefenserCount].m_sCurrMP = ppDefenders[nDefenserCount]->GetStatus().m_nNowMP; Defenser[nDefenserCount].m_wMPHeal = wDefenderMPHeal[nDefenserCount]; Defenser[nDefenserCount].m_wDamage = wDamage; ++nDefenserCount; } CCell* lpCell = m_CellPos.m_lpCell; if (NULL != lpCell) { lpCell->SendAttackInfo(GetCID(), attackType, static_cast(nDefenserCount), &Defenser[0]); } return true; } bool CMonster::HasSkill() { for (int i=0; iGetCID())) { szCharacterName = static_cast(pOffencer)->GetCharacterName(); } // ¿ÜÄ¡±â °Ë»öÇØ¼­ ¿Üħ. CMonsterShout::GetInstance().Shout(m_dwCID, m_MonsterInfo.m_dwKID, static_cast(m_CurrentPos.m_fPointX), static_cast(m_CurrentPos.m_fPointZ), eBehavior, szCharacterName, usShoutSkill_ID); return usDamage; }