void ZActor::CheckDead(float fDelta) { if (!CheckFlag(AF_MY_CONTROL)) return; if (!CheckFlag(AF_DEAD)) { if(m_pModule_HPAP->GetHP()<=0) { SetDeadTime(ZGetGame()->GetTime()); m_Animation.Input(ZA_EVENT_DEATH); SetFlag(AF_DEAD, true); MUID uidKiller = this->GetLastAttacker(); ZPostQuestPeerNPCDead(uidKiller, GetUID()); m_TaskManager.Clear(); } } else { if (!CheckFlag(AF_REQUESTED_DEAD)) { if (ZGetGame()->GetTime()-GetDeadTime() > GetNPCInfo()->fDyingTime) { MUID uidAttacker = GetLastAttacker(); if (uidAttacker == MUID(0, 0)) { uidAttacker = ZGetGameClient()->GetPlayerUID(); } ZPostQuestRequestNPCDead(uidAttacker, GetUID(), GetPosition()); SetFlag(AF_REQUESTED_DEAD, true); } } } }
void ZSurvival::LoadNPCMeshes() { #ifdef _DEBUG // 혼자서 AI 테스트할 경우 if ((ZApplication::GetInstance()->GetLaunchMode() == ZApplication::ZLAUNCH_MODE_STANDALONE_QUEST) || (ZApplication::GetInstance()->GetLaunchMode() == ZApplication::ZLAUNCH_MODE_STANDALONE_AI)) { ZGetNpcMeshMgr()->LoadAll(); ZGetNpcMeshMgr()->ReloadAllAnimation();// 읽지 않은 에니메이션이 있다면 로딩 return; } #endif if (!m_GameInfo.IsInited()) { mlog("not inialized Quest Game Info\n"); _ASSERT(0); return; } for (int i = 0; i < m_GameInfo.GetNPCInfoCount(); i++) { MQUEST_NPC npc = m_GameInfo.GetNPCInfo(i); ZGetNpcMeshMgr()->Load(GetNPCInfo(npc)->szMeshName); } ZGetNpcMeshMgr()->ReloadAllAnimation();// 읽지 않은 에니메이션이 있다면 로딩 }
bool ZSurvival::OnNPCSpawn(MCommand* pCommand) { if (ZGetGame() == NULL) return false; MUID uidChar, uidNPC; unsigned char nNPCType, nPositionIndex; pCommand->GetParameter(&uidChar, 0, MPT_UID); pCommand->GetParameter(&uidNPC, 1, MPT_UID); pCommand->GetParameter(&nNPCType, 2, MPT_UCHAR); pCommand->GetParameter(&nPositionIndex, 3, MPT_UCHAR); MQUEST_NPC NPCType = MQUEST_NPC(nNPCType); ZMapSpawnType nSpawnType = ZMST_NPC_MELEE; ZMapSpawnManager* pMSM = ZGetGame()->GetMapDesc()->GetSpawnManager(); MQuestNPCInfo* pNPCInfo = GetNPCInfo(NPCType); if (pNPCInfo == NULL) return false; switch (pNPCInfo->GetSpawnType()) { case MNST_MELEE: nSpawnType = ZMST_NPC_MELEE; break; case MNST_RANGE: nSpawnType = ZMST_NPC_RANGE; break; case MNST_BOSS: nSpawnType = ZMST_NPC_BOSS; break; default: _ASSERT(0); }; ZMapSpawnData* pSpawnData = pMSM->GetSpawnData(nSpawnType, nPositionIndex); if (pSpawnData == NULL) { // 보스 스폰지점이 없는 맵을 서바이벌맵으로 사용한 경우 그냥 밀리용 지점을 사용하자 if (nSpawnType == ZMST_NPC_BOSS) pSpawnData = pMSM->GetSpawnData(ZMST_NPC_MELEE, nPositionIndex); } rvector NPCPos = rvector(0,0,0); rvector NPCDir = rvector(1,0,0); if (pSpawnData) { NPCPos = pSpawnData->m_Pos; NPCDir = pSpawnData->m_Dir; } // 만약 리소스 로딩을 안했으면 로드 - 이럴일은 테스트빼곤 없어야한다. // if (ZIsLaunchDevelop()) { RMesh* pNPCMesh = ZGetNpcMeshMgr()->Get(pNPCInfo->szMeshName); if (pNPCMesh) { if (!pNPCMesh->m_isMeshLoaded) { ZGetNpcMeshMgr()->Load(pNPCInfo->szMeshName); ZGetNpcMeshMgr()->ReloadAllAnimation(); } } } float fTC = m_GameInfo.GetNPC_TC(); ////////// 하드코딩.. 특정 맵에서 npc 충돌 반지름을 플레이어의 반지름으로 강제한다 ////// bool bForceCollRadius35 = false; if (m_GameInfo.GetMapSectorID(m_GameInfo.GetCurrSectorIndex()) == 207) // Prison_hall2 bForceCollRadius35 = true; ////////////////////////////////////////////////////////////////////////////////////////// ZActor* pNewActor = ZActor::CreateActor(NPCType, fTC, m_GameInfo.GetQuestLevel(), bForceCollRadius35); if (pNewActor) { bool bMyControl = (uidChar == ZGetGameClient()->GetPlayerUID()); pNewActor->SetMyControl(bMyControl); pNewActor->SetUID(uidNPC); pNewActor->SetPosition(NPCPos); pNewActor->SetDirection(NPCDir); ZCharacter *pOwner = ZGetCharacterManager()->Find(uidChar); if(pOwner) pNewActor->SetOwner(pOwner->GetUserName()); if(pNewActor->m_pVMesh) { D3DCOLORVALUE color; color.r = pNPCInfo->vColor.x; color.g = pNPCInfo->vColor.y; color.b = pNPCInfo->vColor.z; color.a = 1.f; pNewActor->m_pVMesh->SetNPCBlendColor(color);//색을 지정한 경우.. } ZGetObjectManager()->Add(pNewActor); ZGetEffectManager()->AddReBirthEffect(NPCPos); if ((pNPCInfo->nGrade == NPC_GRADE_BOSS) || (pNPCInfo->nGrade == NPC_GRADE_LEGENDARY)) { // 만약 보스급 NPC가 스폰하면 자동적으로 boss 등록 m_GameInfo.GetBosses().push_back(uidNPC); // 바운딩볼륨이 큰 보스가 벽 근처에서 스폰될때 바운딩이 벽에 일부 묻힌 채로 스폰될 수가 있다 // 이렇게 되면 간혹 벽 뒤로 이동해버리는 경우가 발생 -> 보이지 않으므로 보스를 죽일 수 없다 -> 게임 진행 막힘 // 보스형 몬스터에 한해서 스폰할때 벽과 충돌체크를 해서 초기위치를 조정한다 if (pNewActor->IsMyControl()) { float radius = bForceCollRadius35 ? 35.f : pNewActor->GetCollRadius(); if (true == ZGetGame()->GetWorld()->GetBsp()->CheckSolid(NPCPos, radius)) { // 맵에 충돌한다면. OutputDebugString("보스몹 스폰지점 충돌검사 실패...\n"); // 가까운 네비게이션 노드의 중앙으로 옮겨버린다 RNavigationMesh* pNavMesh = ZGetGame()->GetWorld()->GetBsp()->GetNavigationMesh(); if (pNavMesh) { RNavigationNode* pNavNode = pNavMesh->FindClosestNode(NPCPos); if (pNavNode) { pNewActor->SetPosition( pNavNode->CenterVertex()); OutputDebugString("스폰위치조정됨!\n"); } } } } } } return true; }
void ZActor::OnDamaged(ZObject* pAttacker, rvector srcPos, ZDAMAGETYPE damageType, MMatchWeaponType weaponType, float fDamage, float fPiercingRatio, int nMeleeType) { if (!CheckFlag(AF_SOUND_WOUNDED)) { bool bMyKill = false; if (pAttacker) { bMyKill = (pAttacker == g_pGame->m_pMyCharacter); } rvector pos_sound = GetPosition(); pos_sound.z += m_Collision.fHeight - 10.0f; ZApplication::GetSoundEngine()->PlayNPCSound(m_pNPCInfo->nID, NPC_SOUND_WOUND, pos_sound, bMyKill); SetFlag(AF_SOUND_WOUNDED, true); } if ((m_pNPCInfo->nGrade == NPC_GRADE_BOSS) || (m_pNPCInfo->nGrade == NPC_GRADE_LEGENDARY)) { if (ZGetQuest()->GetGameInfo()->GetBoss() == GetUID()) { ZGetScreenEffectManager()->ShockBossGauge(fDamage); } } if ((damageType == ZD_BULLET) || (damageType == ZD_BULLET_HEADSHOT)) { m_nDamageCount++; } if(CheckFlag(AF_MY_CONTROL)) { bool bSkipDamagedAnimation = false; if(m_pNPCInfo->bNeverAttackCancel ) { bSkipDamagedAnimation = ZActorAnimation::IsSkippableDamagedAnimation(GetCurrAni()); } if(bSkipDamagedAnimation==false) { if((damageType==ZD_MELEE) || (damageType==ZD_KATANA_SPLASH)) { ZCharacterObject* pCObj = MDynamicCast(ZCharacterObject, pAttacker); bool bLightningDamage = false; if(pCObj) { ZC_ENCHANT etype = pCObj->GetEnchantType(); if( etype == ZC_ENCHANT_LIGHTNING ) bLightningDamage = true; } if(bLightningDamage && (damageType==ZD_KATANA_SPLASH)) { m_Animation.Input(ZA_EVENT_LIGHTNING_DAMAGED); } else { if(nMeleeType%2) m_Animation.Input(ZA_EVENT_MELEE_DAMAGED1); else m_Animation.Input(ZA_EVENT_MELEE_DAMAGED2); } SetVelocity(0,0,0); } else { if( GetNPCInfo()->bNeverPushed == false) { if (m_nDamageCount >= 5) { m_Animation.Input(ZA_EVENT_RANGE_DAMAGED); m_nDamageCount = 0; } } } } } ZObject::OnDamaged(pAttacker,srcPos,damageType,weaponType,fDamage,fPiercingRatio,nMeleeType); }