[ComponentEditorProps(category: "GameScripted/", description: "Voice Range Controller (Per Meter)")] class SCR_VoiceRangeControllerClass: ScriptComponentClass {} class SCR_VoiceRangeController: ScriptComponent { [Attribute("25", UIWidgets.Slider, "Start-Reichweite in Metern", "1 100 1")] protected int m_iCurrentRange; [Attribute("1", UIWidgets.Slider, "Minimale Reichweite", "1 100 1")] protected int m_iMinRange; [Attribute("100", UIWidgets.Slider, "Maximale Reichweite", "1 100 1")] protected int m_iMaxRange; [Attribute("1", UIWidgets.Slider, "Schrittgroesse (Meter pro Tastendruck)", "1 50 1")] protected int m_iStepSize; // Speichert das echte Ring-Objekt (Decal) in der Spielwelt protected IEntity m_RangeRingEntity; // Timer für den Cooldown (verhindert das +1 / -1 Springen bei analogen Tasten) protected int m_iLastInputTime = 0; static SCR_VoiceRangeController GetInstance() { if (!GetGame().GetPlayerController()) return null; return SCR_VoiceRangeController.Cast(GetGame().GetPlayerController().FindComponent(SCR_VoiceRangeController)); } override void OnPostInit(IEntity owner) { super.OnPostInit(owner); SetEventMask(owner, EntityEvent.INIT); } protected override void EOnInit(IEntity owner) { if (!GetGame().GetInputManager()) return; // HIER STEHEN DIE NAMEN DER ACTIONS GetGame().GetInputManager().AddActionListener("GTG_RangeUp", EActionTrigger.DOWN, IncreaseRange); GetGame().GetInputManager().AddActionListener("GTG_RangeDown", EActionTrigger.DOWN, DecreaseRange); } void IncreaseRange() { // Blockiert den Input, wenn seit dem letzten Druck nicht mind. 250ms vergangen sind if (System.GetTickCount() < m_iLastInputTime + 250) return; m_iLastInputTime = System.GetTickCount(); // Speichert die aktuelle Zeit Print("GTG Voice: PLUS gedrueckt!", LogLevel.WARNING); ChangeRange(1); } void DecreaseRange() { // Blockiert den Input auch hier für 250ms if (System.GetTickCount() < m_iLastInputTime + 250) return; m_iLastInputTime = System.GetTickCount(); Print("GTG Voice: MINUS gedrueckt!", LogLevel.WARNING); ChangeRange(-1); } void ChangeRange(int direction) { int newRange = Math.ClampInt(m_iCurrentRange + (direction * m_iStepSize), m_iMinRange, m_iMaxRange); if (m_iCurrentRange == newRange) return; m_iCurrentRange = newRange; Print("GTG Voice: Neue Reichweite ist " + m_iCurrentRange.ToString(), LogLevel.WARNING); ApplyRangeToVON(); UpdateUI(); DrawRangeCircle(); } protected void ApplyRangeToVON() { IEntity player = GetGame().GetPlayerController().GetControlledEntity(); if (!player) return; SCR_VONController vonContr = SCR_VONController.Cast(GetOwner().FindComponent(SCR_VONController)); if (!vonContr) return; // --- HYBRID LÖSUNG --- typename compType = SCR_VoNNormal; // Standard if (m_iCurrentRange <= 15) { compType = SCR_VoNWhispering; } else if (m_iCurrentRange >= 50) { compType = SCR_VoNLoud; } SCR_VoNComponent newVonComp = SCR_VoNComponent.Cast(player.FindComponent(compType)); if (newVonComp) { vonContr.SetVONComponent(newVonComp); } } void UpdateUI() { Print("GTG Voice: Suche HUD Manager...", LogLevel.WARNING); SCR_HUDManagerComponent hud = SCR_HUDManagerComponent.Cast(GetOwner().FindComponent(SCR_HUDManagerComponent)); if (!hud) { Print("GTG Voice: FEHLER - HUD Manager nicht auf dem PlayerController gefunden!", LogLevel.ERROR); return; } Print("GTG Voice: Suche VoiceRangeDisplay...", LogLevel.WARNING); SCR_VoiceRangeDisplay display = SCR_VoiceRangeDisplay.Cast(hud.FindInfoDisplay(SCR_VoiceRangeDisplay)); if (!display) { Print("GTG Voice: FEHLER - SCR_VoiceRangeDisplay fehlt in den Info Displays des HUD Managers!", LogLevel.ERROR); return; } Print("GTG Voice: Display gefunden! Sende Text an UI...", LogLevel.WARNING); display.UpdateRangeText(m_iCurrentRange); } void DrawRangeCircle() { IEntity player = GetGame().GetPlayerController().GetControlledEntity(); if (!player) { Print("GTG Voice: FEHLER - Kein spielbarer Charakter (Koerper) fuer den Kreis gefunden!", LogLevel.ERROR); return; } // Wenn schon ein Ring existiert, sofort löschen if (m_RangeRingEntity) { SCR_EntityHelper.DeleteEntityAndChildren(m_RangeRingEntity); m_RangeRingEntity = null; } // 1. Hole absolute Welt-Position der Füße vector playerPos = player.GetOrigin(); // 2. Erstelle Spawn-Parameter EntitySpawnParams spawnParams = EntitySpawnParams(); spawnParams.TransformMode = ETransformMode.WORLD; spawnParams.Transform[3] = playerPos; // 3. Spawne das Decal-Prefab ResourceName ringPrefab = "{E6268D7D980F8A5D}Prefabs/Props/Military/Compositions/US/Decals/Decal_Helipad_US.et"; Resource res = Resource.Load(ringPrefab); if (res && res.IsValid()) { m_RangeRingEntity = GetGame().SpawnEntityPrefab(res, GetGame().GetWorld(), spawnParams); if (m_RangeRingEntity) { // Skaliere das Objekt passend zu der Meter-Reichweite float scale = m_iCurrentRange * 1.0; m_RangeRingEntity.SetScale(scale); } } else { Print("GTG Voice: Ring Prefab konnte nicht geladen werden!", LogLevel.ERROR); } // Nach 2 Sekunden restlos aus der Welt löschen GetGame().GetCallqueue().Remove(HideRangeCircle); GetGame().GetCallqueue().CallLater(HideRangeCircle, 2000, false); } void HideRangeCircle() { if (m_RangeRingEntity) { SCR_EntityHelper.DeleteEntityAndChildren(m_RangeRingEntity); m_RangeRingEntity = null; } } }