232 lines
6.7 KiB
C
232 lines
6.7 KiB
C
[ComponentEditorProps(category: "GameScripted/Gadgets", description: "Vanilla map gadget with a world-visible rendered surface")]
|
|
class ERRM_SharedMapComponentClass : SCR_MapGadgetComponentClass
|
|
{
|
|
[Attribute("{255E1265F702B4AE}UI/layouts/ERRMap/SharedMapRenderTarget.layout", UIWidgets.ResourcePickerThumbnail, "Off-screen map render target", "layout")]
|
|
protected ResourceName m_sLayoutName;
|
|
|
|
[Attribute("0.12", UIWidgets.EditBox, "Initial map zoom in pixels per metre", "0.01 20", category: "Shared Map")]
|
|
protected float m_fInitialZoom;
|
|
|
|
ResourceName GetSharedMapLayout()
|
|
{
|
|
return m_sLayoutName;
|
|
}
|
|
|
|
float GetInitialZoom()
|
|
{
|
|
return m_fInitialZoom;
|
|
}
|
|
}
|
|
|
|
// This remains a real SCR_MapGadgetComponent, so the normal M-key map,
|
|
// animations, ruler and faction-specific protractor settings keep working.
|
|
// The extra off-screen widget supplies the texture used by the held model.
|
|
class ERRM_SharedMapComponent : SCR_MapGadgetComponent
|
|
{
|
|
protected const ResourceName MAP_CONFIG = "{A786DD4868598F15}Configs/Map/MapPlain.conf";
|
|
protected const int MAP_SETUP_DELAY_MS = 100;
|
|
protected const int MAP_CAPTURE_DELAY_MS = 500;
|
|
protected const int MAP_RETRY_DELAY_MS = 250;
|
|
|
|
protected Widget m_ESMRootWidget;
|
|
protected RTTextureWidget m_ESMRenderTarget;
|
|
protected SCR_MapEntity m_ESMMapEntity;
|
|
protected ref MapConfiguration m_ESMMapConfig;
|
|
protected IEntity m_ESMCharacterOwner;
|
|
protected bool m_bESMInHand;
|
|
protected bool m_bESMOwnsMapRenderer;
|
|
|
|
protected override void EOnInit(IEntity owner)
|
|
{
|
|
super.EOnInit(owner);
|
|
|
|
ERRM_SharedMapComponentClass data = ERRM_SharedMapComponentClass.Cast(GetComponentData(owner));
|
|
WorkspaceWidget workspace = GetGame().GetWorkspace();
|
|
if (!data || !workspace || data.GetSharedMapLayout().IsEmpty())
|
|
return;
|
|
|
|
m_ESMRootWidget = workspace.CreateWidgets(data.GetSharedMapLayout());
|
|
if (!m_ESMRootWidget)
|
|
return;
|
|
|
|
m_ESMRenderTarget = RTTextureWidget.Cast(m_ESMRootWidget.FindAnyWidget("RTTexture0"));
|
|
if (!m_ESMRenderTarget)
|
|
return;
|
|
|
|
m_ESMRenderTarget.SetRenderTarget(owner);
|
|
m_ESMRenderTarget.SetEnabled(false);
|
|
}
|
|
|
|
protected override void ModeSwitch(EGadgetMode mode, IEntity charOwner)
|
|
{
|
|
if (mode != EGadgetMode.IN_HAND || !m_ESMRootWidget || !m_ESMRenderTarget)
|
|
{
|
|
super.ModeSwitch(mode, charOwner);
|
|
return;
|
|
}
|
|
|
|
m_bESMInHand = true;
|
|
m_ESMCharacterOwner = charOwner;
|
|
TryCaptureMapSurface();
|
|
}
|
|
|
|
protected override void ModeClear(EGadgetMode mode)
|
|
{
|
|
if (mode == EGadgetMode.IN_HAND)
|
|
StopMapCapture();
|
|
|
|
super.ModeClear(mode);
|
|
}
|
|
|
|
protected void TryCaptureMapSurface()
|
|
{
|
|
if (!m_bESMInHand)
|
|
return;
|
|
|
|
if (!m_ESMRootWidget || !m_ESMRenderTarget)
|
|
{
|
|
CompleteMapModeSwitch();
|
|
return;
|
|
}
|
|
|
|
m_ESMMapEntity = SCR_MapEntity.GetMapInstance();
|
|
if (!m_ESMMapEntity)
|
|
{
|
|
CompleteMapModeSwitch();
|
|
return;
|
|
}
|
|
|
|
// The owner's normal map has priority. Capture only after it closes;
|
|
// otherwise this auxiliary render would replace or close the M-key UI.
|
|
if (m_ESMMapEntity.IsOpen())
|
|
{
|
|
GetGame().GetCallqueue().CallLater(TryCaptureMapSurface, MAP_RETRY_DELAY_MS, false);
|
|
return;
|
|
}
|
|
|
|
m_ESMRenderTarget.SetEnabled(true);
|
|
MapConfiguration mapConfig = m_ESMMapEntity.SetupMapConfig(EMapEntityMode.PLAIN, MAP_CONFIG, m_ESMRootWidget);
|
|
if (!mapConfig)
|
|
{
|
|
ReleaseMapCapture();
|
|
CompleteMapModeSwitch();
|
|
return;
|
|
}
|
|
|
|
m_ESMMapConfig = mapConfig;
|
|
m_bESMOwnsMapRenderer = true;
|
|
m_ESMMapEntity.OpenMap(mapConfig);
|
|
if (!OwnsMapCapture())
|
|
{
|
|
ReleaseMapCapture();
|
|
if (m_bESMInHand && GetGame())
|
|
GetGame().GetCallqueue().CallLater(TryCaptureMapSurface, MAP_RETRY_DELAY_MS, false);
|
|
return;
|
|
}
|
|
RestoreCharacterCamera();
|
|
|
|
GetGame().GetCallqueue().CallLater(CenterMapOnHolder, MAP_SETUP_DELAY_MS, false);
|
|
GetGame().GetCallqueue().CallLater(FinishMapCapture, MAP_CAPTURE_DELAY_MS, false);
|
|
}
|
|
|
|
protected void CenterMapOnHolder()
|
|
{
|
|
if (!OwnsMapCapture() || !m_ESMCharacterOwner)
|
|
return;
|
|
|
|
ERRM_SharedMapComponentClass data = ERRM_SharedMapComponentClass.Cast(GetComponentData(GetOwner()));
|
|
if (!data)
|
|
return;
|
|
|
|
vector holderPosition = m_ESMCharacterOwner.GetOrigin();
|
|
m_ESMMapEntity.ZoomPanSmooth(data.GetInitialZoom(), holderPosition[0], holderPosition[2], 0.1);
|
|
}
|
|
|
|
protected void FinishMapCapture()
|
|
{
|
|
if (!m_bESMOwnsMapRenderer)
|
|
return;
|
|
if (!OwnsMapCapture())
|
|
{
|
|
ReleaseMapCapture();
|
|
// A native UI replaced this capture. Do not close, pan, fade or
|
|
// focus that UI; use the existing retry path after it closes.
|
|
if (m_bESMInHand && GetGame())
|
|
GetGame().GetCallqueue().CallLater(TryCaptureMapSurface, MAP_RETRY_DELAY_MS, false);
|
|
return;
|
|
}
|
|
|
|
ReleaseMapCapture();
|
|
CompleteMapModeSwitch();
|
|
}
|
|
|
|
protected bool OwnsMapCapture()
|
|
{
|
|
if (!GetGame() || !m_bESMOwnsMapRenderer || !m_ESMMapEntity || !m_ESMMapConfig || !m_ESMRootWidget)
|
|
return false;
|
|
if (SCR_MapEntity.GetMapInstance() != m_ESMMapEntity || !m_ESMMapEntity.IsOpen())
|
|
return false;
|
|
// Vanilla reuses the same config object for equal map modes and changes
|
|
// RootWidgetRef, so pointer identity alone cannot establish ownership.
|
|
return m_ESMMapEntity.GetMapConfig() == m_ESMMapConfig && m_ESMMapConfig.RootWidgetRef == m_ESMRootWidget;
|
|
}
|
|
|
|
protected void ReleaseMapCapture()
|
|
{
|
|
bool ownsRenderer = OwnsMapCapture();
|
|
// Freeze our own target even on interruption or foreign replacement.
|
|
if (m_ESMRenderTarget)
|
|
m_ESMRenderTarget.SetEnabled(false);
|
|
m_bESMOwnsMapRenderer = false;
|
|
m_ESMMapConfig = null;
|
|
if (ownsRenderer)
|
|
m_ESMMapEntity.CloseMap(); // vanilla also restores its character camera
|
|
}
|
|
|
|
protected void CompleteMapModeSwitch()
|
|
{
|
|
if (!m_bESMInHand || !m_ESMCharacterOwner)
|
|
return;
|
|
|
|
// Start the stock map only after the world-visible surface owns a frame.
|
|
super.ModeSwitch(EGadgetMode.IN_HAND, m_ESMCharacterOwner);
|
|
}
|
|
|
|
protected void StopMapCapture()
|
|
{
|
|
m_bESMInHand = false;
|
|
if (GetGame())
|
|
{
|
|
GetGame().GetCallqueue().Remove(TryCaptureMapSurface);
|
|
GetGame().GetCallqueue().Remove(CenterMapOnHolder);
|
|
GetGame().GetCallqueue().Remove(FinishMapCapture);
|
|
}
|
|
ReleaseMapCapture();
|
|
m_ESMCharacterOwner = null;
|
|
m_ESMMapEntity = null;
|
|
}
|
|
|
|
protected void RestoreCharacterCamera()
|
|
{
|
|
if (!OwnsMapCapture()) return;
|
|
PlayerController playerController = GetGame().GetPlayerController();
|
|
if (playerController)
|
|
playerController.SetCharacterCameraRenderActive(true);
|
|
}
|
|
|
|
protected override void OnDelete(IEntity owner)
|
|
{
|
|
StopMapCapture();
|
|
|
|
// The entity may already be destroyed when this callback runs during a
|
|
// world restart. Destroying the widget releases its render binding without
|
|
// passing the deleted entity back into the native renderer.
|
|
if (m_ESMRootWidget)
|
|
m_ESMRootWidget.RemoveFromHierarchy();
|
|
|
|
m_ESMRenderTarget = null;
|
|
m_ESMRootWidget = null;
|
|
super.OnDelete(owner);
|
|
}
|
|
}
|