diff --git a/data/cdata/ui_scripts/MainMenu/CPMainMenu.lua b/data/cdata/ui_scripts/MainMenu/CPMainMenu.lua index 59de819d..b5821958 100644 --- a/data/cdata/ui_scripts/MainMenu/CPMainMenu.lua +++ b/data/cdata/ui_scripts/MainMenu/CPMainMenu.lua @@ -1,188 +1,4 @@ -local f0_local0 = function ( f1_arg0, f1_arg1 ) - Engine.Exec( "xblive_privatematch 0" ) - utils.cp.AliensUtils.AliensRunConfig( f1_arg1.controller ) - LUI.FlowManager.RequestAddMenu( "SystemLinkMenu", false, f1_arg1.controller, false, {}, true ) -end - -local f0_local1 = function ( f2_arg0, f2_arg1 ) - f0_local0( f2_arg0, f2_arg1 ) -end - -local f0_local2 = function ( f3_arg0, f3_arg1 ) - Engine.Exec( MPConfig.default_xboxlive, f3_arg1.controller ) - Engine.SetDvarBool( "xblive_privatematch", true ) - SetIsAliensSolo( true ) - Engine.SetDvarInt( "party_maxplayers", 1 ) - Engine.Exec( "xstartprivatematch" ) - LUI.FlowManager.RequestAddMenu( "CPPrivateMatchMenu", false, f3_arg1.controller, false, { - showPlayNowButton = true, - isPublicMatch = false - } ) -end - -local f0_local3 = function ( f4_arg0, f4_arg1 ) - Engine.Exec( MPConfig.default_xboxlive, f4_arg1.controller ) - Engine.SetDvarBool( "xblive_privatematch", true ) - SetIsAliensSolo( false ) - Engine.Exec( "xstartprivatematch" ) - LUI.FlowManager.RequestAddMenu( "CPPrivateMatchMenu", false, f4_arg1.controller, false, { - showPlayNowButton = true, - isPublicMatch = false - } ) -end - -local f0_local4 = function ( f5_arg0, f5_arg1, f5_arg2 ) - assert( f5_arg0.PublicMatch ) - assert( f5_arg0.SoloMatch ) - assert( f5_arg0.CustomMatch ) - local f5_local0 = LUI.DataSourceInGlobalModel.new( "frontEnd.lobby.areWeGameHost" ) - local f5_local1 = DataSources.frontEnd.lobby.memberCount - local f5_local2 = function () - return Lobby.IsInPrivateParty() and not Lobby.IsPrivatePartyHost() - end - - local f5_local3 = function () - local f7_local0 = f5_local2() - f5_arg0.PublicMatch:SetButtonDisabled( f7_local0 ) - f5_arg0.CustomMatch:SetButtonDisabled( f7_local0 ) - end - - f5_arg0:SubscribeToModel( f5_local0:GetModel( f5_arg1 ), f5_local3 ) - f5_arg0:SubscribeToModel( f5_local1:GetModel( f5_arg1 ), f5_local3 ) - f5_arg0:SubscribeToModel( DataSources.frontEnd.lobby.isSolo:GetModel( f5_arg1 ), function () - local f8_local0 = DataSources.frontEnd.lobby.isSolo:GetValue( f5_arg1 ) - if f8_local0 ~= nil then - f5_arg0.SoloMatch:SetButtonDisabled( not f8_local0 ) - end - end ) - f5_arg0.PublicMatch:addEventHandler( "button_action", f0_local1 ) - f5_arg0.SoloMatch:addEventHandler( "button_action", f0_local2 ) - f5_arg0.Loadout:addEventHandler( "button_action", function ( f9_arg0, f9_arg1 ) - LUI.FlowManager.RequestAddMenu( "CPLoadoutMenu", true, f9_arg1.controller ) - end ) - f5_arg0.Barracks:addEventHandler( "button_action", function ( f10_arg0, f10_arg1 ) - LUI.FlowManager.RequestAddMenu( "Headquarters", true, f10_arg1.controller ) - end ) - f5_arg0.Armory:addEventHandler( "button_action", function ( f11_arg0, f11_arg1 ) - if not Engine.IsUserAGuest( f11_arg1.controller ) then - ACTIONS.OpenMenu( "Armory", true, f11_arg1.controller ) - end - end ) - f5_arg0.CustomMatch:addEventHandler( "button_action", f0_local3 ) - f5_arg0.ContractsButton:addEventHandler( "button_action", function ( f12_arg0, f12_arg1 ) - ACTIONS.OpenMenu( "ContractMenu", true, f12_arg1.controller or f5_arg1 ) - end ) -end - -function CPMainMenuButtons( menu, controller ) - local VNavigator = LUI.UIVerticalNavigator.new() - VNavigator:SetAnchorsAndPosition( 0, 1, 0, 1, 0, 500 * _1080p, 0, 400 * _1080p ) - VNavigator.id = "CPMainMenuButtons" - local f14_local1 = controller and controller.controllerIndex - if not f14_local1 and not Engine.InFrontend() then - f14_local1 = VNavigator:getRootController() - end - assert( f14_local1 ) - local f14_local2 = VNavigator - local ButtonDescription = nil - - ButtonDescription = MenuBuilder.BuildRegisteredType( "ButtonDescriptionText", { - controllerIndex = f14_local1 - } ) - ButtonDescription.id = "ButtonDescription" - ButtonDescription:SetRGBFromTable( SWATCHES.genericButton.textDisabled, 0 ) - ButtonDescription.Description:SetRight( _1080p * 415, 0 ) - ButtonDescription:SetAnchorsAndPosition( 0, 0, 0, 1, 0, 0, _1080p * 336, _1080p * 394 ) - VNavigator:addElement( ButtonDescription ) - VNavigator.ButtonDescription = ButtonDescription - - local PublicMatch = nil - - PublicMatch = MenuBuilder.BuildRegisteredType( "MenuButton", { - controllerIndex = f14_local1 - } ) - PublicMatch.id = "PublicMatch" - PublicMatch.buttonDescription = "Browse for Custom Servers" - PublicMatch.Text:setText( ToUpperCase( "Server Browser" ), 0 ) - PublicMatch:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, 0, _1080p * 30 ) - VNavigator:addElement( PublicMatch ) - VNavigator.PublicMatch = PublicMatch - - local SoloMatch = nil - - SoloMatch = MenuBuilder.BuildRegisteredType( "MenuButton", { - controllerIndex = f14_local1 - } ) - SoloMatch.id = "SoloMatch" - SoloMatch.buttonDescription = Engine.Localize( "LUA_MENU_ZM_SOLO_MATCH_DESC" ) - SoloMatch.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_SOLO_MATCH_CAPS" ) ), 0 ) - SoloMatch:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 40, _1080p * 70 ) - VNavigator:addElement( SoloMatch ) - VNavigator.SoloMatch = SoloMatch - - local CustomMatch = nil - - CustomMatch = MenuBuilder.BuildRegisteredType( "MenuButton", { - controllerIndex = f14_local1 - } ) - CustomMatch.id = "CustomMatch" - CustomMatch.buttonDescription = Engine.Localize( "LUA_MENU_ZM_CUSTOM_MATCH_DESC" ) - CustomMatch.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_CUSTOM_GAME_CAPS" ) ), 0 ) - CustomMatch:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 80, _1080p * 110 ) - VNavigator:addElement( CustomMatch ) - VNavigator.CustomMatch = CustomMatch - - local Loadout = nil - - Loadout = MenuBuilder.BuildRegisteredType( "MenuButton", { - controllerIndex = f14_local1 - } ) - Loadout.id = "Loadout" - Loadout.buttonDescription = Engine.Localize( "LUA_MENU_ZM_LOADOUT_DESC" ) - Loadout.Text:setText( Engine.Localize( "LUA_MENU_ZM_LOADOUT_CAPS" ), 0 ) - Loadout:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 120, _1080p * 150 ) - VNavigator:addElement( Loadout ) - VNavigator.Loadout = Loadout - - local Barracks = nil - - Barracks = MenuBuilder.BuildRegisteredType( "MenuButton", { - controllerIndex = f14_local1 - } ) - Barracks.id = "Barracks" - Barracks.buttonDescription = Engine.Localize( "LUA_MENU_ZM_BARRACKS_DESC" ) - Barracks.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_ZM_BARRACKS_CAPS" ) ), 0 ) - Barracks:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 160, _1080p * 190 ) - VNavigator:addElement( Barracks ) - VNavigator.Barracks = Barracks - - local Armory = nil - - Armory = MenuBuilder.BuildRegisteredType( "MenuButton", { - controllerIndex = f14_local1 - } ) - Armory.id = "Armory" - Armory.buttonDescription = Engine.Localize( "LUA_MENU_ZM_SURVIVAL_DEPOT_DESC" ) - Armory.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_ZM_SURVIVAL_DEPOT" ) ), 0 ) - Armory:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 200, _1080p * 230 ) - VNavigator:addElement( Armory ) - VNavigator.Armory = Armory - - local ContractsButton = nil - - ContractsButton = MenuBuilder.BuildRegisteredType( "ContractsButtonCP", { - controllerIndex = f14_local1 - } ) - ContractsButton.id = "ContractsButton" - ContractsButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 240, _1080p * 300 ) - VNavigator:addElement( ContractsButton ) - VNavigator.ContractsButton = ContractsButton - - f0_local4( VNavigator, f14_local1, controller ) - return VNavigator -end - -CPMainMenu_original = MenuBuilder.m_types["CPMainMenu"] +local CPMainMenu_original = MenuBuilder.m_types["CPMainMenu"] function CPMainMenuStub( menu, controller ) ret = CPMainMenu_original( menu, controller ) @@ -192,6 +8,4 @@ function CPMainMenuStub( menu, controller ) return ret end -MenuBuilder.m_types["CPMainMenu"] = CPMainMenuStub - -MenuBuilder.m_types["CPMainMenuButtons"] = CPMainMenuButtons \ No newline at end of file +MenuBuilder.m_types["CPMainMenu"] = CPMainMenuStub \ No newline at end of file diff --git a/data/cdata/ui_scripts/MainMenu/CPMainMenuButtons.lua b/data/cdata/ui_scripts/MainMenu/CPMainMenuButtons.lua new file mode 100644 index 00000000..537b6484 --- /dev/null +++ b/data/cdata/ui_scripts/MainMenu/CPMainMenuButtons.lua @@ -0,0 +1,201 @@ +local f0_local0 = function ( f1_arg0, f1_arg1 ) + Engine.Exec( "xblive_privatematch 0" ) + utils.cp.AliensUtils.AliensRunConfig( f1_arg1.controller ) + LUI.FlowManager.RequestAddMenu( "SystemLinkMenu", false, f1_arg1.controller, false, {}, true ) +end + +local f0_local1 = function ( f2_arg0, f2_arg1 ) + f0_local0( f2_arg0, f2_arg1 ) +end + +local f0_local2 = function ( f3_arg0, f3_arg1 ) + Engine.Exec( MPConfig.default_xboxlive, f3_arg1.controller ) + Engine.SetDvarBool( "xblive_privatematch", true ) + SetIsAliensSolo( true ) + Engine.SetDvarInt( "party_maxplayers", 1 ) + Engine.Exec( "xstartprivatematch" ) + LUI.FlowManager.RequestAddMenu( "CPPrivateMatchMenu", false, f3_arg1.controller, false, { + showPlayNowButton = true, + isPublicMatch = false + } ) +end + +local f0_local3 = function ( f4_arg0, f4_arg1 ) + Engine.Exec( MPConfig.default_xboxlive, f4_arg1.controller ) + Engine.SetDvarBool( "xblive_privatematch", true ) + SetIsAliensSolo( false ) + Engine.Exec( "xstartprivatematch" ) + LUI.FlowManager.RequestAddMenu( "CPPrivateMatchMenu", false, f4_arg1.controller, false, { + showPlayNowButton = true, + isPublicMatch = false + } ) +end + +local f0_local4 = function ( f5_arg0, f5_arg1, f5_arg2 ) + assert( f5_arg0.PublicMatch ) + assert( f5_arg0.SoloMatch ) + assert( f5_arg0.CustomMatch ) + local f5_local0 = LUI.DataSourceInGlobalModel.new( "frontEnd.lobby.areWeGameHost" ) + local f5_local1 = DataSources.frontEnd.lobby.memberCount + local f5_local2 = function () + return Lobby.IsInPrivateParty() and not Lobby.IsPrivatePartyHost() + end + + local f5_local3 = function () + local f7_local0 = f5_local2() + f5_arg0.PublicMatch:SetButtonDisabled( f7_local0 ) + f5_arg0.CustomMatch:SetButtonDisabled( f7_local0 ) + end + + f5_arg0:SubscribeToModel( f5_local0:GetModel( f5_arg1 ), f5_local3 ) + f5_arg0:SubscribeToModel( f5_local1:GetModel( f5_arg1 ), f5_local3 ) + f5_arg0:SubscribeToModel( DataSources.frontEnd.lobby.isSolo:GetModel( f5_arg1 ), function () + local f8_local0 = DataSources.frontEnd.lobby.isSolo:GetValue( f5_arg1 ) + if f8_local0 ~= nil then + f5_arg0.SoloMatch:SetButtonDisabled( not f8_local0 ) + end + end ) + f5_arg0.PublicMatch:addEventHandler( "button_action", f0_local1 ) + f5_arg0.SoloMatch:addEventHandler( "button_action", f0_local2 ) + f5_arg0.Loadout:addEventHandler( "button_action", function ( f9_arg0, f9_arg1 ) + LUI.FlowManager.RequestAddMenu( "CPLoadoutMenu", true, f9_arg1.controller ) + end ) + f5_arg0.Barracks:addEventHandler( "button_action", function ( f10_arg0, f10_arg1 ) + LUI.FlowManager.RequestAddMenu( "Headquarters", true, f10_arg1.controller ) + end ) + f5_arg0.Armory:addEventHandler( "button_action", function ( f11_arg0, f11_arg1 ) + if not Engine.IsUserAGuest( f11_arg1.controller ) then + ACTIONS.OpenMenu( "Armory", true, f11_arg1.controller ) + end + end ) + f5_arg0.CustomMatch:addEventHandler( "button_action", f0_local3 ) + f5_arg0.ContractsButton:addEventHandler( "button_action", function ( f12_arg0, f12_arg1 ) + ACTIONS.OpenMenu( "ContractMenu", true, f12_arg1.controller or f5_arg1 ) + end ) + + f5_arg0.ModsButton:addEventHandler( "button_action", function ( arg0, arg1 ) + LUI.FlowManager.RequestAddMenu( "ModSelectMenu", true, arg1.controller, false ) + end ) +end + +function CPMainMenuButtons( menu, controller ) + local VNavigator = LUI.UIVerticalNavigator.new() + VNavigator:SetAnchorsAndPosition( 0, 1, 0, 1, 0, 500 * _1080p, 0, 400 * _1080p ) + VNavigator.id = "CPMainMenuButtons" + local f14_local1 = controller and controller.controllerIndex + if not f14_local1 and not Engine.InFrontend() then + f14_local1 = VNavigator:getRootController() + end + assert( f14_local1 ) + local f14_local2 = VNavigator + local ButtonDescription = nil + + ButtonDescription = MenuBuilder.BuildRegisteredType( "ButtonDescriptionText", { + controllerIndex = f14_local1 + } ) + ButtonDescription.id = "ButtonDescription" + ButtonDescription:SetRGBFromTable( SWATCHES.genericButton.textDisabled, 0 ) + ButtonDescription.Description:SetRight( _1080p * 415, 0 ) + ButtonDescription:SetAnchorsAndPosition( 0, 0, 0, 1, 0, 0, _1080p * 336, _1080p * 394 ) + VNavigator:addElement( ButtonDescription ) + VNavigator.ButtonDescription = ButtonDescription + + local PublicMatch = nil + + PublicMatch = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + PublicMatch.id = "PublicMatch" + PublicMatch.buttonDescription = "Browse for Custom Servers" + PublicMatch.Text:setText( ToUpperCase( "Server Browser" ), 0 ) + PublicMatch:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, 0, _1080p * 30 ) + VNavigator:addElement( PublicMatch ) + VNavigator.PublicMatch = PublicMatch + + local SoloMatch = nil + + SoloMatch = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + SoloMatch.id = "SoloMatch" + SoloMatch.buttonDescription = Engine.Localize( "LUA_MENU_ZM_SOLO_MATCH_DESC" ) + SoloMatch.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_SOLO_MATCH_CAPS" ) ), 0 ) + SoloMatch:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 40, _1080p * 70 ) + VNavigator:addElement( SoloMatch ) + VNavigator.SoloMatch = SoloMatch + + local CustomMatch = nil + + CustomMatch = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + CustomMatch.id = "CustomMatch" + CustomMatch.buttonDescription = Engine.Localize( "LUA_MENU_ZM_CUSTOM_MATCH_DESC" ) + CustomMatch.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_CUSTOM_GAME_CAPS" ) ), 0 ) + CustomMatch:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 80, _1080p * 110 ) + VNavigator:addElement( CustomMatch ) + VNavigator.CustomMatch = CustomMatch + + local Loadout = nil + + Loadout = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + Loadout.id = "Loadout" + Loadout.buttonDescription = Engine.Localize( "LUA_MENU_ZM_LOADOUT_DESC" ) + Loadout.Text:setText( Engine.Localize( "LUA_MENU_ZM_LOADOUT_CAPS" ), 0 ) + Loadout:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 120, _1080p * 150 ) + VNavigator:addElement( Loadout ) + VNavigator.Loadout = Loadout + + local Barracks = nil + + Barracks = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + Barracks.id = "Barracks" + Barracks.buttonDescription = Engine.Localize( "LUA_MENU_ZM_BARRACKS_DESC" ) + Barracks.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_ZM_BARRACKS_CAPS" ) ), 0 ) + Barracks:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 160, _1080p * 190 ) + VNavigator:addElement( Barracks ) + VNavigator.Barracks = Barracks + + local Armory = nil + + Armory = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + Armory.id = "Armory" + Armory.buttonDescription = Engine.Localize( "LUA_MENU_ZM_SURVIVAL_DEPOT_DESC" ) + Armory.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_ZM_SURVIVAL_DEPOT" ) ), 0 ) + Armory:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 200, _1080p * 230 ) + VNavigator:addElement( Armory ) + VNavigator.Armory = Armory + + local ModsButton = nil + + ModsButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f14_local1 + } ) + ModsButton.id = "ModsButton" + ModsButton.buttonDescription = Engine.Localize( "LUA_MENU_MODS_DESC" ) + ModsButton.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_MODS_CAPS" ) ), 0 ) + ModsButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 240, _1080p * 270 ) + VNavigator:addElement( ModsButton ) + VNavigator.ModsButton = ModsButton + + local ContractsButton = nil + + ContractsButton = MenuBuilder.BuildRegisteredType( "ContractsButtonCP", { + controllerIndex = f14_local1 + } ) + ContractsButton.id = "ContractsButton" + ContractsButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 340, _1080p * 280, _1080p * 340 ) + VNavigator:addElement( ContractsButton ) + VNavigator.ContractsButton = ContractsButton + + f0_local4( VNavigator, f14_local1, controller ) + return VNavigator +end + +MenuBuilder.m_types["CPMainMenuButtons"] = CPMainMenuButtons \ No newline at end of file diff --git a/data/cdata/ui_scripts/MainMenu/CampaignMenuButtons.lua b/data/cdata/ui_scripts/MainMenu/CampaignMenuButtons.lua new file mode 100644 index 00000000..4179af6b --- /dev/null +++ b/data/cdata/ui_scripts/MainMenu/CampaignMenuButtons.lua @@ -0,0 +1,178 @@ +local f0_local1 = function ( f2_arg0 ) + f2_arg0.ResumeButton:SetButtonDisabled( not Engine.CanResumeGame( f2_arg0._controllerIndex ) ) + if not CONDITIONS.IsTrialLicense( f2_arg0 ) then + local f2_local0 = f2_arg0.MissionSelectButton + local f2_local1 = f2_local0 + f2_local0 = f2_local0.SetButtonDisabled + local f2_local2 = Engine.IsTrialLicense() + if not f2_local2 then + if not Engine.IsDevelopmentBuild() and not Engine.GetDvarBool( "mis_cheat" ) then + f2_local2 = not f0_local0( f2_arg0._controllerIndex ) + else + f2_local2 = false + end + end + f2_local0( f2_local1, f2_local2 ) + end +end + +local f0_local2 = function ( f3_arg0, f3_arg1 ) + LUI.FlowManager.RequestPopupMenu( nil, "FakeLoadingScreenOverlay", true, 0, false, { + onLoadCompleteFunc = function () + Engine.SetDvarString( "ui_char_museum_mode", "credits_1" ) + Engine.Exec( "profile_difficultyLoad" ) + Engine.Exec( "profile_specialistModeLoad" ) + Engine.Exec( "profile_yoloModeLoad" ) + Engine.Exec( "loadgame_continue" ) + Engine.Exec( "updategamerprofile" ) + end + } ) + LUI.FlowManager.RequestLeaveMenu( f3_arg0 ) +end + +local f0_local3 = function ( f5_arg0, f5_arg1 ) + LUI.FlowManager.RequestPopupMenu( nil, "FakeLoadingScreenOverlay", true, 0, false, { + onLoadCompleteFunc = function () + Engine.Exec( "set ui_play_credits 1; map shipcrib_epilogue" ) + end + } ) +end + +--MenuBuilder.registerType( "ResumeGamePopup", function ( f7_arg0, f7_arg1 ) +-- return MenuBuilder.BuildRegisteredType( "PopupYesNo", { +-- message = Engine.Localize( "@MENU_RESUMEGAME_Q" ), +-- yesAction = f0_local2 +-- } ) +--end ) +local f0_local4 = function ( f8_arg0, f8_arg1, f8_arg2 ) + assert( f8_arg0.ResumeButton ) + assert( f8_arg0.NewButton ) + assert( f8_arg0.CreditsButton ) + if not CONDITIONS.IsTrialLicense( f8_arg0 ) then + assert( f8_arg0.MissionSelectButton ) + end + f8_arg0._controllerIndex = f8_arg1 + f8_arg0.ResumeButton:addEventHandler( "button_action", function ( f9_arg0, f9_arg1 ) + Engine.SetDvarString( "start", "" ) + LUI.FlowManager.RequestPopupMenu( f9_arg0, "ResumeGamePopup", false, f9_arg1.controller, false ) + end ) + f8_arg0.NewButton:addEventHandler( "button_action", function ( f10_arg0, f10_arg1 ) + Engine.SetDvarString( "start", "" ) + if Engine.CanResumeGame( f8_arg1 ) then + LUI.FlowManager.RequestPopupMenu( f8_arg0, "overwrite_warning_menu", true, f10_arg1.controller ) + else + LUI.FlowManager.RequestPopupMenu( f8_arg0, "popmenu_autosave_warning", true, f10_arg1.controller ) + end + end ) + if not CONDITIONS.IsTrialLicense( f8_arg0 ) then + f8_arg0.MissionSelectButton:addEventHandler( "button_action", function ( f11_arg0, f11_arg1 ) + Engine.SetDvarString( "start", "" ) + LUI.FlowManager.RequestAddMenu( "LevelSelectMenu", true, f11_arg1.controller, false ) + end ) + end + f8_arg0.CreditsButton:addEventHandler( "button_action", f0_local3 ) + f0_local1( f8_arg0 ) + f8_arg0:addEventHandler( "update_save_game_available_complete", f0_local1 ) + if Engine.GetDvarFloat( "r_filmGrainAtten" ) == 0.25 then + Engine.SetDvarFloat( "r_filmGrainAtten", 1 ) + Engine.ExecNow( "profile_setFilmGrain " .. tostring( 1 ), f8_arg1 ) + end + + f8_arg0.ModsButton:addEventHandler( "button_action", function ( arg0, arg1 ) + LUI.FlowManager.RequestAddMenu( "ModSelectMenu", true, arg1.controller, false ) + end ) +end + +function CampaignMenuButtons( menu, controller ) + local self = LUI.UIVerticalList.new() + self:SetAnchorsAndPosition( 0, 1, 0, 1, 0, 500 * _1080p, 0, 440 * _1080p ) + self.id = "CampaignMenuButtons" + local f12_local1 = controller and controller.controllerIndex + if not f12_local1 and not Engine.InFrontend() then + f12_local1 = self:getRootController() + end + assert( f12_local1 ) + local f12_local2 = self + self:SetSpacing( 10 * _1080p ) + local ResumeButton = nil + + ResumeButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f12_local1 + } ) + ResumeButton.id = "ResumeButton" + ResumeButton.buttonDescription = Engine.Localize( "LUA_MENU_RESUME_GAME_DESC" ) + ResumeButton.Text:setText( Engine.Localize( "MENU_RESUMEGAME_CAPS" ), 0 ) + ResumeButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, 0, _1080p * 30 ) + self:addElement( ResumeButton ) + self.ResumeButton = ResumeButton + + local NewButton = nil + + NewButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f12_local1 + } ) + NewButton.id = "NewButton" + NewButton.buttonDescription = Engine.Localize( "LUA_MENU_NEW_GAME_DESC" ) + NewButton.Text:setText( Engine.Localize( "MENU_NEWGAME_CAPS" ), 0 ) + NewButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 40, _1080p * 70 ) + self:addElement( NewButton ) + self.NewButton = NewButton + + local f12_local5 = nil + if not CONDITIONS.IsTrialLicense( self ) then + f12_local5 = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f12_local1 + } ) + f12_local5.id = "MissionSelectButton" + if not CONDITIONS.IsTrialLicense( self ) then + + else + + end + if not CONDITIONS.IsTrialLicense( self ) then + f12_local5.buttonDescription = Engine.Localize( "LUA_MENU_MISSION_SELECT_DESC" ) + end + f12_local5.Text:setText( Engine.Localize( "MENU_MISSION_SELECT_CAPS" ), 0 ) + f12_local5:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 80, _1080p * 110 ) + self:addElement( f12_local5 ) + self.MissionSelectButton = f12_local5 + end + local CreditsButton = nil + + CreditsButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f12_local1 + } ) + CreditsButton.id = "CreditsButton" + CreditsButton.buttonDescription = Engine.Localize( "LUA_MENU_CREDITS_DESC" ) + CreditsButton.Text:setText( ToUpperCase( Engine.Localize( "MENU_SP_CREDITS_CAPS" ) ), 0 ) + CreditsButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 120, _1080p * 150 ) + self:addElement( CreditsButton ) + self.CreditsButton = CreditsButton + + local ModsButton = nil + + ModsButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f12_local1 + } ) + ModsButton.id = "ModsButton" + ModsButton.buttonDescription = Engine.Localize( "LUA_MENU_MODS_DESC" ) + ModsButton.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_MODS_CAPS" ) ), 0 ) + ModsButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 160, _1080p * 190 ) + self:addElement( ModsButton ) + self.ModsButton = ModsButton + + local ButtonDescription = nil + + ButtonDescription = MenuBuilder.BuildRegisteredType( "ButtonDescriptionText", { + controllerIndex = f12_local1 + } ) + ButtonDescription.id = "ButtonDescription" + ButtonDescription:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 504, _1080p * 200, _1080p * 300 ) + self:addElement( ButtonDescription ) + self.ButtonDescription = ButtonDescription + + f0_local4( self, f12_local1, controller ) + return self +end + +MenuBuilder.m_types["CampaignMenuButtons"] = CampaignMenuButtons \ No newline at end of file diff --git a/data/cdata/ui_scripts/MainMenu/MPMainMenuButtons.lua b/data/cdata/ui_scripts/MainMenu/MPMainMenuButtons.lua new file mode 100644 index 00000000..a82deaf4 --- /dev/null +++ b/data/cdata/ui_scripts/MainMenu/MPMainMenuButtons.lua @@ -0,0 +1,206 @@ +local buttonSpacing = 40 +local f0_local1 = 10 +local f0_local2 = function ( f1_arg0, f1_arg1, f1_arg2 ) + if 0 < f1_arg2 then + local f1_local0, f1_local1, f1_local2, f1_local3 = f1_arg0:getLocalRect() + local f1_local4 = f1_local3 - f1_local1 + f1_arg0:SetTop( f1_local1 - (f1_local4 + f1_arg1) * f1_arg2 ) + f1_arg0:SetBottom( f1_local3 - (f1_local4 + f1_arg1) * f1_arg2 ) + end +end + +local f0_local3 = function ( f2_arg0, f2_arg1, f2_arg2 ) + assert( f2_arg0.ConquestButton ) + if CONDITIONS.IsStoreAllowed( f2_arg0 ) then + assert( f2_arg0.StoreButton ) + end + local f2_local0 = not CONDITIONS.IsTrialLicense( f2_arg0 ) + if f2_local0 then + assert( f2_arg0.CustomGameButton ) + end + f2_arg0.buttonSpacing = _1080p * buttonSpacing + local f2_local1 = function () + return Lobby.IsInPrivateParty() and not Lobby.IsPrivatePartyHost() + end + + local f2_local2 = function () + local f4_local0 = f2_local1() + f2_arg0.ConquestButton:SetButtonDisabled( f4_local0 ) + if f2_arg0.MLGGameBattlesButton ~= nil then + f2_arg0.MLGGameBattlesButton:SetButtonDisabled( f4_local0 ) + end + if f2_local0 then + f2_arg0.CustomGameButton:SetButtonDisabled( f4_local0 ) + end + end + + local f2_local3 = LUI.DataSourceInGlobalModel.new( "frontEnd.lobby.areWeGameHost" ) + local f2_local4 = DataSources.frontEnd.lobby.memberCount + f2_arg0:SubscribeToModel( f2_local3:GetModel( f2_arg1 ), f2_local2 ) + f2_arg0:SubscribeToModel( f2_local4:GetModel( f2_arg1 ), f2_local2 ) + f2_arg0.ConquestButton:addEventHandler( "button_action", function ( f5_arg0, f5_arg1 ) + Engine.SetDvarBool( "cg_mlg_gamebattles_match", false ) + local f5_local0 = function () + LUI.FlowManager.RequestAddMenu( "Missions", false, f5_arg1.controller, false, {}, true ) + end + + if not Onboarding:BeginFlow( Onboarding.RigTutorial, f2_arg1 ) then + f5_local0() + else + LUI.FlowManager.RequestPopupMenu( nil, "MPFullScreenVideoOverlay", true, f2_arg1, nil, { + videoRef = "mp_wolverines_mission_commander", + allowSkip = true, + doIntroFadeOut = false, + doIntroFadeIn = false, + doOutroFadeIn = true, + doOutroFadeOut = true, + fadeColor = COLORS.black + }, nil, true, true ) + local f5_local1 = f2_arg0:Wait( 500 ) + f5_local1.onComplete = f5_local0 + end + end ) + if CONDITIONS.IsGameBattlesAllowed( f2_arg0 ) then + f2_arg0.MLGGameBattlesButton:addEventHandler( "button_action", function ( f7_arg0, f7_arg1 ) + if Engine.GetDvarBool( "splitscreen" ) then + LUI.FlowManager.RequestPopupMenu( f2_arg0, "MLGGamebattlesSplitscreenPopup", true, f7_arg1.controller, false, { + controllerIndex = f2_arg1 + } ) + elseif Lobby.IsNotAloneInPrivateParty() then + LUI.FlowManager.RequestPopupMenu( f2_arg0, "DisbandPartyEnterGameBattlesLobbyPopup", true, f7_arg1.controller, false, { + controllerIndex = f2_arg1 + } ) + else + OpenGameBattlesLobby( f7_arg1.controller ) + end + end ) + end + if f2_local0 then + f2_arg0.CustomGameButton:addEventHandler( "button_action", function ( f8_arg0, f8_arg1 ) + OpenPrivateMatchLobby( f8_arg1 ) + end ) + end + + f2_arg0.ModsButton:addEventHandler( "button_action", function ( arg0, arg1 ) + LUI.FlowManager.RequestAddMenu( "ModSelectMenu", true, arg1.controller, false ) + end ) + + if CONDITIONS.IsStoreAllowed( f2_arg0 ) then + f2_arg0.StoreButton:addEventHandler( "button_action", function ( f9_arg0, f9_arg1 ) + local f9_local0 = STORE.GoToStore + local f9_local1 = f9_arg1.controller + local f9_local2 = f9_arg0:GetCurrentMenu() + f9_local0( f9_local1, f9_local2.id, f9_arg0.id ) + end ) + end + local f2_local5 = _1080p * f0_local1 + local f2_local6 = 0 + if f2_arg0.MLGGameBattlesButton == nil then + f2_local6 = 1 + end + if f2_arg0.CustomGameButton then + f0_local2( f2_arg0.CustomGameButton, f2_local5, f2_local6 ) + else + f2_local6 = f2_local6 + 1 + end + if f2_arg0.ModsButton then + f0_local2( f2_arg0.ModsButton, f2_local5, f2_local6 ) + else + f2_local6 = f2_local6 + 1 + end + if CONDITIONS.IsStoreAllowed( f2_arg0 ) then + f0_local2( f2_arg0.StoreButton, f2_local5, f2_local6 ) + else + f2_local6 = f2_local6 + 1 + end + if f2_arg0.StoreButton then + f2_arg0.StoreButton:SetButtonDescription( STORE.GetStoreDescription() ) + if CONDITIONS.IsTrialLicense() then + f2_arg0.StoreButton.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_BUY_NOW" ) ) ) + end + end +end + +function MPMainMenuButtons( menu, controller ) + local self = LUI.UIVerticalNavigator.new() + self:SetAnchorsAndPosition( 0, 1, 0, 1, 0, 500 * _1080p, 0, 190 * _1080p ) + self.id = "MPMainMenuButtons" + local f11_local1 = controller and controller.controllerIndex + if not f11_local1 and not Engine.InFrontend() then + f11_local1 = self:getRootController() + end + assert( f11_local1 ) + local ConquestButton = nil + + ConquestButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f11_local1 + } ) + ConquestButton.id = "ConquestButton" + ConquestButton.buttonDescription = Engine.Localize( "LUA_MENU_PUBLIC_MATCH_DESC" ) + ConquestButton.Text:setText( Engine.Localize( "LUA_MENU_PUBLIC_MATCH_CAPS" ), 0 ) + ConquestButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, 0, _1080p * 30 ) + self:addElement( ConquestButton ) + self.ConquestButton = ConquestButton + + local MLGGameBattlesButton = nil + if CONDITIONS.IsGameBattlesAllowed( self ) then + MLGGameBattlesButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f11_local1 + } ) + MLGGameBattlesButton.id = "MLGGameBattlesButton" + if CONDITIONS.IsGameBattlesAllowed( self ) then + + else + + end + if CONDITIONS.IsGameBattlesAllowed( self ) then + MLGGameBattlesButton.buttonDescription = Engine.Localize( "LUA_MENU_MLG_GAMEBATTLES_DESC" ) + end + MLGGameBattlesButton.Text:setText( Engine.Localize( "LUA_MENU_MLG_GAMEBATTLES_CAPS" ), 0 ) + MLGGameBattlesButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 40, _1080p * 70 ) + self:addElement( MLGGameBattlesButton ) + self.MLGGameBattlesButton = MLGGameBattlesButton + end + + local CustomGameButton = nil + + CustomGameButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f11_local1 + } ) + CustomGameButton.id = "CustomGameButton" + CustomGameButton.buttonDescription = Engine.Localize( "LUA_MENU_CUSTOM_GAME_DESC" ) + CustomGameButton.Text:setText( Engine.Localize( "LUA_MENU_CUSTOM_GAME_CAPS" ), 0 ) + CustomGameButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 80, _1080p * 110 ) + self:addElement( CustomGameButton ) + self.CustomGameButton = CustomGameButton + + local ModsButton = nil + + ModsButton = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f11_local1 + } ) + ModsButton.id = "ModsButton" + ModsButton.buttonDescription = Engine.Localize( "LUA_MENU_MODS_DESC" ) + ModsButton.Text:setText( ToUpperCase( Engine.Localize( "LUA_MENU_MODS_CAPS" ) ), 0 ) + ModsButton:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 120, _1080p * 150 ) + self:addElement( ModsButton ) + self.ModsButton = ModsButton + + local f11_local6 = nil + if CONDITIONS.IsStoreAllowed( self ) then + f11_local6 = MenuBuilder.BuildRegisteredType( "MenuButton", { + controllerIndex = f11_local1 + } ) + f11_local6.id = "StoreButton" + f11_local6.buttonDescription = Engine.Localize( "LUA_MENU_STORE_DESC" ) + f11_local6.Text:setText( Engine.Localize( "LUA_MENU_STORE_CAPS" ), 0 ) + f11_local6:SetAnchorsAndPosition( 0, 1, 0, 1, 0, _1080p * 500, _1080p * 160, _1080p * 190 ) + self:addElement( f11_local6 ) + self.StoreButton = f11_local6 + end + + f0_local3( self, f11_local1, controller ) + return self +end + +MenuBuilder.m_types["MPMainMenuButtons"] = MPMainMenuButtons \ No newline at end of file diff --git a/data/cdata/ui_scripts/MainMenu/MPMainMenu.lua b/data/cdata/ui_scripts/MainMenu/MissionsVerticalLayout.lua similarity index 100% rename from data/cdata/ui_scripts/MainMenu/MPMainMenu.lua rename to data/cdata/ui_scripts/MainMenu/MissionsVerticalLayout.lua diff --git a/data/cdata/ui_scripts/MainMenu/__init__.lua b/data/cdata/ui_scripts/MainMenu/__init__.lua index e27772cc..2d2926c8 100644 --- a/data/cdata/ui_scripts/MainMenu/__init__.lua +++ b/data/cdata/ui_scripts/MainMenu/__init__.lua @@ -1,2 +1,5 @@ -require("MPMainMenu") +require("CampaignMenuButtons") require("CPMainMenu") +require("CPMainMenuButtons") +require("MissionsVerticalLayout") +require("MPMainMenuButtons") \ No newline at end of file diff --git a/data/cdata/ui_scripts/Mods/ModDownload.lua b/data/cdata/ui_scripts/Mods/ModDownload.lua new file mode 100644 index 00000000..a7a490f6 --- /dev/null +++ b/data/cdata/ui_scripts/Mods/ModDownload.lua @@ -0,0 +1,40 @@ +function ModDownloadCancel( arg0, arg1 ) + download.abort() + LUI.FlowManager.RequestLeaveMenu( arg0 ) +end + +function ModDownloadPopup( arg0, arg1 ) + local popup = MenuBuilder.BuildRegisteredType( "FenceDialogPopup", { + message = "Downloading files...", + controllerIndex = arg1.controllerIndex, + onCancel = ModDownloadCancel + } ) + popup.id = "ModDownloadPopup" + + local file = "" + + popup:registerEventHandler("mod_download_set_file", function(element, event) + file = event.request.name + popup.Message:setText(string.format("Downloading %s...", file)) + end) + + popup:registerEventHandler("mod_download_progress", function(element, event) + popup.Message:setText(string.format("Downloading %s (%i%%)...", file, math.floor(event.fraction * 100))) + end) + + popup:registerEventHandler("mod_download_done", function() + LUI.FlowManager.RequestLeaveMenu(popup) + end) + + return popup +end + +MenuBuilder.registerType( "ModDownloadPopup", ModDownloadPopup ) + +local function reg_func() + Engine.GetLuiRoot():registerEventHandler("mod_download_start", function(element, event) + LUI.FlowManager.RequestPopupMenu( element, "ModDownloadPopup", true, event.controller, false ) + end) +end + +scheduler.once(reg_func) \ No newline at end of file diff --git a/data/cdata/ui_scripts/Mods/ModSelectButton.lua b/data/cdata/ui_scripts/Mods/ModSelectButton.lua new file mode 100644 index 00000000..4cf12612 --- /dev/null +++ b/data/cdata/ui_scripts/Mods/ModSelectButton.lua @@ -0,0 +1,223 @@ +local function PostLoadFunc( f1_arg0, f1_arg1, f1_arg2 ) + assert( f1_arg0.GenericButton ) + + f1_arg0.GenericButton:addEventHandler( "button_action", function ( f3_arg0, f3_arg1 ) + local f3_local0 = f1_arg0:GetDataSource() + f3_local0.buttonOnClickFunction( f3_arg0, f3_arg1 ) + end ) + f1_arg0.GenericButton:addEventHandler( "button_over", function ( f4_arg0, f4_arg1 ) + local f4_local0 = f1_arg0:GetDataSource() + f4_local0.buttonOnHoverFunction( f4_arg0, f4_arg1 ) + f4_local0 = f1_arg0:GetDataSource() + f4_local0 = f4_local0.levelName + end ) + f1_arg0.GenericButton:addEventHandler( "button_up", function ( f5_arg0, f5_arg1 ) + local f5_local0 = f1_arg0:GetDataSource() + f5_local0.buttonOnHoverFunction( f5_arg0, f5_arg1 ) + f5_local0 = f1_arg0:GetDataSource() + f5_local0 = f5_local0.levelName + end ) + f1_arg0:registerEventHandler( "grid_anim", function ( element, event ) + element:SetAlpha( event.value ) + end ) + assert( f1_arg0.MainMissionIcon ) + assert( f1_arg0.SAIcon ) + assert( f1_arg0.JAIcon ) + f1_arg0:SubscribeToDataSourceThroughElement( f1_arg0, nil, function () + local f7_local0 = f1_arg0:GetDataSource() + f7_local0 = f7_local0.levelName + end ) +end + +function ModSelectButton( menu, controller ) + local self = LUI.UIButton.new() + self:SetAnchorsAndPosition( 0, 1, 0, 1, 0, 500 * _1080p, 0, 30 * _1080p ) + self.id = "ModSelectButton" + self._animationSets = {} + self._sequences = {} + local f8_local1 = controller and controller.controllerIndex + if not f8_local1 and not Engine.InFrontend() then + f8_local1 = self:getRootController() + end + assert( f8_local1 ) + local f8_local2 = self + local GenericButton = nil + + GenericButton = MenuBuilder.BuildRegisteredType( "GenericButton", { + controllerIndex = f8_local1 + } ) + GenericButton.id = "GenericButton" + GenericButton:SetAlpha( 0, 0 ) + GenericButton:SetAnchorsAndPosition( 0, 1, 0, 0, 0, _1080p * 500, 0, 0 ) + GenericButton:SubscribeToModelThroughElement( self, "buttonLabel", function () + local f9_local0 = self:GetDataSource() + f9_local0 = f9_local0.buttonLabel:GetValue( f8_local1 ) + if f9_local0 ~= nil then + GenericButton.Text:setText( LocalizeString( ToUpperCase( f9_local0 ) ), 0 ) + end + end ) + self:addElement( GenericButton ) + self.GenericButton = GenericButton + + local GenericListButtonBackground = nil + + GenericListButtonBackground = MenuBuilder.BuildRegisteredType( "GenericListArrowButtonBackground", { + controllerIndex = f8_local1 + } ) + GenericListButtonBackground.id = "GenericListButtonBackground" + GenericListButtonBackground:SetAnchorsAndPosition( 0, 0, 0, 0, 0, 0, 0, 0 ) + self:addElement( GenericListButtonBackground ) + self.GenericListButtonBackground = GenericListButtonBackground + + local Text = nil + + Text = LUI.UIStyledText.new() + Text.id = "Text" + Text:SetRGBFromInt( 14277081, 0 ) + Text:SetFontSize( 22 * _1080p ) + Text:SetFont( FONTS.GetFont( FONTS.MainMedium.File ) ) + Text:SetAlignment( LUI.Alignment.Left ) + Text:SetStartupDelay( 2000 ) + Text:SetLineHoldTime( 400 ) + Text:SetAnimMoveTime( 300 ) + Text:SetEndDelay( 1500 ) + Text:SetCrossfadeTime( 750 ) + Text:SetAutoScrollStyle( LUI.UIStyledText.AutoScrollStyle.ScrollH ) + Text:SetMaxVisibleLines( 1 ) + Text:SetOutlineRGBFromInt( 0, 0 ) + Text:SetAnchorsAndPosition( 0, 0, 0.5, 0.5, _1080p * 44, _1080p * -41, _1080p * -11, _1080p * 11 ) + Text:SubscribeToModelThroughElement( self, "buttonLabel", function () + local f10_local0 = self:GetDataSource() + f10_local0 = f10_local0.buttonLabel:GetValue( f8_local1 ) + if f10_local0 ~= nil then + Text:setText( LocalizeString( ToUpperCase( f10_local0 ) ), 0 ) + end + end ) + self:addElement( Text ) + self.Text = Text + + local Lock = nil + + Lock = LUI.UIImage.new() + Lock.id = "Lock" + Lock:SetRGBFromTable( SWATCHES.genericButton.textDisabled, 0 ) + Lock:SetAlpha( 0, 0 ) + Lock:setImage( RegisterMaterial( "icon_slot_locked" ), 0 ) + Lock:SetAnchorsAndPosition( 1, 0, 0.5, 0.5, _1080p * -32, _1080p * -6, _1080p * -12, _1080p * 14 ) + self:addElement( Lock ) + self.Lock = Lock + + self._animationSets.DefaultAnimationSet = function () + self._sequences.DefaultSequence = function () + + end + + Text:RegisterAnimationSequence( "ButtonOver", { + { + function () + return self.Text:SetRGBFromInt( 0, 0 ) + end + }, + { + function () + return self.Text:SetAlpha( 1, 0 ) + end + } + } ) + Lock:RegisterAnimationSequence( "ButtonOver", { + { + function () + return self.Lock:SetAlpha( 0, 0 ) + end + }, + { + function () + return self.Lock:SetRGBFromTable( SWATCHES.genericButton.textDisabled, 0 ) + end + } + } ) + self._sequences.ButtonOver = function () + Text:AnimateSequence( "ButtonOver" ) + Lock:AnimateSequence( "ButtonOver" ) + end + + Text:RegisterAnimationSequence( "ButtonUp", { + { + function () + return self.Text:SetRGBFromInt( 14277081, 0 ) + end + } + } ) + Lock:RegisterAnimationSequence( "ButtonUp", { + { + function () + return self.Lock:SetAlpha( 0, 0 ) + end + } + } ) + self._sequences.ButtonUp = function () + Text:AnimateSequence( "ButtonUp" ) + Lock:AnimateSequence( "ButtonUp" ) + end + + Text:RegisterAnimationSequence( "ButtonOverDisabled", { + { + function () + return self.Text:SetRGBFromInt( 0, 0 ) + end + }, + { + function () + return self.Text:SetAlpha( 1, 0 ) + end + } + } ) + Lock:RegisterAnimationSequence( "ButtonOverDisabled", { + { + function () + return self.Lock:SetAlpha( 1, 0 ) + end + }, + { + function () + return self.Lock:SetRGBFromInt( 0, 0 ) + end + } + } ) + self._sequences.ButtonOverDisabled = function () + Text:AnimateSequence( "ButtonOverDisabled" ) + Lock:AnimateSequence( "ButtonOverDisabled" ) + end + + Text:RegisterAnimationSequence( "ButtonUpDisabled", { + { + function () + return self.Text:SetRGBFromInt( 14277081, 0 ) + end + } + } ) + Lock:RegisterAnimationSequence( "ButtonUpDisabled", { + { + function () + return self.Lock:SetAlpha( 1, 0 ) + end + }, + { + function () + return self.Lock:SetRGBFromInt( 12566463, 0 ) + end + } + } ) + self._sequences.ButtonUpDisabled = function () + Text:AnimateSequence( "ButtonUpDisabled" ) + Lock:AnimateSequence( "ButtonUpDisabled" ) + end + + end + + self._animationSets.DefaultAnimationSet() + PostLoadFunc( self, f8_local1, controller ) + return self +end + +MenuBuilder.registerType( "ModSelectButton", ModSelectButton ) \ No newline at end of file diff --git a/data/cdata/ui_scripts/Mods/ModSelectMenu.lua b/data/cdata/ui_scripts/Mods/ModSelectMenu.lua new file mode 100644 index 00000000..20be3f40 --- /dev/null +++ b/data/cdata/ui_scripts/Mods/ModSelectMenu.lua @@ -0,0 +1,317 @@ +local f0_local0 = "frontEnd.ModSelect" +f0_local1 = function () + WipeGlobalModelsAtPath( f0_local0 ) +end + +local function getmodname(path) + local name = path + local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name) + + return name, desc +end + +local function set_mod( modname ) + Engine.SetDvarString( "fs_game", modname ) + Engine.Exec( "vid_restart" ) +end + +local unload_mod = function( arg0, arg1 ) + set_mod( "" ) +end + +local f0_local4 = function ( f4_arg0, f4_arg1 ) + LUI.FlowManager.RequestLeaveMenu( f4_arg0 ) +end + +local f0_local8 = function ( f8_arg0, f8_arg1 ) + local f8_local0 = LUI.FlowManager.GetScopedData( f8_arg0 ) + if not f8_local0.currentLabel then + f8_local0.currentLabel = "" + end + if not f8_local0.currentDesc then + f8_local0.currentDesc = "" + end + f8_arg0:processEvent( { + name = "menu_refresh" + } ) + local f8_local2 = f8_arg0:GetCurrentMenu() + assert( f8_local2.ModInfoTitle ) + f8_local2.ModInfoTitle:setText( f8_local0.currentLabel ) + assert( f8_local2.ModInfoText ) + f8_local2.ModInfoText:setText( f8_local0.currentDesc ) +end + +local f0_local9 = function ( f9_arg0, f9_arg1, f9_arg2 ) + set_mod( f9_arg2 ) +end + +local f0_local10 = function ( f11_arg0, f11_arg1, f11_arg2 ) + local f11_local0 = LUI.FlowManager.GetScopedData( f11_arg0 ) + f11_local0.currentLabel = f11_arg2.buttonLabel + f11_local0.currentName = f11_arg2.modName + f11_local0.currentDesc = f11_arg2.objectiveText + f0_local8( f11_arg0, f11_arg1 ) + Engine.PlaySound( CoD.SFX.SPMinimap ) +end + +local f0_local12 = function ( f13_arg0, f13_arg1 ) + local f13_local0 = {} + local mods = io.listfiles("mods/") + for i = 1, #mods do + local name, desc = getmodname(mods[i]) + f13_local0[#f13_local0 + 1] = { + buttonLabel = ToUpperCase(name), + modName = name, + objectiveText = desc, + } + end + local f13_local1 = LUI.DataSourceFromList.new( #f13_local0 ) + f13_local1.MakeDataSourceAtIndex = function ( f14_arg0, f14_arg1, f14_arg2 ) + return { + buttonLabel = LUI.DataSourceInGlobalModel.new( f0_local0 .. ".mods." .. f14_arg1, f13_local0[f14_arg1 + 1].buttonLabel ), + buttonOnClickFunction = function ( f15_arg0, f15_arg1 ) + f0_local9( f15_arg0, f15_arg1, f13_local0[f14_arg1 + 1].modName ) + end + , + buttonOnHoverFunction = function ( f16_arg0, f16_arg1 ) + f0_local10( f16_arg0, f16_arg1, f13_local0[f14_arg1 + 1] ) + end + , + modName = f13_local0[f14_arg1 + 1].modName + } + end + + assert( f13_arg0.ModSelectionList ) + f13_arg0.ModSelectionList:SetGridDataSource( f13_local1, f13_arg1 ) +end + +local function PostLoadFunc( f17_arg0, f17_arg1, f17_arg2 ) + assert( f17_arg0.bindButton ) + f17_arg0.bindButton:addEventHandler( "button_secondary", f0_local4 ) + + local fs_game = Engine.GetDvarString( "fs_game" ) + if fs_game ~= "" then + f17_arg0.LoadedModName:setText( "^3Loaded mod^7: " .. fs_game ) + f17_arg0.bindButton:addEventHandler( "button_alt2", unload_mod ) + else + f17_arg0.LoadedModName:setText( "" ) + end + + --f17_arg0:addEventHandler( "menu_create", f0_local3 ) + f0_local12( f17_arg0, f17_arg1 ) + f17_arg0:addEventHandler( "gain_focus", function ( f18_arg0, f18_arg1 ) + local f18_local0 = f18_arg0.ModSelectionList + local f18_local1 = f18_local0:GetContentOffset( LUI.DIRECTION.vertical ) + f18_local0:SetFocusedPosition( { + x = 0, + y = f18_local1 + }, true ) + local f18_local2 = f18_local0:GetElementAtPosition( 0, f18_local1 ) + if f18_local2 then + f18_local2:processEvent( { + name = "gain_focus", + controllerIndex = f17_arg1 + } ) + end + end ) +end + +function ModSelectMenu( menu, controller ) + local self = LUI.UIElement.new() + self.id = "ModSelectMenu" + + local f20_local1 = controller and controller.controllerIndex + if not f20_local1 and not Engine.InFrontend() then + f20_local1 = self:getRootController() + end + assert( f20_local1 ) + + self:playSound( "menu_open" ) + + if Engine.IsSingleplayer() then + local Background = nil + + Background = LUI.UIImage.new() + Background.id = "Background" + Background:setImage( RegisterMaterial( "sp_frontend_bink_background" ), 0 ) + self:addElement( Background ) + self.Background = Background + + local Bink = nil + + Bink = LUI.UIImage.new() + Bink.id = "Bink" + Bink:setImage( RegisterMaterial( "cinematic" ), 0 ) + self:addElement( Bink ) + self.Bink = Bink + end + + local ButtonHelperBar = nil + + ButtonHelperBar = MenuBuilder.BuildRegisteredType( "ButtonHelperBar", { + controllerIndex = f61_local1 + } ) + ButtonHelperBar.id = "ButtonHelperBar" + ButtonHelperBar:SetAnchorsAndPosition( 0, 0, 1, 0, 0, 0, _1080p * -85, 0 ) + self:addElement( ButtonHelperBar ) + self.ButtonHelperBar = ButtonHelperBar + + MenuTitle = MenuBuilder.BuildRegisteredType( "MenuTitle", { + controllerIndex = f61_local1 + } ) + MenuTitle.id = "MenuTitle" + MenuTitle.MenuTitle:setText( ToUpperCase( Engine.Localize( "LUA_MENU_MODS" ) ), 0 ) + --MenuTitle.MenuTitle:setText( ToUpperCase( "Mods" ), 0 ) + MenuTitle.MenuBreadcrumbs:setText( ToUpperCase( "" ), 0 ) + MenuTitle.Icon:SetTop( _1080p * -28.5, 0 ) + MenuTitle.Icon:SetBottom( _1080p * 61.5, 0 ) + MenuTitle:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 96, _1080p * 1056, _1080p * 54, _1080p * 134 ) + self:addElement( MenuTitle ) + self.MenuTitle = MenuTitle + + local ModInfoTitle = nil + + ModInfoTitle = LUI.UIStyledText.new() + ModInfoTitle.id = "ModInfoTitle" + ModInfoTitle:setText( "", 0 ) + ModInfoTitle:SetFontSize( 30 * _1080p ) + ModInfoTitle:SetFont( FONTS.GetFont( FONTS.MainMedium.File ) ) + ModInfoTitle:SetAlignment( LUI.Alignment.Left ) + ModInfoTitle:SetStartupDelay( 2000 ) + ModInfoTitle:SetLineHoldTime( 400 ) + ModInfoTitle:SetAnimMoveTime( 300 ) + ModInfoTitle:SetEndDelay( 1500 ) + ModInfoTitle:SetCrossfadeTime( 750 ) + ModInfoTitle:SetAutoScrollStyle( LUI.UIStyledText.AutoScrollStyle.ScrollH ) + ModInfoTitle:SetMaxVisibleLines( 1 ) + ModInfoTitle:SetDecodeLetterLength( 15 ) + ModInfoTitle:SetDecodeMaxRandChars( 6 ) + ModInfoTitle:SetDecodeUpdatesPerLetter( 4 ) + ModInfoTitle:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 1254, _1080p * 1824, _1080p * 216, _1080p * 246 ) + self:addElement( ModInfoTitle ) + self.ModInfoTitle = ModInfoTitle + + local ModInfoText = nil + + ModInfoText = LUI.UIStyledText.new() + ModInfoText.id = "ModInfoText" + ModInfoText:setText( "", 0 ) + ModInfoText:SetFontSize( 20 * _1080p ) + ModInfoText:SetFont( FONTS.GetFont( FONTS.MainCondensed.File ) ) + ModInfoText:SetAlignment( LUI.Alignment.Left ) + ModInfoText:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 1254, _1080p * 1824, _1080p * 248, _1080p * 268 ) + self:addElement( ModInfoText ) + self.ModInfoText = ModInfoText + + local ModSelectionList = nil + + ModSelectionList = LUI.UIDataSourceGrid.new( nil, { + maxVisibleColumns = 1, + maxVisibleRows = 17, + controllerIndex = f20_local1, + buildChild = function () + return MenuBuilder.BuildRegisteredType( "ModSelectButton", { + controllerIndex = f20_local1 + } ) + end, + wrapX = true, + wrapY = true, + spacingX = _1080p * 10, + spacingY = _1080p * 10, + columnWidth = _1080p * 500, + rowHeight = _1080p * 30, + scrollingThresholdX = 1, + scrollingThresholdY = 1, + adjustSizeToContent = false, + horizontalAlignment = LUI.Alignment.Left, + verticalAlignment = LUI.Alignment.Top, + springCoefficient = 600, + maxVelocity = 5000 + } ) + ModSelectionList.id = "ModSelectionList" + ModSelectionList:setUseStencil( false ) + ModSelectionList:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 130, _1080p * 630, _1080p * 216, _1080p * 886 ) + self:addElement( ModSelectionList ) + self.ModSelectionList = ModSelectionList + + local ArrowUp = nil + + ArrowUp = MenuBuilder.BuildRegisteredType( "ArrowUp", { + controllerIndex = f20_local1 + } ) + ArrowUp.id = "ArrowUp" + ArrowUp:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 452.5, _1080p * 472.5, _1080p * 887, _1080p * 927 ) + self:addElement( ArrowUp ) + self.ArrowUp = ArrowUp + + local ArrowDown = nil + + ArrowDown = MenuBuilder.BuildRegisteredType( "ArrowDown", { + controllerIndex = f20_local1 + } ) + ArrowDown.id = "ArrowDown" + ArrowDown:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 287.5, _1080p * 307.5, _1080p * 886, _1080p * 926 ) + self:addElement( ArrowDown ) + self.ArrowDown = ArrowDown + + local ListCount = nil + + ListCount = LUI.UIText.new() + ListCount.id = "ListCount" + ListCount:setText( "1/15", 0 ) + ListCount:SetFontSize( 24 * _1080p ) + ListCount:SetFont( FONTS.GetFont( FONTS.MainMedium.File ) ) + ListCount:SetAlignment( LUI.Alignment.Center ) + ListCount:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 307.5, _1080p * 452.5, _1080p * 894, _1080p * 918 ) + self:addElement( ListCount ) + self.ListCount = ListCount + + local LoadedModName = nil + + LoadedModName = LUI.UIText.new() + LoadedModName.id = "LoadedModName" + LoadedModName:setText( "LOADED MOD NAME", 0 ) + LoadedModName:SetFontSize( 20 * _1080p ) + LoadedModName:SetFont( FONTS.GetFont( FONTS.MainBold.File ) ) + LoadedModName:SetAlignment( LUI.Alignment.Left ) + LoadedModName:SetAnchorsAndPosition( 0, 1, 0, 1, _1080p * 130, _1080p * 630, _1080p * 942, _1080p * 966 ) + self:addElement( LoadedModName ) + self.LoadedModName = LoadedModName + + ModSelectionList:AddArrow( ArrowUp ) + ModSelectionList:AddArrow( ArrowDown ) + ModSelectionList:AddItemNumbers( ListCount ) + + self.addButtonHelperFunction = function ( arg0, arg1 ) + arg0:AddButtonHelperText( { + helper_text = Engine.Localize( "MENU_BACK" ), + button_ref = "button_secondary", + side = "left", + clickable = true + } ) + local fs_game = Engine.GetDvarString( "fs_game" ) + if fs_game ~= "" then + arg0:AddButtonHelperText( { + helper_text = Engine.Localize( "LUA_MENU_UNLOAD" ), + button_ref = "button_alt2", + side = "left", + clickable = true + } ) + end + end + + self:addEventHandler( "menu_create", self.addButtonHelperFunction ) + + local bindButton = LUI.UIBindButton.new() + bindButton.id = "selfBindButton" + self:addElement( bindButton ) + self.bindButton = bindButton + + PostLoadFunc( self, f20_local1, controller ) + + return self +end + +MenuBuilder.registerType( "ModSelectMenu", ModSelectMenu ) +LUI.FlowManager.RegisterStackPushBehaviour( "ModSelectMenu", PushFunc ) +LUI.FlowManager.RegisterStackPopBehaviour( "ModSelectMenu", f0_local1 ) \ No newline at end of file diff --git a/data/cdata/ui_scripts/Mods/__init__.lua b/data/cdata/ui_scripts/Mods/__init__.lua new file mode 100644 index 00000000..495f7c52 --- /dev/null +++ b/data/cdata/ui_scripts/Mods/__init__.lua @@ -0,0 +1,6 @@ +require( "ModSelectButton" ) +require( "ModSelectMenu" ) + +if (Engine.InFrontend()) then + require("ModDownload") +end \ No newline at end of file diff --git a/data/cdata/ui_scripts/test/__init__.lua b/data/cdata/ui_scripts/test/__init__.lua deleted file mode 100644 index 1ff8e07c..00000000 --- a/data/cdata/ui_scripts/test/__init__.lua +++ /dev/null @@ -1 +0,0 @@ -print("test") diff --git a/data/cdata/zone/iw7mod_code_post_gfx.ff b/data/cdata/zone/iw7mod_code_post_gfx.ff new file mode 100644 index 00000000..eae7a491 Binary files /dev/null and b/data/cdata/zone/iw7mod_code_post_gfx.ff differ diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 140e6b5e..38a7154f 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -439,7 +439,7 @@ namespace command client_command_mp_hook.create(0x140B105D0, &client_command_mp); client_command_sp_hook.create(0x140483130, &client_command_sp); - parse_commandline_hook.create(0x140F2F67B, parse_commandline); + parse_commandline_hook.create(0x140C039F0, parse_commandline); // SL_Init add_commands(); } diff --git a/src/client/component/download.cpp b/src/client/component/download.cpp new file mode 100644 index 00000000..2eee5190 --- /dev/null +++ b/src/client/component/download.cpp @@ -0,0 +1,254 @@ +#include +#include "loader/component_loader.hpp" + +#include "download.hpp" +#include "console/console.hpp" +#include "scheduler.hpp" +#include "party.hpp" + +#include "game/ui_scripting/execution.hpp" + +#include "utils/hash.hpp" + +#include +#include +#include +#include + +namespace download +{ + namespace + { + struct globals_t + { + bool abort{}; + bool active{}; + }; + + std::atomic_bool kill_downloads = false; + utils::concurrency::container globals; + + bool download_aborted() + { + if (kill_downloads) + { + return true; + } + + return globals.access([](globals_t& globals_) + { + return globals_.abort; + }); + } + + void mark_unactive() + { + globals.access([](globals_t& globals_) + { + globals_.active = false; + }); + } + + void mark_active() + { + globals.access([](globals_t& globals_) + { + globals_.active = true; + }); + } + + bool download_active() + { + return globals.access([](globals_t& globals_) + { + return globals_.active; + }); + } + + auto last_update = std::chrono::high_resolution_clock::now(); + int progress_callback(size_t total, size_t progress) + { + const auto now = std::chrono::high_resolution_clock::now(); + if (now - last_update > 20ms) + { + last_update = std::chrono::high_resolution_clock::now(); + auto fraction = 0.f; + if (total > 0) + { + fraction = static_cast(static_cast(progress) / + static_cast(total)); + } + + scheduler::once([=] + { + ui_scripting::notify("mod_download_progress", + { + {"fraction", fraction}, + }); + }, scheduler::pipeline::lui); + } + + //console::debug("Download progress: %lli/%lli\n", progress, total); + if (download_aborted()) + { + return -1; + } + + return 0; + } + + void menu_error(const std::string& error) + { + throw std::runtime_error(error); + } + } + + void start_download(const game::netadr_s& target, const utils::info_string& info, const std::vector& files) + { + if (download_active()) + { + scheduler::schedule([=] + { + if (!download_active()) + { + start_download(target, info, files); + return scheduler::cond_end; + } + + return scheduler::cond_continue; + }, scheduler::pipeline::main); + + return; + } + + globals.access([&](globals_t& globals_) + { + globals_ = {}; + }); + + const auto base = info.get("sv_wwwBaseUrl"); + if (base.empty()) + { + menu_error("Download failed: Server doesn't have 'sv_wwwBaseUrl' dvar set."); + return; + } + + scheduler::once([] + { + ui_scripting::notify("mod_download_start", {}); + }, scheduler::pipeline::lui); + + scheduler::once([=] + { + { + const auto _0 = gsl::finally(&mark_unactive); + mark_active(); + + if (download_aborted()) + { + return; + } + + for (const auto& file : files) + { + scheduler::once([=] + { + const ui_scripting::table data_table{}; + data_table.set("name", file.name.data()); + + ui_scripting::notify("mod_download_set_file", + { + {"request", data_table} + }); + }, scheduler::pipeline::lui); + + const auto url = utils::string::va("%s/%s", base.data(), file.name.data()); + console::debug("Downloading %s from %s: %s\n", file.name.data(), base.data(), url); + auto data = utils::http::get_data(url, {}, {}, &progress_callback); + if (!data.has_value()) + { + menu_error(utils::string::va("Download failed: An unknown error occurred when getting data from '%s', please try again.", url)); + return; + } + + if (download_aborted()) + { + return; + } + + auto& result = data.value(); + if (result.code != CURLE_OK) + { + if (result.code == CURLE_COULDNT_CONNECT) + { + menu_error(utils::string::va("Download failed: Couldn't connect to server '%s' (%i)\n", + url, result.code)); + return; + } + + menu_error(utils::string::va("Download failed: %s (%i)\n", + curl_easy_strerror(result.code), result.code)); + return; + } + + if (result.response_code >= 400) + { + menu_error(utils::string::va("Download failed: Server returned bad response code (%i)\n", + result.response_code)); + return; + } + + const auto hash = utils::hash::get_buffer_hash(result.buffer, file.name); + if (hash != file.hash) + { + menu_error(utils::string::va("Download failed: File hash doesn't match the server's (%s: %s != %s)\n", + file.name.data(), hash.data(), file.hash.data())); + return; + } + + utils::io::write_file(file.name, result.buffer, false); + } + } + + scheduler::once([] + { + ui_scripting::notify("mod_download_done", {}); + }, scheduler::pipeline::lui); + + scheduler::once([target] + { + party::connect(target); + }, scheduler::pipeline::main); + }, scheduler::pipeline::async); + } + + void stop_download() + { + if (!download_active()) + { + return; + } + + globals.access([&](globals_t& globals_) + { + globals_.abort = true; + }); + + scheduler::once([] + { + ui_scripting::notify("mod_download_done", {}); + game::shared::menu_error("Download failed: Aborted"); + }, scheduler::pipeline::lui); + } + + class component final : public component_interface + { + public: + void pre_destroy() override + { + kill_downloads = true; + } + }; +} + +REGISTER_COMPONENT(download::component) diff --git a/src/client/component/download.hpp b/src/client/component/download.hpp new file mode 100644 index 00000000..ddd95790 --- /dev/null +++ b/src/client/component/download.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "game/game.hpp" + +#include + +namespace download +{ + struct file_t + { + std::string name; + std::string hash; + }; + + void start_download(const game::netadr_s& target, const utils::info_string& info, const std::vector& files); + void stop_download(); +} diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index a9ed3ca3..b2441b99 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -175,21 +175,6 @@ namespace fastfiles } void load_fastfiles1_stub(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode) - { - std::vector data; - merge(&data, zoneInfo, zoneCount); - - // mod is loaded on map start - - if (fastfiles::exists("mod")) - { - data.push_back({ "mod", game::DB_ZONE_GAME | game::DB_ZONE_CUSTOM, 0 }); - } - - game::DB_LoadXAssets(data.data(), static_cast(data.size()), syncMode); - } - - void load_fastfiles2_stub(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode) { std::vector data; merge(&data, zoneInfo, zoneCount); @@ -222,6 +207,26 @@ namespace fastfiles add_zone("iw7mod_ui_mp", game::DB_ZONE_UI | game::DB_ZONE_CUSTOM, 0); } + add_zone("mod", game::DB_ZONE_GLOBAL_TIER1 | game::DB_ZONE_CUSTOM, 1); + + game::DB_LoadXAssets(data.data(), static_cast(data.size()), syncMode); + } + + void load_fastfiles2_stub(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode) + { + std::vector data; + merge(&data, zoneInfo, zoneCount); + + const auto add_zone = [&](const char* name) + { + if (fastfiles::exists(name)) + { + data.push_back({ name, game::DB_ZONE_PERMANENT | game::DB_ZONE_CUSTOM, 0 }); + } + }; + + add_zone("iw7mod_code_post_gfx"); + game::DB_LoadXAssets(data.data(), static_cast(data.size()), syncMode); } } @@ -326,10 +331,10 @@ namespace fastfiles sys_createfile_hook.create(game::Sys_CreateFile, sys_create_file_stub); // Add custom zones in fastfiles load - // (level specific) (mod) - utils::hook::call(0x1403B9E9F, load_fastfiles1_stub); // (global,common) - utils::hook::call(0x1405ADB63, load_fastfiles2_stub); + utils::hook::call(0x1405ADB63, load_fastfiles1_stub); + // (code_post_gfx) + utils::hook::call(0x140E0624B, load_fastfiles2_stub); command::add("loadzone", [](const command::params& params) { diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 8a6257ad..b6674f94 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -13,24 +13,72 @@ #include "profile_infos.hpp" #include "scheduler.hpp" #include "server_list.hpp" +#include "download.hpp" + +#include "utils/hash.hpp" #include #include #include #include +#include namespace party { namespace { - /*struct moddl_file + std::string get_dvar_string(const std::string& dvar) + { + auto* dvar_value = game::Dvar_FindVar(dvar.data()); + if (dvar_value && dvar_value->current.string) + { + return dvar_value->current.string; + } + + return {}; + } + + int get_dvar_int(const std::string& dvar) + { + auto* dvar_value = game::Dvar_FindVar(dvar.data()); + if (dvar_value && dvar_value->current.integer) + { + return dvar_value->current.integer; + } + + return -1; + } + + bool get_dvar_bool(const std::string& dvar) + { + auto* dvar_value = game::Dvar_FindVar(dvar.data()); + if (dvar_value && dvar_value->current.enabled) + { + return dvar_value->current.enabled; + } + + return false; + } + } + + namespace mods + { + void set_mod(const std::string& path, bool [[maybe_unused]] change_fs_game = false) + { + game::Dvar_SetFromStringByName("fs_game", path.data(), game::DvarSetSource::DVAR_SOURCE_INTERNAL); + } + } + + namespace + { + struct fastdl_file { std::string extension; std::string name; bool optional; }; - std::vector mod_files = + std::vector mod_files = { {".ff", "mod_hash", false}, }; @@ -137,7 +185,108 @@ namespace party } return needs_restart; + } + + /*std::string get_whitelist_json_path() + { + return (utils::properties::get_appdata_path() / "whitelist.json").generic_string(); + } + + nlohmann::json get_whitelist_json_object() + { + std::string data; + if (!utils::io::read_file(get_whitelist_json_path(), &data)) + { + return nullptr; + } + + nlohmann::json obj; + try + { + obj = nlohmann::json::parse(data.data()); + } + catch (const nlohmann::json::parse_error& ex) + { + game::shared::menu_error(utils::string::va("%s\n", ex.what())); + return nullptr; + } + + return obj; + } + + std::string target_ip_to_string(const game::netadr_s& target) + { + return utils::string::va("%i.%i.%i.%i", + static_cast(saved_info_response.host.ip[0]), + static_cast(saved_info_response.host.ip[1]), + static_cast(saved_info_response.host.ip[2]), + static_cast(saved_info_response.host.ip[3])); + } + + bool should_user_confirm(const game::netadr_s& target) + { + nlohmann::json obj = get_whitelist_json_object(); + if (obj != nullptr) + { + const auto target_ip = target_ip_to_string(target); + for (const auto& [key, value] : obj.items()) + { + if (value.is_string() && value.get() == target_ip) + { + return false; + } + } + } + + //close_joining_popups(); + command::execute("lui_open_popup popup_confirmdownload", false); + + return true; }*/ + + bool needs_vid_restart = false; + + bool download_files(const game::netadr_s& target, const utils::info_string& info, bool allow_download) + { + try + { + std::vector files{}; + + const auto needs_restart = check_download_mod(info, files); + needs_vid_restart = needs_vid_restart || needs_restart; + + if (files.size() > 0) + { + if (!allow_download/* && should_user_confirm(target)*/) + { + return true; + } + + download::stop_download(); + download::start_download(target, info, files); + return true; + } + else if (needs_restart || needs_vid_restart) + { + command::execute("vid_restart"); + needs_vid_restart = false; + scheduler::once([=]() + { + //mods::read_stats(); + connect(target); + }, scheduler::pipeline::main); + return true; + } + } + catch (const std::exception& e) + { + command::execute("luiLeaveMenu AcceptingInvite", true); + game::shared::menu_error(e.what()); + return true; + } + + return false; + } } namespace @@ -217,39 +366,6 @@ namespace party false); } - std::string get_dvar_string(const std::string& dvar) - { - auto* dvar_value = game::Dvar_FindVar(dvar.data()); - if (dvar_value && dvar_value->current.string) - { - return dvar_value->current.string; - } - - return {}; - } - - int get_dvar_int(const std::string& dvar) - { - auto* dvar_value = game::Dvar_FindVar(dvar.data()); - if (dvar_value && dvar_value->current.integer) - { - return dvar_value->current.integer; - } - - return -1; - } - - bool get_dvar_bool(const std::string& dvar) - { - auto* dvar_value = game::Dvar_FindVar(dvar.data()); - if (dvar_value && dvar_value->current.enabled) - { - return dvar_value->current.enabled; - } - - return false; - } - void com_gamestart_beginclient_stub(const char* mapname, const char* gametype, char a3) { if (preloaded_map) @@ -284,6 +400,13 @@ namespace party { profile_infos::xuid::clear_xuids(); + hash_cache.clear(); + + if (game::environment::is_dedi()) + { + generate_hashes(map); + } + preloaded_map = map_is_preloaded; sv_start_map_for_party_hook.invoke(map, game_type, client_count, agent_count, hardcore, map_is_preloaded, migrate); } @@ -448,7 +571,7 @@ namespace party void connect(const game::netadr_s& target) { - command::execute("luiOpenPopup AcceptingInvite", false); + command::execute("luiOpenPopup AcceptingInvite", true); profile_infos::xuid::clear_xuids(); profile_infos::clear_profile_infos(); @@ -462,9 +585,14 @@ namespace party void info_response_error(const std::string& error) { - console::error("%s\n", error.data()); - command::execute("luiLeaveMenu AcceptingInvite", false); - game::Com_SetLocalizedErrorMessage(error.data(), "MENU_NOTICE"); + command::execute("luiLeaveMenu AcceptingInvite", true); + + game::shared::menu_error(error); + } + + void clear_sv_motd() + { + server_connection_state.motd.clear(); } connection_state get_server_connection_state() @@ -731,7 +859,7 @@ namespace party info.set("sv_discordImageUrl", get_dvar_string("sv_discordImageUrl")); info.set("sv_discordImageText", get_dvar_string("sv_discordImageText")); - /*const auto fs_game = get_dvar_string("fs_game"); + const auto fs_game = get_dvar_string("fs_game"); info.set("fs_game", fs_game); if (!fs_game.empty()) @@ -742,7 +870,7 @@ namespace party fs_game.data(), file.extension.data())); info.set(file.name, hash); } - }*/ + } network::send(target, "infoResponse", info.build(), '\n'); }); @@ -819,12 +947,11 @@ namespace party } server_connection_state.base_url = info.get("sv_wwwBaseUrl"); - /* - if (download_files(target, info, false)) + + if (download_files(target, info, true)) { return; } - */ server_connection_state.motd = info.get("sv_motd"); server_connection_state.max_clients = std::stoi(sv_maxclients_str); diff --git a/src/client/component/party.hpp b/src/client/component/party.hpp index 2842f5af..84a1495e 100644 --- a/src/client/component/party.hpp +++ b/src/client/component/party.hpp @@ -10,7 +10,7 @@ namespace party bool hostDefined; std::string motd; int max_clients; - std::string base_url; // yk, for when we need it ;) + std::string base_url; }; struct discord_information diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index d6570688..f059ff7c 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -24,8 +24,6 @@ namespace scripting std::unordered_map>> script_function_table_sort; std::unordered_map> script_function_table_rev; - utils::concurrency::container shared_table; - std::string current_file; namespace diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp index 12598b9c..46048217 100644 --- a/src/client/component/scripting.hpp +++ b/src/client/component/scripting.hpp @@ -3,15 +3,11 @@ namespace scripting { - using shared_table_t = std::unordered_map; - extern std::unordered_map> fields_table; extern std::unordered_map> script_function_table; extern std::unordered_map>> script_function_table_sort; extern std::unordered_map> script_function_table_rev; - extern utils::concurrency::container shared_table; - extern std::string current_file; void on_shutdown(const std::function& callback); diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 5d720fa6..b801fc9c 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -4,7 +4,6 @@ #include "game/game.hpp" #include "game/dvars.hpp" - #include "command.hpp" #include "console/console.hpp" #include "fastfiles.hpp" @@ -15,6 +14,7 @@ #include "scheduler.hpp" #include "scripting.hpp" #include "server_list.hpp" +#include "download.hpp" #include "game/ui_scripting/execution.hpp" //#include "game/scripting/execution.hpp" @@ -30,6 +30,19 @@ namespace ui_scripting { + namespace lua_calls + { + int64_t is_development_build_stub([[maybe_unused]] game::hks::lua_State* luaVM) + { +#if defined(DEV_BUILD) || defined(DEBUG) + ui_scripting::push_value(true); +#else + ui_scripting::push_value(false); +#endif + return 1; + } + } + namespace { std::unordered_map> converted_functions; @@ -170,60 +183,6 @@ namespace ui_scripting }; */ - game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value) - { - scripting::shared_table.access([key, value](scripting::shared_table_t& table) - { - table[key] = value; - }); - }; - - game_type["sharedget"] = [](const game&, const std::string& key) - { - std::string result; - scripting::shared_table.access([key, &result](scripting::shared_table_t& table) - { - result = table[key]; - }); - return result; - }; - - game_type["sharedclear"] = [](const game&) - { - scripting::shared_table.access([](scripting::shared_table_t& table) - { - table.clear(); - }); - }; - - /* - game_type["assetlist"] = [](const game&, const std::string& type_string) - { - auto table_ = table(); - auto index = 1; - auto type_index = -1; - for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++) - { - if (type_string == ::game::g_assetNames[i]) - { - type_index = i; - } - } - if (type_index == -1) - { - throw std::runtime_error("Asset type does not exist"); - } - const auto type = static_cast<::game::XAssetType>(type_index); - fastfiles::enum_assets(type, [type, &table_, &index](const ::game::XAssetHeader header) - { - const auto asset = ::game::XAsset{type, header}; - const std::string asset_name = ::game::DB_GetXAssetName(&asset); - table_[index++] = asset_name; - }, true); - return table_; - }; - */ - game_type["getcurrentgamelanguage"] = [](const game&) { return steam::SteamApps()->GetCurrentGameLanguage(); @@ -235,11 +194,35 @@ namespace ui_scripting material.data())); }; + auto scheduler = table(); + lua["scheduler"] = scheduler; + + scheduler["once"] = [](const function_argument& args) + { + scheduler::once([args]() + { + auto func = args.as(); + func(); + }, scheduler::lui); + }; + auto server_list_table = table(); lua["serverlist"] = server_list_table; server_list_table["getplayercount"] = server_list::get_player_count; server_list_table["getservercount"] = server_list::get_server_count; + + auto download_table = table(); + lua["download"] = download_table; + + download_table["abort"] = download::stop_download; + + //download_table["userdownloadresponse"] = party::user_download_response; + //download_table["getwwwurl"] = [] + //{ + // const auto state = party::get_server_connection_state(); + // return state.base_url; + //}; } void enable_globals() @@ -311,10 +294,14 @@ namespace ui_scripting } } - void* hks_start_stub(char a1, char a2) + int hks_start_stub() { - const auto _0 = gsl::finally(&try_start); - return hks_start_hook.invoke(a1, a2); + auto result = hks_start_hook.invoke(); + if (result) + { + try_start(); + } + return result; } void hks_shutdown_stub() @@ -458,8 +445,11 @@ namespace ui_scripting hks_load_hook.create(0x1411E0B00, hks_load_stub); hks_package_require_hook.create(0x1411C7F00, hks_package_require_stub); - hks_start_hook.create(0x140615090, hks_start_stub); + hks_start_hook.create(0x1406023A0, hks_start_stub); hks_shutdown_hook.create(0x1406124B0, hks_shutdown_stub); + + // replace LUA engine calls + utils::hook::set(0x1414B4D98, lua_calls::is_development_build_stub); // IsDevelopmentBuild } }; } diff --git a/src/client/game/demonware/services/bdStorage.cpp b/src/client/game/demonware/services/bdStorage.cpp index 118fc460..1da17b94 100644 --- a/src/client/game/demonware/services/bdStorage.cpp +++ b/src/client/game/demonware/services/bdStorage.cpp @@ -133,7 +133,14 @@ namespace demonware std::string bdStorage::get_user_file_path(const std::string& name) { - return "iw7-mod/players2/user/" + name; + const auto regular_path = "iw7-mod/players2/user/"s; + static const auto fs_game = game::Dvar_FindVar("fs_game"); + if (fs_game && fs_game->current.string && *fs_game->current.string) + { + return regular_path + fs_game->current.string + "/" + name; + } + + return regular_path + name; } void bdStorage::uploadAndValidateFiles(service_server* server, byte_buffer* buffer) const diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp index 4e49a04f..d6f8cb74 100644 --- a/src/client/game/game.cpp +++ b/src/client/game/game.cpp @@ -25,15 +25,8 @@ namespace game { void client_println(int client_num, const std::string& text) { - if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP) - { - game::CG_Utils_GameMessage(client_num, text.data(), 0); // why is nothing printed? - } - else - { - game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, - utils::string::va("f \"%s\"", text.data())); - } + game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, + utils::string::va("f \"%s\"", text.data())); } bool cheats_ok(int client_num, bool print) @@ -53,6 +46,12 @@ namespace game return true; } + + void menu_error(const std::string& error) + { + console::error("%s\n", error.data()); + game::Com_SetLocalizedErrorMessage(error.data(), "MENU_NOTICE"); + } } int Cmd_Argc() diff --git a/src/client/game/game.hpp b/src/client/game/game.hpp index 31c5d3f5..b391cb6c 100644 --- a/src/client/game/game.hpp +++ b/src/client/game/game.hpp @@ -45,6 +45,7 @@ namespace game { void client_println(int client_num, const std::string& text); bool cheats_ok(int client_num = 0, bool print = false); + void menu_error(const std::string& error); } int Cmd_Argc(); diff --git a/src/client/utils/hash.cpp b/src/client/utils/hash.cpp new file mode 100644 index 00000000..2f94b5bf --- /dev/null +++ b/src/client/utils/hash.cpp @@ -0,0 +1,157 @@ +#include + +#include "game/game.hpp" + +#include "hash.hpp" +#include "component/console/console.hpp" + +#include +#include +#include + +namespace utils::hash +{ + namespace + { + constexpr auto read_buffer_size = 1ull * 1024ull * 1024ull; // 1MB + + std::string get_file_hash_pakfile(std::ifstream& file_stream, const std::size_t file_size, + const std::string& filename) + { + if (file_size < sizeof(game::XPakHeader)) + { + return {}; + } + + game::XPakHeader header{}; + file_stream.read(reinterpret_cast(&header), sizeof(game::XPakHeader)); + + constexpr auto hash_size = sizeof(game::DB_AuthHash); + + game::DB_AuthHash empty_hash{}; + if (!std::memcmp(header.hash.bytes, empty_hash.bytes, hash_size)) + { + console::warn("Computing pakfile hash because its missing, this may take some time...\n"); + + hash_state state; + sha256_init(&state); + + std::string buffer; + buffer.resize(read_buffer_size); + + auto bytes_to_read = file_size - sizeof(game::XPakHeader); + while (bytes_to_read > 0) + { + const auto read_size = std::min(read_buffer_size, bytes_to_read); + file_stream.read(buffer.data(), read_size); + sha256_process(&state, reinterpret_cast(buffer.data()), static_cast(read_size)); + bytes_to_read -= read_size; + } + + file_stream.close(); + + if (sha256_done(&state, header.hash.bytes) != CRYPT_OK) + { + return {}; + } + + std::fstream out_stream; + out_stream.open(filename, std::ios_base::binary | std::ios_base::out | std::ios_base::in); + out_stream.write(reinterpret_cast(&header), sizeof(game::XPakHeader)); + } + + const auto hash_str = std::string{header.hash.bytes, header.hash.bytes + hash_size}; + + return utils::string::dump_hex(hash_str, ""); + } + + std::string get_file_hash_generic(std::ifstream& file_stream, const std::size_t file_size) + { + auto crc_value = crc32(0L, Z_NULL, 0); + auto bytes_to_read = file_size; + + std::string buffer; + buffer.resize(read_buffer_size); + + while (bytes_to_read > 0) + { + const auto read_size = std::min(bytes_to_read, read_buffer_size); + file_stream.read(buffer.data(), read_size); + crc_value = crc32(crc_value, reinterpret_cast(buffer.data()), static_cast(read_size)); + bytes_to_read -= read_size; + } + + std::string hash; + hash.append(reinterpret_cast(&crc_value), sizeof(crc_value)); + return utils::string::dump_hex(hash, ""); + } + + std::string get_pakfile_buffer_hash(std::string& buffer) + { + if (buffer.size() < sizeof(game::XPakHeader)) + { + return {}; + } + + constexpr auto hash_size = sizeof(game::DB_AuthHash); + const auto header = reinterpret_cast(buffer.data()); + + game::DB_AuthHash empty_hash{}; + if (!std::memcmp(header->hash.bytes, empty_hash.bytes, hash_size)) + { + console::warn("Computing pakfile hash because its missing, this may take some time...\n"); + const auto hash_start = reinterpret_cast(buffer.data() + sizeof(game::XPakHeader)); + const auto len = buffer.size() - sizeof(game::XPakHeader); + const auto hash = utils::cryptography::sha256::compute(hash_start, len, false); + std::memcpy(header->hash.bytes, hash.data(), sizeof(header->hash)); + } + + std::string hash = {header->hash.bytes, header->hash.bytes + hash_size}; + return utils::string::dump_hex(hash, ""); + } + + std::string get_generic_buffer_hash(const std::string& buffer) + { + auto crc_value = crc32(0L, Z_NULL, 0); + crc_value = crc32(crc_value, reinterpret_cast(buffer.data()), + static_cast(buffer.size())); + std::string hash; + hash.append(reinterpret_cast(&crc_value), sizeof(crc_value)); + return utils::string::dump_hex(hash, ""); + } + } + + std::string get_file_hash(const std::string& file) + { + std::ifstream file_stream(file, std::ios::binary); + if (!file_stream.is_open()) + { + return {}; + } + + file_stream.seekg(0, std::ios::end); + const auto file_size = static_cast(file_stream.tellg()); + file_stream.seekg(0, std::ios::beg); + + if (file.ends_with(".pak")) + { + return get_file_hash_pakfile(file_stream, file_size, file); + } + else + { + return get_file_hash_generic(file_stream, file_size); + } + } + + std::string get_buffer_hash(std::string& buffer, const std::string& filename) + { + if (filename.ends_with(".pak")) + { + return get_pakfile_buffer_hash(buffer); + } + else + { + return get_generic_buffer_hash(buffer); + } + } +} diff --git a/src/client/utils/hash.hpp b/src/client/utils/hash.hpp new file mode 100644 index 00000000..4e5c9e56 --- /dev/null +++ b/src/client/utils/hash.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace utils::hash +{ + std::string get_file_hash(const std::string& file); + std::string get_buffer_hash(std::string& buffer, const std::string& filename); +}