From aa7fa9d68f2911a28bd10311e12dcac9d5bb11dc Mon Sep 17 00:00:00 2001 From: Filip Maciejewski Date: Sun, 29 Dec 2024 16:55:06 +0100 Subject: [PATCH] feat: Add AFM Tools menu (#6) * Pause menu dialog PoC * Move logo to the top * Add dialog background * Implement scripted tabs * Add container for storing list of admins * Refactor to AFM Tools * Add stringtable * Add AFM_SettingsComponent to DefaultPlayerController * PoC settings debug serialization * Add AFM_PlainTextContainerSerializer * Move menu classes to tools folder * Add support for more types to plain text serializer * Add support for mission header to settings network component * Add tools submenu with SCR_MissionHeader and ACE Settings debug * Improve spinner behaviour Overlay whole tab area, single widget spinners do not work well as the spinner does not follow the scroll view * Code quality * Add ACE Core dependency * Add "Server Settings" translation --- addons/core/Configs/AFM_AdminList.conf | 10 ++ addons/core/Configs/AFM_AdminList.conf.meta | 17 ++ addons/core/Configs/System/chimeraMenus.conf | 8 + .../Configs/System/chimeraMenus.conf.meta | 17 ++ .../Language/AFM_Core_localization.en_us.conf | 4 + .../AFM_Core_localization.en_us.conf.meta | 17 ++ .../Language/AFM_Core_localization.pl_pl.conf | 4 + .../AFM_Core_localization.pl_pl.conf.meta | 17 ++ addons/core/Language/AFM_Core_localization.st | 19 ++- .../Core/DefaultPlayerController.et | 7 + .../Core/DefaultPlayerController.et.meta | 17 ++ .../UI/layouts/AFM_Tools/AFM_ToolsMenu.layout | 77 +++++++++ .../AFM_Tools/AFM_ToolsMenu.layout.meta | 17 ++ .../AFM_Tools_ServerSettingsSubMenu.layout | 126 ++++++++++++++ ...FM_Tools_ServerSettingsSubMenu.layout.meta | 17 ++ .../AFM_Tools/AFM_Tools_TabContentBase.layout | 25 +++ .../AFM_Tools_TabContentBase.layout.meta | 17 ++ .../layouts/Menus/PauseMenu/pauseMenu.layout | 69 ++++++++ .../Menus/PauseMenu/pauseMenu.layout.meta | 17 ++ addons/core/addon.gproj | 2 +- .../AFM_Tools/AFM_ServerSettingsSubMenu.c | 87 ++++++++++ .../Game/AFM_Core/AFM_Tools/AFM_SubMenuBase.c | 23 +++ .../Game/AFM_Core/AFM_Tools/AFM_ToolsMenu.c | 57 +++++++ .../Game/AFM_Core/Containers/AFM_AdminList.c | 6 + .../Game/AFM_Core/Containers/AFM_AdminUID.c | 6 + .../AFM_PlainTextContainerSerializer.c | 119 +++++++++++++ .../AFM_Core/Settings/AFM_SettingsComponent.c | 159 ++++++++++++++++++ .../Game/AFM_Core/UI/Menu/SCR_PauseMenuUI.c | 20 +++ 28 files changed, 979 insertions(+), 2 deletions(-) create mode 100644 addons/core/Configs/AFM_AdminList.conf create mode 100644 addons/core/Configs/AFM_AdminList.conf.meta create mode 100644 addons/core/Configs/System/chimeraMenus.conf create mode 100644 addons/core/Configs/System/chimeraMenus.conf.meta create mode 100644 addons/core/Language/AFM_Core_localization.en_us.conf.meta create mode 100644 addons/core/Language/AFM_Core_localization.pl_pl.conf.meta create mode 100644 addons/core/Prefabs/Characters/Core/DefaultPlayerController.et create mode 100644 addons/core/Prefabs/Characters/Core/DefaultPlayerController.et.meta create mode 100644 addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout create mode 100644 addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout.meta create mode 100644 addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout create mode 100644 addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout.meta create mode 100644 addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout create mode 100644 addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout.meta create mode 100644 addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout create mode 100644 addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout.meta create mode 100644 addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ServerSettingsSubMenu.c create mode 100644 addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_SubMenuBase.c create mode 100644 addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ToolsMenu.c create mode 100644 addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminList.c create mode 100644 addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminUID.c create mode 100644 addons/core/scripts/Game/AFM_Core/Containers/AFM_PlainTextContainerSerializer.c create mode 100644 addons/core/scripts/Game/AFM_Core/Settings/AFM_SettingsComponent.c create mode 100644 addons/core/scripts/Game/AFM_Core/UI/Menu/SCR_PauseMenuUI.c diff --git a/addons/core/Configs/AFM_AdminList.conf b/addons/core/Configs/AFM_AdminList.conf new file mode 100644 index 0000000..f605832 --- /dev/null +++ b/addons/core/Configs/AFM_AdminList.conf @@ -0,0 +1,10 @@ +AFM_AdminList { + m_aAdminUIDs { + AFM_AdminUID veteran29 { + m_sAdminUIDs { + "fc4bef3e-97a7-44c3-9c30-f6d0bae9b2bd" + "fc633ca4-377a-41bf-bd64-9dcbbe739a1c" + } + } + } +} \ No newline at end of file diff --git a/addons/core/Configs/AFM_AdminList.conf.meta b/addons/core/Configs/AFM_AdminList.conf.meta new file mode 100644 index 0000000..2d34883 --- /dev/null +++ b/addons/core/Configs/AFM_AdminList.conf.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{745B5C3C22B7AA9A}Configs/AFM_AdminList.conf" + Configurations { + CONFResourceClass PC { + } + CONFResourceClass XBOX_ONE : PC { + } + CONFResourceClass XBOX_SERIES : PC { + } + CONFResourceClass PS4 : PC { + } + CONFResourceClass PS5 : PC { + } + CONFResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/Configs/System/chimeraMenus.conf b/addons/core/Configs/System/chimeraMenus.conf new file mode 100644 index 0000000..57f3b4a --- /dev/null +++ b/addons/core/Configs/System/chimeraMenus.conf @@ -0,0 +1,8 @@ +MenuManager { + MenuPresets { + MenuPreset AFM_ToolsMenu { + Layout "{4A825434A1312E28}UI/layouts/AFM_Tools/AFM_ToolsMenu.layout" + Class "AFM_ToolsMenu" + } + } +} \ No newline at end of file diff --git a/addons/core/Configs/System/chimeraMenus.conf.meta b/addons/core/Configs/System/chimeraMenus.conf.meta new file mode 100644 index 0000000..0c10021 --- /dev/null +++ b/addons/core/Configs/System/chimeraMenus.conf.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{C747AFB6B750CE9A}Configs/System/chimeraMenus.conf" + Configurations { + CONFResourceClass PC { + } + CONFResourceClass XBOX_ONE : PC { + } + CONFResourceClass XBOX_SERIES : PC { + } + CONFResourceClass PS4 : PC { + } + CONFResourceClass PS5 : PC { + } + CONFResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/Language/AFM_Core_localization.en_us.conf b/addons/core/Language/AFM_Core_localization.en_us.conf index b361c18..c8b7340 100644 --- a/addons/core/Language/AFM_Core_localization.en_us.conf +++ b/addons/core/Language/AFM_Core_localization.en_us.conf @@ -1,5 +1,7 @@ StringTableRuntime { Ids { + "AFM_Core-AFM_Tools" + "AFM_Core-AFM_Tools_ServerSettings" "AFM_Core-Settings" "AFM_Core-Settings_Radio" "AFM_Core-Settings_Radio_Beep_1" @@ -9,6 +11,8 @@ StringTableRuntime { "AFM_Core-Settings_Radio_Beep_Off" } Texts { + "AFM Tools" + "Server Settings" "ArmaForces" "Radio" "Beep" diff --git a/addons/core/Language/AFM_Core_localization.en_us.conf.meta b/addons/core/Language/AFM_Core_localization.en_us.conf.meta new file mode 100644 index 0000000..5ec356a --- /dev/null +++ b/addons/core/Language/AFM_Core_localization.en_us.conf.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{059E1098F15F3D80}Language/AFM_Core_localization.en_us.conf" + Configurations { + CONFResourceClass PC { + } + CONFResourceClass XBOX_ONE : PC { + } + CONFResourceClass XBOX_SERIES : PC { + } + CONFResourceClass PS4 : PC { + } + CONFResourceClass PS5 : PC { + } + CONFResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/Language/AFM_Core_localization.pl_pl.conf b/addons/core/Language/AFM_Core_localization.pl_pl.conf index 494c789..74643bf 100644 --- a/addons/core/Language/AFM_Core_localization.pl_pl.conf +++ b/addons/core/Language/AFM_Core_localization.pl_pl.conf @@ -1,5 +1,7 @@ StringTableRuntime { Ids { + "AFM_Core-AFM_Tools" + "AFM_Core-AFM_Tools_ServerSettings" "AFM_Core-Settings" "AFM_Core-Settings_Radio" "AFM_Core-Settings_Radio_Beep_1" @@ -9,6 +11,8 @@ StringTableRuntime { "AFM_Core-Settings_Radio_Beep_Off" } Texts { + "Narzędzia AFM" + "Ustawienia Serwera" "ArmaForces" "Radio" "Pisk" diff --git a/addons/core/Language/AFM_Core_localization.pl_pl.conf.meta b/addons/core/Language/AFM_Core_localization.pl_pl.conf.meta new file mode 100644 index 0000000..d7de4bf --- /dev/null +++ b/addons/core/Language/AFM_Core_localization.pl_pl.conf.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{17B214D6474F998C}Language/AFM_Core_localization.pl_pl.conf" + Configurations { + CONFResourceClass PC { + } + CONFResourceClass XBOX_ONE : PC { + } + CONFResourceClass XBOX_SERIES : PC { + } + CONFResourceClass PS4 : PC { + } + CONFResourceClass PS5 : PC { + } + CONFResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/Language/AFM_Core_localization.st b/addons/core/Language/AFM_Core_localization.st index 6498601..cddee1e 100644 --- a/addons/core/Language/AFM_Core_localization.st +++ b/addons/core/Language/AFM_Core_localization.st @@ -1,13 +1,30 @@ StringTable { ItemClassName "CustomStringTableItem" Items { + CustomStringTableItem "{6334C6C7D25A5092}" { + Id "AFM_Core-AFM_Tools" + Target_en_us "AFM Tools" + Target_pl_pl "Narzędzia AFM" + Modified 1664403421 + Author "veteran29" + LastChanged "veteran29" + } + CustomStringTableItem "{633A22210415B8CF}" { + Id "AFM_Core-AFM_Tools_ServerSettings" + Target_en_us "Server Settings" + Target_pl_pl "Ustawienia Serwera" + Modified 1664754356 + Author "veteran29" + LastChanged "veteran29" + } CustomStringTableItem "{63336833D030E5A6}" { Id "AFM_Core-Settings" Target_en_us "ArmaForces" + Target_pl_pl "ArmaForces" ActorGender NONE Terminology NONE NonTranslatable 1 - Modified 1664319029 + Modified 1664754369 Author "veteran29" LastChanged "veteran29" } diff --git a/addons/core/Prefabs/Characters/Core/DefaultPlayerController.et b/addons/core/Prefabs/Characters/Core/DefaultPlayerController.et new file mode 100644 index 0000000..2fe2e9d --- /dev/null +++ b/addons/core/Prefabs/Characters/Core/DefaultPlayerController.et @@ -0,0 +1,7 @@ +SCR_PlayerController { + ID "1A9BA6BEFA06E3B1" + components { + AFM_SettingsComponent "{63353E87437476C9}" { + } + } +} \ No newline at end of file diff --git a/addons/core/Prefabs/Characters/Core/DefaultPlayerController.et.meta b/addons/core/Prefabs/Characters/Core/DefaultPlayerController.et.meta new file mode 100644 index 0000000..4cc4d98 --- /dev/null +++ b/addons/core/Prefabs/Characters/Core/DefaultPlayerController.et.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{6E2BB64764E3BE9B}Prefabs/Characters/Core/DefaultPlayerController.et" + Configurations { + EntityTemplateResourceClass PC { + } + EntityTemplateResourceClass XBOX_ONE : PC { + } + EntityTemplateResourceClass XBOX_SERIES : PC { + } + EntityTemplateResourceClass PS4 : PC { + } + EntityTemplateResourceClass PS5 : PC { + } + EntityTemplateResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout b/addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout new file mode 100644 index 0000000..d946add --- /dev/null +++ b/addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout @@ -0,0 +1,77 @@ +FrameWidgetClass { + Name "AFM_ToolsMenu" + components { + SCR_SuperMenuComponent "{632CF105ABB3A644}" { + } + } + { + OverlayWidgetClass "{632CF10E244EE461}" : "{89D4A2D343602766}UI/layouts/WidgetLibrary/BaseElements/WLib_ReadabilityBackground.layout" { + Name "ReadibilityBackground" + Slot FrameWidgetSlot "{632CF10E244EE3A1}" { + Anchor 0 0 1 1 + PositionX 0 + OffsetLeft 0 + PositionY 0 + OffsetTop 0 + SizeX 0 + OffsetRight 0 + SizeY 0 + OffsetBottom 0 + } + } + OverlayWidgetClass "{632C29305E86E90A}" : "{6F1E013D3D33703C}UI/layouts/WidgetLibrary/BaseElements/WLib_MenuBaseTabbed.layout" { + Name "MenuFrame" + Slot FrameWidgetSlot "{54D8A1A69BDC4E85}" { + } + { + SizeLayoutWidgetClass "{589F093B48C39BEF}" { + Prefab "{589F093B48C39BEF}" + { + VerticalLayoutWidgetClass "{589F093B48C39BEC}" { + Prefab "{589F093B48C39BEC}" + { + SizeLayoutWidgetClass "{589F093B48C39BEA}" { + Prefab "{589F093B48C39BEA}" + { + OverlayWidgetClass "{58A07816DC169F57}" { + Prefab "{58A07816DC169F57}" + { + TextWidgetClass "{589F093B48C39BE9}" { + Prefab "{589F093B48C39BE9}" + Text "AFM Tools" + } + } + } + } + } + VerticalLayoutWidgetClass "{589F093AB3EA94A2}" { + Prefab "{589F093AB3EA94A2}" + { + OverlayWidgetClass "{000000001094E915}" { + Prefab "{000000001094E915}" + { + ImageWidgetClass "{632CF1098E878CFB}" { + Name "BackgroundImage" + Slot OverlayWidgetSlot "{632CF1098E878C3B}" { + HorizontalAlign 3 + VerticalAlign 3 + } + "Z Order" -128 + Opacity 0.5 + Color 0 0 0 1 + Texture "{A592187FD7874100}UI/Textures/Common/GradientTexture_B80-B60-B0@2x.edds" + Size 1024 1024 + Tiling None + } + } + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout.meta b/addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout.meta new file mode 100644 index 0000000..f352de2 --- /dev/null +++ b/addons/core/UI/layouts/AFM_Tools/AFM_ToolsMenu.layout.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{4A825434A1312E28}UI/layouts/AFM_ServerSettings/AFM_ServerSettingsMenu.layout" + Configurations { + LayoutResourceClass PC { + } + LayoutResourceClass XBOX_ONE : PC { + } + LayoutResourceClass XBOX_SERIES : PC { + } + LayoutResourceClass PS4 : PC { + } + LayoutResourceClass PS5 : PC { + } + LayoutResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout new file mode 100644 index 0000000..074e86a --- /dev/null +++ b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout @@ -0,0 +1,126 @@ +HorizontalLayoutWidgetClass : "{0C2FFE60CB37F8CA}UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout" { + components { + AFM_SubMenuBase "{632D326AA6ED1C3D}" { + Enabled 0 + } + AFM_ServerSettingsSubMenu "{63382472B4C2F7CA}" { + } + } + { + ScrollLayoutWidgetClass "{632D326BCA924163}" { + Prefab "{632D326BCA924163}" + Slot LayoutSlot "{632D326BCA924094}" { + Padding 2 4 2 0 + } + { + VerticalLayoutWidgetClass "{632D326BB84185A9}" { + Prefab "{632D326BB84185A9}" + Slot AlignableSlot "{632D326BB84185DF}" { + HorizontalAlign 0 + } + { + VerticalLayoutWidgetClass "{633940800A325E7F}" { + Name "VerticalLayout0" + Slot LayoutSlot "{633940800A325DA5}" { + } + { + HorizontalLayoutWidgetClass "{6339408035CE336A}" : "{829BC1189A899017}UI/layouts/WidgetLibrary/BaseElements/WLib_Label.layout" { + Name "LabelRoot0" + Slot LayoutSlot "{55367D9A1530D6F9}" { + } + { + TextWidgetClass "{554CD830975E8F3E}" { + Prefab "{554CD830975E8F3E}" + Text "Mission header" + } + } + } + PanelWidgetClass "{6338247480AC1BF3}" { + Name "Panel0" + Slot LayoutSlot "{6338247480AC1BE4}" { + } + style pixel_frame + { + ScrollLayoutWidgetClass "{63392EC98914E35F}" { + Name "Scroll0" + Slot FrameWidgetSlot "{63392EC98914E28D}" { + OffsetLeft 0 + OffsetTop 0 + SizeX 1024 + OffsetRight -1024 + SizeY 512 + OffsetBottom -512 + } + { + TextWidgetClass "{6338247485929E47}" { + Name "TextMissionHeader" + Slot AlignableSlot "{63392EC98947F04E}" { + HorizontalAlign 0 + VerticalAlign 0 + Padding 6 5 6 5 + } + Text "" + "No Localization" 1 + } + } + } + } + } + } + } + VerticalLayoutWidgetClass "{63394080137065DB}" { + Name "VerticalLayout1" + Slot LayoutSlot "{633940800A325DA5}" { + Padding 0 15 0 0 + } + { + HorizontalLayoutWidgetClass "{633940802DC4CBC0}" : "{829BC1189A899017}UI/layouts/WidgetLibrary/BaseElements/WLib_Label.layout" { + Name "LabelRoot0" + Slot LayoutSlot "{55367D9A1530D6F9}" { + } + { + TextWidgetClass "{554CD830975E8F3E}" { + Prefab "{554CD830975E8F3E}" + Text "ACE Settings" + } + } + } + PanelWidgetClass "{63394080137065DA}" { + Name "Panel0" + Slot LayoutSlot "{6338247480AC1BE4}" { + } + style pixel_frame + { + ScrollLayoutWidgetClass "{63394080137065D9}" { + Name "Scroll0" + Slot FrameWidgetSlot "{63392EC98914E28D}" { + OffsetLeft 0 + OffsetTop 0 + SizeX 1024 + OffsetRight -1024 + SizeY 512 + OffsetBottom -512 + } + { + TextWidgetClass "{6339408013701C1B}" { + Name "TextAceSettings" + Slot AlignableSlot "{63392EC98947F04E}" { + HorizontalAlign 0 + VerticalAlign 0 + Padding 6 5 6 5 + } + Text "" + "No Localization" 1 + } + } + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout.meta b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout.meta new file mode 100644 index 0000000..c10bbef --- /dev/null +++ b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{DEC85F306F40902A}UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout" + Configurations { + LayoutResourceClass PC { + } + LayoutResourceClass XBOX_ONE : PC { + } + LayoutResourceClass XBOX_SERIES : PC { + } + LayoutResourceClass PS4 : PC { + } + LayoutResourceClass PS5 : PC { + } + LayoutResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout new file mode 100644 index 0000000..f5d5c42 --- /dev/null +++ b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout @@ -0,0 +1,25 @@ +HorizontalLayoutWidgetClass { + Name "TabContent" + components { + AFM_SubMenuBase "{632D326AA6ED1C3D}" { + } + } + { + ScrollLayoutWidgetClass "{632D326BCA924163}" { + Name "ScrollLayout0" + Slot LayoutSlot "{632D326BCA924094}" { + Padding 2 72 2 0 + SizeMode Fill + FillWeight 1 + } + { + VerticalLayoutWidgetClass "{632D326BB84185A9}" { + Name "Content" + Slot AlignableSlot "{632D326BB84185DF}" { + Padding 4 0 4 0 + } + } + } + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout.meta b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout.meta new file mode 100644 index 0000000..7c0f1a1 --- /dev/null +++ b/addons/core/UI/layouts/AFM_Tools/AFM_Tools_TabContentBase.layout.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{0C2FFE60CB37F8CA}UI/layouts/AFM_ServerSettings/AFM_Tools_TabContentBase.layout" + Configurations { + LayoutResourceClass PC { + } + LayoutResourceClass XBOX_ONE : PC { + } + LayoutResourceClass XBOX_SERIES : PC { + } + LayoutResourceClass PS4 : PC { + } + LayoutResourceClass PS5 : PC { + } + LayoutResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout b/addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout new file mode 100644 index 0000000..6331b0f --- /dev/null +++ b/addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout @@ -0,0 +1,69 @@ +FrameWidgetClass { + { + OverlayWidgetClass "{5DB8B556919FE5F2}" { + Prefab "{5DB8B556919FE5F2}" + { + VerticalLayoutWidgetClass "{5DB8B556919FE5EE}" { + Prefab "{5DB8B556919FE5EE}" + { + ButtonWidgetClass "{632C0D60F9A71832}" : "{CFF37D05A7659E5C}UI/layouts/Menus/PauseMenu/PauseMenuButton_ActionIcon.layout" { + Name "AFM_ToolsMenu" + Slot LayoutSlot "{557478C3F0230865}" { + Padding 0 0 0 0 + } + components { + SCR_ButtonTextComponent "{5548F0143AE99095}" { + m_sText "#AFM_Core-AFM_Tools" + } + } + { + SizeLayoutWidgetClass "{557098260F312277}" { + Prefab "{557098260F312277}" + Slot ButtonWidgetSlot "{5548E6E7E6AEDDC1}" { + Padding 7 1 0 1 + } + { + OverlayWidgetClass "{557098260F312272}" { + Prefab "{557098260F312272}" + { + ScaleWidgetClass "{58D09C4047778BAE}" { + Prefab "{58D09C4047778BAE}" + { + ImageWidgetClass "{58D09C40452A7C25}" { + Prefab "{58D09C40452A7C25}" + "Is Visible" 0 + } + } + } + HorizontalLayoutWidgetClass "{557098267862A738}" { + Prefab "{557098267862A738}" + { + ScaleWidgetClass "{557478C2A0AF4E50}" { + Prefab "{557478C2A0AF4E50}" + { + ImageWidgetClass "{557098267D2A9494}" { + Prefab "{557098267D2A9494}" + Texture "{3262679C50EF4F01}UI/Textures/Icons/icons_wrapperUI.imageset" + Image "settings" + Size 32 32 + } + } + } + } + } + } + } + } + } + } + } + ImageWidgetClass "{5DB8B556919FE5EF}" { + Prefab "{5DB8B556919FE5EF}" + "Z Order" -100 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout.meta b/addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout.meta new file mode 100644 index 0000000..6ffd91f --- /dev/null +++ b/addons/core/UI/layouts/Menus/PauseMenu/pauseMenu.layout.meta @@ -0,0 +1,17 @@ +MetaFileClass { + Name "{E81F33FD5F8C893B}UI/layouts/Menus/PauseMenu/pauseMenu.layout" + Configurations { + LayoutResourceClass PC { + } + LayoutResourceClass XBOX_ONE : PC { + } + LayoutResourceClass XBOX_SERIES : PC { + } + LayoutResourceClass PS4 : PC { + } + LayoutResourceClass PS5 : PC { + } + LayoutResourceClass HEADLESS : PC { + } + } +} \ No newline at end of file diff --git a/addons/core/addon.gproj b/addons/core/addon.gproj index 4307899..5b6ab77 100644 --- a/addons/core/addon.gproj +++ b/addons/core/addon.gproj @@ -3,7 +3,7 @@ GameProject { GUID "6122A791D07C3960" TITLE "ArmaForces Mods Core" Dependencies { - "58D0FB3206B6F859" + "58D0FB3206B6F859" "60C4CE4888FF4621" } Configurations { GameProjectConfig PC { diff --git a/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ServerSettingsSubMenu.c b/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ServerSettingsSubMenu.c new file mode 100644 index 0000000..44d5138 --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ServerSettingsSubMenu.c @@ -0,0 +1,87 @@ +class AFM_ServerSettingsSubMenu : AFM_SubMenuBase +{ + protected bool m_bLoadedMissionHeader = false; + protected bool m_bLoadedAceSettings = false; + + protected TextWidget m_wMissionHeaderText; + protected TextWidget m_wAceSettingsText; + + //------------------------------------------------------------------------------------------------ + override void OnTabCreate(Widget menuRoot, ResourceName buttonsLayout, int index) + { + super.OnTabCreate(menuRoot, buttonsLayout, index); + + m_wMissionHeaderText = TextWidget.Cast(m_wRoot.FindAnyWidget("TextMissionHeader")); + m_wAceSettingsText = TextWidget.Cast(m_wRoot.FindAnyWidget("TextAceSettings")); + } + + //------------------------------------------------------------------------------------------------ + override void OnTabShow() + { + super.OnTabShow(); + + if (!m_bLoadedMissionHeader || !m_bLoadedAceSettings) + LoadTabData(); + } + + //------------------------------------------------------------------------------------------------ + protected void LoadTabData() + { + SCR_LoadingOverlay.ShowForWidget(m_wRoot); + + SCR_PlayerController pc = SCR_PlayerController.Cast(GetGame().GetPlayerController()); + AFM_SettingsComponent settingsComponent = AFM_SettingsComponent.Cast(pc.FindComponent(AFM_SettingsComponent)); + + if (m_wMissionHeaderText) + { + Print("UI: Loading mission header data", level: LogLevel.DEBUG); + + settingsComponent.m_pMissionHeaderResponseInvoker.Insert(OnMissionHeader); + #ifdef WORKBENCH + GetGame().GetCallqueue().CallLater(settingsComponent.RequestMissionHeader, 700); + #else + settingsComponent.RequestMissionHeader(); + #endif + } + + if (m_wAceSettingsText) + { + Print("UI: Loading ACE settings data", level: LogLevel.DEBUG); + + settingsComponent.m_pAceSettingsResponseInvoker.Insert(OnAceSettings); + #ifdef WORKBENCH + GetGame().GetCallqueue().CallLater(settingsComponent.RequestAceSettings, 1400); + #else + settingsComponent.RequestAceSettings(); + #endif + } + } + + //------------------------------------------------------------------------------------------------ + //! Set mission header data + protected void OnMissionHeader(AFM_ContainerTextResponse response) + { + m_bLoadedMissionHeader = true; + + m_wMissionHeaderText.SetText(response.m_sContent); + + HandleSpinner(); + } + + //------------------------------------------------------------------------------------------------ + //! Set ACE settings data + protected void OnAceSettings(AFM_ContainerTextResponse response) + { + m_bLoadedAceSettings = true; + + m_wAceSettingsText.SetText(response.m_sContent); + + HandleSpinner(); + } + + protected void HandleSpinner() + { + if (m_bLoadedMissionHeader && m_bLoadedAceSettings) + SCR_LoadingOverlay.HideForWidget(m_wRoot); + } +} diff --git a/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_SubMenuBase.c b/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_SubMenuBase.c new file mode 100644 index 0000000..9b00936 --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_SubMenuBase.c @@ -0,0 +1,23 @@ +class AFM_SubMenuBase: SCR_SubMenuBase +{ + protected ScrollLayoutWidget m_wScroll; + + //------------------------------------------------------------------------------------------------ + override void OnTabCreate(Widget menuRoot, ResourceName buttonsLayout, int index) + { + super.OnTabCreate(menuRoot, buttonsLayout, index); + m_wScroll = ScrollLayoutWidget.Cast(m_wRoot.FindAnyWidget("ScrollLayout0")); + } + + //------------------------------------------------------------------------------------------------ + override void OnTabShow() + { + super.OnTabShow(); + + // Reset the focused Widget, to prevent that a Widget from a previous tab is still focused and controlled. + GetGame().GetWorkspace().SetFocusedWidget(null); + + if (m_wScroll) + m_wScroll.SetSliderPos(0, 0); + } +} diff --git a/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ToolsMenu.c b/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ToolsMenu.c new file mode 100644 index 0000000..0e8c698 --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/AFM_Tools/AFM_ToolsMenu.c @@ -0,0 +1,57 @@ +modded enum ChimeraMenuPreset +{ + AFM_ToolsMenu +} + +class AFM_ToolsMenu: SCR_SuperMenuBase +{ + protected Widget m_wRoot; + protected SCR_TabViewComponent m_Tabs; + + //------------------------------------------------------------------------------------------------ + override void OnMenuOpen() + { + super.OnMenuOpen(); + Print("AFM_ServerSettingsMenu - OnMenuInit", level: LogLevel.DEBUG); + + m_wRoot = GetRootWidget(); + + SCR_InputButtonComponent back = SCR_InputButtonComponent.GetInputButtonComponent("Back", m_wRoot); + back.m_OnActivated.Insert(Close); + + m_Tabs = GetSuperMenu().GetTabView(); + + #ifdef WORKBENCH + AddTestTab(); + #endif + + AddServerSettingsTab(); + + m_Tabs.ShowTab(0, playSound: false); + } + +#ifdef WORKBENCH + private void AddTestTab() + { + m_Tabs.AddTab( + layout: "{0C2FFE60CB37F8CA}UI/layouts/AFM_ServerSettings/AFM_Tools_TabContentBase.layout", + identifier: "test1", + content: "Test 1", + ); + m_Tabs.AddTab( + layout: "{0C2FFE60CB37F8CA}UI/layouts/Menus/SettingsMenu/AFM_Tools_TabContentBase.layout", + identifier: "test2", + content: "Test 2", + ); + } +#endif + + private void AddServerSettingsTab() + { + m_Tabs.AddTab( + layout: "{DEC85F306F40902A}UI/layouts/AFM_Tools/AFM_Tools_ServerSettingsSubMenu.layout", + identifier: "server-settings", + content: "#AFM_Core-AFM_Tools_ServerSettings", + ); + } +} diff --git a/addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminList.c b/addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminList.c new file mode 100644 index 0000000..a526248 --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminList.c @@ -0,0 +1,6 @@ +[BaseContainerProps(configRoot: true)] +class AFM_AdminList +{ + [Attribute(desc: "Unique Player UID. This is not the player ID but the the player UID obtained via BackendApi.GetPlayerIdentityId()")] + protected ref array m_aAdminUIDs; +}; diff --git a/addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminUID.c b/addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminUID.c new file mode 100644 index 0000000..b29f20e --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/Containers/AFM_AdminUID.c @@ -0,0 +1,6 @@ +[BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)] +class AFM_AdminUID +{ + [Attribute(desc: "Unique Player UIDs. This is not the player ID but the the player UID obtained via BackendApi.GetPlayerIdentityId()")] + protected ref array m_sAdminUIDs; +}; diff --git a/addons/core/scripts/Game/AFM_Core/Containers/AFM_PlainTextContainerSerializer.c b/addons/core/scripts/Game/AFM_Core/Containers/AFM_PlainTextContainerSerializer.c new file mode 100644 index 0000000..4cd4b98 --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/Containers/AFM_PlainTextContainerSerializer.c @@ -0,0 +1,119 @@ +class AFM_PlainTextContainerSerializer +{ + //------------------------------------------------------------------------------------------------ + //! Serializes container into a human readable string + string SerializeContainer(BaseContainer container, int level = 0) + { + string output = MakeIndent(level); + if (!container) + { + output += "null"; + return output; + } + + output += string.Format("%1:", container.GetClassName()); + for (int i, count = container.GetNumVars(); i < count; i++) + { + string var = container.GetVarName(i); + DataVarType type = container.GetDataVarType(i); + output += "\n"; + output += SerializeProp(container, var, type, level + 1); + } + + return output; + } + + //------------------------------------------------------------------------------------------------ + //! Serializes container property/entry into a human readable string + string SerializeProp(BaseContainer container, string var, DataVarType type, int level = 0) + { + string output = MakeIndent(level); + switch (type) + { + case DataVarType.BOOLEAN: + { + bool val; + container.Get(var, val); + if (val) + output += string.Format("%1: true", var); + else + output += string.Format("%1: false", var); + + break; + } + + case DataVarType.SCALAR: + { + float val; + container.Get(var, val); + output += string.Format("%1: %2", var, val.ToString(-1, 2)); + + break; + } + + case DataVarType.INTEGER: + { + int val; + container.Get(var, val); + output += string.Format("%1: %2", var, val); + + break; + } + + case DataVarType.STRING: + { + string val; + container.Get(var, val); + output += string.Format("%1: %2", var, string.ToString(val)); + + break; + } + + case DataVarType.RESOURCE_NAME: + { + ResourceName val; + container.Get(var, val); + output += string.Format("%1: %2", var, val); + + break; + } + + case DataVarType.OBJECT: + { + BaseContainer containerObject = container.GetObject(var); + string val = SerializeContainer(containerObject, level + 1); + output += string.Format("%1:\n%2", var, val); + + break; + } + + case DataVarType.OBJECT_ARRAY: + { + BaseContainerList list = container.GetObjectArray(var); + + for (int i, count = list.Count(); i < count; i++) + { + BaseContainer containerEntry = list.Get(i); + string val = SerializeContainer(containerEntry, level + 1); + output += string.Format("%1:\n%2", var, val); + } + + break; + } + + default: + { + string eType = SCR_Enum.GetEnumName(DataVarType, type); + output += string.Format("%1: ", var, eType); + } + } + + return output; + } + + //------------------------------------------------------------------------------------------------ + protected string MakeIndent(int level) + { + return SCR_StringHelper.PadLeft("", level*4, SCR_StringHelper.SPACE); + } +} diff --git a/addons/core/scripts/Game/AFM_Core/Settings/AFM_SettingsComponent.c b/addons/core/scripts/Game/AFM_Core/Settings/AFM_SettingsComponent.c new file mode 100644 index 0000000..cffce17 --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/Settings/AFM_SettingsComponent.c @@ -0,0 +1,159 @@ +[ComponentEditorProps(category: "AFM", description: "")] +class AFM_SettingsComponentClass : ScriptComponentClass +{ +} + +class AFM_SettingsComponent : ScriptComponent +{ + protected ref AFM_PlainTextContainerSerializer m_pSerializer = new AFM_PlainTextContainerSerializer(); + + ref AFM_ContainerTextResponseInvoker m_pMissionHeaderResponseInvoker = new AFM_ContainerTextResponseInvoker(); + ref AFM_ContainerTextResponseInvoker m_pAceSettingsResponseInvoker = new AFM_ContainerTextResponseInvoker(); + + protected SCR_MissionHeader GetMissionHeader() + { + SCR_MissionHeader header = SCR_MissionHeader.Cast(GetGame().GetMissionHeader()); + + #ifdef WORKBENCH + // if world is started directly it will have no header, provide some test data + if (!header) + header = SCR_MissionHeader.Cast(MissionHeader.ReadMissionHeader("{83D06A42096F671C}Missions/MpTest/10_MpTest.conf")); + #endif + + return header; + } + + //################################################################################################ + //! Mission header + void RequestMissionHeader() + { + Print("Requesting mission header", level: LogLevel.DEBUG); + + Rpc(RpcAsk_RequestMissionHeader, SCR_PlayerController.GetLocalPlayerId()); + } + + //------------------------------------------------------------------------------------------------ + [RplRpc(RplChannel.Reliable, RplRcver.Server)] + protected void RpcAsk_RequestMissionHeader(int playerId) + { + AFM_ContainerTextResponse response = new AFM_ContainerTextResponse(); + + SCR_MissionHeader header = GetMissionHeader(); + if (header) + { + Resource holder = BaseContainerTools.CreateContainerFromInstance(header); + BaseContainer headerContainer = holder.GetResource().ToBaseContainer(); + + response.m_sContent += m_pSerializer.SerializeContainer(headerContainer); + } + + Rpc(RpcDo_RespondMissionHeader, response); + + } + + //------------------------------------------------------------------------------------------------ + [RplRpc(RplChannel.Reliable, RplRcver.Owner)] + protected void RpcDo_RespondMissionHeader(AFM_ContainerTextResponse response) + { + Print("Mission header response", level: LogLevel.DEBUG); + + m_pMissionHeaderResponseInvoker.Invoke(response); + } + + //################################################################################################ + //! ACE Settings + void RequestAceSettings() + { + Print("Requesting ACE settings", level: LogLevel.DEBUG); + + Rpc(RpcAsk_RequestAceSettings, SCR_PlayerController.GetLocalPlayerId()); + } + + //------------------------------------------------------------------------------------------------ + [RplRpc(RplChannel.Reliable, RplRcver.Server)] + protected void RpcAsk_RequestAceSettings(int playerId) + { + AFM_ContainerTextResponse response = new AFM_ContainerTextResponse(); + + ACE_SettingsConfig config = ArmaReforgerScripted.ACE_GetSettingsConfig(); + foreach(ACE_ModSettings settings : config.GetAllModSettings()) + { + Resource holder = BaseContainerTools.CreateContainerFromInstance(settings); + BaseContainer settingsContainer = holder.GetResource().ToBaseContainer(); + + response.m_sContent += m_pSerializer.SerializeContainer(settingsContainer); + response.m_sContent += "\n"; + } + + Rpc(RpcDo_RespondAceSettings, response); + } + + //------------------------------------------------------------------------------------------------ + [RplRpc(RplChannel.Reliable, RplRcver.Owner)] + protected void RpcDo_RespondAceSettings(AFM_ContainerTextResponse response) + { + Print("ACE settings response", level: LogLevel.DEBUG); + + m_pAceSettingsResponseInvoker.Invoke(response); + } +} + +void ScriptInvoker_AFM_MissionHeaderResponse(AFM_ContainerTextResponse response); +typedef func ScriptInvoker_AFM_MissionHeaderResponse; +typedef ScriptInvokerBase AFM_ContainerTextResponseInvoker; + +class AFM_ContainerTextResponse +{ + string m_sContent = ""; + + //################################################################################################ + //! Codec methods + //------------------------------------------------------------------------------------------------ + //! Property mem to snapshot extraction. + // Extracts relevant properties from an instance of type T into snapshot. Opposite of Inject() + static bool Extract(AFM_ContainerTextResponse instance, ScriptCtx ctx, SSnapSerializerBase reader) + { + reader.SerializeString(instance.m_sContent); + + return true; + } + + //! From snapshot to packet. + // Takes snapshot and compresses it into packet. Opposite of Decode() + static void Encode(SSnapSerializerBase snapshot, ScriptCtx ctx, ScriptBitSerializer packet) + { + snapshot.EncodeString(packet); + } + + //! From packet to snapshot. + // Takes packet and decompresses it into snapshot. Opposite of Encode() + static bool Decode(ScriptBitSerializer packet, ScriptCtx ctx, SSnapSerializerBase snapshot) + { + snapshot.DecodeString(packet); + + return true; + } + + //! Snapshot to property memory injection. + // Injects relevant properties from snapshot into an instance of type T . Opposite of Extract() + static bool Inject(SSnapSerializerBase writer, ScriptCtx ctx, AFM_ContainerTextResponse instance) + { + writer.SerializeString(instance.m_sContent); + + return true; + } + + //! Snapshot to snapshot comparison. + // Compares two snapshots to see whether they are the same or not + static bool SnapCompare(SSnapSerializerBase lhs, SSnapSerializerBase rhs, ScriptCtx ctx) + { + return lhs.CompareStringSnapshots(rhs); + } + + //! Property mem to snapshot comparison. + // Compares instance and a snapshot to see if any property has changed enough to require a new snapshot + static bool PropCompare(AFM_ContainerTextResponse instance, SSnapSerializerBase snapshot, ScriptCtx ctx) + { + return snapshot.CompareString(instance.m_sContent); + } +} diff --git a/addons/core/scripts/Game/AFM_Core/UI/Menu/SCR_PauseMenuUI.c b/addons/core/scripts/Game/AFM_Core/UI/Menu/SCR_PauseMenuUI.c new file mode 100644 index 0000000..6e591ab --- /dev/null +++ b/addons/core/scripts/Game/AFM_Core/UI/Menu/SCR_PauseMenuUI.c @@ -0,0 +1,20 @@ +modded class PauseMenuUI: ChimeraMenuBase +{ + protected SCR_ButtonTextComponent m_AFM_ServerSettingsButton; + + //------------------------------------------------------------------------------------------------ + override void OnMenuOpen() + { + super.OnMenuOpen(); + + m_AFM_ServerSettingsButton = SCR_ButtonTextComponent.GetButtonText("AFM_ToolsMenu", m_wRoot); + if (m_AFM_ServerSettingsButton) + m_AFM_ServerSettingsButton.m_OnClicked.Insert(AFM_OnServerSettings); + } + + //------------------------------------------------------------------------------------------------ + private void AFM_OnServerSettings() + { + GetGame().GetMenuManager().OpenMenu(ChimeraMenuPreset.AFM_ToolsMenu); + } +};