void CGameAIRecorder::AddRecordComment(EntityId requesterId, const char* szComment) { assert(szComment && szComment[0]); assert(requesterId > 0); assert(gEnv->bServer); if (m_bIsRecording && szComment && szComment[0]) { if (!gEnv->bServer) { CryLogAlways("[AI] Recorder comment requested on Client. Only the Server can do this!"); return; } IEntity *pEntity = gEnv->pEntitySystem->GetEntity(requesterId); IAIObject *pAI = pEntity ? pEntity->GetAI() : NULL; if (!pAI) { CryLogAlways("[AI] Attempting to add recorder comment, but the requester does not have an AI!"); return; } // Output to log CryLogAlways("[AI] --- RECORDER COMMENT ADDED ---"); CryLogAlways("[AI] By: %s", pEntity->GetName()); CryLogAlways("[AI] Comment: %s", szComment); // Add comment to stream RecordLuaComment(pAI, "Comment: %s", szComment); } }
bool CCannonBall::FilterFriendlyAIHit(IEntity* pHitTarget) { bool bResult = false; if (!gEnv->bMultiplayer && pHitTarget) { const bool bIsClient = (m_ownerId == g_pGame->GetIGameFramework()->GetClientActorId()); IEntity* pOwnerEntity = gEnv->pEntitySystem->GetEntity(m_ownerId); //Filter client hits against friendly AI if (pOwnerEntity && bIsClient) { IAIObject *pOwnerAI = pOwnerEntity->GetAI(); IAIObject *pTargetAI = pHitTarget->GetAI(); if (pOwnerAI && pTargetAI && !pTargetAI->IsHostile(pOwnerAI)) { const bool bEnableFriendlyHit = g_pGameCVars->g_enableFriendlyPlayerHits != 0; if (!bEnableFriendlyHit) { g_pGame->GetGameRules()->SetEntityToIgnore(pHitTarget->GetId()); bResult = true; } } } } return bResult; }
EEntityType GetEntityType(EntityId id) { int type = eET_Unknown; IEntitySystem *pEntitySystem = gEnv->pEntitySystem; if (pEntitySystem) { IEntity *pEntity = pEntitySystem->GetEntity(id); if (pEntity) { type = eET_Valid; IEntityClass *pClass = pEntity->GetClass(); if (pClass) { const char* className = pClass->GetName(); // Check AI if (pEntity->GetAI()) { type |= eET_AI; } // Check actor IActorSystem *pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); if (pActorSystem) { IActor *pActor = pActorSystem->GetActor(id); if (pActor) { type |= eET_Actor; } } // Check vehicle IVehicleSystem *pVehicleSystem = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem(); if (pVehicleSystem) { if (pVehicleSystem->IsVehicleClass(className)) { type |= eET_Vehicle; } } // Check item IItemSystem *pItemSystem = gEnv->pGame->GetIGameFramework()->GetIItemSystem(); if (pItemSystem) { if (pItemSystem->IsItemClass(className)) { type |= eET_Item; } } } } } return (EEntityType)type; }
void CPersistantDebug::AddEntityTag(const SEntityTagParams& params, const char *tagContext) { // Create tag SEntityTag tag; tag.params = params; tag.params.column = max(1, tag.params.column); tag.params.visibleTime = max(0.f, tag.params.visibleTime); tag.params.fadeTime = max(0.f, tag.params.fadeTime); if (tagContext != NULL && *tagContext != '\0') tag.params.tagContext = tagContext; tag.totalTime = tag.params.visibleTime + tag.params.fadeTime; tag.totalFadeTime = tag.params.fadeTime; tag.vScreenPos.zero(); SObj *obj = FindObj(params.entity); if (!obj) { // Create new object to push back SObj sobj; sobj.obj = eOT_EntityTag; sobj.entityId = params.entity; sobj.entityHeight = 0.f; sobj.timeRemaining = tag.totalTime; sobj.totalTime = tag.totalTime; sobj.columns.resize(params.column); AddToTagList(sobj.tags, tag); m_objects[entityTagsContext].push_back(sobj); } else { obj->timeRemaining = max(obj->timeRemaining, tag.totalTime); obj->totalTime = obj->timeRemaining; int size = max(int(obj->columns.size()), params.column); if (obj->columns.size() < size) obj->columns.resize(size); AddToTagList(obj->tags, tag); } if (m_pETLog->GetIVal() > 0) { IEntity *ent = gEnv->pEntitySystem->GetEntity(params.entity); if (ent) { CryLog("[Entity Tag] %s added tag: %s", ent->GetName(), params.text.c_str()); if (m_pETLog->GetIVal() > 1) { char text[256]; _snprintf(text, sizeof(text), "[Entity Tag] %s", params.text.c_str()); gEnv->pAISystem->Record(ent->GetAI(), IAIRecordable::E_NONE, text); } } } }
bool CDialogActorContext::ExecuteAI(int& goalPipeID, const char* signalText, IAISignalExtraData* pExtraData, bool bRegisterAsListener) { IEntitySystem* pSystem = gEnv->pEntitySystem; IEntity* pEntity = pSystem->GetEntity(m_entityID); if (pEntity == 0) return false; IAIObject* pAI = pEntity->GetAI(); if (pAI == 0) return false; unsigned short nType=pAI->GetAIType(); if ( nType != AIOBJECT_ACTOR ) { if ( nType == AIOBJECT_PLAYER ) { goalPipeID = -1; // not needed for player // pAI->SetSignal( 10, signalText, pEntity, NULL ); // 10 means this signal must be sent (but sent[!], not set) // even if the same signal is already present in the queue return true; } // invalid AIObject type return false; } IPipeUser* pPipeUser = pAI->CastToIPipeUser(); if (pPipeUser) { if (goalPipeID > 0) { pPipeUser->RemoveSubPipe(goalPipeID, true); pPipeUser->UnRegisterGoalPipeListener( this, goalPipeID ); goalPipeID = 0; } } goalPipeID = gEnv->pAISystem->AllocGoalPipeId(); if (pExtraData == 0) pExtraData = gEnv->pAISystem->CreateSignalExtraData(); pExtraData->iValue = goalPipeID; if (pPipeUser && bRegisterAsListener) { pPipeUser->RegisterGoalPipeListener( this, goalPipeID, "CDialogActorContext::ExecuteAI"); } IAIActor* pAIActor = CastToIAIActorSafe(pAI); if(pAIActor) pAIActor->SetSignal( 10, signalText, pEntity, pExtraData ); // 10 means this signal must be sent (but sent[!], not set) // even if the same signal is already present in the queue return true; }
//------------------------------------------------------------------------ void CProjectile::InitWithAI() { // register with ai if needed //FIXME //make AI ignore grenades thrown by AI; needs proper/readable grenade reaction if (m_pAmmoParams->aiType!=AIOBJECT_NONE) { bool isFriendlyGrenade(true); IEntity *pOwnerEntity = gEnv->pEntitySystem->GetEntity(m_ownerId); if (pOwnerEntity && pOwnerEntity->GetAI()) isFriendlyGrenade = (pOwnerEntity->GetAI()->GetAIType() == AIOBJECT_ACTOR); if (!isFriendlyGrenade) { GetEntity()->RegisterInAISystem(AIObjectParams(m_pAmmoParams->aiType)); } } GetGameObject()->SetAIActivation(eGOAIAM_Always); }
//------------------------------------------------------------------------ int CScriptBind_Action::SetAimQueryMode(IFunctionHandler* pH, ScriptHandle entityId, int mode) { IEntity* entity = gEnv->pEntitySystem->GetEntity(static_cast<EntityId>(entityId.n)); IAIObject* ai = entity ? entity->GetAI() : NULL; CAIProxy* proxy = ai ? static_cast<CAIProxy*>(ai->GetProxy()) : NULL; if (proxy) proxy->SetAimQueryMode(static_cast<CAIProxy::AimQueryMode>(mode)); return pH->EndFunction(); }
//------------------------------------------------------------------------ void CProjectile::SetParams(EntityId ownerId, EntityId hostId, EntityId weaponId, int damage, int hitTypeId, float damageDrop /*= 0.0f*/, float damageDropMinR /*=0.0f*/) { m_ownerId = ownerId; m_weaponId = weaponId; m_hostId = hostId; m_damage = damage; m_hitTypeId = hitTypeId; m_damageDropPerMeter = damageDrop; m_damageDropMinDisSqr = damageDropMinR*damageDropMinR; if(m_hostId || m_ownerId) { IEntity *pSelfEntity = GetEntity(); if(pSelfEntity) pSelfEntity->AddEntityLink("Shooter", m_ownerId); IEntity *pEntity = gEnv->pEntitySystem->GetEntity(m_hostId?m_hostId:m_ownerId); if(pEntity) { if(pSelfEntity) { //need to set AI species to the shooter - not to be scared of it's own rockets IAIObject *projectileAI = pSelfEntity->GetAI(); IAIObject *shooterAI = pEntity->GetAI(); if(projectileAI && shooterAI) projectileAI->SetFactionID(shooterAI->GetFactionID()); } if(m_pPhysicalEntity && m_pPhysicalEntity->GetType()==PE_PARTICLE) { pe_params_particle pparams; pparams.pColliderToIgnore = pEntity->GetPhysics(); m_pPhysicalEntity->SetParams(&pparams); } } } }
CAIAwarenessToPlayerHelper::VisorIconColor CAIAwarenessToPlayerHelper::GetMarkerColorForAgent(const EntityId entityId) const { CAIAwarenessToPlayerHelper::VisorIconColor defaultColor = Green; IEntity* entity = gEnv->pEntitySystem->GetEntity(entityId); const IAIObject* ai = entity ? entity->GetAI() : NULL; const IAIActor* aiActor = ai ? ai->CastToIAIActor() : NULL; IAIObject* playerAiObject = NULL; CActor* playerActor = static_cast<CActor*>(gEnv->pGame->GetIGameFramework()->GetClientActor()); if (playerActor) { if (IEntity* playerEntity = playerActor->GetEntity()) { playerAiObject = playerEntity->GetAI(); } } if (!playerActor || !playerAiObject) return defaultColor; const bool playerIsCloaked = playerActor->IsCloaked(); if (aiActor) { const int alertness = GetAlertnessAffectedByVisibility(*aiActor, *playerAiObject, playerIsCloaked); if (alertness == 0) return Green; else if (alertness == 1) return Orange; else return Red; } else { // Turrets are not AI actors so they are treated a bit differently // TODO: extend this to generic IAwarenessEntity. for now in C3 is fine as we dont want Towers to be show here. if (CTurret* turret = TurretHelpers::FindTurret(entityId)) { if (IEntity* turretEntity = turret->GetEntity()) { if (IAIObject* turretAI = turretEntity->GetAI()) { const bool turretIsDeployed = (turret->GetStateId() == eTurretBehaviorState_Deployed); if (!playerIsCloaked && turretIsDeployed && turretAI->IsHostile(playerAiObject) && turret->IsVisionIdInVisionRange(playerAiObject->GetVisionID())) return Red; else return Green; } } } } return defaultColor; }
// Description: // // Arguments: // // Return: // IAIObject const* CPersonalRangeSignaling::GetEntityAI(EntityId entityId) const { IAIObject const* pAIObject = NULL; IEntity *pEntity = gEnv->pEntitySystem->GetEntity(entityId); if (pEntity) { pAIObject = pEntity->GetAI(); } return pAIObject; }
//------------------------------------------------------------------------ void CProjectile::SetParams(EntityId ownerId, EntityId hostId, EntityId weaponId, int fmId, int damage, int hitTypeId) { m_ownerId = ownerId; m_weaponId = weaponId; m_fmId = fmId; m_hostId = hostId; m_damage = damage; m_hitTypeId = hitTypeId; if (m_hostId || m_ownerId) { IEntity* pSelfEntity = GetEntity(); if (pSelfEntity) pSelfEntity->AddEntityLink("Shooter", m_ownerId); IEntity *pEntity = gEnv->pEntitySystem->GetEntity(m_hostId?m_hostId:m_ownerId); if (pEntity) { if (pSelfEntity) { //need to set AI species to the shooter - not to be scared of it's own rockets IAIActor* pAIActor = CastToIAIActorSafe(pSelfEntity->GetAI()); IAIActor* pShooterAIActor = CastToIAIActorSafe(pEntity->GetAI()); if (pAIActor && pShooterAIActor) { AgentParameters ap = pAIActor->GetParameters(); ap.m_nSpecies = pShooterAIActor->GetParameters().m_nSpecies; pAIActor->SetParameters(ap); } } if (m_pPhysicalEntity && m_pPhysicalEntity->GetType()==PE_PARTICLE) { pe_params_particle pparams; pparams.pColliderToIgnore = pEntity->GetPhysics(); m_pPhysicalEntity->SetParams(&pparams); } } } }
//------------------------------------------------------------------------ int CScriptBind_Action::HasAI(IFunctionHandler* pH, ScriptHandle entityId) { bool bResult = false; const EntityId id = (EntityId)entityId.n; IEntity *pEntity = gEnv->pEntitySystem->GetEntity(id); if (pEntity) { bResult = (pEntity->GetAI() != 0); } return pH->EndFunction(bResult); }
// Description: // // Arguments: // // Return: // void CPersonalSignalTimer::SendSignal() { CRY_ASSERT( m_bInit == true ); IEntity *pEntity = GetEntity(); if (pEntity && gEnv->pAISystem) { IAISignalExtraData* pData = gEnv->pAISystem->CreateSignalExtraData(); pData->iValue = ++m_iSignalsSinceLastReset; pData->fValue = m_fTimerSinceLastReset; gEnv->pAISystem->SendSignal( SIGNALFILTER_SENDER, 1, m_sSignal, pEntity->GetAI(), pData ); } }
// Description: // // Arguments: // // Return: // float CAngleAlert::GetAngleTo( const Vec3& vPos ) const { float fResult = 0.0f; IEntity* pEntity = m_pPersonal->GetEntity(); CRY_ASSERT(pEntity); if (pEntity) { const Vec3& vEntityPos = pEntity->GetPos(); const Vec3& vEntityDir = pEntity->GetAI()->GetViewDir(); Vec3 vDiff = vPos - vEntityPos; vDiff.NormalizeSafe(); fResult = acosf(vEntityDir.Dot(vDiff)); } return fResult; }
// Description: // // Arguments: // // Return: // void CPersonalSignalTimer::SetListener(bool bAdd) { IEntity *pEntity = GetEntity();; if (pEntity) { IAIObject *pAIObject = pEntity->GetAI(); if (pAIObject) { CAIProxy* pAIProxy = (CAIProxy*)pAIObject->GetProxy(); if (pAIProxy) { if (bAdd) pAIProxy->AddListener(this); else pAIProxy->RemoveListener(this); } } } }
uint8 CAutoAimManager::GetLocalPlayerFaction() const { if (m_localPlayerFaction != IFactionMap::InvalidFactionID) { return m_localPlayerFaction; } else { IEntity* pLocalPlayerEntity = gEnv->pEntitySystem->GetEntity(g_pGame->GetIGameFramework()->GetClientActorId()); if (pLocalPlayerEntity) { IAIObject* pAIObject = pLocalPlayerEntity->GetAI(); if (pAIObject) { m_localPlayerFaction = pAIObject->GetFactionID(); } } } return m_localPlayerFaction; }
////////////////////////////////////////////////////////////////////////// // IsMountedWeaponUsableWithTarget // A piece of game-code moved from CryAction when scriptbind_AI moved to the AI system ////////////////////////////////////////////////////////////////////////// int CScriptBind_Game::IsMountedWeaponUsableWithTarget(IFunctionHandler *pH) { int paramCount = pH->GetParamCount(); if(paramCount<2) { GameWarning("%s: too few parameters.", __FUNCTION__); return pH->EndFunction(); } GET_ENTITY(1); if(!pEntity) { GameWarning("%s: wrong entity id in parameter 1.", __FUNCTION__); return pH->EndFunction(); } IAIObject* pAI = pEntity->GetAI(); if (!pAI) { GameWarning("%s: Entity '%s' does not have AI.",__FUNCTION__, pEntity->GetName()); return pH->EndFunction(); } EntityId itemEntityId; ScriptHandle hdl2; if(!pH->GetParam(2,hdl2)) { GameWarning("%s: wrong parameter 2 format.", __FUNCTION__); return pH->EndFunction(); } itemEntityId = (EntityId)hdl2.n; if (!itemEntityId) { GameWarning("%s: wrong entity id in parameter 2.", __FUNCTION__); return pH->EndFunction(); } IGameFramework *pGameFramework = gEnv->pGame->GetIGameFramework(); IItem* pItem = pGameFramework->GetIItemSystem()->GetItem(itemEntityId); if (!pItem) { //gEnv->pAISystem->Warning("<CScriptBind> ", "entity in parameter 2 is not an item/weapon"); GameWarning("%s: entity in parameter 2 is not an item/weapon.", __FUNCTION__); return pH->EndFunction(); } float minDist = 7; bool bSkipTargetCheck = false; Vec3 targetPos(ZERO); if(paramCount > 2) { for(int i=3;i <= paramCount ; i++) { if(pH->GetParamType(i) == svtBool) pH->GetParam(i,bSkipTargetCheck); else if(pH->GetParamType(i) == svtNumber) pH->GetParam(i,minDist); else if(pH->GetParamType(i) == svtObject) pH->GetParam(i,targetPos); } } IAIActor* pAIActor = CastToIAIActorSafe(pAI); if (!pAIActor) { GameWarning("%s: entity '%s' in parameter 1 is not an AI actor.", __FUNCTION__, pEntity->GetName()); return pH->EndFunction(); } IEntity* pItemEntity = pItem->GetEntity(); if(!pItemEntity) return pH->EndFunction(); if(!pItem->GetOwnerId()) { // weapon is not used, check if it is on a vehicle IEntity* pParentEntity = pItemEntity->GetParent(); if(pParentEntity) { IAIObject* pParentAI = pParentEntity->GetAI(); if(pParentAI && pParentAI->GetAIType()==AIOBJECT_VEHICLE) { // (MATT) Feature was cut and code was tricky, hence ignore weapons in vehicles {2008/02/15:11:08:51} return pH->EndFunction(); } } } else if( pItem->GetOwnerId()!= pEntity->GetId()) // item is used by someone else? return pH->EndFunction(false); // check target if(bSkipTargetCheck) return pH->EndFunction(true); IAIObject* pTarget = pAIActor->GetAttentionTarget(); if(targetPos.IsZero()) { if(!pTarget) return pH->EndFunction(); targetPos = pTarget->GetPos(); } Vec3 targetDir(targetPos - pItemEntity->GetWorldPos()); Vec3 targetDirXY(targetDir.x, targetDir.y, 0); float length2D = targetDirXY.GetLength(); if(length2D < minDist || length2D<=0) return pH->EndFunction(); targetDirXY /= length2D;//normalize IWeapon* pWeapon = pItem->GetIWeapon(); bool vehicleGun = pWeapon && pWeapon->GetHostId(); if (!vehicleGun) { Vec3 mountedAngleLimits(pItem->GetMountedAngleLimits()); float yawRange = DEG2RAD(mountedAngleLimits.z); if(yawRange > 0 && yawRange < gf_PI) { float deltaYaw = pItem->GetMountedDir().Dot(targetDirXY); if(deltaYaw < cosf(yawRange)) return pH->EndFunction(false); } float minPitch = DEG2RAD(mountedAngleLimits.x); float maxPitch = DEG2RAD(mountedAngleLimits.y); //maxPitch = (maxPitch - minPitch)/2; //minPitch = -maxPitch; float pitch = atanf(targetDir.z / length2D); if ( pitch < minPitch || pitch > maxPitch ) return pH->EndFunction(false); } if(pTarget) { IEntity* pTargetEntity = pTarget->GetEntity(); if(pTargetEntity) { // check target distance and where he's going IPhysicalEntity *phys = pTargetEntity->GetPhysics(); if(phys) { pe_status_dynamics dyn; phys->GetStatus(&dyn); Vec3 velocity ( dyn.v); velocity.z = 0; float speed = velocity.GetLength2D(); if(speed>0) { //velocity /= speed; if(length2D< minDist * 0.75f && velocity.Dot(targetDirXY)<=0) return pH->EndFunction(false); } } } } return pH->EndFunction(true); }
//------------------------------------------------------------------------ void CProjectile::Launch(const Vec3 &pos, const Vec3 &dir, const Vec3 &velocity, float speedScale) { Matrix34 worldTM=Matrix34(Matrix33::CreateRotationVDir(dir.GetNormalizedSafe())); worldTM.SetTranslation(pos); GetEntity()->SetWorldTM(worldTM); //Must set velocity after position, if not velocity could be reseted for PE_RIGID SetVelocity(pos, dir, velocity, speedScale); m_initial_pos = pos; m_initial_dir = dir; m_initial_vel = velocity; m_last = pos; // Attach effect when fired (not first update) if (m_trailEffectId<0) TrailEffect(true); IAIObject* pAI = 0; if ((pAI = GetEntity()->GetAI()) != NULL && pAI->GetAIType() == AIOBJECT_GRENADE) { IEntity *pOwnerEntity = gEnv->pEntitySystem->GetEntity(m_ownerId); if (pOwnerEntity && pOwnerEntity->GetAI()) { pe_status_dynamics dyn; pe_status_dynamics dynProj; if (pOwnerEntity->GetAI()->GetProxy() && pOwnerEntity->GetPhysics() && pOwnerEntity->GetPhysics()->GetStatus(&dyn) && GetEntity()->GetPhysics()->GetStatus(&dynProj)) { Vec3 ownerVel( dyn.v); Vec3 grenadeDir(dynProj.v.GetNormalizedSafe()); // Trigger the signal at the predicted landing position. Vec3 predictedPos = pos; float dummySpeed; if (GetWeapon()) GetWeapon()->PredictProjectileHit(pOwnerEntity->GetPhysics(), pos, dir, velocity, speedScale * m_pAmmoParams->speed, predictedPos, dummySpeed); /* bool res = pOwnerEntity->GetAI()->GetProxy()->GetSecWeapon()->PredictProjectileHit( pOwnerEntity->GetPhysics(), GetEntity()->GetPos(), grenadeDir, ownerVel, 1, predictedPos, speed);*/ gEnv->pAISystem->GrenadeEvent(predictedPos, 0.0f, AIGE_GRENADE_THROWN, GetEntity(), pOwnerEntity); // Inform the AI that sees the throw /* IAIObject* pOwnerAI = pOwnerEntity->GetAI(); AutoAIObjectIter it(gEnv->pAISystem->GetFirstAIObjectInRange(IAISystem::OBJFILTER_TYPE, AIOBJECT_PUPPET, predictedPos, 20.0f, false)); for(; it->GetObject(); it->Next()) { IAIObject* pAI = it->GetObject(); if (!pAI->IsEnabled()) continue; if (pOwnerAI && !pOwnerAI->IsHostile(pAI,false)) continue; // Only sense grenades that are on front of the AI and visible when thrown. // Another signal is sent when the grenade hits the ground. Vec3 delta = GetEntity()->GetPos() - pAI->GetPos(); // grenade to AI float dist = delta.NormalizeSafe(); const float thr = cosf(DEG2RAD(160.0f)); if (delta.Dot(pAI->GetViewDir()) > thr) { ray_hit hit; static const int objTypes = ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid; static const unsigned int flags = rwi_stop_at_pierceable|rwi_colltype_any; int res = gEnv->pPhysicalWorld->RayWorldIntersection(pAI->GetPos(), delta*dist, objTypes, flags, &hit, 1); if (!res || hit.dist > dist*0.9f) { IAISignalExtraData* pEData = gEnv->pAISystem->CreateSignalExtraData(); // no leak - this will be deleted inside SendAnonymousSignal pEData->point = predictedPos; pEData->nID = pOwnerEntity->GetId(); pEData->iValue = 1; gEnv->pAISystem->SendSignal(SIGNALFILTER_SENDER, 1, "OnGrenadeDanger", pAI, pEData); } } } */ } } } }
bool CDialogActorContext::Update(float dt) { if (IsAborted()) return true; // FIXME: this should never happen, as we should get a notification before. // temp leave in for tracking down a bug [AlexL: 23/11/2006] IEntity* pEntity = gEnv->pEntitySystem->GetEntity(m_entityID); if (pEntity == 0) { m_pIActor = 0; GameWarning("[DIALOG] CDialogActorContext::Update: %s Actor=%d (EntityId=%d) no longer existent.", m_pSession->GetDebugName(), m_actorID, m_entityID); AbortContext(true, CDialogSession::eAR_EntityDestroyed); return true; } CDialogSession::AlertnessInterruptMode alertnessInterruptMode = m_pSession->GetAlertnessInterruptMode(); if(alertnessInterruptMode != CDialogSession::None) { if(IAIObject* aiObject = pEntity->GetAI()) { if(IAIActorProxy* aiActorProxy = aiObject->GetProxy()) { if(aiActorProxy->GetAlertnessState() >= alertnessInterruptMode) { AbortContext(true, CDialogSession::eAR_AIAborted); return true; } } } } float now = m_pSession->GetCurTime(); if (!CheckActorFlags(CDialogSession::eDACF_NoActorDeadAbort) && m_pIActor && m_pIActor->IsDead()) { if (!IsAborted()) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Actor=%d (EntityId=%d) has died. Aborting.", m_pSession->GetDebugName(), m_actorID, m_entityID); AbortContext(true, CDialogSession::eAR_ActorDead); return true; } } if (m_bIsLocalPlayer) { // when the local player is involved in the conversation do some special checks if (DoLocalPlayerChecks(dt) == false) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Abort from LocalPlayer.", m_pSession->GetDebugName()); AbortContext(true, m_bIsAwareInRange == false ? CDialogSession::eAR_PlayerOutOfRange : CDialogSession::eAR_PlayerOutOfView); return true; } } if (m_bAbortFromAI) { m_bAbortFromAI = false; if (!IsAborted()) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Abort from AI.", m_pSession->GetDebugName()); AbortContext(true, CDialogSession::eAR_AIAborted); return true; } } if (SessionAllowsLookAt()) { DoStickyLookAt(); } int loop = 0; do { // DiaLOG::Log("[DIALOG] DiaLOG::eAlways"CDialogActorContext::Update: %s now=%f actorId=%d loop=%d", m_pSession->GetDebugName(), now, m_actorID, loop); bool bAdvance = false; switch (m_phase) { case eDAC_Idle: break; case eDAC_NewLine: { m_bHasScheduled = false; m_lookAtTimeOut = LOOKAT_TIMEOUT; m_animTimeOut = ANIM_TIMEOUT; m_soundTimeOut = SOUND_TIMEOUT; // wait one second until sound is timed out m_bAnimScheduled = false; m_bAnimStarted = false; m_bSoundScheduled = false; m_bSoundStarted = false; m_soundLength = 0.0f; if (m_pCurLine->m_flagResetLookAt) { m_stickyLookAtActorID = CDialogScript::NO_ACTOR_ID; m_lookAtActorID = CDialogScript::NO_ACTOR_ID; } // check if look-at sticky is set else if (m_pCurLine->m_lookatActor != CDialogScript::NO_ACTOR_ID) { if (m_pCurLine->m_flagLookAtSticky == false) { m_lookAtActorID = m_pCurLine->m_lookatActor; m_stickyLookAtActorID = CDialogScript::NO_ACTOR_ID; } else { m_stickyLookAtActorID = m_pCurLine->m_lookatActor; m_lookAtActorID = CDialogScript::NO_ACTOR_ID; } } bAdvance = true; // handle the first sticky look-at here if (SessionAllowsLookAt()) { DoStickyLookAt(); } } break; case eDAC_LookAt: { bool bWaitForLookAtFinish = true; bAdvance = true; if (SessionAllowsLookAt() == false) { break; } // Question: maybe do this although EP is requested if (bWaitForLookAtFinish && m_lookAtActorID != CDialogScript::NO_ACTOR_ID && m_pCurLine->m_flagAGEP == false) { IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID); IEntity* pLookAtEntity = m_pSession->GetActorEntity(m_lookAtActorID); if (pActorEntity != 0) { m_lookAtTimeOut -= dt; bool bTargetReached; //bool bSuccess = DoLookAt(pActorEntity, pLookAtEntity, bTargetReached); DoLookAt(pActorEntity, pLookAtEntity, bTargetReached); //DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_LookAt %d", // m_pSession->GetDebugName(), now, m_actorID, bTargetReached); if (/* bSuccess == false || */ bTargetReached || m_lookAtTimeOut<=0.0f) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_LookAt %s", m_pSession->GetDebugName(), now, m_actorID, bTargetReached ? "Target Reached" : "Timed Out"); m_lookAtActorID = CDialogScript::NO_ACTOR_ID; } else bAdvance = false; } } } break; case eDAC_Anim: { bAdvance = true; if (SessionAllowsLookAt() && m_lookAtActorID != CDialogScript::NO_ACTOR_ID) // maybe: don't do this when EP is requested [&& m_pCurLine->m_flagAGEP == false] { IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID); IEntity* pLookAtEntity = m_pSession->GetActorEntity(m_lookAtActorID); if (pActorEntity != 0) { m_lookAtTimeOut -= dt; bool bTargetReached; //bool bSuccess = DoLookAt(pActorEntity, pLookAtEntity, bTargetReached); DoLookAt(pActorEntity, pLookAtEntity, bTargetReached); if (/* bSuccess == false || */ bTargetReached || m_lookAtTimeOut<=0.0f) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_Anim %s", m_pSession->GetDebugName(), now, m_actorID, bTargetReached ? "Target Reached" : "Timed Out"); m_lookAtActorID = CDialogScript::NO_ACTOR_ID; } else bAdvance = false; } } const bool bHasAnim = !m_pCurLine->m_anim.empty(); if (SessionAllowsAnim() && bHasAnim) { bAdvance = false; if (!m_bAnimScheduled) { m_bSoundStopsAnim = false; // whenever there is a new animation, no need for the still playing sound // to stop the animation m_bAnimStarted = false; m_bAnimScheduled = true; // schedule animation IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID); if (pActorEntity) DoAnimAction(pActorEntity, m_pCurLine->m_anim, m_pCurLine->m_flagAGSignal, m_pCurLine->m_flagAGEP); else bAdvance = true; } else { // we scheduled it already // wait until it starts or timeout m_animTimeOut-=dt; bAdvance = m_animTimeOut <= 0.0f || m_bAnimStarted; if (bAdvance) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_Anim %s", m_pSession->GetDebugName(), now, m_actorID, m_bAnimStarted ? "Anim Started" : "Anim Timed Out"); } } } } break; case eDAC_ScheduleSoundPlay: { bAdvance = true; const bool bHasSound = !m_pCurLine->m_sound.empty(); if (bHasSound) { if (m_bSoundScheduled == false) { IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID); if (pActorEntity == 0) break; IEntityAudioProxy* pActorAudioProxy = m_pSession->GetEntityAudioProxy(pActorEntity); if (pActorAudioProxy == 0) break; REINST("play voice line"); // start a new sound //int sFlags = CDialogActorContext::SOUND_FLAGS; //if (CDialogSystem::sLoadSoundSynchronously != 0) // sFlags |= FLAG_SOUND_LOAD_SYNCHRONOUSLY; // //// if it's the local player make the sound relative to the entity //if (m_bIsLocalPlayer) // sFlags |= FLAG_SOUND_RELATIVE; //const char* soundKey = GetSoundKey(m_pCurLine->m_sound); //ISound* pSound = 0; //pSound = gEnv->pAudioSystem->CreateSound(soundKey, sFlags); //DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContex::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Starting '%s' [key=%s]", // m_pSession->GetDebugName(), now, m_actorID, m_pCurLine->m_sound.c_str(), soundKey); //if (!pSound) //{ // GameWarning("[DIALOG] CDialogActorContext::Update: %s ActorID=%d: Cannot play sound '%s'", // m_pSession->GetDebugName(), m_actorID, m_pCurLine->m_sound.c_str()); //} //if (pSound) //{ // if (m_bSoundStopsAnim) // { // m_bSoundStopsAnim = false; // if (m_pAGState != 0) // { // ResetAGState(); // DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Stopping old animation", // m_pSession->GetDebugName(), now, m_actorID); // } // } // StopSound(); // apparently, if we dont explicitely stop the sound, in rare cases it never stops. Im pretty sure that is an engine/fmod bug. // // stopping it this way may lead to some subtitles staying for an extra couple seconds on screen (this comes from another engine bug/problem related to an extra fade out time added to cover the dummy NAX soudns) // m_soundID = pSound->GetId(); // m_bSoundStarted = false; // m_bSoundScheduled = true; // // tell the animation whether to stop on sound stop // m_bSoundStopsAnim = m_pCurLine->m_flagSoundStopsAnim; // DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Reg as listener on 0x%p '%s'", // m_pSession->GetDebugName(), now, m_actorID, pSound, pSound->GetName()); // pSound->SetSemantic(eSoundSemantic_Dialog); // pSound->AddEventListener(this, "DialogActor"); // event listener will set m_soundLength // // sound proxy uses head pos on dialog sounds // const bool bResult = pActorAudioProxy->PlaySound(pSound, Vec3(ZERO), FORWARD_DIRECTION, 1.0f); // if (bResult) // { // // Fetch some localization info for the sound // ILocalizationManager* pLocMgr = gEnv->pSystem->GetLocalizationManager(); // if (pLocMgr && CDialogSystem::sWarnOnMissingLoc != 0) // { // SLocalizedInfoGame GameInfo; // const bool bFound = pLocMgr->GetLocalizedInfoByKey(soundKey, GameInfo); // if (!bFound) // { // GameWarning("[DIALOG] CDialogActorContext::Update: '%s' DialogScript '%s': Localized info for '%s' not found!", // m_pSession->GetDebugName(), m_pSession->GetScript()->GetID().c_str(), m_pCurLine->m_sound.c_str()); // } // } // bAdvance = false; // don't advance // } // else // { // GameWarning("[DIALOG] CDialogActorContext::Update: %s ActorID=%d: Cannot play sound '%s' [AudioProxy ignored playback]", // m_pSession->GetDebugName(), m_actorID, m_pCurLine->m_sound.c_str()); // pSound->Stop(ESoundStopMode_AtOnce); // m_soundID = INVALID_SOUNDID; // pSound->RemoveEventListener(this); // pSound = NULL; // } //} //else //{ // GameWarning("[DIALOG] CDialogActorContext::Update: %s ActorID=%d: Cannot play sound '%s' [AudioProxy returns invalid sound]", // m_pSession->GetDebugName(), m_actorID, m_pCurLine->m_sound.c_str()); //} } else { // sound has been scheduled // wait for sound start or timeout m_soundTimeOut-=dt; const bool bTimedOut = m_soundTimeOut <= 0.0f; bAdvance = bTimedOut || m_bSoundStarted; if (bAdvance) { if (bTimedOut) StopSound(true); // unregister from sound as listener and free resource DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay %s", m_pSession->GetDebugName(), now, m_actorID, m_bSoundStarted ? "Sound Started" : "Sound Timed Out"); } } } } break; case eDAC_SoundFacial: { bAdvance = true; const bool bHasSound = !m_pCurLine->m_sound.empty(); const bool bHasFacial = !m_pCurLine->m_facial.empty() || m_pCurLine->m_flagResetFacial; if (bHasFacial) { IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID); if (pActorEntity) DoFacialExpression(pActorEntity, m_pCurLine->m_facial, m_pCurLine->m_facialWeight, m_pCurLine->m_facialFadeTime); } float delay = m_pCurLine->m_delay; if (bHasSound && m_bSoundStarted) { if (m_soundLength < 0.0f) { m_soundLength = Random(2.0f,6.0f); DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_SoundFacial Faking SoundTime to %f", m_pSession->GetDebugName(), now, m_actorID, m_soundLength); } DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_SoundFacial Delay=%f SoundTime=%f", m_pSession->GetDebugName(), now , m_actorID, delay, m_soundLength); delay += m_soundLength; } // schedule END if (delay < 0.0f) delay = 0.0f; m_pSession->ScheduleNextLine(delay+0.05f); // ~1 frame at 20 fps. we now stop the current line sound before going for next line. Sometimes, this stop call is right before the // sound has actually stopped in engine side. When that happens, the engine is adding ~2 seconds extra to the sound duration (because fade problems related to NAX header) // and that looks bad if subtitles are enabled. So we add this small delay here to try to minimize that situation. // this is part of the workaround for some dialogs apparently never finishing in the engine side (rare). m_bHasScheduled = true; } break; case eDAC_EndLine: bAdvance = true; if (m_bHasScheduled == false) { m_bHasScheduled = true; m_pSession->ScheduleNextLine(m_pCurLine->m_delay); } break; } if (bAdvance) { AdvancePhase(); dt = 0.0f; ++loop; assert (loop <= eDAC_EndLine+1); if (loop > eDAC_EndLine+1) { DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Actor=%d InternalLoopCount=%d. Final Exit!", m_pSession->GetDebugName(), m_actorID, loop); } } else break; } while (true); return true; }
void CDialogActorContext::CancelCurrent(bool bResetStates) { if (!m_bNeedsCancel) return; assert (m_bInCancel == false); if (m_bInCancel == true) return; m_bInCancel = true; // remove from AG if (m_pAGState != 0) { m_pAGState->RemoveListener(this); if (bResetStates) { ResetAGState(); } m_pAGState = 0; } m_queryID = 0; m_bAnimStarted = false; // reset lookat IEntity* pEntity = gEnv->pEntitySystem->GetEntity(m_entityID); if (pEntity) { IAIObject* pAI = pEntity->GetAI(); if (pAI) { IAIActor* pAIActor = pAI->CastToIAIActor(); if (pAIActor) { if (m_bLookAtNeedsReset) { pAIActor->ResetLookAt(); m_bLookAtNeedsReset = false; } IPipeUser* pPipeUser = pAI->CastToIPipeUser(); if (pPipeUser) { if (m_goalPipeID > 0) { if (GetAIBehaviourMode() == CDialogSession::eDIB_InterruptMedium) { int dummyPipe = 0; ExecuteAI(dummyPipe, "ACT_DIALOG_OVER", 0, false); } pPipeUser->UnRegisterGoalPipeListener( this, m_goalPipeID ); pPipeUser->RemoveSubPipe(m_goalPipeID, true); m_goalPipeID = 0; } if (m_exPosAnimPipeID > 0) { pPipeUser->UnRegisterGoalPipeListener( this, m_exPosAnimPipeID ); pPipeUser->CancelSubPipe(m_exPosAnimPipeID); pPipeUser->RemoveSubPipe(m_exPosAnimPipeID, false); m_exPosAnimPipeID = 0; } } } } } // facial expression is always reset // if (bResetStates) { // Reset Facial Expression IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID); if (pActorEntity) { DoFacialExpression(pActorEntity, "", 1.0f, 0.0f); } } // should we stop the current sound? // we don't stop the sound if the actor aborts and has eDACF_NoAbortSound set. // if actor died (entity destroyed) though, sound is stopped anyway const bool bDontStopSound = (IsAborted() == false && CheckActorFlags(CDialogSession::eDACF_NoAbortSound)) || (IsAborted() && CheckActorFlags(CDialogSession::eDACF_NoAbortSound) && GetAbortReason() != CDialogSession::eAR_ActorDead && GetAbortReason() != CDialogSession::eAR_EntityDestroyed); if (bDontStopSound == false) { // stop sound (this also forces m_soundId to be INVALID_SOUNDID) and markes Context as non-playing! // e.g. IsStillPlaying will then return false // we explicitly stop the sound in the d'tor if we don't take this path here StopSound(); } IEntitySystem* pES = gEnv->pEntitySystem; pES->RemoveEntityEventListener( m_entityID, ENTITY_EVENT_AI_DONE, this ); pES->RemoveEntityEventListener( m_entityID, ENTITY_EVENT_DONE, this ); pES->RemoveEntityEventListener( m_entityID, ENTITY_EVENT_RESET, this ); m_phase = eDAC_Idle; m_bInCancel = false; m_bNeedsCancel = false; }
//------------------------------------------------------------------------ void CGameRules::ClientExplosion(const ExplosionInfo &explosionInfo) { // let 3D engine know about explosion (will create holes and remove vegetation) //if (explosionInfo.hole_size > 0.0f) //{ //gEnv->p3DEngine->OnExplosion(explosionInfo.pos, explosionInfo.hole_size, true); //} TExplosionAffectedEntities affectedEntities; if (gEnv->bServer) { CullEntitiesInExplosion(explosionInfo); pe_explosion explosion; explosion.epicenter = explosionInfo.pos; explosion.rmin = explosionInfo.minRadius; explosion.rmax = explosionInfo.radius; if (explosion.rmax==0) explosion.rmax=0.0001f; explosion.r = explosion.rmin; explosion.impulsivePressureAtR = explosionInfo.pressure; explosion.epicenterImp = explosionInfo.pos; explosion.explDir = explosionInfo.dir; explosion.nGrow = 2; explosion.rminOcc = 0.07f; // we separate the calls to SimulateExplosion so that we can define different radii for AI and physics bodies explosion.holeSize = 0.0f; explosion.nOccRes = explosion.rmax>50.0f ? 0:32; gEnv->pPhysicalWorld->SimulateExplosion( &explosion, 0, 0, ent_living); CreateScriptExplosionInfo(m_scriptExplosionInfo, explosionInfo); UpdateAffectedEntitiesSet(affectedEntities, &explosion); // check vehicles IVehicleSystem *pVehicleSystem = g_pGame->GetIGameFramework()->GetIVehicleSystem(); uint32 vcount = pVehicleSystem->GetVehicleCount(); if (vcount > 0) { IVehicleIteratorPtr iter = g_pGame->GetIGameFramework()->GetIVehicleSystem()->CreateVehicleIterator(); while (IVehicle* pVehicle = iter->Next()) { if(IEntity *pEntity = pVehicle->GetEntity()) { AABB aabb; pEntity->GetWorldBounds(aabb); IPhysicalEntity* pEnt = pEntity->GetPhysics(); if (pEnt && aabb.GetDistanceSqr(explosionInfo.pos) <= explosionInfo.radius*explosionInfo.radius) { float affected = gEnv->pPhysicalWorld->CalculateExplosionExposure(&explosion, pEnt); AddOrUpdateAffectedEntity(affectedEntities, pEntity, affected); } } } } explosion.rmin = explosionInfo.minPhysRadius; explosion.rmax = explosionInfo.physRadius; if (explosion.rmax==0) explosion.rmax=0.0001f; explosion.r = explosion.rmin; explosion.holeSize = explosionInfo.hole_size; explosion.nOccRes = -1; // makes second call re-use occlusion info gEnv->pPhysicalWorld->SimulateExplosion( &explosion, 0, 0, ent_rigid|ent_sleeping_rigid|ent_independent|ent_static ); UpdateAffectedEntitiesSet(affectedEntities, &explosion); CommitAffectedEntitiesSet(m_scriptExplosionInfo, affectedEntities); float fSuitEnergyBeforeExplosion = 0.0f; float fHealthBeforeExplosion = 0.0f; IActor *pClientActor = g_pGame->GetIGameFramework()->GetClientActor(); if(pClientActor) { fSuitEnergyBeforeExplosion = static_cast<CPlayer *>(pClientActor)->GetNanoSuit()->GetSuitEnergy(); fHealthBeforeExplosion = pClientActor->GetHealth(); } CallScript(m_serverStateScript, "OnExplosion", m_scriptExplosionInfo); if(pClientActor) { float fDeltaSuitEnergy = fSuitEnergyBeforeExplosion - static_cast<CPlayer *>(pClientActor)->GetNanoSuit()->GetSuitEnergy(); float fDeltaHealth = fHealthBeforeExplosion - pClientActor->GetHealth(); if(fDeltaSuitEnergy >= 50.0f || fDeltaHealth >= 20.0f) { SAFE_SOUNDMOODS_FUNC(AddSoundMood(SOUNDMOOD_EXPLOSION,MIN(fDeltaSuitEnergy+fDeltaHealth,100.0f))); } } // call hit listeners if any if (m_hitListeners.empty() == false) { THitListenerVec::iterator iter = m_hitListeners.begin(); while (iter != m_hitListeners.end()) { (*iter)->OnServerExplosion(explosionInfo); ++iter; } } } if (gEnv->bClient) { if (explosionInfo.pParticleEffect) explosionInfo.pParticleEffect->Spawn(true, IParticleEffect::ParticleLoc(explosionInfo.pos, explosionInfo.dir, explosionInfo.effect_scale)); if (!gEnv->bServer) { CreateScriptExplosionInfo(m_scriptExplosionInfo, explosionInfo); } else { affectedEntities.clear(); CommitAffectedEntitiesSet(m_scriptExplosionInfo, affectedEntities); } CallScript(m_clientStateScript, "OnExplosion", m_scriptExplosionInfo); // call hit listeners if any if (m_hitListeners.empty() == false) { THitListenerVec::iterator iter = m_hitListeners.begin(); while (iter != m_hitListeners.end()) { (*iter)->OnExplosion(explosionInfo); ++iter; } } } ProcessClientExplosionScreenFX(explosionInfo); ProcessExplosionMaterialFX(explosionInfo); IEntity *pShooter = m_pEntitySystem->GetEntity(explosionInfo.shooterId); if (gEnv->pAISystem && !gEnv->bMultiplayer) { IAIObject *pShooterAI(pShooter!=NULL ? pShooter->GetAI() : NULL); gEnv->pAISystem->ExplosionEvent(explosionInfo.pos,explosionInfo.radius, pShooterAI); } }
void CTacBullet::HandleEvent(const SGameObjectEvent &event) { if (m_destroying) return; CProjectile::HandleEvent(event); if (event.event == eGFE_OnCollision) { EventPhysCollision *pCollision = reinterpret_cast<EventPhysCollision *>(event.ptr); if (!pCollision) return; IEntity *pTarget = pCollision->iForeignData[1]==PHYS_FOREIGN_ID_ENTITY ? (IEntity*)pCollision->pForeignData[1]:0; if (pTarget) { CGameRules *pGameRules = g_pGame->GetGameRules(); EntityId targetId = pTarget->GetId(); CActor *pActor = (CActor *)gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(targetId); if(pActor && pActor->CanSleep()) { if(pActor->GetLinkedVehicle() && !gEnv->bMultiplayer) { SleepTargetInVehicle(m_ownerId,pActor); } else if(IEntity* pOwnerEntity = gEnv->pEntitySystem->GetEntity(m_ownerId)) { if (pTarget->GetAI() && !pTarget->GetAI()->IsFriendly(pOwnerEntity->GetAI(), false)) { float sleepTime = 30.0f; if(m_pAmmoParams) sleepTime = m_pAmmoParams->sleepTime; SimpleHitInfo info(m_ownerId, targetId, m_weaponId, 1, sleepTime); // 0=tag,1=tac info.remote=IsRemote(); pGameRules->ClientSimpleHit(info); } } } else { Vec3 dir(0, 0, 0); if (pCollision->vloc[0].GetLengthSquared() > 1e-6f) dir = pCollision->vloc[0].GetNormalized(); HitInfo hitInfo(m_ownerId, pTarget->GetId(), m_weaponId, (float)m_damage, 0.0f, pGameRules->GetHitMaterialIdFromSurfaceId(pCollision->idmat[1]), pCollision->partid[1], m_hitTypeId, pCollision->pt, dir, pCollision->n); hitInfo.remote = IsRemote(); hitInfo.projectileId = GetEntityId(); hitInfo.bulletType = m_pAmmoParams->bulletType; pGameRules->ClientHit(hitInfo); } } else if (pCollision->pEntity[0]->GetType() == PE_PARTICLE) { float bouncy, friction; uint32 pierceabilityMat; gEnv->pPhysicalWorld->GetSurfaceParameters(pCollision->idmat[1], bouncy, friction, pierceabilityMat); pierceabilityMat&=sf_pierceable_mask; pe_params_particle params; if(pCollision->pEntity[0]->GetParams(¶ms)==0) SetDefaultParticleParams(¶ms); if (pierceabilityMat>params.iPierceability && pCollision->idCollider!=-1) return; } Destroy(); } }
//--------------------------------------------------------------------------- void CVehicleWeapon::CheckForFriendlyAI(float frameTime) { if (m_pVehicle) { if (CActor* pOwnerActor = GetOwnerActor()) { if (pOwnerActor->IsPlayer() && !gEnv->bMultiplayer) { m_timeToUpdate -= frameTime; if (m_timeToUpdate > 0.f) return; m_timeToUpdate = 0.15f; if (IMovementController* pMC = pOwnerActor->GetMovementController()) { SMovementState info; pMC->GetMovementState(info); LowerWeapon(false); // Try ray hit ray_hit rayhit; IPhysicalEntity* pSkipEnts[10]; int nSkip = CSingle::GetSkipEntities(this, pSkipEnts, 10); int intersect = gEnv->pPhysicalWorld->RayWorldIntersection(info.weaponPosition, info.aimDirection * 150.f, ent_all, rwi_stop_at_pierceable | rwi_colltype_any, &rayhit, 1, pSkipEnts, nSkip); if (intersect && rayhit.pCollider) { if (IEntity* pLookAtEntity = m_pEntitySystem->GetEntityFromPhysics(rayhit.pCollider)) { if (EntityId lookAtEntityId = pLookAtEntity->GetId()) { IAIObject* pLookAtAIObject = pLookAtEntity->GetAI(); IEntity* pOwnerEntity = pOwnerActor->GetEntity(); if (pOwnerEntity && pLookAtAIObject && (lookAtEntityId != GetEntityId())) { if (IVehicle* pVehicle = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(lookAtEntityId)) { if (pVehicle->HasFriendlyPassenger(pOwnerEntity)) { LowerWeapon(true); StopFire(); // Just in case } } else { if (pLookAtAIObject->IsFriendly(pOwnerEntity->GetAI(), false)) { LowerWeapon(true); StopFire(); // Just in case } } } else { // Special case (Animated objects), check for script table value "bNoFriendlyFire" if (IScriptTable* pScriptTable = pLookAtEntity->GetScriptTable()) { SmartScriptTable props; if (pScriptTable->GetValue("Properties", props)) { int isFriendly; if (props->GetValue("bNoFriendlyFire", isFriendly) && (isFriendly != 0)) { LowerWeapon(true); StopFire(); // Just in case } } } } } } } } } } } }
//------------------------------------------------------------------------ void CBullet::HandleEvent(const SGameObjectEvent &event) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); CProjectile::HandleEvent(event); if (event.event == eGFE_OnCollision) { if (m_destroying) return; EventPhysCollision *pCollision = reinterpret_cast<EventPhysCollision *>(event.ptr); if (!pCollision) return; IEntity *pTarget = pCollision->iForeignData[1]==PHYS_FOREIGN_ID_ENTITY ? (IEntity*)pCollision->pForeignData[1]:0; //Only process hits that have a target if(pTarget) { Vec3 dir(0, 0, 0); if (pCollision->vloc[0].GetLengthSquared() > 1e-6f) dir = pCollision->vloc[0].GetNormalized(); CGameRules *pGameRules = g_pGame->GetGameRules(); IActor* pActor = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_ownerId); bool ok = true; if(!gEnv->bMultiplayer && pActor && pActor->IsPlayer()) { IActor* pAITarget = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pTarget->GetId()); if(pAITarget && pTarget->GetAI() && pTarget->GetAI()->IsFriendly(pActor->GetEntity()->GetAI(), false)) { pGameRules->SetEntityToIgnore(pTarget->GetId()); ok = false; } } if(ok) { HitInfo hitInfo(m_ownerId, pTarget->GetId(), m_weaponId, (float)m_damage, 0.0f, pGameRules->GetHitMaterialIdFromSurfaceId(pCollision->idmat[1]), pCollision->partid[1], m_hitTypeId, pCollision->pt, dir, pCollision->n); hitInfo.remote = IsRemote(); hitInfo.projectileId = GetEntityId(); hitInfo.bulletType = m_pAmmoParams->bulletType; pGameRules->ClientHit(hitInfo); // Notify AI if (gEnv->pAISystem && !gEnv->bMultiplayer) { static int htMelee = pGameRules->GetHitTypeId("melee"); if (m_ownerId && m_hitTypeId != htMelee) { ISurfaceType *pSurfaceType = pGameRules->GetHitMaterial(hitInfo.material); const ISurfaceType::SSurfaceTypeAIParams* pParams = pSurfaceType ? pSurfaceType->GetAIParams() : 0; const float radius = pParams ? pParams->fImpactRadius : 2.5f; const float soundRadius = pParams ? pParams->fImpactSoundRadius : 20.0f; // Associate event with vehicle if the shooter is in a vehicle (tank cannon shot, etc) EntityId ownerId = m_ownerId; if (pActor && pActor->GetLinkedVehicle() && pActor->GetLinkedVehicle()->GetEntityId()) ownerId = pActor->GetLinkedVehicle()->GetEntityId(); SAIStimulus stim(AISTIM_BULLET_HIT, 0, ownerId, 0, pCollision->pt, ZERO, radius); gEnv->pAISystem->RegisterStimulus(stim); SAIStimulus stimSound(AISTIM_SOUND, AISOUND_COLLISION_LOUD, ownerId, 0, pCollision->pt, ZERO, soundRadius, AISTIMPROC_FILTER_LINK_WITH_PREVIOUS); gEnv->pAISystem->RegisterStimulus(stimSound); } } } } else { // Notify AI // The above case only catches entity vs. entity hits, the AI is interested in all hits. if (gEnv->pAISystem && !gEnv->bMultiplayer) { CGameRules *pGameRules = g_pGame->GetGameRules(); static int htMelee = pGameRules->GetHitTypeId("melee"); if (m_ownerId && m_hitTypeId != htMelee) { int material = pGameRules->GetHitMaterialIdFromSurfaceId(pCollision->idmat[1]); ISurfaceType *pSurfaceType = pGameRules->GetHitMaterial(material); const ISurfaceType::SSurfaceTypeAIParams* pParams = pSurfaceType ? pSurfaceType->GetAIParams() : 0; const float radius = pParams ? pParams->fImpactRadius : 2.5f; const float soundRadius = pParams ? pParams->fImpactSoundRadius : 20.0f; // Associate event with vehicle if the shooter is in a vehicle (tank cannon shot, etc) EntityId ownerId = m_ownerId; IActor* pActor = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_ownerId); if (pActor && pActor->GetLinkedVehicle() && pActor->GetLinkedVehicle()->GetEntityId()) ownerId = pActor->GetLinkedVehicle()->GetEntityId(); SAIStimulus stim(AISTIM_BULLET_HIT, 0, ownerId, 0, pCollision->pt, ZERO, radius); gEnv->pAISystem->RegisterStimulus(stim); SAIStimulus stimSound(AISTIM_SOUND, AISOUND_COLLISION_LOUD, ownerId, 0, pCollision->pt, ZERO, soundRadius, AISTIMPROC_FILTER_LINK_WITH_PREVIOUS); gEnv->pAISystem->RegisterStimulus(stimSound); } } } if (pCollision->pEntity[0]->GetType() == PE_PARTICLE) { float bouncy, friction; uint32 pierceabilityMat; gEnv->pPhysicalWorld->GetSurfaceParameters(pCollision->idmat[1], bouncy, friction, pierceabilityMat); pierceabilityMat&=sf_pierceable_mask; pe_params_particle params; if(pCollision->pEntity[0]->GetParams(¶ms)==0) SetDefaultParticleParams(¶ms); //Under water trail Vec3 pos=pCollision->pt; if ((pCollision->idmat[1] == CBullet::m_waterMaterialId) && (pCollision->pEntity[1]!=gEnv->pPhysicalWorld->AddGlobalArea() || !gEnv->p3DEngine->GetVisAreaFromPos(pos))) { //Reduce drastically bullet velocity (to be able to see the trail effect) //pe_params_particle pparams; //if(m_pPhysicalEntity->GetParams(&pparams)==0) //SetDefaultParticleParams(&pparams); //pparams.velocity = 25.0f; //m_pPhysicalEntity->SetParams(&pparams); if(m_trailUnderWaterId<0) { //Check terrain/against water level float terrainHeight = gEnv->p3DEngine->GetTerrainElevation(pCollision->pt.x,pCollision->pt.y); float waterLevel = gEnv->p3DEngine->GetWaterLevel(&(pCollision->pt)); if(waterLevel>terrainHeight) { TrailEffect(true,true); return; } } } if (pierceabilityMat<=params.iPierceability || pCollision->idCollider==-1) //Do not destroy if collides water Destroy(); } } }
void CVisibleObjectsHelper::CheckVisibilityToAI(const TActiveVisibleObjects &activeVisibleObjects, const Agent& agent) const { assert(agent.IsValid()); IScriptSystem *pSS = gEnv->pScriptSystem; assert(pSS); IEntity *pAIEntity = gEnv->pEntitySystem->GetEntity(agent.GetEntityID()); TActiveVisibleObjects::const_iterator itObject = activeVisibleObjects.begin(); TActiveVisibleObjects::const_iterator itObjectEnd = activeVisibleObjects.end(); for (; itObject != itObjectEnd; ++itObject) { const SVisibleObject *visibleObject = (*itObject); assert(visibleObject); const bool bVisible = IsObjectVisible(agent, *visibleObject); if (bVisible) { // Callback function if (visibleObject->pFunc) { visibleObject->pFunc(agent, visibleObject->entityId, visibleObject->pFuncArg); } // Notify on seen if (pAIEntity != NULL && eVOR_FlagNotifyOnSeen == (visibleObject->rule & eVOR_FlagNotifyOnSeen)) { IVisionMap& visionMap = *gEnv->pAISystem->GetVisionMap(); const ObservableParams* pObservableParams = visionMap.GetObservableParams(visibleObject->visionId); if (pObservableParams != NULL) { IEntity* pTargetEntity = gEnv->pEntitySystem->GetEntity(pObservableParams->entityId); ITargetTrackManager *pTargetTrackManager = gEnv->pAISystem->GetTargetTrackManager(); if (pTargetEntity != NULL && pTargetTrackManager != NULL) { const tAIObjectID aiOwnerId = pAIEntity->GetAIObjectID(); const tAIObjectID aiTargetId = pTargetEntity->GetAIObjectID(); TargetTrackHelpers::SStimulusEvent eventInfo; eventInfo.vPos = pTargetEntity->GetWorldPos(); eventInfo.eStimulusType = TargetTrackHelpers::eEST_Visual; eventInfo.eTargetThreat = AITHREAT_INTERESTING; pTargetTrackManager->HandleStimulusEventForAgent(aiOwnerId, aiTargetId, "SeeThrownObject", eventInfo); IAIObject* pAIObjectSender = pAIEntity->GetAI(); if (pAIObjectSender) { IAISignalExtraData *pSignalData = gEnv->pAISystem->CreateSignalExtraData(); if (pSignalData) { pSignalData->nID = visibleObject->entityId; } gEnv->pAISystem->SendSignal(SIGNALFILTER_SENDER, 1, "OnSawObjectMove", pAIObjectSender, pSignalData); } } } } } } }
//------------------------------------------------------------------------ void CMelee::Hit(const Vec3 &pt, const Vec3 &dir, const Vec3 &normal, IPhysicalEntity *pCollider, int partId, int ipart, int surfaceIdx, float damageScale, bool remote) { // generate the damage IEntity *pTarget = gEnv->pEntitySystem->GetEntityFromPhysics(pCollider); // Report punch to AI system. // The AI notification must come before the game rules are // called so that the death handler in AIsystem understands that the hit // came from the player. bool ok = true; if(pTarget) { CActor *pActor = m_pWeapon->GetOwnerActor(); if(!gEnv->bMultiplayer && pActor && pActor->IsPlayer()) { if (IActor *pAITarget = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pTarget->GetId())) { if (IAIObject *pAITargetObject = pTarget->GetAI()) { if (pAITargetObject->IsFriendly(pActor->GetEntity()->GetAI(), false)) { ok = false; m_noImpulse = true; } } } } if(ok) { CGameRules *pGameRules = g_pGame->GetGameRules(); HitInfo info(m_pWeapon->GetOwnerId(), pTarget->GetId(), m_pWeapon->GetEntityId(), m_pShared->meleeparams.damage * damageScale * m_meleeScale, 0.0f, pGameRules->GetHitMaterialIdFromSurfaceId(surfaceIdx), partId, pGameRules->GetHitTypeId(m_pShared->meleeparams.hit_type.c_str()), pt, dir, normal); info.remote = remote; pGameRules->ClientHit(info); } } // play effects if(ok) { if(IMaterialEffects *pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects()) { TMFXEffectId effectId = pMaterialEffects->GetEffectId("melee", surfaceIdx); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; params.pos = pt; params.normal = -dir; params.playflags = MFX_PLAY_ALL | MFX_DISABLE_DELAY; params.soundSemantic = eSoundSemantic_Player_Foley; pMaterialEffects->ExecuteEffect(effectId, params); } } } ApplyCameraShake(true); m_pWeapon->PlayAction(m_pShared->meleeactions.hit.c_str()); }
uint8 CAutoAimManager::GetTargetFaction( IEntity& targetEntity ) const { IAIObject* pAIObject = targetEntity.GetAI(); return pAIObject ? pAIObject->GetFactionID() : IFactionMap::InvalidFactionID; }
//------------------------------------------------------------------------ void CMelee::Hit(const Vec3 &pt, const Vec3 &dir, const Vec3 &normal, IPhysicalEntity *pCollider, int partId, int ipart, int surfaceIdx, float damageScale, bool remote) { // generate the damage IEntity *pTarget = gEnv->pEntitySystem->GetEntityFromPhysics(pCollider); // Report punch to AI system. // The AI notification must come before the game rules are // called so that the death handler in AIsystem understands that the hit // came from the player. CActor *pActor = m_pWeapon->GetOwnerActor(); if (pActor && pActor->GetActorClass() == CPlayer::GetActorClassType()) { CPlayer *pPlayer = (CPlayer *)pActor; if (pPlayer && pPlayer->GetNanoSuit()) { if (pPlayer->GetEntity() && pPlayer->GetEntity()->GetAI()) { SAIEVENT AIevent; AIevent.targetId = pTarget ? pTarget->GetId() : 0; // pPlayer->GetNanoSuit()->GetMode() == NANOMODE_STRENGTH pPlayer->GetEntity()->GetAI()->Event(AIEVENT_PLAYER_STUNT_PUNCH, &AIevent); } } } bool ok = true; if(pTarget) { if(!gEnv->bMultiplayer && pActor && pActor->IsPlayer()) { IActor* pAITarget = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pTarget->GetId()); if(pAITarget && pTarget->GetAI() && !pTarget->GetAI()->IsHostile(pActor->GetEntity()->GetAI(),false)) { ok = false; m_noImpulse = true; } } if(ok) { CGameRules *pGameRules = g_pGame->GetGameRules(); HitInfo info(m_pWeapon->GetOwnerId(), pTarget->GetId(), m_pWeapon->GetEntityId(), m_meleeparams.damage*damageScale*m_meleeScale, 0.0f, pGameRules->GetHitMaterialIdFromSurfaceId(surfaceIdx), partId, pGameRules->GetHitTypeId(m_meleeparams.hit_type.c_str()), pt, dir, normal); info.remote = remote; if (m_pWeapon->GetForcedHitMaterial() != -1) info.material=pGameRules->GetHitMaterialIdFromSurfaceId(m_pWeapon->GetForcedHitMaterial()); pGameRules->ClientHit(info); } } // play effects if(ok) { IMaterialEffects* pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); TMFXEffectId effectId = pMaterialEffects->GetEffectId("melee", surfaceIdx); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; params.pos = pt; params.playflags = MFX_PLAY_ALL | MFX_DISABLE_DELAY; params.soundSemantic = eSoundSemantic_Player_Foley; pMaterialEffects->ExecuteEffect(effectId, params); } } ApplyCameraShake(true); m_pWeapon->PlayAction(m_meleeactions.hit.c_str()); }
// //----------------------------------------------------------------------------------------------------------- // (MATT) Moved here from Scriptbind_AI when that was moved to the AI system {2008/02/15:15:23:16} int CScriptBind_Action::RegisterWithAI(IFunctionHandler *pH) { if (gEnv->bMultiplayer && !gEnv->bServer) return pH->EndFunction(); int type; ScriptHandle hdl; if (!pH->GetParams(hdl, type)) return pH->EndFunction(); EntityId entityID = (EntityId)hdl.n; IEntity *pEntity = gEnv->pEntitySystem->GetEntity(entityID); if(!pEntity) { GameWarning("RegisterWithAI: Tried to set register with AI nonExisting entity with id [%d]. ", entityID); return pH->EndFunction(); } // Apparently we can't assume that there is just one IGameObject to an entity, because we choose between (at least) Actor and Vehicle objects. // (MATT) Do we really need to check on the actor system here? {2008/02/15:18:38:34} IGameFramework *pGameFramework = gEnv->pGame->GetIGameFramework(); IVehicleSystem* pVSystem = pGameFramework->GetIVehicleSystem(); IActorSystem* pASystem = pGameFramework->GetIActorSystem(); if(!pASystem) { GameWarning("RegisterWithAI: no ActorSystem for %s.", pEntity->GetName()); return pH->EndFunction(); } AIObjectParams params(type, 0, entityID); bool autoDisable(true); // For most types, we need to parse the tables // For others we leave them blank switch (type) { case AIOBJECT_ACTOR: case AIOBJECT_2D_FLY: case AIOBJECT_BOAT: case AIOBJECT_CAR: case AIOBJECT_HELICOPTER: case AIOBJECT_INFECTED: case AIOBJECT_ALIENTICK: case AIOBJECT_HELICOPTERCRYSIS2: if(gEnv->pAISystem && ! gEnv->pAISystem->ParseTables(3, true, pH, params, autoDisable)) return pH->EndFunction(); default:; } // Most types check these, so just get them in advance IActor* pActor = pASystem->GetActor( pEntity->GetId() ); IVehicle* pVehicle = NULL; if( pVSystem ) pVehicle = pVSystem->GetVehicle( pEntity->GetId() ); // Set this if we've found something to create a proxy from IGameObject* pGameObject = NULL; switch(type) { case AIOBJECT_ACTOR: case AIOBJECT_2D_FLY: case AIOBJECT_INFECTED: case AIOBJECT_ALIENTICK: { // (MATT) The pActor/pVehicle test below - is it basically trying to distiguish between the two cases above? If so, separate them! {2008/02/15:19:38:08} if(!pActor) { GameWarning("RegisterWithAI: no Actor for %s.", pEntity->GetName()); return pH->EndFunction(); } pGameObject = pActor->GetGameObject(); } break; case AIOBJECT_BOAT: case AIOBJECT_CAR: { if(!pVehicle) { GameWarning("RegisterWithAI: no Vehicle for %s (Id %i).", pEntity->GetName(), pEntity->GetId()); return pH->EndFunction(); } pGameObject = pVehicle->GetGameObject(); } break; case AIOBJECT_HELICOPTER: case AIOBJECT_HELICOPTERCRYSIS2: { if(!pVehicle) { GameWarning("RegisterWithAI: no Vehicle for %s (Id %i).", pEntity->GetName(), pEntity->GetId()); return pH->EndFunction(); } pGameObject = pVehicle->GetGameObject(); params.m_moveAbility.b3DMove = true; } break; case AIOBJECT_PLAYER: { if(IsDemoPlayback()) return pH->EndFunction(); SmartScriptTable pTable; if (pH->GetParamCount() > 2) pH->GetParam(3,pTable); else return pH->EndFunction(); pGameObject = pActor->GetGameObject(); pTable->GetValue("groupid",params.m_sParamStruct.m_nGroup); const char* faction = 0; if (pTable->GetValue("esFaction", faction) && gEnv->pAISystem) { params.m_sParamStruct.factionID = gEnv->pAISystem->GetFactionMap().GetFactionID(faction); if (faction && *faction && (params.m_sParamStruct.factionID == IFactionMap::InvalidFactionID)) { GameWarning("Unknown faction '%s' being set...", faction); } } else { // Márcio: backwards compatibility int species = -1; if (!pTable->GetValue("eiSpecies", species)) pTable->GetValue("species", species); if (species > -1) params.m_sParamStruct.factionID = species; } pTable->GetValue("commrange",params.m_sParamStruct.m_fCommRange); //Luciano - added to use GROUPONLY signals SmartScriptTable pPerceptionTable; if(pTable->GetValue("Perception",pPerceptionTable)) { pPerceptionTable->GetValue( "sightrange", params.m_sParamStruct.m_PerceptionParams.sightRange); } } break; case AIOBJECT_SNDSUPRESSOR: { // (MATT) This doesn't need a proxy? {2008/02/15:19:45:58} SmartScriptTable pTable; // Properties table if (pH->GetParamCount() > 2) pH->GetParam(3,pTable); else return pH->EndFunction(); if (!pTable->GetValue("radius",params.m_moveAbility.pathRadius)) params.m_moveAbility.pathRadius = 10.f; break; } case AIOBJECT_WAYPOINT: break; /* // this block is commented out since params.m_sParamStruct is currently ignored in pEntity->RegisterInAISystem() // instead of setting the group id here, it will be set from the script right after registering default: // try to get groupid settings for anchors params.m_sParamStruct.m_nGroup = -1; params.m_sParamStruct.m_nSpecies = -1; { SmartScriptTable pTable; if ( pH->GetParamCount() > 2 ) pH->GetParam( 3, pTable ); if ( *pTable ) pTable->GetValue( "groupid", params.m_sParamStruct.m_nGroup ); } break; */ } // Remove any existing AI object pEntity->RegisterInAISystem(AIObjectParams(0)); // Register in AI to get a new AI object, deregistering the old one in the process pEntity->RegisterInAISystem(params); // (MATT) ? {2008/02/15:19:46:29} // AI object was not created (possibly AI System is disabled) if (IAIObject* aiObject = pEntity->GetAI()) { if(type==AIOBJECT_SNDSUPRESSOR) aiObject->SetRadius(params.m_moveAbility.pathRadius); else if(type>=AIANCHOR_FIRST) // if anchor - set radius { SmartScriptTable pTable; // Properties table if (pH->GetParamCount() > 2) pH->GetParam(3,pTable); else return pH->EndFunction(); float radius(0.f); pTable->GetValue("radius",radius); int groupId = -1; pTable->GetValue("groupid", groupId); aiObject->SetGroupId(groupId); aiObject->SetRadius(radius); } if (IAIActorProxy* proxy = aiObject->GetProxy()) proxy->UpdateMeAlways(!autoDisable); } return pH->EndFunction(); }
void CGameAIRecorder::AddRecordBookmark(EntityId requesterId) { assert(requesterId > 0); assert(gEnv->bServer); if (m_bIsRecording) { if (!gEnv->bServer) { CryLogAlways("[AI] Recorder bookmark requested on Client. Only the Server can do this!"); return; } IEntity *pEntity = gEnv->pEntitySystem->GetEntity(requesterId); IAIObject *pAI = pEntity ? pEntity->GetAI() : NULL; if (!pAI) { CryLogAlways("[AI] Attempting to add recorder bookmark, but the requester does not have an AI!"); return; } static int g_iBookmarkCounter = 0; int iBookmark = ++g_iBookmarkCounter; // (Kevin) We need to unify the timestamp for this, which should be set when the Recorder itself starts. // This way, all screenshots will match up with the recorder per date/time/build. Then we need to // move these into subfolders that contain the date/time/build as the name. (10/08/2009) time_t ltime; time(<ime); tm *pTm = localtime(<ime); char szDate[128]; strftime(szDate, 128, "Date(%d %b %Y) Time(%H %M %S)", pTm); // Get current version line const SFileVersion& fileVersion = gEnv->pSystem->GetFileVersion(); const bool bTakeScreenshot = (requesterId == g_pGame->GetIGameFramework()->GetClientActorId()); // Output to log CryLogAlways("[AI] --- RECORDER BOOKMARK ADDED ---"); CryLogAlways("[AI] Id: %d", iBookmark); CryLogAlways("[AI] %s", szDate); CryLogAlways("[AI] By: %s", pEntity->GetName()); if (!bTakeScreenshot) { CryLogAlways("[AI] No Screenshot was made for this bookmark, because requester is not the server!"); } else { string sScreenShotFile; sScreenShotFile.Format("Recorder_Bookmark(%d) Build(%d) %s", iBookmark, fileVersion[0], szDate); const string sScreenShotPath = PathUtil::Make("Recordings", sScreenShotFile.c_str(), "jpg"); // Take screenshot CryLogAlways("[AI] Screenshot: \'%s\'", sScreenShotPath.c_str()); gEnv->pRenderer->ScreenShot(sScreenShotPath.c_str()); OnAddBookmark(sScreenShotPath); } // Add bookmark to stream IAIRecordable::RecorderEventData data((float)iBookmark); pAI->RecordEvent(IAIRecordable::E_BOOKMARK, &data); } }