void Body::CreateHitBox(const BODYINITSTRUCT& bi) { if (m_hHitBox) return; LTVector vPos; g_pLTServer->GetObjectPos(m_hObject, &vPos); HCLASS hClass = g_pLTServer->GetClass("CCharacterHitBox"); if (!hClass) return; ObjectCreateStruct theStruct; INIT_OBJECTCREATESTRUCT(theStruct); theStruct.m_Pos = vPos; g_pLTServer->GetObjectRotation(m_hObject, &theStruct.m_Rotation); // Allocate an object... CCharacterHitBox* pHitBox = (CCharacterHitBox *)g_pLTServer->CreateObject(hClass, &theStruct); if (!pHitBox) return; m_hHitBox = pHitBox->m_hObject; pHitBox->Init(m_hObject); pHitBox->SetCanActivate(LTFALSE); if (m_hHitBox) { LTVector vDims; g_pLTServer->GetObjectDims(m_hObject, &vDims); g_pLTServer->SetObjectDims(m_hHitBox, &vDims); } }
LTBOOL CProjectile::HandlePotentialHitBoxImpact(IntersectInfo & iInfo, LTVector & vFrom) { CCharacterHitBox *pCharHitBox = (CCharacterHitBox*) g_pLTServer->HandleToObject(iInfo.m_hObject); if (!pCharHitBox) return LTFALSE; return pCharHitBox->HandleImpact(this, iInfo, m_vDir, vFrom); }
void Body::UpdateHitBox() { if ( !m_hHitBox ) return; CCharacterHitBox* pHitBox = (CCharacterHitBox*) g_pLTServer->HandleToObject(m_hHitBox); if ( pHitBox ) { pHitBox->Update(); } }
LTBOOL DoVectorFilterFn(HOBJECT hObj, void *pUserData) { // We're not attacking our self... if (SpecificObjectFilterFn(hObj, pUserData)) { // CharacterHitBox objects are used for vector impacts, don't // impact on the character/body prop object itself.... if (IsCharacter(hObj) || IsBody(hObj) || IsKindOf(hObj, "Intelligence")) { return LTFALSE; } // Check special character hit box cases... if (IsCharacterHitBox(hObj)) { CCharacterHitBox *pCharHitBox = (CCharacterHitBox*) g_pLTServer->HandleToObject(hObj); if (pCharHitBox) { // Make sure we don't hit ourself... HOBJECT hUs = (HOBJECT)pUserData; HOBJECT hTestObj = pCharHitBox->GetModelObject(); if (!hTestObj) return LTFALSE; if (hTestObj == hUs) { return LTFALSE; } // Do special AI hitting AI case... if (IsAI(hUs) && IsAI(hTestObj)) { CAI *pAI = (CAI*) g_pLTServer->HandleToObject(hUs); if (!pAI) return LTFALSE; // We can't hit guys we like, unless they're NEUTRAL CCharacter* pB = (CCharacter*)g_pLTServer->HandleToObject(hTestObj); if (!pB) return LTFALSE; CharacterClass cc = pB->GetCharacterClass(); if (cc != NEUTRAL) { return LIKE != GetAlignement(pAI->GetCharacterClass(), cc); } } // Check for friendly fire if (g_pGameServerShell->GetGameType() == COOPERATIVE_ASSAULT && g_vtNetFriendlyFire.GetFloat() < 1.0f) { // We can't hit guys on our team unless friendly fire is turned on if (IsPlayer(hUs) && IsPlayer(hTestObj)) { CPlayerObj* pUs = (CPlayerObj*) g_pLTServer->HandleToObject(hUs); if (!pUs) return LTFALSE; CPlayerObj* pThem = (CPlayerObj*) g_pLTServer->HandleToObject(hTestObj); if (!pThem) return LTFALSE; if (pUs->GetTeamID() == pThem->GetTeamID()) return LTFALSE; } } } } return LTTRUE; } return LTFALSE; }
void CProjectile::HandleTouch(HOBJECT hObj) { if (m_bObjectRemoved) return; // Don't process any touches until this has been cleared... if (m_bProcessInvImpact) return; // Let it get out of our bounding box... if (hObj == m_hFiredFrom) return; CCharacterHitBox* pHitBox = LTNULL; // If we've hit a character (or body), let its hit box take control... if (IsCharacter(hObj)) { CCharacter* pChar = (CCharacter*)g_pLTServer->HandleToObject(hObj); if (pChar) { hObj = pChar->GetHitBox(); } } else if (IsBody(hObj)) { Body* pBody = (Body*)g_pLTServer->HandleToObject(hObj); if (pBody) { hObj = pBody->GetHitBox(); } } if (IsCharacterHitBox(hObj)) { pHitBox = (CCharacterHitBox*)g_pLTServer->HandleToObject(hObj); if (!pHitBox) return; if (pHitBox->GetModelObject() == m_hFiredFrom) return; } // Don't hit our own type of projectiles (for multi-projectile weapons // and projectiles that stick to objects)... if (IsKindOf(hObj, m_hObject)) { CProjectile* pObj = (CProjectile*)g_pLTServer->HandleToObject(hObj); if (pObj) { if (pObj->GetFiredFrom() == m_hFiredFrom) { return; } } } // See if we want to impact on this object... uint32 dwUsrFlags = g_pLTServer->GetObjectUserFlags(hObj); if (dwUsrFlags & USRFLG_IGNORE_PROJECTILES) return; LTBOOL bIsWorld = IsMainWorld(hObj); // Don't impact on non-solid objects...unless it is a CharacterHitBox // object... uint32 dwFlags = g_pLTServer->GetObjectFlags(hObj); if (!bIsWorld && !(dwFlags & FLAG_SOLID)) { if (pHitBox) { // See if we really impacted on the box... if (pHitBox->DidProjectileImpact(this)) { // This is the object that we really hit... hObj = pHitBox->GetModelObject(); } else { return; } } else if (!(dwFlags & FLAG_RAYHIT)) { // If we have ray hit set to true, projectiles should // impact on us too... return; } } // See if we hit the sky... if (bIsWorld || (OT_WORLDMODEL == g_pLTServer->GetObjectType(hObj))) { CollisionInfo info; g_pLTServer->GetLastCollision(&info); SurfaceType eType = GetSurfaceType(info); if (eType == ST_SKY) { RemoveObject(); return; } else if (eType == ST_INVISIBLE) { // Update 1.002 [KLS] - If multiplayer and we hit an invisible // surface, just treat it like a normal surface... if (!IsMultiplayerGame()) { m_bProcessInvImpact = LTTRUE; g_pLTServer->GetObjectPos(m_hObject, &m_vInvisNewPos); g_pLTServer->GetVelocity(m_hObject, &m_vInvisVel); m_vInvisNewPos += (m_vInvisVel * g_pLTServer->GetFrameTime()); // Make sure this new position is inside the world...else // just blow up... if (LT_INSIDE == g_pLTServer->Common()->GetPointStatus(&m_vInvisNewPos)) { return; } } } } HandleImpact(hObj); }
void Body::Update() { if ( !m_hObject ) return; LTVector vPos, vMin, vMax; g_pLTServer->GetWorldBox(vMin, vMax); g_pLTServer->GetObjectPos(m_hObject, &vPos); if (vPos.x < vMin.x || vPos.y < vMin.y || vPos.z < vMin.z || vPos.x > vMax.x || vPos.y > vMax.y || vPos.z > vMax.z) { RemoveObject(); } // Update the animator m_Animator.Update(); // Make sure our hit box is in the correct position... UpdateHitBox(); if ( m_bFirstUpdate ) { m_bFirstUpdate = LTFALSE; m_fStartTime = g_pLTServer->GetTime(); } SetNextUpdate(s_fUpdateDelta); // We keep the body active to update dims for 2.0 seconds LTBOOL bUpdatingDims = g_pLTServer->GetTime() < m_fStartTime + 2.0f; // The deactivate-check, update, activate-check is ordered so that // deactivation will always happen one update after you enter a state, // but activation will always happen when you just entered the state. // This avoids a lot of issues with changing between active/inactive states. if ( !bUpdatingDims && (!m_pState || m_pState->CanDeactivate()) ) { // Finalize the dims of our hit box... if (m_hHitBox) { CCharacterHitBox* pHitBox = (CCharacterHitBox*) g_pLTServer->HandleToObject(m_hHitBox); if ( pHitBox ) { // For now just make the hit box 50% larger than our dims... LTVector vDims, vNewDims; g_pLTServer->GetObjectDims(m_hHitBox, &vDims); vNewDims = vDims; vNewDims.x *= 3.0f; vNewDims.z *= 3.0f; // Update 1.003, some cases the dims can get weird, not // sure why...Just make sure they are reasonable... if (vNewDims.x <= 0.0f || vNewDims.x > 75.0f) { vNewDims.x = 75.0f; } if (vNewDims.z <= 0.0f || vNewDims.z > 75.0f) { vNewDims.z = 75.0f; } // Only shrink us down if we're not sitting or stuck to a wall if ( m_eBodyStatePrevious == eBodyStateChair ) { } else if ( m_eBodyStatePrevious == eBodyStateArrow ) { } else { vNewDims.y = 15.0f; } g_pLTServer->SetObjectDims(m_hHitBox, &vNewDims); LTVector vOffset(0, vNewDims.y - vDims.y, 0); pHitBox->SetOffset(vOffset); pHitBox->Update(); } } SetNextUpdate(0.0f); } if ( m_pState ) { m_pState->Update(); } if ( bUpdatingDims || (m_pState && !m_pState->CanDeactivate()) ) { SetNextUpdate(s_fUpdateDelta); } }
uint32 PickupItem::EngineMessageFn(uint32 messageID, void *pData, LTFLOAT fData) { switch(messageID) { case MID_UPDATE: { Update(); } break; case MID_TOUCHNOTIFY: { if( !m_bTouchPickup ) break; HOBJECT hObj = (HOBJECT)pData; // If this is a character hit box, use its object... CCharacterHitBox* pHitBox = LTNULL; if (IsCharacterHitBox(hObj)) { pHitBox = (CCharacterHitBox*)g_pLTServer->HandleToObject(hObj); if (pHitBox) { hObj = pHitBox->GetModelObject(); } } // If the object is dead, it can't pick up stuff... if (IsPlayer(hObj)) { CPlayerObj* pPlayer = (CPlayerObj*)g_pLTServer->HandleToObject(hObj); if (!pPlayer || pPlayer->IsDead()) break; SetPlayerObj(hObj); } else { SetPlayerObj(LTNULL); break; } ObjectTouch(hObj); } break; case MID_PRECREATE: { ObjectCreateStruct* pInfo = (ObjectCreateStruct*)pData; if (fData == PRECREATE_WORLDFILE) { ReadProp(pInfo); } else if (fData == PRECREATE_STRINGPROP) { ReadProp(pInfo); // Show ourself... pInfo->m_Flags |= FLAG_VISIBLE; } PostPropRead(pInfo); } break; case MID_INITIALUPDATE: { if (fData != INITIALUPDATE_SAVEGAME) { InitialUpdate(); } } break; case MID_SAVEOBJECT: { Save((ILTMessage_Write*)pData, (uint32)fData); } break; case MID_LOADOBJECT: { Load((ILTMessage_Read*)pData, (uint32)fData); uint32 dwRet = GameBase::EngineMessageFn(messageID, pData, fData); // We need to reset our sfx message since values // could have changed across save versions. CreateSpecialFX( ); return dwRet; } break; default : break; } return GameBase::EngineMessageFn(messageID, pData, fData); }
void Prop::HandleTouch(HOBJECT hToucher) { // Only characters can touch props. if( ( hToucher != LTNULL ) && ( !IsCharacter(hToucher) ) && ( !IsExplosion(hToucher) ) ) { return; } if ( (m_eState == kState_PropDestroyed) || (m_eState == kState_PropKnocked) || (!(m_pDisturb && m_pDisturb->pPD)) ) { return; } // Resolve the toucher to a character handle. HOBJECT hCharacter = LTNULL; if( IsCharacterHitBox( hToucher ) ) { CCharacterHitBox* pHB = (CCharacterHitBox*)g_pLTServer->HandleToObject(hToucher); hCharacter = pHB->GetModelObject(); } else if( IsExplosion( hToucher ) ) { Explosion* pExplosion = (Explosion*)g_pLTServer->HandleToObject(hToucher); hCharacter = pExplosion->GetFiredFrom(); } else if( IsCharacter( hToucher ) ) { hCharacter = hToucher; } // Only players can touch (for now). if((hCharacter != LTNULL) && !IsPlayer(hCharacter)) return; if(m_eState == kState_PropTouching) { // Clear sound if done playing. ClearTouchSoundIfDone(LTFALSE); // Check if both sound and animation are done, or just the sound for a "knock." if( m_pDisturb->hTouchSound == LTNULL ) { switch( m_pDisturb->eTouchAnimType ) { // Touch animations reset. case kPA_Touch: ClearTouchAnimIfDone(m_pDisturb->hTouchAnim, LTFALSE); if( g_pLTServer->GetModelAnimation(m_hObject) != m_pDisturb->hTouchAnim ) { m_eState = kState_PropDefault; return; } break; // Knock animations remain on the last frame forever. case kPA_Knock: if( MS_PLAYDONE & g_pLTServer->GetModelPlaybackState(m_hObject) ) { m_bTouchable = LTFALSE; g_pCommonLT->SetObjectFlags(m_hObject, OFT_User, 0, USRFLG_CAN_ACTIVATE); m_eState = kState_PropKnocked; return; } break; } } } // Play the touch sound and animation. if( (m_eState == kState_PropDefault) && (hCharacter != LTNULL)) { LTVector vPos; g_pLTServer->GetObjectPos(m_hObject, &vPos); // Play sound. PlayTouchSound(vPos); // Play animation. if(m_pDisturb->hTouchAnim != INVALID_ANI) { g_pLTServer->SetModelAnimation(m_hObject, m_pDisturb->hTouchAnim); g_pLTServer->SetModelLooping(m_hObject, LTFALSE); g_pLTServer->ResetModelAnimation(m_hObject); } // Register touch disturbance stimulus. if( (m_pDisturb->pPD->nTouchAlarmLevel > 0) && (m_pDisturb->pPD->fStimRadius > 0.f) ) { g_pAIStimulusMgr->RegisterStimulus( kStim_EnemyDisturbanceSound, m_pDisturb->pPD->nTouchAlarmLevel, hCharacter, m_hObject, vPos, m_pDisturb->pPD->fStimRadius ); // Props that are knocked over have a visual stimulus too. if( m_pDisturb->eTouchAnimType == kPA_Knock ) { g_pAIStimulusMgr->RegisterStimulus( kStim_EnemyDisturbanceVisible, m_pDisturb->pPD->nTouchAlarmLevel + 1, hCharacter, m_hObject, vPos, m_pDisturb->pPD->fStimRadius ); } } m_eState = kState_PropTouching; } // Update while playing touch sound and/or animation. SetNextUpdate(UPDATE_NEXT_FRAME); }
uint32 Prop::EngineMessageFn(uint32 messageID, void *pData, LTFLOAT fData) { switch(messageID) { case MID_TOUCHNOTIFY: { if(m_bTouchable) { HOBJECT hToucher = (HOBJECT)pData; if( IsCharacterHitBox( hToucher ) ) { CCharacterHitBox* pHB = (CCharacterHitBox*)g_pLTServer->HandleToObject(hToucher); hToucher = pHB->GetModelObject(); } // Could be a body. if( IsCharacter(hToucher) ) { HandleTouch( hToucher ); } } else if( m_bAttachmentShotOff ) { HandleAttachmentTouch( (HOBJECT)pData ); } } break; case MID_UPDATE: { Update(); } break; case MID_MODELSTRINGKEY : { HandleModelString( (ArgList*)pData ); } break; case MID_PRECREATE: { if (fData == PRECREATE_WORLDFILE || fData == PRECREATE_STRINGPROP) { ObjectCreateStruct* pStruct = (ObjectCreateStruct*)pData; ReadProp(pStruct); // If this prop is spawned, assume it should be visible (if they // specify Visible 0, our parent class will handle it ;) if (fData == PRECREATE_STRINGPROP) { m_dwFlags |= FLAG_VISIBLE; } } // We must remove aggregates before sending the message to the base classs... if( !m_bCanTransition ) { // Disallow transitioning of this object through TransAMs... DestroyTransitionAggregate(); } uint32 dwRet = GameBase::EngineMessageFn(messageID, pData, fData); PostPropRead((ObjectCreateStruct*)pData); return dwRet; } break; case MID_INITIALUPDATE: { if (fData != INITIALUPDATE_SAVEGAME) { InitialUpdate(); } } break; case MID_SAVEOBJECT: { Save((ILTMessage_Write*)pData, (uint32)fData); } break; case MID_LOADOBJECT: { Load((ILTMessage_Read*)pData, (uint32)fData); } break; default : break; } return GameBase::EngineMessageFn(messageID, pData, fData); }
void Prop::HandleHit( HOBJECT hDamager ) { if( ( hDamager != LTNULL ) && ( !IsCharacter(hDamager) ) && ( !IsExplosion(hDamager) ) ) { return; } if( (m_eState == kState_PropDestroyed) || (m_eState == kState_PropKnocked) || (!(m_pDisturb && m_pDisturb->pPD)) ) { return; } // If we don't have a valid hit animation default to the touch animation... if( m_pDisturb->hHitAnim == INVALID_ANI ) HandleTouch( hDamager ); // Resolve the toucher to a character handle. HOBJECT hCharacter = LTNULL; if( IsCharacterHitBox( hDamager ) ) { CCharacterHitBox* pHB = (CCharacterHitBox*)g_pLTServer->HandleToObject(hDamager); hCharacter = pHB->GetModelObject(); } else if( IsExplosion( hDamager ) ) { Explosion* pExplosion = (Explosion*)g_pLTServer->HandleToObject(hDamager); hCharacter = pExplosion->GetFiredFrom(); } else if( IsCharacter( hDamager ) ) { hCharacter = hDamager; } // Only characters can hit (for now). if((hCharacter != LTNULL) && !IsCharacter(hCharacter)) return; if(m_eState == kState_PropHit) { // Check if both sound and animation are done ClearHitSoundIfDone( LTFALSE ); if( m_pDisturb->hHitSound == LTNULL ) { ClearHitAnimIfDone( LTFALSE ); if( g_pLTServer->GetModelAnimation(m_hObject) != m_pDisturb->hHitAnim ) { m_eState = kState_PropDefault; return; } } } // Play the hit sound and animation. if( (m_eState == kState_PropDefault) && (hCharacter != LTNULL)) { LTVector vPos; g_pLTServer->GetObjectPos(m_hObject, &vPos); // Play sound. PlayHitSound(vPos); // Play animation. if(m_pDisturb->hHitAnim != INVALID_ANI) { g_pLTServer->SetModelAnimation( m_hObject, m_pDisturb->hHitAnim ); g_pLTServer->SetModelLooping( m_hObject, LTFALSE ); g_pLTServer->ResetModelAnimation(m_hObject); } // Register touch disturbance stimulus. if( (m_pDisturb->pPD->nHitAlarmLevel > 0) && (m_pDisturb->pPD->fStimRadius > 0.f) ) { g_pAIStimulusMgr->RegisterStimulus( kStim_EnemyDisturbanceSound, m_pDisturb->pPD->nHitAlarmLevel, hCharacter, m_hObject, vPos, m_pDisturb->pPD->fStimRadius ); } m_eState = kState_PropHit; } // Update while playing touch sound and/or animation. SetNextUpdate(UPDATE_NEXT_FRAME); }