Files
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

201 lines
13 KiB
Plaintext

#ifdef WORKBENCH
// 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_ERRViewSamples;
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<ref SCR_PersistentDamageEffect> 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 handler = CameraHandlerComponent.Cast(m_ERRCameraBody.FindComponent(CameraHandlerComponent));
if (handler) handler.SetThirdPerson(false);
m_ERRCameraStage = -10; m_ERRCameraWait = now + 2000; return;
}
if (m_ERRCameraStage == -10)
{
vector actual[4], observed[4], owner[4];
pc.GetPlayerCamera().GetWorldTransform(actual);
m_ERRCameraBody.GetWorldTransform(owner);
ERR_FollowSurvivorCameraComponent.PlayerView(m_ERRCameraBody, observed);
CharacterHeadAimingComponent head = character.GetHeadAimingComponent();
PrintFormat("ERR VIEW COMPARE: sample=%1 native=%2 spectator=%3 body=%4 look=%5 head=%6 aim=%7", m_ERRViewSamples, actual[2], observed[2], owner[2], head.GetLookAngles(), head.GetAimingRotation(), ChimeraCharacter.Cast(m_ERRCameraBody).GetAimRotationModel());
vector look[4], offset = "0 0.03 -0.07", rotation;
head.GetLookTransformationMS(m_ERRCameraBody.GetAnimation().GetBoneIndex("Head"), EDirectBoneMode.RelativePosition, offset, rotation, look);
PrintFormat("ERR VIEW POSITION: sample=%1 native=%2 spectator=%3 eyes=%4 lookMS=%5 lookDir=%6 lean=%7", m_ERRViewSamples, actual[3], observed[3], ChimeraCharacter.Cast(m_ERRCameraBody).EyePosition(), look[3].Multiply4(owner), look[2].Multiply3(owner), character.GetLeaning());
if (++m_ERRViewSamples >= 26)
{
character.GetInputContext().SetFreelook(false);
character.GetInputContext().SetLean(0);
m_ERRCameraStage = 0;
return;
}
if (m_ERRViewSamples < 10) character.GetInputContext().SetAimingAngles(Vector(m_ERRViewSamples * 0.12, 0.15, 0));
else if (m_ERRViewSamples < 16)
{
character.GetInputContext().SetFreelook(true);
character.GetInputContext().SetLookAtAngles(Vector(0.5, -0.2, 0));
}
else if (m_ERRViewSamples < 21)
{
character.GetInputContext().SetFreelook(false);
character.GetInputContext().SetLean(1);
}
else character.GetInputContext().SetLean(-1);
m_ERRCameraWait = now + 1000;
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