#ifdef WORKBENCH modded class SCR_CharacterControllerComponent { bool m_ERRViewInput; vector m_ERRViewAim, m_ERRViewLook; float m_ERRViewLean; override protected void OnApplyControls(IEntity owner, float timeSlice) { super.OnApplyControls(owner, timeSlice); if (!m_ERRViewInput) return; CharacterInputContext input = GetInputContext(); input.SetAimingAngles(m_ERRViewAim); input.SetFreelook(m_ERRViewLook != vector.Zero); input.SetLookAtAngles(m_ERRViewLook); input.SetLean(m_ERRViewLean); } } // The local native runner has one real player. A physical stand-in tests camera // tracking; canonical multiplayer target selection is tested separately. modded class SCR_PlayerController { protected IEntity m_ERRCameraProbeTarget; void ERR_ProbeCameraTarget(IEntity target) { m_ERRCameraProbeTarget = target; } override IEntity ERR_ObservedEntity() { string outcome; if (m_ERRCameraProbeTarget && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "unconscious-camera") return m_ERRCameraProbeTarget; return super.ERR_ObservedEntity(); } ERR_FollowSurvivorCameraComponent ERR_ProbeFollowComponent() { if (!m_ERRObserverCamera) return null; return ERR_FollowSurvivorCameraComponent.Cast(m_ERRObserverCamera.FindCameraComponent(ERR_FollowSurvivorCameraComponent)); } bool ERR_ProbeCameraActive() { return m_ERRObserverCamera && GetGame().GetCameraManager().CurrentCamera() == m_ERRObserverCamera; } } modded class ERR_GameMode { protected int m_ERRCameraStage = -2; protected int m_ERRCameraDeadline; protected IEntity m_ERRCameraWeapon; protected float m_ERRCameraWait; protected IEntity m_ERRCameraBody, m_ERRCameraTarget; protected SCR_CharacterDamageManagerComponent m_ERRCameraDamage; protected SCR_ManualCamera m_ERRCameraOther; protected int m_ERRViewSample; protected override void EOnInit(IEntity owner) { super.EOnInit(owner); string outcome; if (SCR_Global.IsEditMode() || !Replication.IsServer() || !System.GetCLIParam("errSmokeOutcome", outcome) || outcome != "unconscious-camera") return; m_ERRCameraDeadline = System.GetTickCount() + 180000; GetGame().GetCallqueue().CallLater(ERR_CameraProbeTick, 500, true); } protected bool ERR_CameraCheck(bool ok, string message) { if (ok) { Print("ERR SMOKE PASS: unconscious camera " + message); return true; } Print("ERR SMOKE FAIL: unconscious camera " + message, LogLevel.ERROR); GetGame().GetCallqueue().Remove(ERR_CameraProbeTick); return false; } protected void ERR_CameraKnockout() { m_ERRCameraDamage.ForceUnconsciousness(); array effects = m_ERRCameraDamage.GetAllPersistentEffectsOfType(SCR_PassiveHitZoneRegenDamageEffect); foreach (SCR_PersistentDamageEffect effect : effects) m_ERRCameraDamage.TerminateDamageEffect(effect); m_ERRCameraDamage.SetGroupIsBeingHealed(ECharacterHitZoneGroup.RIGHTARM, true); } protected void ERR_CameraProbeTick() { if (System.GetTickCount() >= m_ERRCameraDeadline || !PreparationError().IsEmpty()) { ERR_CameraCheck(false, "deadline/preparation " + PreparationError()); return; } if (!Active()) return; SCR_PlayerController pc = SCR_PlayerController.Cast(GetGame().GetPlayerController()); if (!pc || !pc.GetControlledEntity()) return; float now = GetWorld().GetWorldTime(); if (now < m_ERRCameraWait) return; if (m_ERRCameraStage == -2) { if (!ERR_CameraCheck(Settings().Get("keepWeaponOnUnconscious") == 1, "weapon retention defaults to enabled")) return; m_ERRCameraBody = pc.GetControlledEntity(); InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(m_ERRCameraBody.FindComponent(InventoryStorageManagerComponent)); m_ERRCameraWeapon = ERR_Actors.Spawn("{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et", m_ERRCameraBody.GetOrigin()); Own(m_ERRCameraWeapon); if (!ERR_CameraCheck(m_ERRCameraWeapon && inventory && inventory.TryInsertItem(m_ERRCameraWeapon, EStoragePurpose.PURPOSE_WEAPON_PROXY), "one physical rifle granted to the player")) return; m_ERRCameraStage = -1; m_ERRCameraWait = now + 1500; return; } SCR_CharacterControllerComponent character = SCR_CharacterControllerComponent.Cast(m_ERRCameraBody.FindComponent(SCR_CharacterControllerComponent)); InventoryStorageManagerComponent playerInventory = character.GetInventoryStorageManager(); if (m_ERRCameraStage == -1) { BaseWeaponComponent weapon = BaseWeaponComponent.Cast(m_ERRCameraWeapon.FindComponent(BaseWeaponComponent)); if (!ERR_CameraCheck(playerInventory.Contains(m_ERRCameraWeapon) && character.SelectWeapon(weapon), "rifle selected with native equip animation")) return; CameraHandlerComponent.Cast(m_ERRCameraBody.FindComponent(CameraHandlerComponent)).SetThirdPerson(false); character.m_ERRViewInput = true; character.m_ERRViewAim = "0.6 0.15 0"; m_ERRCameraStage = -10; m_ERRCameraWait = now + 2000; return; } if (m_ERRCameraStage == -10) { vector actual[4], observed[4], world[4]; pc.GetPlayerCamera().GetWorldTransform(actual); ERR_FollowSurvivorCameraComponent.PlayerView(m_ERRCameraBody, observed); m_ERRCameraBody.GetWorldTransform(world); ChimeraCharacter body = ChimeraCharacter.Cast(m_ERRCameraBody); vector pose = body.GetAimRotationModel(); vector poseDirection = (pose * Math.RAD2DEG).AnglesToVector().Multiply3(world); PrintFormat("ERR VIEW POSE: sample=%1 native=%2 current=%3 pose=%4 look=%5 aim=%6 head=%7", m_ERRViewSample, actual[2], observed[2], poseDirection, character.GetHeadAimingComponent().GetLookAngles(), pose, character.GetHeadAimingComponent().GetAimingRotation()); PrintFormat("ERR VIEW STATE: freeLook=%1 lean=%2 nativePosition=%3 headViewPosition=%4", character.IsFreeLookEnabled(), character.GetLeaning(), actual[3], observed[3]); m_ERRViewSample++; if (m_ERRViewSample == 2) character.m_ERRViewLook = "0.5 -0.2 0"; if (m_ERRViewSample == 4) { character.m_ERRViewLook = vector.Zero; character.m_ERRViewLean = 1; } if (m_ERRViewSample == 6) character.m_ERRViewLean = -1; if (m_ERRViewSample == 8) { character.m_ERRViewLean = 0; m_ERRCameraBody.SetYawPitchRoll("90 0 0"); } if (m_ERRViewSample >= 10) { character.m_ERRViewInput = false; m_ERRCameraStage = 0; } m_ERRCameraWait = now + 2000; return; } if (m_ERRCameraStage == 0) { if (!ERR_CameraCheck(character.GetWeaponManagerComponent().GetCurrentWeapon() && character.GetWeaponManagerComponent().GetCurrentWeapon().GetOwner() == m_ERRCameraWeapon, "original rifle actually in hand before knockout")) return; m_ERRCameraBody = pc.GetControlledEntity(); m_ERRCameraDamage = SCR_CharacterDamageManagerComponent.Cast(m_ERRCameraBody.FindComponent(SCR_CharacterDamageManagerComponent)); if (!ERR_CameraCheck(m_ERRCameraDamage && m_ERRCameraDamage.ERR_IsRevivablePlayer(), "admitted player retains treatable body")) return; ERR_CameraKnockout(); m_ERRCameraStage = 1; m_ERRCameraWait = now + 3000; return; } if (m_ERRCameraStage == 1) { if (!ERR_CameraCheck(pc.ERR_ObserverState() == ERR_ObserverReason.UNCONSCIOUS && pc.ERR_ProbeCameraActive(), "native knockout automatically opens the manual third-person camera")) return; if (!ERR_CameraCheck(pc.GetControlledEntity() == m_ERRCameraBody && ERR_Actors.Alive(m_ERRCameraBody) && !ERR_Actors.CanFight(m_ERRCameraBody), "possession and living unconscious body unchanged")) return; if (!ERR_CameraCheck(!pc.ERR_ObservedEntity() && vector.DistanceXZ(pc.ERR_ObserverAnchor(), m_ERRCameraBody.GetOrigin()) < 1, "no teammate falls back to the casualty body")) return; if (!ERR_CameraCheck(playerInventory.Contains(m_ERRCameraWeapon), "enabled retention keeps the exact rifle with the unconscious player")) return; vector point = ERR_Actors.Ground(m_ERRCameraBody.GetOrigin() + "25 0 25"); m_ERRCameraTarget = ERR_Actors.Spawn(Factions().PrisonerPrefab, point); Own(m_ERRCameraTarget); if (!ERR_CameraCheck(m_ERRCameraTarget != null, "physical follow-target stand-in created")) return; pc.ERR_ProbeCameraTarget(m_ERRCameraTarget); m_ERRCameraStage = 2; m_ERRCameraWait = now + 1500; return; } if (m_ERRCameraStage == 2) { vector camera = GetGame().GetCameraManager().CurrentCamera().GetOrigin(); PrintFormat("ERR CAMERA TRACK: camera=%1 target=%2 body=%3 active=%4 conscious=%5 reason=%6", camera, m_ERRCameraTarget.GetOrigin(), m_ERRCameraBody.GetOrigin(), pc.ERR_ProbeCameraActive(), ERR_Actors.CanFight(m_ERRCameraBody), pc.ERR_ObserverState()); if (!ERR_CameraCheck(vector.Distance(camera, m_ERRCameraTarget.GetOrigin()) < 13 && vector.Distance(camera, m_ERRCameraBody.GetOrigin()) > 15, "camera follows target in 3D without moving the casualty")) return; m_ERRCameraTarget.SetOrigin(ERR_Actors.Ground(m_ERRCameraTarget.GetOrigin() + "15 0 0")); m_ERRCameraStage = 3; m_ERRCameraWait = now + 1500; return; } if (m_ERRCameraStage == 3) { if (!ERR_CameraCheck(vector.Distance(GetGame().GetCameraManager().CurrentCamera().GetOrigin(), m_ERRCameraTarget.GetOrigin()) < 13, "camera tracks a moving target")) return; InputManager input = GetGame().GetInputManager(); ERR_FollowSurvivorCameraComponent follow = pc.ERR_ProbeFollowComponent(); if (!ERR_CameraCheck(follow != null, "follow component exists")) return; ResourceName configuredInput; GameProject.GetModuleConfig("InputManagerSettings").Get("Default", configuredInput); PrintFormat("ERR CAMERA INPUT: default=%1 context=%2 previous=%3 next=%4 view=%5 orbit=%6", configuredInput, input.IsContextActive("ERR_ObserverContext"), input.IsActionActive("ERR_ObserverPrevious"), input.IsActionActive("ERR_ObserverNext"), input.IsActionActive("ERR_ObserverView"), input.IsActionActive("ManualCameraRotateYaw")); for (int inputIndex = 0; inputIndex < input.GetActionCount(); inputIndex++) { string inputName = input.GetActionName(inputIndex); if (inputName.Contains("Observer") || inputName == "ManualCameraRotateYaw") Print("ERR CAMERA INPUT REGISTERED: " + inputName); } if (!ERR_CameraCheck(follow.InputContextActive() && input.IsActionActive("ERR_ObserverPrevious") && input.IsActionActive("ERR_ObserverNext") && input.IsActionActive("ERR_ObserverView") && input.IsActionActive("ManualCameraRotateYaw"), "dedicated arrow/Enter actions and inherited orbit inputs loaded and active")) return; follow.Input(true, false, false, true); if (!ERR_CameraCheck(follow.IsPlayerView(), "Enter handler selects player view")) return; follow.Input(true, false, false, true); if (!ERR_CameraCheck(follow.IsPlayerView(), "held Enter does not toggle again")) return; follow.Input(true, false, false, false); m_ERRCameraStage = 30; m_ERRCameraWait = now + 1000; return; } if (m_ERRCameraStage == 30) { vector expected[4]; if (!ERR_CameraCheck(ERR_FollowSurvivorCameraComponent.PlayerView(m_ERRCameraTarget, expected) && vector.Distance(GetGame().GetCameraManager().CurrentCamera().GetOrigin(), expected[3]) < 0.1, "native camera follows teammate head in player view")) return; pc.ERR_ProbeFollowComponent().Input(true, false, false, true); pc.ERR_ProbeFollowComponent().Input(true, false, false, false); m_ERRCameraStage = 31; m_ERRCameraWait = now + 1000; return; } if (m_ERRCameraStage == 31) { if (!ERR_CameraCheck(!pc.ERR_ProbeFollowComponent().IsPlayerView() && vector.Distance(GetGame().GetCameraManager().CurrentCamera().GetOrigin(), m_ERRCameraTarget.GetOrigin()) > 2, "second Enter restores orbit view")) return; Resource otherResource = Resource.Load("{EB1210AAE1BB8F54}Prefabs/ERR/SpectatorCamera.et"); m_ERRCameraOther = SCR_ManualCamera.Cast(GetGame().SpawnEntityPrefab(otherResource, GetWorld())); if (!ERR_CameraCheck(m_ERRCameraOther && GetGame().GetCameraManager().CurrentCamera() == m_ERRCameraOther && !pc.ERR_ProbeCameraActive(), "separate manual camera takes over before recovery")) return; pc.ERR_ProbeCameraTarget(null); ERR_Actors.DeleteSpawned(m_ERRCameraTarget); m_ERRCameraTarget = null; m_ERRCameraDamage.SetGroupIsBeingHealed(ECharacterHitZoneGroup.RIGHTARM, false); m_ERRCameraDamage.FullHeal(); m_ERRCameraStage = 4; m_ERRCameraWait = now + 4000; return; } if (m_ERRCameraStage == 4) { if (!ERR_CameraCheck(ERR_Actors.CanFight(m_ERRCameraBody) && pc.GetControlledEntity() == m_ERRCameraBody && pc.ERR_ObserverState() == ERR_ObserverReason.NONE && !pc.ERR_HasObserverCamera(), "native healing closes spectator and retains the original conscious player")) return; if (!ERR_CameraCheck(GetGame().GetCameraManager().CurrentCamera() == pc.GetPlayerCamera(), "normal player camera restored")) return; if (m_ERRCameraOther) { m_ERRCameraOther.Terminate(); delete m_ERRCameraOther; m_ERRCameraOther = null; } if (!ERR_CameraCheck(character.GetWeaponManagerComponent().GetCurrentWeapon() && character.GetWeaponManagerComponent().GetCurrentWeapon().GetOwner() == m_ERRCameraWeapon, "revival equips the same rifle without ground pickup")) return; if (!ERR_CameraCheck(Settings().Set("keepWeaponOnUnconscious", 0), "retention disabled through persistent setting")) return; ERR_CameraKnockout(); m_ERRCameraStage = 5; m_ERRCameraWait = now + 3000; return; } if (!ERR_CameraCheck(pc.ERR_ProbeCameraActive(), "second knockout reopens camera without stale target")) return; if (!ERR_CameraCheck(!playerInventory.Contains(m_ERRCameraWeapon), "disabled retention leaves normal native weapon drop on ground")) return; m_ERRCameraDamage.SetGroupIsBeingHealed(ECharacterHitZoneGroup.RIGHTARM, false); m_ERRCameraDamage.FullHeal(); GetGame().GetCallqueue().Remove(ERR_CameraProbeTick); Print("ERR SMOKE COMPLETE: unconscious spectator camera, physical tracking, retained treatment body, recovery and repeated knockout; target stand-in, not multiplayer proof"); } } #endif