//---------------------------------------------------------------------------- // // ROUTINE: CAIHumanStateResurrecting::Init() // // PURPOSE: // //---------------------------------------------------------------------------- /*virtual*/ LTBOOL CAIHumanStateResurrecting::Init(CAIHuman* pAIHuman) { if ( !CAIHumanState::Init(pAIHuman) ) { return LTFALSE; } // Set the time tracking vars to invalid times m_fResurrectCompleteTime = -1; m_fResurrectCompleteDuration = -1; GetAI()->SetBlinking(LTFALSE); m_eStateStatus = kSStat_Resurrecting; CDestructible* pDestructable = GetAI()->GetDestructible(); m_bEntryCanDistruct = pDestructable->GetNeverDestroy(); pDestructable->SetNeverDestroy( LTFALSE ); pDestructable->SetHitPoints( pDestructable->GetMaxHitPoints() ); // Ensure that node tracking is disabled. m_pAIHuman->DisableNodeTracking(); return LTTRUE; }
bool CAISensorDamageFX::StimulateSensor( CAIStimulusRecord* pStimulusRecord ) { // Intentionally do not call super::StimulateSensor. // Just monitor the AI's health and create FX. Do not affect WorkingMemory. // Bail if the wrong type of stimulus. if( !( m_pSensorRecord->dwStimulusTypes & pStimulusRecord->m_eStimulusType ) ) { return false; } // Sanity check. CDestructible* pDestructible = m_pAI->GetDestructible(); if( ( m_fFullHealth <= 0.f ) || ( !pDestructible ) ) { return false; } // Create damage FX based on the percentage of remaining health. float fHealthPercent = pDestructible->GetHitPoints() / m_fFullHealth; switch( m_eDamageFXState ) { case kDamageFX_None: if( fHealthPercent < 0.75f ) { m_eDamageFXState = kDamageFX_75; CreateDamageFX( kDamageFX_75 ); } break; case kDamageFX_75: if( fHealthPercent < 0.5f ) { m_eDamageFXState = kDamageFX_50; CreateDamageFX( kDamageFX_None ); CreateDamageFX( kDamageFX_50 ); } break; case kDamageFX_50: if( fHealthPercent < 0.25f ) { m_eDamageFXState = kDamageFX_25; CreateDamageFX( kDamageFX_None ); CreateDamageFX( kDamageFX_25 ); } break; } // Always return false, to let other sensors handle damage. return false; }
//---------------------------------------------------------------------------- // // ROUTINE: CAIHumanStateResurrecting::~CAIHumanStateResurrecting() // // PURPOSE: // //---------------------------------------------------------------------------- /*virtual*/ CAIHumanStateResurrecting::~CAIHumanStateResurrecting() { // Destruct if ( GetAI() && !GetAI()->IsDead() ) { CDestructible* pDestructable = GetAI()->GetDestructible(); pDestructable->SetNeverDestroy( m_bEntryCanDistruct ); pDestructable->SetHitPoints( pDestructable->GetMaxHitPoints() ); } }
bool CAISensorEnemyDamagedSuicide::UpdateSensor() { if( !super::UpdateSensor() ) { return false; } // AI is not targeting anyone. if( !m_pAI->HasTarget( kTarget_Character ) ) { return false; } // Bail if no target. HOBJECT hTarget = m_pAI->GetAIBlackBoard()->GetBBTargetObject(); CCharacter* pChar = (CCharacter*)g_pLTServer->HandleToObject( hTarget ); if( !pChar ) { return false; } // Bail if no destructible. CDestructible* pDestructible = pChar->GetDestructible(); if( !pDestructible ) { return false; } // Bail if we were not the last damager. if( pDestructible->GetLastDamager() != m_pAI->m_hObject ) { return false; } // No new damage. if( pDestructible->GetLastDamageTime() <= 0.f ) { return false; } // We damaged the target. // So kill myself. g_pCmdMgr->QueueMessage( m_pAI, m_pAI, "DESTROY" ); return true; }
// ----------------------------------------------------------------------- // // // ROUTINE: CoreDump::HandleComplete // // PURPOSE: Transfer the inventory/energy to the player // // ----------------------------------------------------------------------- // void CoreDump::HandleComplete(HOBJECT hSender) { if(!IsPlayer(hSender)) { g_pLTServer->CPrint("ERROR - CoreDump received a complete message from an object other than a player!\n"); ASSERT(FALSE); return; } // Invoke the inventory commands int i; char *szCmd; GEN_INVENTORY_LIST::iterator iter; for(iter=m_lstInventory.begin();iter!=m_lstInventory.end();iter++) { // First get the command szCmd = g_pInventoryButeMgr->GetItemCommand(iter->nItemID); for(i=0;i<iter->nCount;i++) { // Now process it N times (once for each instance of the item we have) if(g_pCmdMgr->IsValidCmd(szCmd)) { g_pCmdMgr->Process(szCmd,m_hObject,NULL); } } } // Now give the player some energy CTronPlayerObj* pPlayer = (CTronPlayerObj*) g_pLTServer->HandleToObject(hSender); // Get the player's damage aggregage CDestructible* pDest = m_pPlayer->GetDestructible(); ASSERT(pDest); int nMax = (int)pDest->GetMaxEnergy(); int nNew = (int)m_fCurrentEnergy + (int)pDest->GetEnergy(); // Check if(nNew <= nMax) { // And set pDest->SetEnergy((float)nNew); } // And we're done. Term(); }
void Body::Init(const BODYINITSTRUCT& bi) { if (!bi.pCharacter || !bi.pCharacter->m_hObject) return; // Get the death type etc m_eDeathType = bi.pCharacter->GetDeathType(); m_eModelId = bi.pCharacter->GetModelId(); m_eModelSkeleton = bi.pCharacter->GetModelSkeleton(); m_eModelStyle = bi.pCharacter->GetModelStyle(); // Get the body lifetime m_fLifetime = bi.fLifetime; // Create the SFX BODYCREATESTRUCT bcs; bcs.eBodyState = bi.eBodyState; if (IsPlayer(bi.pCharacter->m_hObject)) { CPlayerObj* pPlayer = (CPlayerObj*) bi.pCharacter; bcs.nClientId = (uint8) g_pLTServer->GetClientID(pPlayer->GetClient()); } HMESSAGEWRITE hMessage = g_pLTServer->StartSpecialEffectMessage(this); g_pLTServer->WriteToMessageByte(hMessage, SFX_BODY_ID); bcs.Write(g_pLTServer, hMessage); g_pLTServer->EndMessage(hMessage); // Create the death scene CreateDeathScene(bi.pCharacter); // We'll handle creating the necessary debris... m_damage.m_bCreatedDebris = LTTRUE; m_damage.SetCanDamage(LTFALSE);//bi.pCharacter->CanDamageBody()); m_damage.SetApplyDamagePhysics(LTFALSE);//bi.pCharacter->CanDamageBody()); m_damage.SetMass(g_pModelButeMgr->GetModelMass(m_eModelId)); // Let us get hit by decay powder m_damage.ClearCantDamageTypes(DamageTypeToFlag(DT_GADGET_DECAYPOWDER)); switch ( g_pModelButeMgr->GetModelType(m_eModelId) ) { case eModelTypeVehicle: { m_eDeathType = CD_GIB; } break; } CDestructible* pDest = bi.pCharacter->GetDestructible(); if (pDest) { m_eDamageType = pDest->GetDeathType(); VEC_COPY(m_vDeathDir, pDest->GetDeathDir()); VEC_NORM(m_vDeathDir); VEC_MULSCALAR(m_vDeathDir, m_vDeathDir, 1.0f + (pDest->GetDeathDamage() / pDest->GetMaxHitPoints())); } LTFLOAT fHitPts = pDest->GetMaxHitPoints(); m_damage.Reset(fHitPts, 0.0f); m_damage.SetHitPoints(fHitPts); m_damage.SetMaxHitPoints(fHitPts); m_damage.SetArmorPoints(0.0f); m_damage.SetMaxArmorPoints(0.0f); // Copy our user flags over, setting our surface type to flesh uint32 dwUsrFlags = g_pLTServer->GetObjectUserFlags(m_hObject) | USRFLG_NIGHT_INFRARED; dwUsrFlags |= SurfaceToUserFlag(ST_FLESH); g_pLTServer->SetObjectUserFlags(m_hObject, dwUsrFlags); // Make sure model doesn't slide all over the place... g_pLTServer->SetFrictionCoefficient(m_hObject, 500.0f); LTVector vDims; g_pLTServer->GetObjectDims(bi.pCharacter->m_hObject, &vDims); // Set the dims. If we can't set the dims that big, set them // as big as possible... if (g_pLTServer->SetObjectDims2(m_hObject, &vDims) == LT_ERROR) { g_pLTServer->SetObjectDims2(m_hObject, &vDims); } // Create the box used for weapon impact detection... CreateHitBox(bi); LTFLOAT r, g, b, a; g_pLTServer->GetObjectColor(bi.pCharacter->m_hObject, &r, &g, &b, &a); g_pLTServer->SetObjectColor(m_hObject, r, g, b, a); LTVector vScale; g_pLTServer->GetObjectScale(bi.pCharacter->m_hObject, &vScale); g_pLTServer->ScaleObject(m_hObject, &vScale); // Copy our animation over HMODELANIM hAni = g_pLTServer->GetModelAnimation(bi.pCharacter->m_hObject); g_pLTServer->SetModelAnimation(m_hObject, hAni); g_pLTServer->SetModelLooping(m_hObject, LTFALSE); // Copy the flags from the character to us uint32 dwFlags = g_pLTServer->GetObjectFlags(bi.pCharacter->m_hObject); m_dwFlags |= FLAG_REMOVEIFOUTSIDE; g_pLTServer->SetObjectFlags(m_hObject, dwFlags); // Move the attachments aggregate from the char to us... if (!m_pAttachments && bi.eBodyState != eBodyStateLaser) { m_hWeaponItem = bi.pCharacter->TransferWeapon(m_hObject); // Make sure we're playing the correct ani... if (m_hWeaponItem) { uint32 dwAni = g_pLTServer->GetAnimIndex(m_hWeaponItem, "Hand"); if (dwAni != INVALID_ANI) { g_pLTServer->SetModelAnimation(m_hWeaponItem, dwAni); g_pLTServer->SetModelLooping(m_hWeaponItem, LTFALSE); } } } if (!m_pAttachments) { m_pAttachments = bi.pCharacter->TransferAttachments(); if (m_pAttachments) { AddAggregate(m_pAttachments); m_pAttachments->ReInit(m_hObject); } } // Set up the spears bi.pCharacter->TransferSpears(this); // Set our state SetState(bi.eBodyState); }
bool CAISensorInstantDeath::UpdateSensor() { if( !super::UpdateSensor() ) { return false; } // This sensor is somewhat of a hack. It was added because the AIActionInstantDeath // runs a couple frames too late to catch all situations where we would like AI to // die instantly. Ideally, we would refactor the way we handle damage to allow // more immediate planner reaction. // This sensor is irrelevant once the AI has targeted something. if( m_pAI->GetAIBlackBoard()->GetBBTargetType() != kTarget_None ) { m_pAI->GetAISensorMgr()->RemoveAISensor( kSensor_InstantDeath ); return false; } // Sanity check. CDestructible *pDestructible = m_pAI->GetDestructible(); if( !pDestructible ) { return false; } // No new damage. if( pDestructible->GetLastDamageTime() == 0.f ) { return false; } // This sensor is irrelevant after the first time we have been damaged. m_pAI->GetAISensorMgr()->RemoveAISensor( kSensor_InstantDeath ); // Don't die instantly if NeverDestroy is set. if( pDestructible->GetNeverDestroy() ) { return false; } // AI cannot take damage. if ( !m_pAI->GetDestructible()->GetCanDamage() ) { return false; } // AI does not accept this type of damage. DamageStruct Damage = m_pAI->GetAIBlackBoard()->GetBBLastDamage(); if( pDestructible->IsCantDamageType( Damage.eType ) ) { return false; } // Die instantly if taken by surprise and hit in the head or torso. if( m_pAI->GetAIBlackBoard()->GetBBTargetType() == kTarget_None ) { // Determine which body part was hit. ModelsDB::HNODE hModelNode = m_pAI->GetModelNodeLastHit(); if( hModelNode ) { EnumAnimProp eBodyAnipProp = g_pModelsDB->GetNodeBodyAnimProp( hModelNode ); if( ( eBodyAnipProp == kAP_BODY_Head ) || ( eBodyAnipProp == kAP_BODY_Torso ) ) { // Die instantly! Use the data from the last damage received -- just // lower the AI's hitpoints to less than 1. This will insure this action behaves // exactly like a standard death without any additional prompting. pDestructible->SetHitPoints( 0.1f ); pDestructible->SetArmorPoints( 0.0f ); Damage.fDamage = 2.0f; Damage.DoDamage( m_pAI->m_hObject, m_pAI->m_hObject ); // Die silently. m_pAI->SetLastPainVolume( 0.1f ); return true; } } } // Always allow other sensors to update. return false; }