├── 0 ├── Apply_patch.bat ├── about_print_debug.txt ├── base_common.lvl_goes_in_here │ └── copy_your_common.lvl_file_in_here.bat ├── bin │ ├── LVLTool.exe │ ├── ScriptMunge.exe │ ├── about_these_programs.txt │ ├── luac.exe │ └── run_after_changes.js ├── hud-options │ ├── anthony_bf2_hud1 │ │ ├── 0xDC27B03D.hud_ │ │ ├── Notes.txt │ │ ├── apply_hud.bat │ │ ├── hud_clone.jpg │ │ ├── hud_map.jpg │ │ └── hud_texture_pack.lvl │ ├── default_aspyr_hud │ │ ├── 0xDC27B03D.hud_ │ │ ├── Notes.txt │ │ ├── apply_hud.bat │ │ ├── hud_clone.jpg │ │ ├── hud_jedi.jpg │ │ ├── hud_map.jpg │ │ └── hud_texture_pack.lvl │ └── docs │ │ ├── README.txt │ │ ├── munge_hud.bat │ │ └── zri6jc.hud ├── in-game-options │ ├── disabled │ │ └── disable_scripts_by_placing_in_here.txt │ └── docs │ │ ├── README_OPTIONS.txt │ │ ├── un_modded_mp_lobby.jpg │ │ └── user_script_ifs_mp_lobby_extra_buttons.jpg ├── load-options │ ├── 2005_console_inspired │ │ ├── apply_load.bat │ │ ├── common.lvl │ │ ├── load.lvl │ │ ├── loading.png │ │ ├── loadscreen.jpg │ │ └── loadtipsbox_pieces.png │ ├── 2005_console_style │ │ ├── apply_load.bat │ │ ├── common.lvl │ │ ├── load.lvl │ │ ├── loading.png │ │ ├── loadscreen.jpg │ │ └── loadtipsbox_pieces.png │ ├── 2005_pc_style │ │ ├── apply_load.bat │ │ ├── common.lvl │ │ ├── load.lvl │ │ ├── loading.png │ │ ├── loadscreen.jpg │ │ └── loadtipsbox_pieces.png │ ├── default_aspyr_load │ │ ├── apply_load.bat │ │ ├── common.lvl │ │ ├── load.lvl │ │ ├── loading.png │ │ ├── loadscreen.jpg │ │ └── loadtipsbox_pieces.png │ └── docs │ │ └── README.txt ├── movies │ └── fly │ │ ├── MakePreviewMovies.7z │ │ └── fly.lvl ├── patch_scripts │ ├── 0_patch_scripts_only.txt │ └── v1.3patch_strings.lvl ├── run_after_making_changes_linux.sh ├── run_after_making_changes_switch.bat ├── run_after_making_changes_windows.bat └── shell-options │ ├── disabled │ └── README.txt │ └── use_0_patch_instant_action_screen.txt ├── .gitignore ├── CC_BF1 └── 0 │ ├── README.txt │ ├── bin │ └── LVLTool.exe │ ├── initial_setup.bat │ └── run_after_making_changes.bat ├── Current hierarchy.txt ├── MakePreviewMovies ├── README.txt ├── Temp │ └── movie_list.mlst ├── fly.mcfg ├── movies │ └── about_movies_folder.txt ├── munge_movies.bat └── output │ └── about_output_folder.txt ├── deploy_lvl ├── all_weap_hero_hanpistol.lvl └── cis_tread_tank.lvl ├── documentation ├── Readme.md ├── mod_menu_tree_examples │ ├── launch_my_mission │ │ ├── addme.lua │ │ └── munge.bat │ ├── pick_and_launch_any_addon_mission │ │ ├── addme.lua │ │ └── munge.bat │ ├── set_alternate_default_path │ │ ├── addme.lua │ │ ├── munge.bat │ │ └── user_script_alternate_default_path.lua │ └── simple_settings │ │ ├── addme.lua │ │ └── munge.bat └── user_script_examples │ ├── alternate_default.7z │ └── alternate_default │ ├── README.txt │ ├── data │ └── put_files_in_here.txt │ ├── disabled │ └── Disable_scripts_by_moving_them_here.txt │ ├── munge.bat │ └── user_script_alternate_default.lua ├── munge.bat ├── readme.md ├── src ├── addme.req ├── effects │ └── sfo_kmsx.fx ├── patch_ingame.req ├── patch_shell.req ├── scripts │ ├── addme.lua │ ├── fakeconsole_functions.lua │ ├── globals.lua │ ├── ifs_fakeconsole_zero_patch.lua │ ├── ifs_ingame_log.lua │ ├── ifs_missionselect_console.lua │ ├── ifs_mod_menu_tree.lua │ ├── patch_ingame.lua │ ├── patch_paths.lua │ ├── patch_shell.lua │ ├── popup_prompt.lua │ ├── user_script_ai_hero_no_turret.lua │ ├── user_script_cc_music_fix.lua │ ├── user_script_cis_tread_tank.lua │ ├── user_script_han_fix.lua │ ├── user_script_hero_vo.lua │ ├── user_script_ifs_mp_lobby_extra_buttons.lua │ ├── user_script_loc_fix.lua │ ├── user_script_locals_yellow_team.lua │ ├── user_script_rhenvar2_cp_fix.lua │ ├── utility_functions2.lua │ └── zero_patch_fs.lua └── textures │ ├── border_3_pieces.tga │ ├── border_3a_pieces.tga │ ├── border_popup.tga │ └── opaque_rect.tga └── switch_notes.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.lvl 3 | *.bik 4 | *.mp4 5 | *.script 6 | *.mvs 7 | MUNGED/ 8 | play/ 9 | 0/readme.txt 10 | -------------------------------------------------------------------------------- /0/Apply_patch.bat: -------------------------------------------------------------------------------- 1 | 2 | :: At some point we could possibly do an automatic copy from the user's _lvl_common\common.lvl 3 | :: but for now, let's keep it simple. (if doing teh copy we'd want to check if it had already been patched, maybe) 4 | @echo off 5 | 6 | if not exist "base_common.lvl_goes_in_here\common.lvl" ( 7 | echo Error: file does not exist, place your common.lvl file into the 'base_common.lvl_goes_in_here' folder 8 | pause 9 | exit /b 10 | ) 11 | 12 | REM If you want to run this on Linux there are options. 13 | REM 1. Manually run LVLTool.exe through Wine or Bottles, load the common.lvl and replace the 'globals' script with the one in 'patch_scripts', save. 14 | REM 2. Run under a Bottles or Wine command line env (untested). 15 | 16 | bin\LVLTool.exe -file base_common.lvl_goes_in_here\common.lvl -r patch_scripts\globals.script -o common.lvl 17 | 18 | -------------------------------------------------------------------------------- /0/about_print_debug.txt: -------------------------------------------------------------------------------- 1 | The 'zero patch' has a debug log accessible via the Mod Menu Tree and available via the pause menu ingame. 2 | By default not all print statements go into this log ( to cut down the chatter, 3 | only messages with 'info:' 'error' 'warn' 'debug' make it in by default). 4 | But if you rename this file to 'debug.txt' (or create a sibling file called 'debug.txt') all print statements will 5 | then go into the debug log. -------------------------------------------------------------------------------- /0/base_common.lvl_goes_in_here/copy_your_common.lvl_file_in_here.bat: -------------------------------------------------------------------------------- 1 | copy /Y ..\..\..\data2\_lvl_common\common.lvl . 2 | -------------------------------------------------------------------------------- /0/bin/LVLTool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/bin/LVLTool.exe -------------------------------------------------------------------------------- /0/bin/ScriptMunge.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/bin/ScriptMunge.exe -------------------------------------------------------------------------------- /0/bin/about_these_programs.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ====== only used for the switch 0 patch ======= 4 | luac.exe -- Lua compiler from the BF2 modtools package; used by ScriptMunge.exe 5 | ScriptMunge.exe -- Creates .script files (from .lua files) that can be read by BF2. 6 | =============================================== 7 | 8 | 9 | LVLTool.exe -- Used by the 'ApplyPatch.bat' batch file; replaces the 'globals' script inside common.lvl 10 | to give a hook into the game. GitHub repo https://github.com/BAD-AL/LVLTool 11 | -------------------------------------------------------------------------------- /0/bin/luac.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/bin/luac.exe -------------------------------------------------------------------------------- /0/bin/run_after_changes.js: -------------------------------------------------------------------------------- 1 | // uses Windows Script Host jScript (.js) support 2 | // cmd> cscript path\to\your\script.js 3 | 4 | // debug with Visual Studio: 5 | // C:\Windows\System32\cscript.exe /x run_after_changes.js 6 | // more about debugging wscript (.vbs, .js) files: 7 | // https://stackoverflow.com/questions/23210905/can-you-debug-vbscript-in-visual-studio/24693127#24693127 8 | 9 | 10 | var forSwitch = false; 11 | var shell = WScript.CreateObject("WScript.Shell"); 12 | var fso = new ActiveXObject("Scripting.FileSystemObject"); // https://www.devguru.com/content/technologies/vbscript/objects-filesystemobject.html 13 | var currentDir = shell.CurrentDirectory; 14 | var expectedFolder1 = "addon" 15 | var expectedFolder2 = "addon2" 16 | 17 | var foldersRenamed = 0; 18 | 19 | function print(arg){ 20 | WScript.Echo(arg); 21 | } 22 | 23 | function showMessageBox(message, title){ 24 | // shell.Popup: https://www.vbsedit.com/html/f482c739-3cf9-4139-a6af-3bde299b8009.asp 25 | // WScript.Shell.Popup(strText, [nSecondsToWait], [strTitle], [nType]); 26 | shell.Popup(message, 0, title, 0x0 + 0x40); 27 | } 28 | 29 | function showErrorBox(message, title){ 30 | shell.Popup(message, 0, title, 0x0 + 0x10); 31 | } 32 | 33 | function runCommand(c) { 34 | var command = "cmd /c " + c; 35 | var exec = shell.Exec(command); 36 | var output = exec.StdOut.ReadAll(); 37 | return output.split('\r\n'); // Split the output into an array of paths 38 | } 39 | 40 | function runProgram(programPath, arguments){ 41 | var command = programPath +" "+ arguments ; 42 | try{ 43 | print("Running: " + command); 44 | shell.run(command, 1, true); 45 | } catch( exception){ 46 | showErrorBox("Error running command:\r\n" + command 47 | + exception + "\r\n" , "Error!"); 48 | } 49 | } 50 | 51 | function endsWith(str, suffix) { 52 | return str.substr(str.length - suffix.length,suffix.length) === suffix; 53 | } 54 | 55 | function writeFile(filePath, contents) { 56 | var file = fso.OpenTextFile(filePath,2, true); 57 | file.Write(contents); 58 | file.Close(); 59 | WScript.Echo("File written successfully: " + filePath); 60 | } 61 | 62 | // Gets all the files under the given folder, populates the fileList array. 63 | // folder: a folder file system object (fso.GetFolder(path)) 64 | // fileList: a JavaScript array, initially should be empty. 65 | function getAllFiles(folder, fileList) { 66 | var file, files = new Enumerator(folder.files); 67 | for (; !files.atEnd(); files.moveNext()) { 68 | file = files.item(); 69 | fileList.push(file.Path); 70 | } 71 | var subFolders = new Enumerator(folder.SubFolders); 72 | for (; !subFolders.atEnd(); subFolders.moveNext()) { 73 | getAllFiles(subFolders.item(), fileList); 74 | } 75 | } 76 | 77 | function writeFakeFileSystem(){ 78 | var folder = fso.GetFolder("."), 79 | allFiles = []; 80 | 81 | getAllFiles(folder, allFiles); 82 | var luaFileContent = "zero_patch_files_string = [[\r\n"; 83 | for(var i =0; i < allFiles.length; i++){ 84 | luaFileContent += allFiles[i] +"\r\n"; 85 | } 86 | luaFileContent += "]]\r\n"; 87 | writeFile("0\\patch_scripts\\fs.lua", luaFileContent); 88 | } 89 | 90 | // ===================== used for switch ============================== 91 | function lowerCaseFiles(strDirectory) { 92 | var fso = new ActiveXObject("Scripting.FileSystemObject"); 93 | var folder = fso.GetFolder(strDirectory); 94 | var files = new Enumerator(folder.Files); 95 | var file, tmp, oldName, newName, lowerName; 96 | 97 | for (; !files.atEnd(); files.moveNext()) { 98 | file = files.item(); 99 | lowerName = file.Name.toLowerCase(); 100 | if (file.Name != lowerName) { 101 | oldName = file.Path; 102 | newName = fso.BuildPath(file.ParentFolder, lowerName); 103 | tmp = oldName + "_tmp_"; 104 | fso.MoveFile(oldName, tmp); 105 | fso.MoveFile(tmp, newName); 106 | print("Renamed to: " + newName); 107 | } 108 | } 109 | } 110 | 111 | function lowerCaseDirectories(strDirectory) { 112 | var fso = new ActiveXObject("Scripting.FileSystemObject"); 113 | var folder = fso.GetFolder(strDirectory); 114 | var subFolders = new Enumerator(folder.SubFolders); 115 | var lowerName = folder.Name.toLowerCase(); 116 | var oldName = folder.Path; 117 | var newName = fso.BuildPath(folder.ParentFolder, lowerName); 118 | var tmp; 119 | 120 | lowerCaseFiles(strDirectory); 121 | 122 | if (folder.Name != lowerName) { 123 | tmp = oldName + "__tmp__"; 124 | fso.MoveFolder(oldName, tmp); 125 | fso.MoveFolder(tmp, newName); 126 | print("Renamed to: " + newName); 127 | } 128 | 129 | folder = fso.GetFolder(newName); 130 | subFolders = new Enumerator(folder.SubFolders); 131 | for (; !subFolders.atEnd(); subFolders.moveNext()) { 132 | lowerCaseDirectories(subFolders.item().Path); 133 | } 134 | } 135 | // ===================== used for switch end ============================== 136 | 137 | // start of script functionality 138 | var message = ""; 139 | print("Current Dir> " + currentDir); 140 | 141 | // make sure we're in a good folder. 142 | if(endsWith(currentDir,"addon") || endsWith(currentDir,"addon2")){ 143 | print("Current folder is an addon folder, nice."); 144 | } else { 145 | showErrorBox("Current folder is NOT an addon folder! Exiting!!", "Error!"); 146 | WScript.Quit(1); 147 | } 148 | 149 | // running for the switch version? 150 | for(var i = 0; i < WScript.Arguments.length; i++){ 151 | if(WScript.Arguments(i).toLowerCase() === "-switch" ){ 152 | forSwitch = true; 153 | print("will do switch specific lower-case operation"); 154 | } 155 | } 156 | 157 | // Start the renaming process from the current directory 158 | if(endsWith(currentDir,"addon2")){ 159 | print("Check for folders to rename..."); 160 | //renameFolders(currentDir); 161 | var foldersToRename = runCommand("dir /b /s | findstr /i _lvl_pc$ "); 162 | var current, target; 163 | for(var i=0; i < foldersToRename.length; i++){ 164 | current = foldersToRename[i]; 165 | target = current.toLowerCase().replace("_lvl_pc", "_lvl_common"); 166 | if( fso.FolderExists(current) && !fso.FolderExists(target)){ 167 | fso.MoveFolder( current, target); 168 | foldersRenamed++; 169 | } 170 | } 171 | message += "renamed " + foldersRenamed + " folders.\r\n"; 172 | 173 | if(forSwitch){ 174 | print("Lower-case the files for switch.") 175 | lowerCaseDirectories(currentDir); 176 | } 177 | } 178 | 179 | print("Create the fake file system") 180 | writeFakeFileSystem(); 181 | message += "Created fake File System\r\n"; 182 | 183 | if(forSwitch){ 184 | print("Creating fs.script for the switch version") 185 | runProgram("0\\bin\\ScriptMunge.exe", 186 | " -sourcedir 0\\patch_scripts\\ -inputfile fs.lua -outputdir 0\\patch_scripts\\ "); 187 | // verify file exists "0\\patch_scripts\\fs.script" 188 | if(!fso.FileExists("0\\patch_scripts\\fs.script")){ 189 | showErrorBox("'0\\patch_scripts\\fs.script' was not created by scriptmunge", "Error!"); 190 | WScript.Quit(1); 191 | } 192 | } 193 | 194 | //print("Done") 195 | showMessageBox(message, "All done!"); -------------------------------------------------------------------------------- /0/hud-options/anthony_bf2_hud1/0xDC27B03D.hud_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/anthony_bf2_hud1/0xDC27B03D.hud_ -------------------------------------------------------------------------------- /0/hud-options/anthony_bf2_hud1/Notes.txt: -------------------------------------------------------------------------------- 1 | This is the hud delivered in the initial '0' patch by AnthonyBF2 2 | for the Aspyr Classic Collection BF2 3 | 4 | ============================ String hashing special-ness ============================ 5 | The BF2 build often runs a special hash operation on filenames (and other strings) to compress strings into 4 bytes. 6 | 7 | We don't know the real filename for the Aspyr 1 player Hud. 8 | The name was translated to the hash '0xdc27b03d' 9 | Which has a matching un-hash of 'zri6jc' 10 | 11 | Surely Not the name aspyr chose, but it'll work. 12 | So if you want to create a deployable hud for Aspyr BF2, you'll want to name 13 | your HUD file 'zri6jc.hud' 14 | Then run: 15 | md MUNGED 16 | C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.hud -continue -platform PC -sourcedir . -outputdir MUNGED 17 | 18 | The output file in the MUNGED folder will be named 'zri6jc.config'; re-name it to '0xdc27b03d.hud_' 19 | to get it to replace the existing one using LVLTool. 20 | -------------------------------------------------------------------------------- /0/hud-options/anthony_bf2_hud1/apply_hud.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist ..\..\..\..\data2\_lvl_common\ingame.lvl ( 4 | REM use LVLTool to replace the player 1 hud file 5 | ..\..\bin\LVLTool.exe -file ..\..\..\..\data2\_lvl_common\ingame.lvl -r 0xDC27B03D.hud_ 6 | 7 | REM overwrite any hud_texture_pack.lvl file 8 | echo copy hud_texture_pack.lvl now... 9 | copy /y hud_texture_pack.lvl .. 10 | ) else ( 11 | echo Error! Could not find ingame.lvl; hud not applied 12 | ) 13 | 14 | 15 | REM Let user see the messages above 16 | pause 17 | -------------------------------------------------------------------------------- /0/hud-options/anthony_bf2_hud1/hud_clone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/anthony_bf2_hud1/hud_clone.jpg -------------------------------------------------------------------------------- /0/hud-options/anthony_bf2_hud1/hud_map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/anthony_bf2_hud1/hud_map.jpg -------------------------------------------------------------------------------- /0/hud-options/anthony_bf2_hud1/hud_texture_pack.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/anthony_bf2_hud1/hud_texture_pack.lvl -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/0xDC27B03D.hud_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/default_aspyr_hud/0xDC27B03D.hud_ -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/Notes.txt: -------------------------------------------------------------------------------- 1 | Notes: 2 | 1. This hud is was only tested with Aspyr CC BF2 3 | 4 | 2. The 'hud_texture_pack.lvl' contains only a small unused image. 5 | It is intended to overwrite any 'hud-options\hud_texture_pack.lvl' from other mods to restore the default hud. 6 | -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/apply_hud.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist ..\..\..\..\data2\_lvl_common\ingame.lvl ( 4 | REM use LVLTool to replace the player 1 hud file 5 | ..\..\bin\LVLTool.exe -file ..\..\..\..\data2\_lvl_common\ingame.lvl -r 0xDC27B03D.hud_ 6 | 7 | REM overwrite any hud_texture_pack.lvl file 8 | echo copy hud_texture_pack.lvl now... 9 | copy /y hud_texture_pack.lvl .. 10 | ) else ( 11 | echo Error! Could not find ingame.lvl; hud not applied 12 | ) 13 | 14 | 15 | REM Let user see the messages above 16 | pause 17 | -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/hud_clone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/default_aspyr_hud/hud_clone.jpg -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/hud_jedi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/default_aspyr_hud/hud_jedi.jpg -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/hud_map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/default_aspyr_hud/hud_map.jpg -------------------------------------------------------------------------------- /0/hud-options/default_aspyr_hud/hud_texture_pack.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/hud-options/default_aspyr_hud/hud_texture_pack.lvl -------------------------------------------------------------------------------- /0/hud-options/docs/README.txt: -------------------------------------------------------------------------------- 1 | 2 | ** Note Process in this file is for targeting BF2 Classic Collection. A similar process could be used for the 3 | base game, but the hud filename '0xDC27B03D.hud_' won't work for the base game (unless replacing manually 4 | through the LVLTool GUI). 5 | 6 | The pattern for delivering a hud for BF2 Classic Collection is to deliver a well-named-folder 7 | with the following files: 8 | preview_images -- Include a few preview images so that users can see why your hud is so nice. 9 | 0xDC27B03D.hud_ -- The munged Hud file. Special build instructions below. 10 | apply_hud.bat -- Should be ok to just copy one from the default aspyr hud folder since your 11 | hud folder is to be deployed next to all the other huds (inside '0\hud-options\') 12 | hud_texture_pack.lvl -- If you don't use any special textures just copy the one from the default aspyr 13 | folder and deploy it with your hud. 14 | 15 | You can use the munge_hud.bat file in this folder to munge your .hud file. 16 | (BF2 Modtools required, adjust batch file if your modtools aren't stored at C:\BF2_ModTools\) 17 | 18 | ============================ String hashing special-ness ============================ 19 | The BF2 build often runs a special hash operation on filenames (and other strings) to compress strings into 4 bytes. 20 | 21 | We don't know the real filename for the Aspyr 1 player Hud. 22 | The name was translated to the hash '0xdc27b03d' 23 | Which has a matching un-hash of 'zri6jc'; surely Not the name aspyr chose, 24 | but since the hash matches it'll work. 25 | 26 | After you've crafted a beautiful hud and tested with the ModTools debugger (or whatever your process is), 27 | name your HUD file 'zri6jc.hud' 28 | Then run the included batch file. 29 | 30 | The output file in the MUNGED folder will/may be named 'zri6jc.config'; re-name it to '0xdc27b03d.hud_' 31 | to get it to replace the existing one using the apply_hud.bat (which uses LVLTool). 32 | -------------------------------------------------------------------------------- /0/hud-options/docs/munge_hud.bat: -------------------------------------------------------------------------------- 1 | MD MUNGED 2 | C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.hud -continue -platform PC -sourcedir . -outputdir MUNGED -------------------------------------------------------------------------------- /0/in-game-options/disabled/disable_scripts_by_placing_in_here.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/in-game-options/disabled/disable_scripts_by_placing_in_here.txt -------------------------------------------------------------------------------- /0/in-game-options/docs/README_OPTIONS.txt: -------------------------------------------------------------------------------- 1 | 2 | Two options that by default are on are: 3 | 1. ai_heroes 4 | 2. 'Extra buttons' on the ifs_mp_lobby screen 5 | 6 | These are in-game options that are stored in the "0\in-game-options\" folder. 7 | If you don't want one of these to be on, simply move to the '0\in-game-options\disabled' folder. 8 | 9 | AI Heroes: Allows an AI to spawn as a hero for each level. 10 | 11 | ifs_mp_lobby un-modded: un_modded_mp_lobby.jpg 12 | ifs_mp_lobby modded: user_script_ifs_mp_lobby_extra_buttons.jpg 13 | -------------------------------------------------------------------------------- /0/in-game-options/docs/un_modded_mp_lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/in-game-options/docs/un_modded_mp_lobby.jpg -------------------------------------------------------------------------------- /0/in-game-options/docs/user_script_ifs_mp_lobby_extra_buttons.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/in-game-options/docs/user_script_ifs_mp_lobby_extra_buttons.jpg -------------------------------------------------------------------------------- /0/load-options/2005_console_inspired/apply_load.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist ..\..\..\..\data2\_lvl_common\load\common.lvl ( 4 | 5 | copy /y common.lvl ..\..\..\..\data2\_lvl_common\load\ 6 | copy /y load.lvl ..\..\..\..\data2\_lvl_common\load\ 7 | 8 | ) else ( 9 | echo Error! Could not find load\common.lvl; Load option not applied 10 | ) 11 | 12 | REM Let user see the messages above 13 | pause 14 | -------------------------------------------------------------------------------- /0/load-options/2005_console_inspired/common.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_inspired/common.lvl -------------------------------------------------------------------------------- /0/load-options/2005_console_inspired/load.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_inspired/load.lvl -------------------------------------------------------------------------------- /0/load-options/2005_console_inspired/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_inspired/loading.png -------------------------------------------------------------------------------- /0/load-options/2005_console_inspired/loadscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_inspired/loadscreen.jpg -------------------------------------------------------------------------------- /0/load-options/2005_console_inspired/loadtipsbox_pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_inspired/loadtipsbox_pieces.png -------------------------------------------------------------------------------- /0/load-options/2005_console_style/apply_load.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist ..\..\..\..\data2\_lvl_common\load\common.lvl ( 4 | 5 | copy /y common.lvl ..\..\..\..\data2\_lvl_common\load\ 6 | copy /y load.lvl ..\..\..\..\data2\_lvl_common\load\ 7 | 8 | ) else ( 9 | echo Error! Could not find load\common.lvl; Load option not applied 10 | ) 11 | 12 | REM Let user see the messages above 13 | pause 14 | -------------------------------------------------------------------------------- /0/load-options/2005_console_style/common.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_style/common.lvl -------------------------------------------------------------------------------- /0/load-options/2005_console_style/load.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_style/load.lvl -------------------------------------------------------------------------------- /0/load-options/2005_console_style/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_style/loading.png -------------------------------------------------------------------------------- /0/load-options/2005_console_style/loadscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_style/loadscreen.jpg -------------------------------------------------------------------------------- /0/load-options/2005_console_style/loadtipsbox_pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_console_style/loadtipsbox_pieces.png -------------------------------------------------------------------------------- /0/load-options/2005_pc_style/apply_load.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist ..\..\..\..\data2\_lvl_common\load\common.lvl ( 4 | 5 | copy /y common.lvl ..\..\..\..\data2\_lvl_common\load\ 6 | copy /y load.lvl ..\..\..\..\data2\_lvl_common\load\ 7 | 8 | ) else ( 9 | echo Error! Could not find load\common.lvl; Load option not applied 10 | ) 11 | 12 | REM Let user see the messages above 13 | pause 14 | -------------------------------------------------------------------------------- /0/load-options/2005_pc_style/common.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_pc_style/common.lvl -------------------------------------------------------------------------------- /0/load-options/2005_pc_style/load.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_pc_style/load.lvl -------------------------------------------------------------------------------- /0/load-options/2005_pc_style/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_pc_style/loading.png -------------------------------------------------------------------------------- /0/load-options/2005_pc_style/loadscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_pc_style/loadscreen.jpg -------------------------------------------------------------------------------- /0/load-options/2005_pc_style/loadtipsbox_pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/2005_pc_style/loadtipsbox_pieces.png -------------------------------------------------------------------------------- /0/load-options/default_aspyr_load/apply_load.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist ..\..\..\..\data2\_lvl_common\load\common.lvl ( 4 | 5 | copy /y common.lvl ..\..\..\..\data2\_lvl_common\load\ 6 | copy /y load.lvl ..\..\..\..\data2\_lvl_common\load\ 7 | 8 | ) else ( 9 | echo Error! Could not find load\common.lvl; Load option not applied 10 | ) 11 | 12 | REM Let user see the messages above 13 | pause 14 | -------------------------------------------------------------------------------- /0/load-options/default_aspyr_load/common.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/default_aspyr_load/common.lvl -------------------------------------------------------------------------------- /0/load-options/default_aspyr_load/load.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/default_aspyr_load/load.lvl -------------------------------------------------------------------------------- /0/load-options/default_aspyr_load/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/default_aspyr_load/loading.png -------------------------------------------------------------------------------- /0/load-options/default_aspyr_load/loadscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/default_aspyr_load/loadscreen.jpg -------------------------------------------------------------------------------- /0/load-options/default_aspyr_load/loadtipsbox_pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/load-options/default_aspyr_load/loadtipsbox_pieces.png -------------------------------------------------------------------------------- /0/load-options/docs/README.txt: -------------------------------------------------------------------------------- 1 | 2 | A 'load-options' will consist of the following files: 3 | 4 | preview images -- Show the user why your load textures are neat. 5 | apply_load.bat -- Simple batch file that copies the common.lvl and load.lvl 6 | to the game's _lvl_common\load folder. If you are making a new 7 | load-options' package just copy the one from the default 8 | aspyr folder. 9 | common.lvl -- .lvl file conatining the default loading texture. 10 | load.lvl -- .lvl file containing the 'load' bubbles and tips border 11 | texture (and a configuration file that tells the game which 12 | background image to use for which map) -------------------------------------------------------------------------------- /0/movies/fly/MakePreviewMovies.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/movies/fly/MakePreviewMovies.7z -------------------------------------------------------------------------------- /0/movies/fly/fly.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/movies/fly/fly.lvl -------------------------------------------------------------------------------- /0/patch_scripts/0_patch_scripts_only.txt: -------------------------------------------------------------------------------- 1 | This folder is for the '0' patch scripts. 2 | Don't put other stuff in here. 3 | -------------------------------------------------------------------------------- /0/patch_scripts/v1.3patch_strings.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/patch_scripts/v1.3patch_strings.lvl -------------------------------------------------------------------------------- /0/run_after_making_changes_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the name of the current directory 4 | current_directory=$(basename "$PWD") 5 | 6 | # Verify the script is run from a directory named 'addon2' 7 | if [[ "$current_directory" != "addon2" && "$current_directory" != "addon" ]]; then 8 | echo "Error: This script must be run from a directory named 'addon' or 'addon2'. Exiting." 9 | exit 1 10 | fi 11 | 12 | if [[ "$current_directory" == "addon2" ]]; then 13 | echo Checking folders for the classic collection... 14 | # Search for _LVL_PC directories case-insensitively and rename them 15 | find . -type d -iname _LVL_PC | while read -r dir; do 16 | # Construct the new directory name (_lvl_common) based on the found directory 17 | new_dir=$(dirname "$dir")/_lvl_common 18 | 19 | # Check if _lvl_common already exists 20 | if [ -e "$new_dir" ]; then 21 | echo "Directory $new_dir already exists, skipping." 22 | else 23 | # Rename _LVL_PC to _lvl_common 24 | mv "$dir" "$new_dir" 25 | if [ $? -eq 0 ]; then 26 | echo "Renamed $dir to $new_dir" 27 | else 28 | echo "Failed to rename $dir" 29 | fi 30 | fi 31 | done 32 | fi 33 | echo done checking folders. 34 | 35 | echo creating fake file system... 36 | echo zero_patch_files_string = [[ > "0/patch_scripts/fs.lua" 37 | 38 | find $PWD -type f >> "0/patch_scripts/fs.lua" 39 | 40 | echo ]] >> "0/patch_scripts/fs.lua" 41 | 42 | echo done creating fake file system. 43 | zenity --width=400 --info --text="All done checking and renaming directories;\ncreated fake file system for BF2 zero patch" --title="Go Run Battlefront 2 :)" 44 | -------------------------------------------------------------------------------- /0/run_after_making_changes_switch.bat: -------------------------------------------------------------------------------- 1 | :: to debug with visual studio do: 2 | :: cscript /x "0\\bin\\run_after_changes.js" -switch 3 | cscript "0\\bin\\run_after_changes.js" -switch 4 | -------------------------------------------------------------------------------- /0/run_after_making_changes_windows.bat: -------------------------------------------------------------------------------- 1 | :: to debug with visual studio do: 2 | :: cscript /x "0\\bin\\run_after_changes.js" 3 | cscript "0\\bin\\run_after_changes.js" 4 | 5 | -------------------------------------------------------------------------------- /0/shell-options/disabled/README.txt: -------------------------------------------------------------------------------- 1 | Drag option files in here to disable the options 2 | -------------------------------------------------------------------------------- /0/shell-options/use_0_patch_instant_action_screen.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/0/shell-options/use_0_patch_instant_action_screen.txt -------------------------------------------------------------------------------- /CC_BF1/0/README.txt: -------------------------------------------------------------------------------- 1 | ===== '0' patch for Battlefront 1 Classic Collection ===== 2 | 1. Place this '0' folder inside the 'addon1' folder. 3 | 2. Run the 'initial_setup.bat' file 4 | (once 'initial_setup.bat' has been run, you can move it to the 'bin' folder to reduce clutter) 5 | 3. Copy the 'run_after_making_changes.bat' in the 'addon1' folder 6 | 4. Run the 'run_after_making_changes.bat' from the addon1 folder 7 | 8 | 9 | Functionality Summary: 10 | Localization fix (Limited testing; verified to work on small number of mods); 11 | all BF1 mods will still need to be re-built to be used with Classic Collection 12 | (due in the Lua 4.0 -> Lua 5.0.2 change) 13 | 14 | (initial_setup.bat) 15 | * Backup original '_lvl_pc\loading.lvl' to _lvl_pc\ZZ_BACKUP\loading.lvl' 16 | * Copy original '_lvl_pc\loading.lvl' to '0\base.loading.lvl' 17 | 18 | (run_after_making_changes.bat) 19 | * Gathers Strings found in in the different addons and merges them into the localization 20 | sections of "data1\_lvl_pc\loading.lvl" 21 | 22 | -------------------------------------------------------------------------------- /CC_BF1/0/bin/LVLTool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/CC_BF1/0/bin/LVLTool.exe -------------------------------------------------------------------------------- /CC_BF1/0/initial_setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if not exist ..\..\data1\_lvl_pc\ZZ_BACKUP\loading.lvl ( 3 | 4 | :: make backup dir 5 | mkdir ..\..\data1\_lvl_pc\ZZ_BACKUP\ 6 | 7 | :: Backup the lvl 8 | copy ..\..\data1\_lvl_pc\loading.lvl ..\..\data1\_lvl_pc\ZZ_BACKUP\ 9 | 10 | :: Copy it to the 0 folder 11 | copy ..\..\data1\_lvl_pc\loading.lvl base.loading.lvl 12 | ) else ( 13 | echo Backup folder already created 14 | ) 15 | 16 | if not exist base.loading.lvl ( 17 | :: setup for alt addon system 18 | echo Something went wrong , please copy loading.lvl to 0\base.loading.lvl 19 | ) else ( 20 | echo Looks like setup is done 21 | ) -------------------------------------------------------------------------------- /CC_BF1/0/run_after_making_changes.bat: -------------------------------------------------------------------------------- 1 | 2 | @echo off 3 | cd 0 4 | 5 | if exist base.loading.lvl ( 6 | :: Merge Core (collect all new strings from core.lvl files and add them to tha game's core.lvl) 7 | :: this program can leave around a string hash 'dictionary', let's delete it so we don't confuse the user. 8 | 9 | echo Merging strings ... 10 | :: The "| findstr -v " part suppresses a confusing message 11 | bin\LVLTool.exe -file base.loading.lvl -merge_strings .. -o ..\..\data1\_lvl_pc\loading.lvl | findstr /v specified 12 | 13 | ) 14 | pause 15 | -------------------------------------------------------------------------------- /Current hierarchy.txt: -------------------------------------------------------------------------------- 1 | addon2 2 | └── "0" 3 | ├── addme.script 4 | ├── readme.txt 5 | ├── Apply_patch.bat 6 | ├── run_after_adding_mod_linux.sh 7 | ├── run_after_adding_mod_switch.bat 8 | ├── run_after_adding_mod_windows.bat 9 | ├── base_common.lvl_goes_in_here 10 | | └──put_your_common.lvl_file_in_here.txt 11 | ├── movies 12 | | └── fly 13 | | └── fly.lvl 14 | └── patch_scripts 15 | ├── globals.script 16 | ├── patch_paths.script 17 | ├── patch_shell.lvl 18 | └── patch_ingame.lvl 19 | 20 | data2 21 | └── _lvl_common 22 | └── common.lvl (replace the "globals.lua" with LVLTool) 23 | -------------------------------------------------------------------------------- /MakePreviewMovies/README.txt: -------------------------------------------------------------------------------- 1 | For finding preview movies we'll follow 2 conventions. 2 | 1. Priority is given to the 'UOP' way where movieName and path to the movie is attached to an entry in the 'sp_missionselect_listbox_contents' 3 | 4 | local entry = { 5 | mapluafile = "ABC%s_%s", 6 | movieFile = "..\..\addon\ABC\data\_LVL_PC\Movies\abc", 7 | movieName = "preview", 8 | era_c= 1, 9 | mode_con_c= 1 10 | } 11 | 2. Look in the following location for the movie file: 12 | addon2\0\movies\FLY\ABCfly.mvs 13 | 14 | Get the map fly-through video as follows: 15 | prefix of 'mapluafile' + 'fly.mvs' 16 | (The inner movie of the .mvs file needs to be named 'preview') 17 | 18 | Example: 19 | Corsaunt Jedi Temple has a missionlist entry of: 20 | { mapluafile = "cor1%s_%s", era_g = 1, era_c = 1, mode_con_c = 1, mode_con_g = 1, mode_ctf_c = 1, mode_ctf_g = 1,}, 21 | 22 | mapluafile = "cor1%s_%s" 23 | 24 | prefix = "cor1" 25 | 26 | movieFileName = "cor1fly.mvs" 27 | 28 | We will look in the following location for the movie files (if not otherwise specified with movieFile and movieName ): 29 | addon2\0\movies\FLY\ 30 | 31 | Note: 32 | To Convert .mp4 movies to XBOX format (.xmv) you can use the package found at: 33 | https://github.com/BAD-AL/SWBF2_Xbox_mod_effort/tree/master/XBOX/XBOX_Video_Converter 34 | 35 | To convert .mp4 to .bik you can use the 'Rad Video' tools 36 | https://www.radgametools.com/bnkdown.htm 37 | 38 | bink compression notes: 39 | compress to 90% gives a nice file size, quality for mostly static shots (1080p). 40 | 41 | ============================================================================================================ 42 | Making a preview movie: 43 | 1. Record your short preview movie, convert it to the appropiate format (recommend 3-4 shots [about 4 seconds each shot]). 44 | 2. Be sure to name it according to the convension mentioned above (i.e. 'ABCfly.bik') 45 | 3. Place the movie in the 'movies' folder. 46 | 4. Run the 'munge_movies.bat' file 47 | 48 | Note 2: 49 | Check over the 'munge_movies.bat' batch file to ensure the modtools folder is referenced correctly for your box and 50 | make sure it's using the correct file extension for the platform you are targeting. 51 | 52 | Note 3: 53 | The included 'fly.mcfg' and 'fly.lvl' include the definitions for the 'preview' and 'preview-loop' movie definitions. 54 | -------------------------------------------------------------------------------- /MakePreviewMovies/Temp/movie_list.mlst: -------------------------------------------------------------------------------- 1 | TEMP\preview.bik 2 | -------------------------------------------------------------------------------- /MakePreviewMovies/fly.mcfg: -------------------------------------------------------------------------------- 1 | //SoundStream.SubObjectID : SegmentList 2 | // 3 | //Property | Description 4 | //------------------------------------------------------------------------------- 5 | //Segment | Segment("segmentName", weight). The segmentName is the name 6 | // | of a segment within the stream specified by the stream ID 7 | // | above. The weight is used to randomly choose a segment to 8 | // | play each time the SoundStream is triggered. 1..N 9 | // | Segments must be specified. 10 | // | Segment("segementName", weight, playInterval, playIntervalDev); 11 | // | playInterval is the time in seconds which must elapse before 12 | // | the segment is played again. The playIntervalDev varies the 13 | // | playInterval. For example to play a segment every 60..120 14 | // | seconds write the following ... 15 | // | Segment("segmentName", 1.0, 90.0, 30.0); 16 | MovieProperties() 17 | { 18 | Name("transition_template"); 19 | FadeInTime(0.0); 20 | FadeOutTime(0.2); 21 | } 22 | 23 | MovieProperties() 24 | { 25 | Name("screen_template"); 26 | FadeInTime(0.1); 27 | FadeOutTime(0.5); 28 | } 29 | 30 | MovieProperties() 31 | { 32 | Name("flythrough_template"); 33 | FadeInTime(1.0); 34 | FadeOutTime(1.0); 35 | } 36 | 37 | 38 | 39 | MovieProperties() 40 | { 41 | Name("preview"); 42 | Inherit("flythrough_template"); 43 | Movie("pre-movie"); 44 | SegmentList() 45 | { 46 | Segment("preview", 1, -1); 47 | } 48 | 49 | } 50 | 51 | MovieProperties() 52 | { 53 | Name("preview-loop"); 54 | Movie("pre-movie"); 55 | FadeInTime(0); 56 | FadeOutTime(0); 57 | SegmentList() 58 | { 59 | Segment("preview-loop", 1, -1); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /MakePreviewMovies/movies/about_movies_folder.txt: -------------------------------------------------------------------------------- 1 | This folder should contain the movies (flyABC1.bik) you are making into preview movies. 2 | -------------------------------------------------------------------------------- /MakePreviewMovies/munge_movies.bat: -------------------------------------------------------------------------------- 1 | ::@echo off 2 | setlocal enabledelayedexpansion 3 | 4 | :: PATH setup 5 | set "MyPath=C:\BF2_ModTools\ToolsFL\bin" 6 | echo %PATH% | findstr /C:"%MyPath%" > nul 7 | if errorlevel 1 set "PATH=%MyPath%;%PATH%" 8 | 9 | for %%b in (movies\*) do ( 10 | set "outputName=%%~nb.mvs" 11 | echo Processing file: %%b to !outputName! 12 | 13 | REM change ext for PC (.bik), PS2 (.pss), XBOX (.xmv) 14 | 15 | REM internally, all movies will be called 'preview' 16 | copy /Y %%b TEMP\preview.bik 17 | 18 | REM Create the movie list 19 | echo TEMP\preview.bik > TEMP\movie_list.mlst 20 | 21 | MovieMunge.exe -input TEMP\movie_list.mlst -output output\!outputName! -checkdate 22 | 23 | ) 24 | 25 | :end 26 | endlocal 27 | -------------------------------------------------------------------------------- /MakePreviewMovies/output/about_output_folder.txt: -------------------------------------------------------------------------------- 1 | This folder will contain the output (xxxfly.mvs) files after they are munged -------------------------------------------------------------------------------- /deploy_lvl/all_weap_hero_hanpistol.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/deploy_lvl/all_weap_hero_hanpistol.lvl -------------------------------------------------------------------------------- /deploy_lvl/cis_tread_tank.lvl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/deploy_lvl/cis_tread_tank.lvl -------------------------------------------------------------------------------- /documentation/Readme.md: -------------------------------------------------------------------------------- 1 | What to know for zero Patch Mod development: 2 | 3 | 1. Fake file system 4 | + Perhaps the most interesting aspect of the zero patch 2.0 is the addition of the 'fake file system' (addon folder only). 5 | + API: 6 | - zero_patch_fs.getFiles(pattern, ext_list) 7 | + Example: 8 | - ```local files = zero_patch_fs.getFiles("custom_gc", {".lvl", ".script"}) -- gets all the .script and .lvl files that have 'custom_gc' names.``` 9 | + Note: 10 | - Files inside folders called 'disabled' are excluded (this is how we can disable scripts). 11 | - Files are sorted when returned. 12 | - Lua has a more limited pattern matching system than most programming languages, 13 | check https://riptutorial.com/lua/example/20315/lua-pattern-matching for more details. 14 | 15 | 2. custom_gc scripts 16 | + Like the 1.3 uop custom_gc scripts are supported and run before the addmes are processed. 17 | + Unlike the 1.3 uop these should be kept in the addon mod folder 18 | + Valid names are: 19 | - ```custom_gc_.lvl``` 20 | - ```custom_gc_.script``` 21 | + Be careful not to be too generic with your script names, as with BF2's scripting system script names must be unique. 22 | - If there are 2 scripts named 'custom_gc_xyz', BF2 will only execute the first one. 23 | 3. user_script scripts 24 | + Like the 1.3 uop user_script scripts are supported and run at ingame time during game_interface execution 25 | (when a mission calls --> ReadDataFile("ingame.lvl") ) 26 | + Unlike the 1.3 uop these should be kept in the addon mod folder 27 | + Valid names are: 28 | - ```user_script_.lvl``` 29 | - ```user_script_.script``` 30 | 31 | 4. Data passed through to ingame from shell. 32 | + Currently we are passing the 'zero_patch_data' table from shell --> ingame. 33 | + This table can hold about 200 characters, so do not pack too much data onto it. 34 | 35 | 5. Mod Menu Tree 36 | + The mod menu tree is intended to offer an easy way to get input from a user. 37 | + What is the Mod Menu Tree? 38 | - It's a list 39 | + What properties do the list items need to have? 40 | - Each item has an id, displayStr and action. 41 | + id: string 42 | + displayStr: can be a const string, localization string id or function that returns a string. 43 | + action: can be a string, function or table 44 | - string --> The mod menu tree will do a 'ScriptCB_PushScreen(action)' 45 | - function --> The mod menu tree will call function(id) 46 | - table --> The mod menu tree will load the contents of the table into the current display. 47 | Not just any table will work, Must be a list of valid menu tree items. 48 | + AddModMenuItem function examples: 49 | ```LUA 50 | AddModMenuItem( "font_test", "font test", "ifs_fonttest") -- adds to the end of the main menu, launches the 'ifs_fonttest' screen when selected 51 | local abc_mod_options={ } 52 | AddModMenuItem( "my_id", "ABC option 1", myFunction1, abc_mod_options) -- adds item to 'abc_mod_options', will call 'myFunction1("my_id")' when selected. 53 | AddModMenu({id="abc_items", displayStr="ABC Mod Items", action=abc_mod_options}) -- will add a parent menu to the end of the top level list, will display children items when selected. 54 | ``` 55 | + What's with 'OptionsSettings'? 56 | - This is a special pattern that should make it easy to present the user with options. 57 | - Call the 'CreateOptionsSetting(mySettingsTable)' function with the appropriate table filled out and then add the result 58 | to the mod menu tree. 59 | - Where 'mySettingsTable' should have a form like: 60 | ```LUA 61 | local item1 = CreateOptionsSetting({ 62 | default = 9, -- > used when target_table[property_name] is nil 63 | target_table = zero_patch_data, -- > the table we'll set the data on 64 | property_name = "my_setting", -- > saved to --> target_table.my_setting 65 | title = "My Setting", 66 | callback = nil, -- > Callback once the data is set 67 | options = {0,1,2,3,4,5,6,7,8,9} -- > List of options to show 68 | }) 69 | AddModMenu(item1) 70 | ``` 71 | + When can I add items to the Mod Menu Tree? 72 | - In your addme; after the '0' addme has been processed the mod menu tree functions will be available. 73 | 6. Full Mod Menu Tree Examples: 74 | + [launch_my_mission](https://github.com/Gametoast/ClassicCollectionModPatch/tree/master/documentation/mod_menu_tree_examples/launch_my_mission) 75 | + [set_alternate_default_path](https://github.com/Gametoast/ClassicCollectionModPatch/tree/master/documentation/mod_menu_tree_examples/set_alternate_default_path) 76 | + [simple_settings](https://github.com/Gametoast/ClassicCollectionModPatch/tree/master/documentation/mod_menu_tree_examples/simple_settings) 77 | 78 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/launch_my_mission/addme.lua: -------------------------------------------------------------------------------- 1 | -- Mod Menu Tree Example usage 2 | -- author: BAD-AL 3 | 4 | -- shows the user a list of missions from the 'The Clone Wars Revised' mod 5 | -- Launches the mission when selected 6 | 7 | if( AddModMenuItem ~= nil) then -- check if ModMenuTree's 'AddModMenuItem' function is available 8 | 9 | if ( ScriptCB_IsFileExist("..\\..\\addon\\995\\data\\_lvl_pc\\mission.lvl") == 1) then -- Check if 'The Clone Wars Revised' mod is available 10 | local function LaunchMyMission(missionName) 11 | print('Function: LaunchMyMission() ' .. 'mission= ' .. missionName) 12 | ScriptCB_SetMissionNames({{Map = missionName, dnldable = nil, Side = 1, SideChar = nil, Team1 = 'team1', Team2 = 'team2'}}, false) 13 | ScriptCB_SetTeamNames(0,0) 14 | ScriptCB_EnterMission() 15 | end 16 | 17 | local my_menu_options = { 18 | -- When the 'action' property is a function; that function will get called with the list item id on selected 19 | {id="tan1l_c4", showstr="Tantive IV TCWR c4", action=LaunchMyMission }, 20 | {id="tan1l_con", showstr="Tantive IV TCWR", action=LaunchMyMission }, 21 | {id="tan1l_ord66", showstr="Tantive IV TCWR Order66", action=LaunchMyMission }, 22 | {id="uta1l_c3", showstr="Uta c3", action=LaunchMyMission }, 23 | {id="uta1l_con", showstr="Uta con", action=LaunchMyMission }, 24 | {id="uta1l_ord66", showstr="Uta order 66", action=LaunchMyMission }, 25 | {id="yav1l_con", showstr="Yav TCWR", action=LaunchMyMission }, 26 | } 27 | 28 | -- Add my options to the 'Mod Launcher' menu tree 29 | AddModMenuItem( "missions", "Launch a TCWR mission", my_menu_options) 30 | else 31 | print("info: TCWR not detected, not adding menus for it.") 32 | end 33 | end -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/launch_my_mission/munge.bat: -------------------------------------------------------------------------------- 1 | md MUNGED 2 | del /Q MUNGED\* 3 | ::C:\BF2_ModTools\ToolsFL\bin\pc_TextureMunge.exe -inputfile $*.tga -checkdate -continue -platform PC -sourcedir . -outputdir MUNGED 4 | 5 | C:\BF2_ModTools\ToolsFL\bin\ScriptMunge.exe -inputfile *.lua -continue -platform PC -sourcedir . -outputdir MUNGED 6 | 7 | ::C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.mcfg -continue -platform PC -sourcedir . -outputdir MUNGED -hashstrings 8 | 9 | ::C:\BF2_ModTools\ToolsFL\bin\levelpack.exe -inputfile addme.req -writefiles MUNGED\addme.files -continue -platform PC -sourcedir . -inputdir MUNGED\ -outputdir . 10 | 11 | move /Y MUNGED\*.script . 12 | move *.log MUNGED 13 | 14 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/pick_and_launch_any_addon_mission/addme.lua: -------------------------------------------------------------------------------- 1 | -- mod menu tree example that adds a menu allowing the user to launch any addon mission 2 | -- Does not localize the showstr for each mission, just uses mission name 3 | -- Author BAD_AL 4 | 5 | print("pick_and_launch\\addme.lua start") 6 | 7 | if( zero_patch_addon_mission_list ~= nil ) then 8 | print("pick_and_launch\\addme.lua add the menu stuff") 9 | 10 | local function LaunchTheMission(missionName) 11 | print('Function: LaunchTheMission() ' .. 'mission= ' .. missionName) 12 | ScriptCB_SetMissionNames({{Map = missionName, dnldable = nil, Side = 1, SideChar = nil, Team1 = 'team1', Team2 = 'team2'}}, false) 13 | ScriptCB_SetTeamNames(0,0) 14 | ScriptCB_EnterMission() 15 | end 16 | 17 | local launch_mission_list = {} 18 | local newEntry = nil 19 | for _,value in zero_patch_addon_mission_list do 20 | -- entry needs to be 3 item list; string1, string2, 21 | -- : 22 | -- string => try to launch a screen named 23 | -- function => call a function called with param 24 | -- list => load a new list of stuff (sub menu) 25 | newEntry = {id=value, showstr=value, action=LaunchTheMission} 26 | table.insert(launch_mission_list, newEntry) 27 | end 28 | 29 | AddModMenuItem( "missions", "Select addon mission to launch", launch_mission_list) 30 | end 31 | print("pick_and_launch\\addme.lua end") 32 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/pick_and_launch_any_addon_mission/munge.bat: -------------------------------------------------------------------------------- 1 | md MUNGED 2 | del /Q MUNGED\* 3 | ::C:\BF2_ModTools\ToolsFL\bin\pc_TextureMunge.exe -inputfile $*.tga -checkdate -continue -platform XBOX -sourcedir . -outputdir MUNGED 4 | 5 | C:\BF2_ModTools\ToolsFL\bin\ScriptMunge.exe -inputfile *.lua -continue -platform XBOX -sourcedir . -outputdir MUNGED 6 | 7 | ::C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.mcfg -continue -platform XBOX -sourcedir . -outputdir MUNGED -hashstrings 8 | 9 | ::C:\BF2_ModTools\ToolsFL\bin\levelpack.exe -inputfile addme.req -writefiles MUNGED\addme.files -continue -platform XBOX -sourcedir . -inputdir MUNGED\ -outputdir . 10 | 11 | move /Y MUNGED\*.script . 12 | move *.log MUNGED 13 | 14 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/set_alternate_default_path/addme.lua: -------------------------------------------------------------------------------- 1 | -- Mod Menu Tree Example usage 2 | -- author: BAD-AL 3 | 4 | -- Settings option that works in conjunction with 'user_script_alternate_default_path.lua' 5 | 6 | -- Looks for different mods that have 'nab2.lvl' and shows those mod folders to the user. 7 | -- When one is selected, will set the 'zero_patch_data.override_path' property, which will get 8 | -- passed through to ingame and will be read by the 'user_script_alternate_default_path' script 9 | -- to do the work. 10 | 11 | if( zero_patch_fs ~= nil and zero_patch_data ~= nil) then -- check if the zero_patch_fs and zero_patch_data are available 12 | print("info: setting up 'Mod Folder Override Path'") 13 | 14 | 15 | -- helper function; 16 | -- try not to pollute the global env with functions no one else will know how to use 17 | -- returns the mod folder name 18 | local function getBaseFolder(filePath) 19 | local pattern = "\\([A-Za-z0-9_-]+)\\data\\" 20 | local s,e, retVal = string.find(filePath, pattern) 21 | return retVal 22 | end 23 | 24 | -- we'll try to get an idea about which mods are installed by getting all the 'nab2.lvl' files 25 | local files = zero_patch_fs.getFiles("nab2.lvl") --> "..\..\addon2\HF2\data\_lvl_common\dea\nab2.lvl" 26 | local options = { "nil",} -- by default let's do nil; that surely won't exist 27 | local current = nil 28 | 29 | for k,v in files do 30 | current = getBaseFolder(v) 31 | if( current ~= nil) then 32 | print("info: current: " .. tostring(current)) 33 | table.insert(options, current) 34 | end 35 | end 36 | 37 | local optionData = { 38 | default = "nil", 39 | target_table = zero_patch_data, 40 | property_name = "override_path", 41 | title = "Mod Folder Override Path", 42 | callback = nil, 43 | options = options, 44 | } 45 | 46 | local menuSettingsItem = CreateOptionsSetting(optionData) 47 | AddModMenu(menuSettingsItem) 48 | print("info: 'Mod Folder Override Path' added to mod menu tree") 49 | else 50 | print("info: can't setup 'Mod Folder Override Path'") 51 | end 52 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/set_alternate_default_path/munge.bat: -------------------------------------------------------------------------------- 1 | md MUNGED 2 | del /Q MUNGED\* 3 | ::C:\BF2_ModTools\ToolsFL\bin\pc_TextureMunge.exe -inputfile $*.tga -checkdate -continue -platform PC -sourcedir . -outputdir MUNGED 4 | 5 | C:\BF2_ModTools\ToolsFL\bin\ScriptMunge.exe -inputfile *.lua -continue -platform PC -sourcedir . -outputdir MUNGED 6 | 7 | ::C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.mcfg -continue -platform PC -sourcedir . -outputdir MUNGED -hashstrings 8 | 9 | ::C:\BF2_ModTools\ToolsFL\bin\levelpack.exe -inputfile addme.req -writefiles MUNGED\addme.files -continue -platform PC -sourcedir . -inputdir MUNGED\ -outputdir . 10 | 11 | move /Y MUNGED\*.script . 12 | move *.log MUNGED 13 | 14 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/set_alternate_default_path/user_script_alternate_default_path.lua: -------------------------------------------------------------------------------- 1 | -- 'user_script' companion to 'set_alternate_path' Mod Menu Tree Example 2 | -- author: BAD-AL 3 | 4 | -- Attempts to override the base game path. 5 | -- Will check to see if there exists a file at the override file path location and if so, will read that file instead. 6 | 7 | if( zero_patch_data ~= nil and zero_patch_data.override_path ~= nil) then -- check for the setting before we attempt to do any of this stuff 8 | print("info: setup alternate default path from addon folder.") 9 | 10 | -- we'll use the more traditional (OG BF2 ) style path here and let the zero patch take care of 11 | -- re-directs for 'addon2' and '_lvl_common' 12 | local base_path = string.format("..\\..\\addon\\%s\\data\\_lvl_pc\\", zero_patch_data.override_path ) 13 | print("base path check -> " .. base_path) 14 | local oldReadDataFile = ReadDataFile 15 | 16 | ReadDataFile = function(...) 17 | local testPath = base_path .. arg[1] 18 | if(ScriptCB_IsFileExist(testPath) == 1 ) then 19 | arg[1] = testPath 20 | print("info: alternate default path re-direct> " .. arg[1]) 21 | end 22 | return oldReadDataFile(unpack(arg)) 23 | end 24 | end -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/simple_settings/addme.lua: -------------------------------------------------------------------------------- 1 | 2 | -- Mod Menu Tree Example usage 3 | -- author: BAD-AL 4 | 5 | -- silly example used to demponstrate an easy way to give the user a choice from an option list. 6 | 7 | print("info: simple_settings\\addme.lua start") 8 | 9 | 10 | if CreateOptionsSetting ~= nil then -- Test to see if the mod menu tree functionality is present 11 | 12 | -- ================================================================================== 13 | -- simple options setting that just sets data on a lua table. 14 | local optionData = { 15 | default = 9, -- > used when target_table[property_name] is nil 16 | target_table = zero_patch_data, -- > the table we'll set the data on (for the 'zero_patch' this will be passed through to ingame) 17 | property_name = "my_iq", -- > saved to --> target_table.my_setting 18 | title = "My IQ", 19 | callback = nil, -- > no callback 20 | options = {0,10,50,80,100,110,130,150,180,900} 21 | } 22 | 23 | local menuSettingsItem = CreateOptionsSetting(optionData) 24 | AddModMenu(menuSettingsItem) 25 | 26 | 27 | -- ================================================================================== 28 | -- options setting that sets data on a lua table and utilizes a callback function . 29 | 30 | -- the callback function for when the setting is made 31 | local function momCallback(option_id) 32 | print("info: Your momn is: " .. tostring(option_id)) 33 | end 34 | 35 | local optionData2 = { 36 | default = "Mazda", 37 | target_table = zero_patch_data, 38 | property_name = "car", 39 | title = "Your car brand is", 40 | callback = momCallback, 41 | options = { 42 | "Toyota", 43 | "Honda", 44 | "Mazda", 45 | "Chevy", 46 | "Ford", 47 | "Tesla", 48 | "Hyundai", 49 | "Volkswagen", 50 | "Some brand owned by a French company", 51 | } 52 | } 53 | local menuSettingsItem2 = CreateOptionsSetting(optionData2) 54 | AddModMenu(menuSettingsItem2) 55 | else 56 | print("the function 'CreateOptionsSetting' for the mod_menu_tree' was not found, can't add settings.") 57 | end 58 | 59 | 60 | print("info: simple_settings\\addme.lua end") 61 | -------------------------------------------------------------------------------- /documentation/mod_menu_tree_examples/simple_settings/munge.bat: -------------------------------------------------------------------------------- 1 | md MUNGED 2 | del /Q MUNGED\* 3 | ::C:\BF2_ModTools\ToolsFL\bin\pc_TextureMunge.exe -inputfile $*.tga -checkdate -continue -platform PC -sourcedir . -outputdir MUNGED 4 | 5 | C:\BF2_ModTools\ToolsFL\bin\ScriptMunge.exe -inputfile *.lua -continue -platform PC -sourcedir . -outputdir MUNGED 6 | 7 | ::C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.mcfg -continue -platform PC -sourcedir . -outputdir MUNGED -hashstrings 8 | 9 | ::C:\BF2_ModTools\ToolsFL\bin\levelpack.exe -inputfile addme.req -writefiles MUNGED\addme.files -continue -platform PC -sourcedir . -inputdir MUNGED\ -outputdir . 10 | 11 | move /Y MUNGED\*.script . 12 | move *.log MUNGED 13 | 14 | -------------------------------------------------------------------------------- /documentation/user_script_examples/alternate_default.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/documentation/user_script_examples/alternate_default.7z -------------------------------------------------------------------------------- /documentation/user_script_examples/alternate_default/README.txt: -------------------------------------------------------------------------------- 1 | For use with the zero patch 2.0 2 | 3 | Use this 'alternate_default' folder to install mod stuff into instead of overwriting your 4 | base game files. 5 | 6 | Place in your addon/addon2 folder 7 | 8 | 9 | -BAD_AL 10 | -------------------------------------------------------------------------------- /documentation/user_script_examples/alternate_default/data/put_files_in_here.txt: -------------------------------------------------------------------------------- 1 | For Classic collection, put under a '_lvl_common' folder. 2 | 3 | If using the zero patch with OG BF2, place in a '_lvl_pc' folder. 4 | -------------------------------------------------------------------------------- /documentation/user_script_examples/alternate_default/disabled/Disable_scripts_by_moving_them_here.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/documentation/user_script_examples/alternate_default/disabled/Disable_scripts_by_moving_them_here.txt -------------------------------------------------------------------------------- /documentation/user_script_examples/alternate_default/munge.bat: -------------------------------------------------------------------------------- 1 | md MUNGED 2 | del /Q MUNGED\* 3 | ::C:\BF2_ModTools\ToolsFL\bin\pc_TextureMunge.exe -inputfile $*.tga -checkdate -continue -platform PC -sourcedir . -outputdir MUNGED 4 | 5 | C:\BF2_ModTools\ToolsFL\bin\ScriptMunge.exe -inputfile *.lua -continue -platform PC -sourcedir . -outputdir MUNGED 6 | 7 | ::C:\BF2_ModTools\ToolsFL\bin\ConfigMunge.exe -inputfile $*.mcfg -continue -platform PC -sourcedir . -outputdir MUNGED -hashstrings 8 | 9 | ::C:\BF2_ModTools\ToolsFL\bin\levelpack.exe -inputfile addme.req -writefiles MUNGED\addme.files -continue -platform PC -sourcedir . -inputdir MUNGED\ -outputdir . 10 | 11 | move /Y MUNGED\*.script . 12 | move *.log MUNGED 13 | -------------------------------------------------------------------------------- /documentation/user_script_examples/alternate_default/user_script_alternate_default.lua: -------------------------------------------------------------------------------- 1 | -- user_script_alternate_default.lua 2 | -- place the files you'd like to use as 'alternate' default files under this folder. 3 | -- author: BAD-AL 4 | 5 | -- Attempts to override the base game path. 6 | -- Will check to see if there exists a file at the override file path location and if so, will read that file instead. 7 | print("info: setup alternate default path from addon folder.") 8 | 9 | -- we'll use the more traditional (OG BF2 ) style path here and let the zero patch take care of 10 | -- re-directs for 'addon2' and '_lvl_common' 11 | local base_path = "..\\..\\addon\\alternate_default\\data\\_lvl_pc\\" 12 | print("base path check -> " .. base_path) 13 | local oldReadDataFile = ReadDataFile 14 | 15 | ReadDataFile = function(...) 16 | local testPath = base_path .. arg[1] 17 | if(ScriptCB_IsFileExist(testPath) == 1 ) then 18 | arg[1] = testPath 19 | print("info: alternate default path re-direct> " .. arg[1]) 20 | end 21 | return oldReadDataFile(unpack(arg)) 22 | end 23 | 24 | 25 | local oldOpenAudioStream = OpenAudioStream 26 | OpenAudioStream = function(...) 27 | local testPath = base_path .. arg[1] 28 | if(ScriptCB_IsFileExist(testPath) == 1 ) then 29 | arg[1] = testPath 30 | print("info: alternate default path re-direct> " .. arg[1]) 31 | end 32 | return oldOpenAudioStream(unpack(arg)) 33 | end 34 | 35 | local oldAudioStreamAppendSegments = AudioStreamAppendSegments 36 | AudioStreamAppendSegments = function(...) 37 | local testPath = base_path .. arg[1] 38 | if(ScriptCB_IsFileExist(testPath) == 1 ) then 39 | arg[1] = testPath 40 | print("info: alternate default path re-direct> " .. arg[1]) 41 | end 42 | return oldAudioStreamAppendSegments(unpack(arg)) 43 | end 44 | -------------------------------------------------------------------------------- /munge.bat: -------------------------------------------------------------------------------- 1 | 2 | 3 | :: Building the patch/mod/thing 4 | :: This folder should be under your BF2_ModTools Folder. 5 | :: Run this munge.bat file to build the addme and 'patch_ingame.lvl''. 6 | 7 | md MUNGED 8 | del /Q MUNGED\* 9 | :: currently no textures are used. 10 | ..\ToolsFL\bin\pc_TextureMunge.exe -inputfile $*.tga -continue -platform PC -sourcedir src\textures -outputdir MUNGED 11 | 12 | ..\ToolsFL\bin\ScriptMunge.exe -inputfile $*.lua -continue -platform PC -sourcedir src\scripts -outputdir MUNGED 13 | 14 | ..\ToolsFL\bin\configmunge.exe -inputfile $*.fx -continue -platform PC -sourcedir src\effects -outputdir MUNGED 15 | 16 | :: currently no config files are used. 17 | ::..\ToolsFL\bin\ConfigMunge.exe -inputfile $*.mcfg -continue -platform PC -sourcedir src\config -outputdir MUNGED -hashstrings 18 | 19 | ..\ToolsFL\bin\levelpack.exe -inputfile addme.req -writefiles MUNGED\addme.files -continue -platform PC -sourcedir src -inputdir MUNGED\ -outputdir . 20 | 21 | ..\ToolsFL\bin\levelpack.exe -inputfile patch_ingame.req -writefiles MUNGED\patch_ingame.files -continue -platform PC -sourcedir src -inputdir MUNGED\ -outputdir . 22 | 23 | ..\ToolsFL\bin\levelpack.exe -inputfile patch_shell.req -writefiles MUNGED\patch_shell.files -continue -platform PC -sourcedir src -inputdir MUNGED\ -outputdir . 24 | 25 | move *.log MUNGED 26 | 27 | md "0" 28 | del "0/*" 29 | copy /Y MUNGED\globals.script "0\patch_scripts\" 30 | copy /Y MUNGED\patch_paths.script "0\patch_scripts\" 31 | copy /Y patch_shell.lvl "0\patch_scripts\" 32 | copy /Y patch_ingame.lvl "0\patch_scripts\patch_ingame.lvl" 33 | copy /Y MUNGED\user_script_*.script "0\in-game-options\" 34 | copy /Y deploy_lvl\*.lvl "0\in-game-options\" 35 | copy /Y .\addme.lvl "0\addme.script" 36 | 37 | copy /y readme.md "0\readme.txt" 38 | time /t -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | [comment]: <> (VS Code markdown preview -> Ctrl+k, v) 3 | # Classic Collection Mod Patch 4 | 5 | ## GitHub repo 6 | https://github.com/Gametoast/ClassicCollectionModPatch 7 | 8 | ## Videos 9 | - [Windows install](https://youtu.be/fgMN85mlxiI) 10 | - [Steamdeck install](https://youtu.be/3Cg_rfeJIZA) 11 | - [Mod Developer guide](https://youtu.be/8G9ApaOUGpE) 12 | 13 | ## Compatibility 14 | This patch is compatible with 15 | - Battlefront Classic Collection (BF2) [Steamdeck, Switch, Windows] 16 | - Original Star Wars Battlefront II [steam, GOG, DVD] 17 | 18 | But please note that most mods released in the last 20 years have not been tested against the Battlefront Classic Collection and some may encounter issues that this patch cannot solve. 19 | 20 | ## Goals of Patch 21 | 1. Make it easy to install 'standard' and 1.3 uop based mods/addons to Battldfront Classic Collection (BF2) 22 | 2. Minimize the destruction/overwrite of game files. 23 | 3. Support as many features of the 1.3 uop as possible. 24 | 4. Be compatible with all versions of the (base) game. [XBOX, PS2 & PSP versions are untested with this patch, but should be compatible] 25 | 26 | ## Initial Patch Application (initial setup requires 'virgin' setup; i.e. ingame.lvl, shell.lvl, common.lvl should be the originals) 27 | 1. Place the '0' folder in your addon2 folder. 28 | 2. Copy your 'data2\\_lvl_common\\commom.lvl' file to the '0\\base_common.lvl_goes_in_here\\' folder 29 | 3. Run the 'Apply_patch.bat' file to create a 'patched' common.lvl (saved to '0\\common.lvl') 30 | 4. Copy the '0\common.lvl' file back over to your Game's 'data2/_lvl_common/' folder 31 | 5. Copy the (platform specific) 'run_after_making_changes_xxx' script to the addon2 folder (different script flavors for Windows, Linux/steamdeck, switch). 32 | 33 | What this does-> replaces a file inside common.lvl that we can use as a hook into the game's runtime. 34 | 35 | #### Note: 36 | If you remove the '0' folder, you should basically be running an un-patched game; becasue none of the 'zero patch' runtime adjustments/modification will be performed if that '0' folder is gone. 37 | 38 | ## Adding mods/addons & making file changes 39 | 1. Place the downloaded mod into the 'addon2' (or 'addon') folder 40 | 2. Run the 'run_after_making_changes_xxx' script ( windows, switch & linux/steamdeck supported) 41 | 3. Note: Some mods have 'custom_gc' or 'user_script' files that go along with them. Put these in the mod folder that they belong to. If a mod has some scripts that should not be run, create a 'disabled' folder for that mod and place them inside that 'disabled' folder. Files inside a 'disabled' folder will be ignored. 42 | 43 | #### What the 'run_after_making_changes_xxx' script does: 44 | 1. Renames the '_lvl_pc' folders to '_lvl_common' inside the addon folders (the Game seems to be able to better reference the dlc files that way) 45 | 2. Creates a file that is read in at runtime that acts as a 'fake file system' (this gives the system the ability to easily find mod scripts and allows for better naming of scripts too). 46 | 47 | The 'Switch' specific script also re-names all the files and folders under 'addon2/' to be lower case. 48 | 49 | ## Steamdeck users 50 | Q: Do I need to 'chmod +x' the 'run_after_making_changes_linux.sh' script? 51 | 52 | A: The .sh file will need to be 'executable'; this can be done with ```chmod +x run_after_making_changes_linux.sh``` 53 | or by other means. Once it has been made 'executable' it should run by double-clicking or by calling it through the terminal. 54 | 55 | Q: Do I need to run the Apply_Patch.bat script on a Windows machine? 56 | 57 | A: No, it may be most easy to copy the common.lvl file over to windows to do the apply_patch.bat step, but it can also be accomnplished through 'Bottles' as the Steamdeck install video demonstrates. 58 | 59 | ## Is this different than the 1.3 UOP? 60 | Yes. We do try to make it feature compatible with the 1.3 UOP. But not all features are possible, 'Freecam' is not supported in BF CC, so we're out of luck there. 61 | But if you are seeing some 1.3 UOP features not present or not working then please file a bug on the [GitHub repo issues page](https://github.com/Gametoast/ClassicCollectionModPatch/issues). 62 | 63 | ## Releases 64 | Releases will be in the ['Releases' portion of this repo](https://github.com/Gametoast/ClassicCollectionModPatch/releases). 65 | 66 | ### Battlefront 1 modding notes 67 | As of initial release (14 March 2024) the BF1 CC game seems to have switched from Lua 4.0 to using Lua 5.0.2. Unless the go back to Lua 4.0, it means that every BF1 mod will need to be re-built. :( 68 | 69 | ### Battlefront 2 (classic collection) mod developer notes 70 | * 'ReadDataFile' and 'OpenAudioStream' do work with the 'dc:' file prefix from an addon's '_lvl_common' folder. 71 | * With regards to ScriptCB_IsFileExist and 'ReadDataFile' and the other functions that reference files, we will re-direct to the appropriate location if we detect 'addon\\' or '\\_lvl_pc\\' in the path being referenced. 72 | 73 | ### Building the 'zero patch' 74 | Put/clone this git repo under your 'BF2_ModTools\\' Folder for the 'munge' to work. 75 | 76 | 77 | The following calls have not yet been checked with the 'dc:' prefix: 78 | 1. ScriptCB_OpenMovie 79 | 2. PlayAudioStream 80 | 3. PlayAudioStreamUsingProperties 81 | 82 | 83 | ### Zero Patch development Team 84 | - BAD-AL 85 | - MileHighGuy (milehighguy-dev) 86 | - S1thK3nny 87 | - marth8880 88 | - iamastupid/imashaymin 89 | - AnthonyBF2 90 | - BK2-modder 91 | - Rayman1103 92 | 93 | ### Zero Patch Test Team 94 | - Burumaru 95 | - lui 96 | 97 | ### Legacy Contributors 98 | - Pandemic 99 | - GTAnakin 100 | - Zerted 101 | 102 | 103 | 104 | # July 26 2024 Release notes (Release 2.1) 105 | 106 | - Intended for use with Aspyr Update 3 (20 June 2024) 107 | 108 | Mod compatibility Game Settings 109 | For increased compatibility with mods make the following changes to your video settings 110 | 111 | Options > Video Settings > (X) Custom Options: 112 | - Lighting Quality - Low 113 | - Lignt Bloom - Off 114 | 115 | |Added fixes for:| | | 116 | |----------------|------|----| 117 | | | Aspyr broke mod localization (this fix is imperfect, but usually works). | user_script_loc_fix.script | 118 | | | Aspyr broke in-game-music for most mods. | user_script_cc_music_fix.script | 119 | | | Command Posts 1 and 4 aren't counted as valid CPs for Rhen Var Citadel Conquest.| user_script_rhenvar2_cp_fix.script | 120 | | | Command Posts 1 and 4 aren't counted as valid CPs for Rhen Var Citadel Conquest.| user_script_rhenvar2_cp_fix.script | 121 | | | Adjust to Aspyr making AI heroes work in-game. | removed 'user_script_ai_hero_support.script' | 122 | 123 | 124 | 125 | |Added features:| | | 126 | |---------------|---|---| 127 | | | Option to keep Aspyr instant action screen (0\shell-options\use_0_patch_instant_action_screen.txt)| | 128 | | | Windows users now have option to use batch file to copy over common.script for the patch.| | 129 | | | Locals get yellow command posts. user_script_locals_yellow_team.script| | 130 | | | Keep heroes out of turrets. user_script_ai_hero_no_turret.script| | 131 | | | Play hero voices when entering/exiting the Battlefield. user_script_hero_vo.script| | 132 | | | Fake Console - Made specific commands only appear depending on if a controller was used to open the Fake Console menu or mouse & keyboard.| | 133 | | | Fake Console - All team specific commands will now display the team name in brackets.| | 134 | | | Fake Console - Added support to automatically check the number of teams when building the command list.| | 135 | | | Fake Console - Updated the "Reset Carried Flags" command to automatically set the flag names instead of using manually set names.| | 136 | | | Fake Console - Added various Command Post related commands if playing Conquest mode.| | 137 | | | Fake Console - Added Kit Fisto and Assajj Ventress to the list of Hero commands.| | 138 | | | Fake Console - Minor spelling errors were fixed in a few commands.| | 139 | 140 | # July 27 2024 Release notes (Release 2.2) 141 | |Added fixes for:| | | 142 | |----------------|------|-----| 143 | | | Aspyr broke Han Solo| user_script_han_fix.script + all_weap_hero_hanpistol.lvl | 144 | 145 | # Aug 9 2024 Release notes (Release 2.3) 146 | |Added fixes for:| | | 147 | |----------------|------|-----| 148 | | | fix for snail tank treads not moving| user_script_tread_tank_fix.script + cis_tread_tank.lvl | 149 | | | BF1 mod localization | addon1/0 | 150 | -------------------------------------------------------------------------------- /src/addme.req: -------------------------------------------------------------------------------- 1 | ucft 2 | { 3 | REQN 4 | { 5 | "script" 6 | "addme" 7 | "ifs_missionselect_console" 8 | "ifs_mod_menu_tree" 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /src/effects/sfo_kmsx.fx: -------------------------------------------------------------------------------- 1 | ParticleEmitter("Cracks") 2 | { 3 | MaxParticles(1, 1); 4 | StartDelay(0, 0); 5 | BurstDelay(0.001000, 0.001000); 6 | BurstCount(1, 1); 7 | MaxLodDist(1100); 8 | MinLodDist(1000); 9 | BoundingRadius(5); 10 | SoundName(""); 11 | NoRegisterStep(); 12 | Size(1, 1); 13 | Hue(255, 255); 14 | Saturation(255, 255); 15 | Value(255, 255); 16 | Alpha(255, 255); 17 | Spawner() 18 | { 19 | Spread() 20 | { 21 | PositionX(0, 1); 22 | PositionY(0.033000, 0.033000); 23 | PositionZ(1.500000, 1.500000); 24 | } 25 | 26 | Offset() 27 | { 28 | PositionX(0, 0); 29 | PositionY(0.080000, 0.080000); 30 | PositionZ(0, 0); 31 | } 32 | 33 | PositionScale(0, 0); 34 | VelocityScale(0, 0); 35 | InheritVelocityFactor(0, 0); 36 | Size(0, 0.100000, 0.100000); 37 | Hue(0, 0, 0); 38 | Saturation(0, 0, 0); 39 | Value(0, 10, 10); 40 | Alpha(0, 255, 255); 41 | StartRotation(0, -90, 0); 42 | RotationVelocity(0, 0, 0); 43 | FadeInTime(0); 44 | } 45 | 46 | Transformer() 47 | { 48 | LifeTime(1.750000); 49 | Position() 50 | { 51 | LifeTime(0.500000); 52 | } 53 | 54 | Size(0) 55 | { 56 | LifeTime(0.050000); 57 | Scale(2.500000); 58 | } 59 | 60 | Color(0) 61 | { 62 | LifeTime(1.500000); 63 | Move(0, 0, 0, 255); 64 | Next() 65 | { 66 | LifeTime(0.500000); 67 | Move(0, 0, 0, -255); 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | Geometry() 75 | { 76 | BlendMode("NORMAL"); 77 | Type("BILLBOARD"); 78 | Texture("com_sfx_scorchmark"); 79 | } 80 | 81 | ParticleEmitter("Pebbles") 82 | { 83 | MaxParticles(20, 20); 84 | StartDelay(0, 0); 85 | BurstDelay(0.001000, 0.001000); 86 | BurstCount(20, 20); 87 | MaxLodDist(50); 88 | MinLodDist(10); 89 | BoundingRadius(5); 90 | SoundName(""); 91 | NoRegisterStep(); 92 | Size(1, 1); 93 | Hue(255, 255); 94 | Saturation(255, 255); 95 | Value(255, 255); 96 | Alpha(255, 255); 97 | Spawner() 98 | { 99 | Circle() 100 | { 101 | PositionX(-1, 1); 102 | PositionY(1, 3); 103 | PositionZ(-1, 1); 104 | } 105 | 106 | Offset() 107 | { 108 | PositionX(0, 0); 109 | PositionY(-1, -1); 110 | PositionZ(0, 0); 111 | } 112 | 113 | PositionScale(1, 1); 114 | VelocityScale(1, 5); 115 | InheritVelocityFactor(0, 0); 116 | Size(0, 0.002000, 0.004000); 117 | Hue(0, 0, 20); 118 | Saturation(0, 20, 80); 119 | Value(0, 10, 80); 120 | Alpha(0, 255, 255); 121 | StartRotation(0, 0, 255); 122 | RotationVelocity(0, 255, 975); 123 | FadeInTime(0); 124 | } 125 | 126 | Transformer() 127 | { 128 | LifeTime(2); 129 | Position() 130 | { 131 | LifeTime(2); 132 | Accelerate(0, -10, 0); 133 | } 134 | 135 | Size(0) 136 | { 137 | LifeTime(0.050000); 138 | Scale(15); 139 | } 140 | 141 | Color(0) 142 | { 143 | LifeTime(1.500000); 144 | Move(0, 0, 0, 255); 145 | } 146 | 147 | } 148 | 149 | Geometry() 150 | { 151 | BlendMode("NORMAL"); 152 | Type("PARTICLE"); 153 | Texture("com_sfx_explosion2"); 154 | } 155 | 156 | ParticleEmitter("Smoke") 157 | { 158 | MaxParticles(2, 2); 159 | StartDelay(0, 0); 160 | BurstDelay(0.050000, 0.050000); 161 | BurstCount(2, 2); 162 | MaxLodDist(2100); 163 | MinLodDist(2000); 164 | BoundingRadius(30); 165 | SoundName(""); 166 | Size(1, 1); 167 | Hue(255, 255); 168 | Saturation(255, 255); 169 | Value(255, 255); 170 | Alpha(255, 255); 171 | Spawner() 172 | { 173 | Spread() 174 | { 175 | PositionX(-0.500000, 0.500000); 176 | PositionY(0.500000, 1); 177 | PositionZ(-0.500000, 0.500000); 178 | } 179 | 180 | Offset() 181 | { 182 | PositionX(0, 0); 183 | PositionY(0, 0); 184 | PositionZ(0, 0); 185 | } 186 | 187 | PositionScale(0.200000, 0.200000); 188 | VelocityScale(0.500000, 0.500000); 189 | InheritVelocityFactor(0, 0); 190 | Size(0, 0.200000, 0.450000); 191 | Red(0, 64, 64); 192 | Green(0, 64, 64); 193 | Blue(0, 64, 64); 194 | Alpha(0, 128, 128); 195 | StartRotation(0, -90, 0); 196 | RotationVelocity(0, -100, 100); 197 | FadeInTime(0); 198 | } 199 | 200 | Transformer() 201 | { 202 | LifeTime(3); 203 | Position() 204 | { 205 | LifeTime(3); 206 | Scale(0); 207 | } 208 | 209 | Size(0) 210 | { 211 | LifeTime(0.500000); 212 | Scale(3); 213 | } 214 | 215 | Color(0) 216 | { 217 | LifeTime(3); 218 | Reach(32, 32, 32, 0); 219 | } 220 | 221 | } 222 | 223 | Geometry() 224 | { 225 | BlendMode("NORMAL"); 226 | Type("PARTICLE"); 227 | Texture("com_sfx_smoke3"); 228 | } 229 | 230 | ParticleEmitter("Flare") 231 | { 232 | MaxParticles(1, 1); 233 | StartDelay(0, 0); 234 | BurstDelay(0.010000, 0.010000); 235 | BurstCount(1, 1); 236 | MaxLodDist(2100); 237 | MinLodDist(2000); 238 | BoundingRadius(30); 239 | SoundName(""); 240 | Size(1, 1); 241 | Hue(255, 255); 242 | Saturation(255, 255); 243 | Value(255, 255); 244 | Alpha(255, 255); 245 | Spawner() 246 | { 247 | Spread() 248 | { 249 | PositionX(0, 0); 250 | PositionY(0, 0); 251 | PositionZ(0, 0); 252 | } 253 | 254 | Offset() 255 | { 256 | PositionX(0, 0); 257 | PositionY(0, 0); 258 | PositionZ(0, 0); 259 | } 260 | 261 | PositionScale(0, 0); 262 | VelocityScale(0, 0); 263 | InheritVelocityFactor(0, 0); 264 | Size(0, 0.500000, 0.500000); 265 | Hue(0, 20, 30); 266 | Saturation(0, 50, 100); 267 | Value(0, 255, 255); 268 | Alpha(0, 255, 255); 269 | StartRotation(0, -90, 0); 270 | RotationVelocity(0, -100, 100); 271 | FadeInTime(0); 272 | } 273 | 274 | Transformer() 275 | { 276 | LifeTime(0.100000); 277 | Position() 278 | { 279 | LifeTime(2); 280 | Scale(0); 281 | } 282 | 283 | Size(0) 284 | { 285 | LifeTime(0.100000); 286 | Scale(3); 287 | } 288 | 289 | Color(0) 290 | { 291 | LifeTime(0.100000); 292 | Move(0, 0, 0, -255); 293 | } 294 | 295 | } 296 | 297 | Geometry() 298 | { 299 | BlendMode("ADDITIVE"); 300 | Type("PARTICLE"); 301 | Texture("com_sfx_flashball2"); 302 | } 303 | 304 | ParticleEmitter("Fire") 305 | { 306 | MaxParticles(4, 4); 307 | StartDelay(0, 0); 308 | BurstDelay(0.010000, 0.010000); 309 | BurstCount(1, 1); 310 | MaxLodDist(2100); 311 | MinLodDist(2000); 312 | BoundingRadius(5); 313 | SoundName(""); 314 | Size(1, 1); 315 | Hue(255, 255); 316 | Saturation(255, 255); 317 | Value(255, 255); 318 | Alpha(255, 255); 319 | Spawner() 320 | { 321 | Spread() 322 | { 323 | PositionX(-0.500000, 0.500000); 324 | PositionY(0.500000, 1); 325 | PositionZ(-0.500000, 0.500000); 326 | } 327 | 328 | Offset() 329 | { 330 | PositionX(0, 0); 331 | PositionY(0, 0); 332 | PositionZ(0, 0); 333 | } 334 | 335 | PositionScale(0, 0); 336 | VelocityScale(1, 2); 337 | InheritVelocityFactor(0, 0); 338 | Size(0, 0.050000, 0.100000); 339 | Red(0, 255, 255); 340 | Green(0, 255, 255); 341 | Blue(0, 255, 255); 342 | Alpha(0, 255, 255); 343 | StartRotation(0, 0, 360); 344 | RotationVelocity(0, -100, 100); 345 | FadeInTime(0); 346 | } 347 | 348 | Transformer() 349 | { 350 | LifeTime(0.300000); 351 | Position() 352 | { 353 | LifeTime(0.500000); 354 | Scale(0); 355 | } 356 | 357 | Size(0) 358 | { 359 | LifeTime(0.300000); 360 | Scale(5); 361 | } 362 | 363 | Color(0) 364 | { 365 | LifeTime(0.100000); 366 | Reach(255, 255, 255, 255); 367 | Next() 368 | { 369 | LifeTime(0.200000); 370 | Reach(255, 255, 255, 0); 371 | } 372 | 373 | } 374 | 375 | } 376 | 377 | Geometry() 378 | { 379 | BlendMode("ADDITIVE"); 380 | Type("PARTICLE"); 381 | Texture("com_sfx_explosion4"); 382 | } 383 | 384 | } 385 | 386 | } 387 | 388 | } 389 | 390 | } 391 | 392 | } 393 | 394 | -------------------------------------------------------------------------------- /src/patch_ingame.req: -------------------------------------------------------------------------------- 1 | ucft 2 | { 3 | 4 | REQN 5 | { 6 | "config" 7 | "sfo_kmsx" 8 | } 9 | 10 | REQN 11 | { 12 | "script" 13 | "patch_ingame" 14 | "popup_prompt" 15 | "utility_functions2" 16 | "fakeconsole_functions" 17 | "ifs_fakeconsole_zero_patch" 18 | "ifs_ingame_log" 19 | "zero_patch_fs" 20 | } 21 | 22 | REQN 23 | { 24 | "texture" 25 | "border_3_pieces" 26 | "border_3a_pieces" 27 | "border_popup" 28 | "opaque_rect" 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/patch_shell.req: -------------------------------------------------------------------------------- 1 | ucft 2 | { 3 | REQN 4 | { 5 | "script" 6 | "patch_shell" 7 | "zero_patch_fs" 8 | "ifs_ingame_log" 9 | } 10 | 11 | REQN 12 | { 13 | "texture" 14 | "border_3_pieces" 15 | "border_3a_pieces" 16 | "border_popup" 17 | "opaque_rect" 18 | } 19 | } -------------------------------------------------------------------------------- /src/scripts/addme.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: deprecated 2 | -- STAR WARS BATTLEFRONT CLASSIC COLLECTION - Old Mod Patcher 3 | -- Greetings from Kenny 4 | 5 | local __scriptName__ = "[zero_patch: addme.script]: " 6 | 7 | print("zero_patch: Start 0/addme.script") 8 | 9 | 10 | if(printf == nil) then 11 | function printf (...) print(string.format(unpack(arg))) end 12 | end 13 | 14 | if( tprint == nil ) then 15 | function getn(v) 16 | local v_type = type(v); 17 | if v_type == "table" then 18 | return table.getn(v); 19 | elseif v_type == "string" then 20 | return string.len(v); 21 | else 22 | return; 23 | end 24 | end 25 | 26 | if string.starts == nil then 27 | function string.starts(str, Start) 28 | return string.sub(str, 1, string.len(Start)) == Start; 29 | end 30 | end 31 | 32 | function tprint(t, indent) 33 | if not indent then indent = 1, print(tostring(t) .. " {") end 34 | if t then 35 | for key,value in pairs(t) do 36 | if not string.starts(tostring(key), "__") then 37 | local formatting = string.rep(" ", indent) .. tostring(key) .. "= "; 38 | if value and type(value) == "table" then 39 | print(formatting .. --[[tostring(value) ..]] " {") 40 | tprint(value, indent+1); 41 | else 42 | if(type(value) == "string") then 43 | --print(formatting .."'" .. tostring(value) .."'" ..",") 44 | printf("%s'%s',",formatting, tostring(value)) 45 | else 46 | print(formatting .. tostring(value) ..",") 47 | end 48 | end 49 | end 50 | end 51 | print(string.rep(" ", indent - 1) .. "},") 52 | end 53 | end 54 | end 55 | 56 | --- 57 | 58 | -- functionality to add strings 59 | if( modStringTable == nil ) then 60 | modStringTable = {} -- table to hold custom strings 61 | 62 | -- function to add custom strings 63 | function addModString(stringId, content) 64 | modStringTable[stringId] = ScriptCB_tounicode(content) 65 | end 66 | 67 | if oldScriptCB_getlocalizestr == nil then 68 | -- Overwrite 'ScriptCB_getlocalizestr()' to first check for the strings we added 69 | print("redefine: ScriptCB_getlocalizestr() ") 70 | 71 | oldScriptCB_getlocalizestr = ScriptCB_getlocalizestr 72 | ScriptCB_getlocalizestr = function (...) 73 | local stringId = " " 74 | if( table.getn(arg) > 0 ) then 75 | stringId = arg[1] 76 | end 77 | if( modStringTable[stringId] ~= nil) then -- first check 'our' strings 78 | retVal = modStringTable[stringId] 79 | else 80 | retVal = oldScriptCB_getlocalizestr( unpack(arg) ) 81 | end 82 | return retVal 83 | end 84 | end 85 | end 86 | -- Force 'IFText_fnSetString' to use strings from our 'modStringTable' too 87 | if ( oldIFText_fnSetString == nil )then 88 | oldIFText_fnSetString = IFText_fnSetString 89 | IFText_fnSetString = function(...) 90 | if( table.getn(arg) > 1 and modStringTable[arg[2]] ~= nil ) then 91 | arg[2] = modStringTable[arg[2]] 92 | IFText_fnSetUString(unpack(arg)) 93 | return 94 | end 95 | oldIFText_fnSetString(unpack(arg)) 96 | end 97 | end 98 | 99 | addModString("ifs.console.action","Instant Action (alt)") 100 | -- 101 | ----- ADD uop eras & modes-------------------------------------------------------------------------------------- 102 | print("zero_patch: Add UOP Eras and Game Modes: Start") 103 | uopMapEras = { 104 | { Team2Name= 'common.sides.cis.name', key= 'era_c', icon2= 'rep_icon', subst= 'c', showstr= 'common.era.cw', icon1= 'cis_icon', Team1Name= 'common.sides.rep.name', }, 105 | { Team2Name= 'common.sides.imp.name', key= 'era_g', icon2= 'all_icon', subst= 'g', showstr= 'common.era.gcw', icon1= 'imp_icon', Team1Name= 'common.sides.all.name', }, 106 | { Team2Name= 'common.sides.k.name', key= 'era_k', icon2= 'kotor_icon', subst= 'k', showstr= 'common.era.k', icon1= 'k_icon', Team1Name= 'common.sides.k.name', }, 107 | { Team2Name= 'common.sides.n.name', key= 'era_n', icon2= 'newrep_icon', subst= 'n', showstr= 'common.era.n', icon1= 'n_icon', Team1Name= 'common.sides.n.name', }, 108 | { Team2Name= 'common.sides.y.name', key= 'era_y', icon2= 'yuz_icon', subst= 'y', showstr= 'common.era.y', icon1= 'y_icon', Team1Name= 'common.sides.y.name', }, 109 | { Team2Name= 'common.sides.a.name', key= 'era_a', icon2= 'bfx_cw_icon', subst= 'a', showstr= 'common.era.a', icon1= 'a_icon', Team1Name= 'common.sides.a.name', }, 110 | { Team2Name= 'common.sides.b.name', key= 'era_b', icon2= 'bfx_gcw_icon', subst= 'b', showstr= 'common.era.b', icon1= 'b_icon', Team1Name= 'common.sides.b.name', }, 111 | { Team2Name= 'common.sides.d.name', key= 'era_d', icon2= 'newsithwars_icon', subst= 'd', showstr= 'common.era.d', icon1= 'd_icon', Team1Name= 'common.sides.d.name', }, 112 | { Team2Name= 'common.sides.e.name', key= 'era_e', icon2= 'earth_icon', subst= 'e', showstr= 'common.era.e', icon1= 'e_icon', Team1Name= 'common.sides.e.name', }, 113 | { Team2Name= 'common.sides.f.name', key= 'era_f', icon2= 'front_icon', subst= 'f', showstr= 'common.era.f', icon1= 'f_icon', Team1Name= 'common.sides.f.name', }, 114 | { Team2Name= 'common.sides.h.name', key= 'era_h', icon2= 'halo_icon', subst= 'h', showstr= 'common.era.h', icon1= 'h_icon', Team1Name= 'common.sides.h.name', }, 115 | { Team2Name= 'common.sides.i.name', key= 'era_i', icon2= 'i_icon', subst= 'i', showstr= 'common.era.i', icon1= 'i_icon', Team1Name= 'common.sides.i.name', }, 116 | { Team2Name= 'common.sides.j.name', key= 'era_j', icon2= 'j_icon', subst= 'j', showstr= 'common.era.j', icon1= 'j_icon', Team1Name= 'common.sides.j.name', }, 117 | { Team2Name= 'common.sides.l.name', key= 'era_l', icon2= 'lego_icon', subst= 'l', showstr= 'common.era.l', icon1= 'l_icon', Team1Name= 'common.sides.l.name', }, 118 | { Team2Name= 'common.sides.m.name', key= 'era_m', icon2= 'imp_icon', subst= 'm', showstr= 'common.era.m', icon1= 'm_icon', Team1Name= 'common.sides.m.name', }, 119 | { Team2Name= 'common.sides.o.name', key= 'era_o', icon2= 'oldsith_icon', subst= 'o', showstr= 'common.era.o', icon1= 'o_icon', Team1Name= 'common.sides.o.name', }, 120 | { Team2Name= 'common.sides.p.name', key= 'era_p', icon2= 'rep_icon', subst= 'p', showstr= 'common.era.p', icon1= 'p_icon', Team1Name= 'common.sides.p.name', }, 121 | { Team2Name= 'common.sides.q.name', key= 'era_q', icon2= 'all_icon', subst= 'q', showstr= 'common.era.q', icon1= 'q_icon', Team1Name= 'common.sides.q.name', }, 122 | { Team2Name= 'common.sides.r.name', key= 'era_r', icon2= 'rvb_icon', subst= 'r', showstr= 'common.era.r', icon1= 'r_icon', Team1Name= 'common.sides.r.name', }, 123 | { Team2Name= 'common.sides.s.name', key= 'era_s', icon2= 'rebirth_icon', subst= 's', showstr= 'common.era.s', icon1= 's_icon', Team1Name= 'common.sides.s.name', }, 124 | { Team2Name= 'common.sides.t.name', key= 'era_t', icon2= 'toys_icon', subst= 't', showstr= 'common.era.t', icon1= 't_icon', Team1Name= 'common.sides.t.name', }, 125 | { Team2Name= 'common.sides.u.name', key= 'era_u', icon2= 'u_icon', subst= 'u', showstr= 'common.era.u', icon1= 'u_icon', Team1Name= 'common.sides.u.name', }, 126 | { Team2Name= 'common.sides.v.name', key= 'era_v', icon2= 'v_icon', subst= 'v', showstr= 'common.era.v', icon1= 'v_icon', Team1Name= 'common.sides.v.name', }, 127 | { Team2Name= 'common.sides.w.name', key= 'era_w', icon2= 'wacky_icon', subst= 'w', showstr= 'common.era.w', icon1= 'w_icon', Team1Name= 'common.sides.w.name', }, 128 | { Team2Name= 'common.sides.x.name', key= 'era_x', icon2= 'exGCW_icon', subst= 'x', showstr= 'common.era.x', icon1= 'x_icon', Team1Name= 'common.sides.x.name', }, 129 | { Team2Name= 'common.sides.z.name', key= 'era_z', icon2= 'z_icon', subst= 'z', showstr= 'common.era.z', icon1= 'z_icon', Team1Name= 'common.sides.z.name', }, 130 | { Team2Name= 'common.sides.1.name', key= 'era_1', icon2= 'cis_icon', subst= '1', showstr= 'common.era.1', icon1= '1_icon', Team1Name= 'common.sides.1.name', }, 131 | { Team2Name= 'common.sides.2.name', key= 'era_2', icon2= 'imp_icon', subst= '2', showstr= 'common.era.2', icon1= '2_icon', Team1Name= 'common.sides.2.name', }, 132 | } 133 | 134 | 135 | 136 | uopMapModes = { 137 | { key= 'mode_con', subst= 'con', showstr= 'modename.name.con', descstr= 'modename.description.con', icon= 'mode_icon_con', }, 138 | { key= 'mode_ctf', subst= 'ctf', showstr= 'modename.name.ctf', descstr= 'modename.description.ctf', icon= 'mode_icon_2ctf', }, 139 | { key= 'mode_1flag', subst= '1flag', showstr= 'modename.name.1flag', descstr= 'modename.description.1flag', icon= 'mode_icon_ctf', }, 140 | { key= 'mode_assault', subst= 'ass', showstr= 'modename.name.spa-assault', descstr= 'modename.description.assault', icon= 'mode_icon_ass', }, 141 | { key= 'mode_hunt', subst= 'hunt', showstr= 'modename.name.hunt', descstr= 'modename.description.hunt', icon= 'mode_icon_hunt', }, 142 | { key= 'mode_eli', subst= 'eli', showstr= 'modename.name.hero-assault', descstr= 'modename.description.elimination', icon= 'mode_icon_eli', }, 143 | { key= 'mode_tdm', subst= 'tdm', showstr= 'modename.name.tdm', descstr= 'modename.description.tdm', icon= 'mode_icon_tdm', }, 144 | { key= 'mode_xl', subst= 'xl', showstr= 'modename.name.xl', descstr= 'modename.description.xl', icon= 'mode_icon_xl', }, 145 | { key= 'mode_obj', subst= 'obj', showstr= 'modename.name.obj', descstr= 'modename.description.obj', icon= 'mode_icon_obj', }, 146 | { key= 'mode_c', subst= 'c', showstr= 'modename.name.c', descstr= 'modename.description.c', icon= 'mode_icon_c', }, 147 | { key= 'mode_uber', subst= 'uber', showstr= 'modename.name.uber', descstr= 'modename.description.uber', icon= 'mode_icon_uber', }, 148 | { key= 'mode_bf1', subst= 'bf1', showstr= 'modename.name.bf1', descstr= 'modename.description.bf1', icon= 'mode_icon_bf1', }, 149 | { key= 'mode_holo', subst= 'holo', showstr= 'modename.name.holo', descstr= 'modename.description.holo', icon= 'mode_icon_holo', }, 150 | { key= 'mode_ord66', subst= 'ord66', showstr= 'modename.name.ord66', descstr= 'modename.description.ord66', icon= 'mode_icon_ord66', }, 151 | { key= 'mode_dm', subst= 'dm', showstr= 'modename.name.dm', descstr= 'modename.description.dm', icon= 'mode_icon_dm', }, 152 | { key= 'mode_space', subst= 'space', showstr= 'modename.name.space', descstr= 'modename.description.space', icon= 'mode_icon_space', }, 153 | { key= 'mode_c1', subst= 'c1', showstr= 'modename.name.c1', descstr= 'modename.description.c1', icon= 'mode_icon_c1', }, 154 | { key= 'mode_c2', subst= 'c2', showstr= 'modename.name.c2', descstr= 'modename.description.c2', icon= 'mode_icon_c2', }, 155 | { key= 'mode_c3', subst= 'c3', showstr= 'modename.name.c3', descstr= 'modename.description.c3', icon= 'mode_icon_c3', }, 156 | { key= 'mode_c4', subst= 'c4', showstr= 'modename.name.c4', descstr= 'modename.description.c4', icon= 'mode_icon_c4', }, 157 | { key= 'mode_hctf', subst= 'hctf', showstr= 'modename.name.hctf', descstr= 'modename.description.hctf', icon= 'mode_icon_hctf', }, 158 | { key= 'mode_vhcon', subst= 'vhcon', showstr= 'modename.name.vhcon', descstr= 'modename.description.vhcon', icon= 'mode_icon_vehicle', }, 159 | { key= 'mode_vhtdm', subst= 'vhtdm', showstr= 'modename.name.vhtdm', descstr= 'modename.description.vhtdm', icon= 'mode_icon_vehicle', }, 160 | { key= 'mode_vhctf', subst= 'vhctf', showstr= 'modename.name.vhctf', descstr= 'modename.description.vhctf', icon= 'mode_icon_vehicle', }, 161 | { key= 'mode_avh', subst= 'avh', showstr= 'modename.name.avh', descstr= 'modename.description.avh', icon= 'mode_icon_avh', }, 162 | { key= 'mode_lms', subst= 'lms', showstr= 'modename.name.lms', descstr= 'modename.description.lms', icon= 'mode_icon_lms', }, 163 | { key= 'mode_vh', subst= 'vh', showstr= 'modename.name.vh', descstr= 'modename.description.vh', icon= 'mode_icon_vehicle', }, 164 | { key= 'mode_race', subst= 'race', showstr= 'modename.name.race', descstr= 'modename.description.race', icon= 'mode_icon_race', }, 165 | { key= 'mode_koh', subst= 'koh', showstr= 'modename.name.koh', descstr= 'modename.description.koh', icon= 'mode_icon_koh', }, 166 | { key= 'mode_tdf', subst= 'tdf', showstr= 'modename.name.tdf', descstr= 'modename.description.tdf', icon= 'mode_icon_tdf', }, 167 | { key= 'mode_surv', subst= 'surv', showstr= 'modename.name.surv', descstr= 'modename.description.surv', icon= 'mode_icon_survival', }, 168 | { key= 'mode_rpg', subst= 'rpg', showstr= 'modename.name.rpg', descstr= 'modename.description.rpg', icon= 'mode_icon_rpg', }, 169 | { key= 'mode_wav', subst= 'wav', showstr= 'modename.name.wav', descstr= 'modename.description.wav', icon= 'mode_icon_wav', }, 170 | { key= 'mode_ctrl', subst= 'ctrl', showstr= 'modename.name.ctrl', descstr= 'modename.description.ctrl', icon= 'mode_icon_control', }, 171 | { key= 'mode_seige', subst= 'seige', showstr= 'modename.name.seige', descstr= 'modename.description.seige', icon= 'mode_icon_siege', }, 172 | { key= 'mode_siege', subst= 'siege', showstr= 'modename.name.siege', descstr= 'modename.description.siege', icon= 'mode_icon_siege', }, 173 | { key= 'mode_jhu', subst= 'jhu', showstr= 'modename.name.jhu', descstr= 'modename.description.jhu', icon= 'mode_icon_jhu', }, 174 | { key= 'mode_wea', subst= 'wea', showstr= 'modename.name.wea', descstr= 'modename.description.wea', icon= 'mode_icon_wea', }, 175 | { key= 'mode_ins', subst= 'ins', showstr= 'modename.name.ins', descstr= 'modename.description.ins', icon= 'mode_icon_ins', }, 176 | } 177 | 178 | function zero_patch_AddEra(entry) 179 | if( entry.key ~= nil and entry.showstr ~= nil and entry.subst ~= nil and 180 | entry.Team1Name ~= nil and entry.Team2Name ~= nil ) then 181 | ---------- check if it's already present ---------- 182 | for key,value in gMapEras do -- check if entry is already present 183 | if( value.key == entry.key) then 184 | print("zero_patch_AddEra(): Era with key '".. value.key .. "' is already present.") 185 | return 186 | end 187 | end 188 | if(entry.icon1 == nil) then 189 | print("zero_patch_AddEra: Warning, adding era without property 'icon1'") 190 | end 191 | --------------------------------------------------- 192 | table.insert( gMapEras, entry ) 193 | print("zero_patch_AddEra(): added Era: " .. tostring(entry.key)) 194 | else 195 | print("zero_patch_AddEra: Error adding Era. Must specify properties [key, showstr, subst, Team1Name, Team2Name ]\n" .. 196 | "See 'gMapEras' (missionlist.lua) to see format of existing eras.") 197 | end 198 | end 199 | function zero_patch_AddGameMode(entry) 200 | if( entry.key ~= nil and entry.showstr ~= nil and entry.descstr ~= nil and entry.subst ~= nil ) then 201 | ---------- check if it's already present ---------- 202 | for key,value in gMapModes do 203 | if( value.key == entry.key) then 204 | print("zero_patch_AddGameMode(): Mode with key '".. value.key .. "' is already present.") 205 | return 206 | end 207 | end 208 | if(entry.icon == nil) then 209 | print("zero_patch_AddGameMode: Warning, adding game mode without property 'icon'") 210 | end 211 | --------------------------------------------------- 212 | table.insert( gMapModes, entry ) 213 | print("zero_patch_AddGameMode(): added Era: " .. tostring(entry.key)) 214 | else 215 | print("zero_patch_AddGameMode: Error adding Game mode. Must specify [key, showstr, descstr, subst]\n" .. 216 | "See 'gMapModes' to see format of existing Game mode entries.") 217 | end 218 | end 219 | 220 | if( custom_GetSPMissionList == nil ) then -- will be nil if the game doesn't have the 1.3 un-official patch 221 | print("zero_patch: Add UOP Eras and Game Modes: Start") 222 | local i = 1 223 | local limit = table.getn(uopMapEras) 224 | while i < limit do 225 | zero_patch_AddEra(uopMapEras[i]) 226 | i = i + 1 227 | end 228 | 229 | i = 1 230 | limit = table.getn(uopMapModes) 231 | while i < limit do 232 | zero_patch_AddGameMode(uopMapModes[i]) 233 | i = i + 1 234 | end 235 | print("zero_patch: Add UOP Eras and Game Modes: End") 236 | else 237 | print("zero_patch: WARNING! zero_patch is not compatible with UOP!!!") 238 | end 239 | 240 | -- A place to attach settings to. 241 | -- TODO: Implement Save Settings to .gc file 242 | zero_patch_data = { greeting = "Hello"} 243 | 244 | ScriptCB_DoFile("ifs_missionselect_console") 245 | ScriptCB_DoFile("ifs_mod_menu_tree") 246 | 247 | -- from ifs_instant_top.lua 248 | --ScriptCB_SetIFScreen("ifs_missionselect") 249 | 250 | function OverrideInstantAction() 251 | local old_ScriptCB_SetIFScreen = ScriptCB_SetIFScreen 252 | ScriptCB_SetIFScreen = function(...) 253 | local screen_name = arg[1] 254 | print("ScriptCB_SetIFScreen: ".. screen_name) 255 | if(screen_name == "ifs_missionselect" ) then 256 | arg[1] = "ifs_missionselect_console" 257 | end 258 | return old_ScriptCB_SetIFScreen(unpack(arg)) 259 | end 260 | end 261 | if( ScriptCB_IsFileExist("..\\..\\addon2\\0\\patch_scripts\\patch_paths.script") == 1 ) then 262 | -- only do this for classic collection 263 | -- give user option to not use the replacement instant action screen 264 | if( ScriptCB_IsFileExist("..\\..\\addon2\\0\\shell-options\\use_0_patch_instant_action_screen.txt") == 1 ) then 265 | print("overriding instant action screen") 266 | OverrideInstantAction() 267 | end 268 | end 269 | 270 | function HandleCustomGC(tag) 271 | if custom_PressedGCButton(tag) ~= nil then 272 | ifelm_shellscreen_fnPlaySound(this.acceptSound) 273 | ifs_movietrans_PushScreen(ifs_freeform_main) 274 | end 275 | end 276 | 277 | -- Temp Solution? for Custom GC 278 | local addon_gc_list = custom_GetGCButtonList() 279 | local display_text = "" 280 | local custom_gc_count = table.getn(addon_gc_list) 281 | if( custom_gc_count > 0 ) then 282 | print("info: addon_gc_list.count: " .. custom_gc_count) 283 | local gc_menu = {} 284 | -- from gc docs -> local ourButton = { tag = gcTag, string = gcString, } 285 | for _, value in addon_gc_list do 286 | display_text = value.string 287 | if(string.find(display_text, "%.")) then -- try to localize 288 | print("info: try to localize " .. value.string) 289 | display_text = ScriptCB_ununicode( ScriptCB_getlocalizestr(display_text)) 290 | if(display_text) then 291 | display_text = display_text 292 | end 293 | else 294 | display_text = value.string 295 | end 296 | AddModMenuItem(value.tag, display_text, HandleCustomGC, gc_menu) 297 | end 298 | AddModMenuItem("gc_menu", "Custom GC Mods ", gc_menu) 299 | end 300 | 301 | AddModMenuItem( "IA", "Instant Action (alt)", "ifs_missionselect_console") 302 | AddModMenuItem( "IA", "Instant Action", "ifs_missionselect") 303 | AddModMenuItem( "campaignList", "ifs.sp.campaign", "ifs_sp_briefing") 304 | AddModMenuItem( "font_test", "font test", "ifs_fonttest") 305 | AddModMenuItem("spacetraining", "Space Training", "ifs_spacetraining") -- add back in a way to get to this 306 | AddModMenuItem("ifs_ingame_log", "Debug Log", "ifs_ingame_log") 307 | 308 | ifs_mod_menu_tree.SaveSettings = function(this) 309 | print("TODO: Implement Save Settings") 310 | end 311 | 312 | -- keep track of the addon missions, for fun. 313 | zero_patch_addon_mission_list = {} 314 | 315 | -- keep track of mission count 316 | __ADDDOWNLOADABLECONTENT_COUNT__ = 0 -- same name from uop 317 | local oldAddDownloadableContent = AddDownloadableContent 318 | AddDownloadableContent = function(mapLuaFile, missionName, defaultMemoryModelPlus) 319 | __ADDDOWNLOADABLECONTENT_COUNT__ = __ADDDOWNLOADABLECONTENT_COUNT__ + 1 320 | table.insert(zero_patch_addon_mission_list,missionName) 321 | return oldAddDownloadableContent(mapLuaFile, missionName, defaultMemoryModelPlus) 322 | end 323 | 324 | -- stringify a table into "key:value;" pairs. 325 | -- Won't truncate a key/value pair but will stop serializing key-value pairs 326 | -- when the limit is reached. 327 | local function stringify(data, maxSize) 328 | local retVal = "" 329 | for key, value in data do 330 | retVal = retVal .. string.format("%s:%s;", key,value) 331 | if( maxSize ~= nil and string.len(retVal) > maxSize)then 332 | print("stringify maxSize reached.") 333 | break 334 | end 335 | end 336 | return retVal 337 | end 338 | 339 | -- Plumb through 'zero_patch_data' to ingame 340 | local zeroPatch_original_ScriptCB_EnterMission = ScriptCB_EnterMission 341 | ScriptCB_EnterMission = function() 342 | print("ScriptCB_EnterMission ") 343 | local missionSetup = { } 344 | if ScriptCB_IsMissionSetupSaved() then 345 | missionSetup = ScriptCB_LoadMissionSetup() 346 | end 347 | -- attach the data 348 | missionSetup.zero_patch_data = stringify(zero_patch_data, 200) 349 | ScriptCB_SaveMissionSetup(missionSetup) 350 | print("ScriptCB_EnterMission calling orig ScriptCB_EnterMission ...") 351 | zeroPatch_original_ScriptCB_EnterMission() 352 | end 353 | 354 | local orig_ScriptCB_SaveMissionSetup = ScriptCB_SaveMissionSetup 355 | ScriptCB_SaveMissionSetup = function(missionSetup) 356 | if( missionSetup~= nil and missionSetup.zero_patch_data == nil and zero_patch_data ~= nil) then 357 | missionSetup.zero_patch_data = stringify(zero_patch_data) 358 | end 359 | return orig_ScriptCB_SaveMissionSetup(missionSetup) 360 | end 361 | 362 | print("info: platform> " .. ScriptCB_GetPlatform() ) 363 | print("zero_patch: End 0/addme.script") 364 | -------------------------------------------------------------------------------- /src/scripts/globals.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (c) 2005 Pandemic Studios, LLC. All rights reserved. 3 | -- 4 | 5 | -- Tuning values for the game, broken off into a lua script so that 6 | -- people can muck with them easily w/o editing the game exe directly. 7 | 8 | -- Note: items in here are just shoved in lua globals. Items added 9 | -- will require the exe to be modified to do something with them. 10 | -- TwoPI = 6.28318 5307179586476925286766559 11 | --Current anim times: (goes by the lockinput time 12 | --Land = .5 13 | --ProneToStand = 1.33 14 | --Dive (needs adjusting, its too fast right now) = .5f 15 | --crouchToProne = 1.33 16 | 17 | 18 | RollLeft = { 19 | Size = 3, --number of points interpolated between current math.max is 6, each point MUST have a vec,slop,ore,rslope and time associated with it 20 | 21 | Vec = { 22 | {-0.1,-0.2,0.0}, -- x y z, offset from the camera's position (keep in mind the camera's position moves on its own as well) 23 | {0.0,-0.3,0.0}, 24 | {0.1,0.0,0.0}, 25 | }, 26 | 27 | Slope = { 28 | {0.4, 0.3, 0.0}, --x y z the slope of the points, adjusts the curve 29 | {0.2, -0.3, 0.0}, 30 | {0.0, 0.0, 0.0}, 31 | }, 32 | 33 | Ore = { 34 | {0.0, 0.0, 0.0}, --euler angles, x y z 35 | {0.0, 0.0, 0.0}, --two pi 36 | {0.0, 0.0, 0.0}, 37 | }, 38 | 39 | RSlope = { 40 | {0.0, 0.0, 0.0}, -- slope of the rotation, adjusts the rotation interpolation 41 | {0.0, 0.0, 0.0}, 42 | {0.0, 0.0, 0.0}, 43 | }, 44 | 45 | Time = { 46 | .3, .5, .3 --times, in magical floating point frontline time. 47 | }, 48 | } 49 | 50 | RollRight = { 51 | Size = 3, 52 | 53 | Vec = { 54 | {0.1,-0.2,0.0}, 55 | {0.0,-0.3,0.0}, 56 | {-0.1,0.0,0.0}, 57 | }, 58 | 59 | Slope = { 60 | {0.4, 0.3, 0.0}, 61 | {0.2, -0.3, 0.0}, 62 | {0.0, 0.0, 0.0}, 63 | }, 64 | 65 | Ore = { 66 | {0.0, 0.0, 0.0}, 67 | {0.0, 0.0, -0.0}, 68 | {0.0, 0.0, -0.0}, 69 | }, 70 | 71 | RSlope = { 72 | {0.0, 0.0, 0.0}, -- slope of the rotation, adjusts the rotation interpolation 73 | {0.0, 0.0, 0.0}, 74 | {0.0, 0.0, 0.0}, 75 | }, 76 | Time = { 77 | .3, .5, .3 78 | }, 79 | } 80 | 81 | Crouch = { 82 | Size = 3, 83 | 84 | Vec = { 85 | {0.0, -1.0, 0.0}, 86 | {0.0, -0.0, 0.0}, 87 | {0.0, 0.0, 0.0}, 88 | }, 89 | 90 | Slope = { 91 | {0.0, 0.2, 0.0}, 92 | {0.0, -0.2, 0.0}, 93 | {0.0, 0.0, 0.0}, 94 | }, 95 | 96 | Ore = { 97 | {0.0, 0.0, 0.0}, 98 | {0.0, 0.0, 0.0}, 99 | {0.0, 0.0, 0.0}, 100 | }, 101 | 102 | RSlope = { 103 | {0.0, 0.0, 0.5}, 104 | {0.0, 0.0, 0.3}, 105 | {0.0, 0.0, 0.4}, 106 | }, 107 | 108 | Time = { 109 | .2, .1, .2 110 | }, 111 | } 112 | 113 | 114 | CrouchToProne = { 115 | Size = 3, 116 | 117 | Vec = { 118 | {0.0,-0.0,0.4}, 119 | {0.0, 0.0, 0.2}, 120 | {0.0, 0.0, 0.0}, 121 | }, 122 | 123 | Slope = { 124 | {0.0, 0.0, 0.0}, 125 | {0.0, -0.0, 0.0}, 126 | {0.0, 0.0, 0.0}, 127 | }, 128 | 129 | Ore = { 130 | {-0.4, 0.0, 0.0}, 131 | {-0.2, 0.0, 0.0}, 132 | {0.0, 0.0, 0.0}, 133 | }, 134 | 135 | RSlope = { 136 | {0.0, 0.0, 0.3}, 137 | {0.0, 0.0, -0.3}, 138 | {0.0, 0.0, 0.0}, 139 | }, 140 | 141 | Time = { 142 | .6, .5, .5 143 | }, 144 | } 145 | 146 | 147 | Land = { 148 | Size = 3, 149 | 150 | Vec = { 151 | {0.0,-1.0,0.0}, 152 | {0.0, 0.2, 0.0}, 153 | {0.0, 0.0, 0.0}, 154 | }, 155 | 156 | Slope = { 157 | {0.0, 0.2, 0.0}, 158 | {0.0, -0.2, 0.0}, 159 | {0.0, 0.0, 0.0}, 160 | }, 161 | 162 | Ore = { 163 | {0.0, 0.0, 0.0}, 164 | {0.0, 0.0, 0.0}, 165 | {0.0, 0.0, 0.0}, 166 | }, 167 | 168 | RSlope = { 169 | {0.0, 0.0, 0.0}, 170 | {0.0, 0.0, 0.0}, 171 | {0.0, 0.0, 0.0}, 172 | }, 173 | 174 | Time = { 175 | .17, .17, .17 176 | }, 177 | } 178 | 179 | StandToProne = { -- Note this never actually gets called, game logic currently has the person jump to the crouch state b4 going prone 180 | Size = 3, 181 | 182 | Vec = { 183 | {0.0,-0.0,0.4}, 184 | {0.0, 0.0, 0.2}, 185 | {0.0, 0.0, 0.0}, 186 | }, 187 | 188 | Slope = { 189 | {0.0, 0.0, 0.0}, 190 | {0.0, -0.0, 0.0}, 191 | {0.0, 0.0, 0.0}, 192 | }, 193 | 194 | Ore = { 195 | {-0.4, 0.0, 0.0}, 196 | {-0.2, 0.0, 0.0}, 197 | {0.0, 0.0, 0.0}, 198 | }, 199 | 200 | RSlope = { 201 | {0.0, 0.0, 0.3}, 202 | {0.0, 0.0, -0.3}, 203 | {0.0, 0.0, 0.0}, 204 | }, 205 | 206 | Time = { 207 | .6, .5, .5 208 | }, 209 | } 210 | 211 | ProneToStand = { 212 | Size = 3, 213 | 214 | Vec = { 215 | {0.0,-0.2,0.4}, 216 | {0.0, 0.0, 0.2}, 217 | {0.0, 0.0, 0.0}, 218 | }, 219 | 220 | Slope = { 221 | {0.0, 0.0, 0.0}, 222 | {0.0, -0.2, 0.0}, 223 | {0.0, 0.0, 0.0}, 224 | }, 225 | 226 | Ore = { 227 | {-0.6, 0.0, 0.0}, 228 | {-0.2, 0.0, 0.0}, 229 | {0.0, 0.0, 0.0}, 230 | }, 231 | 232 | RSlope = { 233 | {0.0, 0.0, 0.3}, 234 | {0.0, 0.0, -0.3}, 235 | {0.0, 0.0, 0.0}, 236 | }, 237 | 238 | Time = { 239 | .6, .5, .4 240 | }, 241 | } 242 | 243 | CrouchToStand = { 244 | Size = 3, 245 | 246 | Vec = { 247 | {0.0,0.2,0.0}, 248 | {0.0, 0.0, 0.0}, 249 | {0.0, 0.0, 0.0}, 250 | }, 251 | 252 | Slope = { 253 | {0.0, -0.1, 0.0}, 254 | {0.0, 0.1, 0.0}, 255 | {0.0, 0.0, 0.0}, 256 | }, 257 | 258 | Ore = { 259 | {0.0, 0.0, 0.0}, 260 | {0.0, 0.0, 0.0}, 261 | {0.0, 0.0, 0.0}, 262 | }, 263 | 264 | RSlope = { 265 | {0.0, 0.0, -0.5}, 266 | {0.0, 0.0, -0.3}, 267 | {0.0, 0.0, -0.4}, 268 | }, 269 | 270 | Time = { 271 | .12, .12, .12 272 | }, 273 | } 274 | 275 | --Rumble Region Junk 276 | RumbleSmall = { 277 | ---In A math.min/math.max string.format 278 | Interval = { 4.0 , 15.0 }, --how often the rumble gets set off 279 | Light = {1.0, 1.0}, 280 | Heavy = {0.0,.2}, 281 | HeavyDecay = { .5,.5 }, 282 | LightDecay = {.5, .5 }, 283 | DelayLight = {0.0, 0.0 }, 284 | DelayHeavy = { 0.0, 0.0 }, 285 | TimeLeftHeavy = {1.0, 1.0}, --how long each rumble lasts 286 | TimeLeftLight = {0.5, 1.0}, 287 | ShakeAmt = { 0.5 , 1.0 }, 288 | ShakeLen = { 0.5, 1.5 }, --Camera shake length 289 | FXName = "Doesn'tWorkYet", --the particle effect that gets triggered, is read in, but not used yet. 290 | 291 | } 292 | RumbleMedium = { 293 | ---In A math.min/math.max string.format 294 | Interval = { 4.0 , 15.0 }, --how often the rumble gets set off 295 | Light = {1.0, 1.0}, 296 | Heavy = {.2,.4}, 297 | HeavyDecay = { .5,.5 }, 298 | LightDecay = {.5, .5 }, 299 | DelayLight = {0.0, 0.0 }, 300 | DelayHeavy = { 0.0, 0.0 }, 301 | TimeLeftHeavy = {1.0, 1.0}, --how long each rumble lasts 302 | TimeLeftLight = {1.0, 1.0}, 303 | ShakeAmt = { 1.5 , 2.5 }, 304 | ShakeLen = { 0.5, 1.5 }, --Camera shake length 305 | FXName = "Doesn'tWorkYet", --the particle effect that gets triggered, is read in, but not used yet. 306 | 307 | } 308 | 309 | RumbleLarge = { 310 | ---In A math.min/math.max string.format 311 | Interval = { 4.0 , 15.0 }, --how often the rumble gets set off 312 | Light = {1.0, 1.0}, 313 | Heavy = {.6,.8}, 314 | HeavyDecay = { .5,.5 }, 315 | LightDecay = {.5, .5 }, 316 | DelayLight = {0.0, 0.0 }, 317 | DelayHeavy = { 0.0, 0.0 }, 318 | TimeLeftHeavy = {1.0, 1.0}, --how long each rumble lasts 319 | TimeLeftLight = {1.0, 1.0}, 320 | ShakeAmt = { 3.0 , 4.0 }, 321 | ShakeLen = { 0.5, 1.5 }, --Camera shake length 322 | FXName = "Doesn'tWorkYet", --the particle effect that gets triggered, is read in, but not used yet. 323 | 324 | } 325 | 326 | GAME_VERSION = "SWBF2_OG" 327 | GLOBALS_DEBUG = "" 328 | 329 | local addon_path = "..\\..\\addon\\" 330 | if(ScriptCB_IsFileExist("..\\..\\addon2\\0\\patch_scripts\\patch_paths.script") == 1) then 331 | print("globals: This Collection is truly the classic one") 332 | addon_path = "..\\..\\addon2\\" 333 | GAME_VERSION = "SWBF2_CC" 334 | else 335 | print("globals: This is not classic ") 336 | end 337 | 338 | 339 | -- Enter patch stuff! 340 | local target = addon_path .. "0\\patch_scripts\\patch_paths.script" 341 | if (ScriptCB_IsFileExist(target) == 1 and addon_path == "..\\..\\addon2\\" ) then 342 | ReadDataFile(target) 343 | ScriptCB_DoFile("patch_paths") 344 | GLOBALS_DEBUG = GLOBALS_DEBUG .. " patch_paths called" 345 | end 346 | 347 | -- since globals runs in Shell and ingame, we call out selectively to setup 'patching' 348 | -- for the respective lua env 349 | if ( ScriptInit ~= nil ) then -- this will exist at mission script execution/ingame time 350 | -- this should run during game_interface execution 351 | local target = addon_path .. "0\\patch_scripts\\patch_ingame.lvl" 352 | if(ScriptCB_IsFileExist(target) == 1 ) then 353 | print("globals: call 'patch_ingame'") 354 | ReadDataFile(target) 355 | ScriptCB_DoFile("patch_ingame") 356 | GLOBALS_DEBUG = GLOBALS_DEBUG .. " patch_ingame called" 357 | end 358 | else 359 | -- running at shell time 360 | local target = addon_path .. "0\\patch_scripts\\patch_shell.lvl" 361 | if(ScriptCB_IsFileExist(target) == 1 ) then 362 | print("globals: call 'patch_shell'") 363 | ReadDataFile(target) 364 | ScriptCB_DoFile("patch_shell") 365 | GLOBALS_DEBUG = GLOBALS_DEBUG .. " patch_shell called" 366 | end 367 | end -------------------------------------------------------------------------------- /src/scripts/ifs_ingame_log.lua: -------------------------------------------------------------------------------- 1 | -- begin comment 2 | --[[ 3 | SWBF2 2005 Lua script 4 | created by MileHighGuy from Gametoast.com 5 | 6 | to see the entry point of the code, search for "ifs_ingame_log = NewIFShellScreen" 7 | 8 | Singleplayer only for now. 9 | if you want to make it work in multiplayer, 10 | you at least have to change the events 11 | which push the screen in addInGameKeyScreen 12 | it might be possible 13 | 14 | TO INSTALL 15 | 16 | paste this at the top of your mission script (above the other DoFile calls) 17 | your mission script is like ABCc_con.lua 18 | 19 | --BEGIN COPY/PASTE 20 | 21 | local IGK_originalDoFile = ScriptCB_DoFile 22 | ScriptCB_DoFile = function(filename) 23 | 24 | if(filename == "ifs_pausemenu") then 25 | print("DEBUG: IGK: loading filename ifs_ingame_log") 26 | IGK_originalDoFile("ifs_ingame_log") 27 | 28 | IGK_addButtonToPauseMenu() 29 | 30 | print("DEBUG: IGK: loading filename " .. tostring(filename)) 31 | IGK_originalDoFile(filename) 32 | ScriptCB_DoFile = IGK_originalDoFile 33 | return 34 | else 35 | print("DEBUG: IGK: loading filename " .. tostring(filename)) 36 | IGK_originalDoFile(filename) 37 | end 38 | end 39 | 40 | --END COPY/PASTE 41 | 42 | paste this in your mission ScriptInit() function, 43 | after it loads the ReadDataFile("ingame.lvl") 44 | 45 | addInGameKeyScreen() 46 | 47 | also add ifs_ingame_log to the mission.req 48 | 49 | that's all! 50 | 51 | --]] -- end comment 52 | 53 | print("DEBUG: added ingame screen") 54 | 55 | -- Helper function. Given a layout (x,y,width, height), returns a 56 | -- fully-built item. 57 | function ingamekeys_Listbox_CreateItem(layout) 58 | 59 | local insidewidth = layout.width - 20; 60 | -- Make a coordinate system pegged to the top-left of where the cursor would go. 61 | local Temp = NewIFContainer { 62 | x = layout.x - 0.5 * insidewidth, 63 | y = layout.y + 2, 64 | --bInertPos = 1, --- what's this do ??? 65 | bHotspot = true, 66 | fHotspotH = layout.height, 67 | fHotspotW = layout.width, 68 | fHotspotX = 0, 69 | fHotspotY = -layout.height /2, 70 | } 71 | local FontHeight = ingamekeys_listbox_layout.fontheight 72 | Temp.showstr = NewIFText{ 73 | x = 10, 74 | y = FontHeight * -0.5, 75 | textw = insidewidth, 76 | texth = layout.height, 77 | halign = "left", valign = "vcenter", 78 | font = ingamekeys_listbox_layout.font, 79 | ColorR= 255, ColorG = 255, ColorB = 255, 80 | style = "normal", 81 | nocreatebackground=1, 82 | inert_all = 1, 83 | } 84 | return Temp 85 | end 86 | 87 | -- Helper function. For a destination item (previously created w/ 88 | -- CreateItem), fills it in with data, which may be nil (==blank it) 89 | function ingamekeys_Listbox_PopulateItem(Dest, Data, bSelected, iColorR, iColorG, iColorB, fAlpha) 90 | if(Data) then 91 | -- Contents to show. Do so. 92 | if(gBlankListbox) then 93 | IFText_fnSetString(Dest.showstr,"") -- reduce glyphcache usage 94 | else 95 | IFText_fnSetString(Dest.showstr,Data.ShowStr) 96 | end 97 | 98 | IFObj_fnSetColor(Dest.showstr, iColorR, iColorG, iColorB) 99 | IFObj_fnSetAlpha(Dest.showstr, fAlpha) 100 | end 101 | 102 | IFObj_fnSetVis(Dest,Data) -- Show if there are contents 103 | end 104 | 105 | local scrnW, scrnH = ScriptCB_GetScreenInfo() 106 | ingamekeys_listbox_layout = { 107 | -- Height is calculated from yHeight, Spacing, showcount. 108 | yHeight = 22, 109 | ySpacing = 0, 110 | showcount = 20, 111 | bCreateSlider = true, 112 | font = "gamefont_tiny", 113 | 114 | --width = 900,--320, 115 | width = scrnW *0.7, 116 | x = 0, 117 | slider = 1, 118 | CreateFn = ingamekeys_Listbox_CreateItem, 119 | PopulateFn = ingamekeys_Listbox_PopulateItem, 120 | } 121 | 122 | -- the actual contents of the listbox we 123 | -- display in the corner of the screen during gameplay 124 | gInGameDebugList = { 125 | [1] = { ShowStr = "first entry" }, 126 | [2] = { ShowStr = "time " .. tostring(ScriptCB_GetMissionTime())}, 127 | } 128 | 129 | 130 | -- just tell if an alphanumeric key was pressed 131 | local function isKey(pressedKeyEnum, neededKeyName) 132 | 133 | local success, pressedKeyName = pcall(function() 134 | return string.char(pressedKeyEnum) 135 | end) 136 | -- will not succeed for SHIFT, CTRL, ALT 137 | if success then 138 | neededKeyName = string.upper(neededKeyName) 139 | pressedKeyName = string.upper(pressedKeyName) 140 | --print("DEBUG: pressed key " .. tostring(pressedKeyName) 141 | --.. " needed key " .. tostring(neededKeyName)) 142 | 143 | return neededKeyName == pressedKeyName 144 | else 145 | -- will return false for non-alphanumeric keys 146 | return false 147 | --could do this, but too lazy 148 | --local bShift, bCtrl = ScriptCB_GetKeyboardPCFlags() 149 | end 150 | end 151 | 152 | -- this is the "Entry Point" of the code 153 | -- The IF screen that will create the list box 154 | ifs_ingame_log = NewIFShellScreen { 155 | version = 1.0, 156 | nologo = 1, 157 | bAcceptIsSelect = 0, 158 | movieIntro = nil, -- played before the screen is displayed 159 | movieBackground = nil, -- played while the screen is displayed 160 | bDimBackdrop = nil, 161 | bNohelptext_backPC = 1, 162 | 163 | -- if we access this from pause menu 164 | fromPauseMenu = nil, 165 | 166 | -- set this when you want to hide and show the in-game list box 167 | bShowBox = true, 168 | -- holds previous value 169 | bShowBoxOld = true, 170 | 171 | --called when the screen starts 172 | Enter = function(this, bFwd) 173 | print("entered ingame key screen ============") 174 | --remove cursor 175 | ScriptCB_EnableCursor(nil) 176 | 177 | -- did we come from the pause menu? 178 | this.fromPauseMenu = ScriptCB_IsScreenInStack("ifs_pausemenu") or ScriptCB_IsScreenInStack("ifs_mod_menu_tree") 179 | 180 | -- always visible from pauseMenu 181 | if this.fromPauseMenu then 182 | IFObj_fnSetVis(ifs_ingame_log.listbox, true) 183 | end 184 | 185 | print("ifs_ingame_log : fromPauseMenu " .. tostring(this.fromPauseMenu)) 186 | 187 | -- just an example, add a new entry every time we enter the screen 188 | table.insert(gInGameDebugList, { ShowStr = "entered screen" }) 189 | --gInGameDebugList[3].ShowStr = "addon mission count " .. tostring(__ADDDOWNLOADABLECONTENT_COUNT__) 190 | if( __ADDDOWNLOADABLECONTENT_COUNT__ ~= nil and this.addedMissionCount == nil ) then 191 | print("info: addon mission count: ".. tostring(__ADDDOWNLOADABLECONTENT_COUNT__)) 192 | this.addedMissionCount = true 193 | end 194 | 195 | local testTable = { a = { "ace", "azzameen"}, 196 | b = {"milehighguy"} 197 | } 198 | 199 | --prints the contents of the table to the ingame debug as JSON. useful! 200 | --tableToJsonLines(testTable, gInGameDebugList, "test table") 201 | 202 | -- focus always at latest entry 203 | local numEntries = table.getn(gInGameDebugList) 204 | numEntries = numEntries or 1 205 | 206 | ingamekeys_listbox_layout.FirstShownIdx = numEntries 207 | ingamekeys_listbox_layout.SelectedIdx = numEntries 208 | ingamekeys_listbox_layout.CursorIdx = numEntries 209 | 210 | ListManager_fnFillContents(ifs_ingame_log.listbox,gInGameDebugList, ingamekeys_listbox_layout) 211 | 212 | end, 213 | 214 | --called when the screen closes 215 | Exit = function(this, bFwd) 216 | print("exited ingame log screen ===========") 217 | ScriptCB_EnableCursor(true) 218 | 219 | -- set back to user preference 220 | if this.fromPauseMenu then 221 | IFObj_fnSetVis(ifs_ingame_log.listbox, this.bShowBox) 222 | end 223 | gBlankListbox = 1 224 | ListManager_fnFillContents(ifs_ingame_log.listbox,gInGameDebugList, ingamekeys_listbox_layout) 225 | gBlankListbox = nil 226 | end, 227 | 228 | --called continuously, every tick 229 | Update = function(this, fDt) 230 | ScriptCB_EnableCursor(nil) 231 | 232 | -- flip this bit so we only update the vis once its changed (not every tick) 233 | if this.bShowBox ~= this.bShowBoxOld then 234 | this.bShowBoxOld = this.bShowBox 235 | IFObj_fnSetVis(ifs_ingame_log.listbox, ifs_ingame_log.bShowBox) 236 | end 237 | 238 | --update the contents as they change 239 | gInGameDebugList[2].ShowStr = "time " .. tostring(ScriptCB_GetMissionTime()) 240 | 241 | 242 | ListManager_fnFillContents(ifs_ingame_log.listbox,gInGameDebugList, ingamekeys_listbox_layout) 243 | end, 244 | 245 | HandleMouse = function(this, x, y) 246 | gHandleMouse(this,x,y) 247 | end, 248 | 249 | Input_Accept = function(this) 250 | -- If base class handled this work, then we're done 251 | if(gShellScreen_fnDefaultInputAccept(this,1)) then 252 | -- this code chunk helps/makes slider function 253 | return 254 | end 255 | 256 | if(gMouseListBox) then 257 | if(gMouseListBox.Layout.SelectedIdx ~= gMouseListBox.Layout.CursorIdx) then 258 | gMouseListBox.Layout.SelectedIdx = gMouseListBox.Layout.CursorIdx 259 | ListManager_fnMoveCursor(this.listbox,ifs_mod_menu_tree_listbox_layout) 260 | end 261 | end 262 | end, 263 | 264 | --Back button quits this screen 265 | Input_Back = function(this) 266 | --TODO this doesn't seem to trigger on PC 267 | -- might be fine on console 268 | print("pressed back") 269 | if this.fromPauseMenu then 270 | -- go back to pause menu 271 | ScriptCB_SndPlaySound("shell_menu_exit"); 272 | ScriptCB_PopScreen() 273 | end 274 | end, 275 | 276 | Input_GeneralUp = function(this) 277 | if this.fromPauseMenu then 278 | ListManager_fnNavUp(this.listbox, gInGameDebugList, ingamekeys_listbox_layout) 279 | 280 | end 281 | end, 282 | Input_GeneralDown = function(this) 283 | if this.fromPauseMenu then 284 | ListManager_fnNavDown(this.listbox, gInGameDebugList, ingamekeys_listbox_layout) 285 | 286 | end 287 | end, 288 | 289 | Input_LTrigger = function(this) 290 | if this.fromPauseMenu then 291 | ListManager_fnPageUp(this.listbox, gInGameDebugList, ingamekeys_listbox_layout) 292 | end 293 | end, 294 | Input_RTrigger = function(this) 295 | if this.fromPauseMenu then 296 | ListManager_fnPageDown(this.listbox, gInGameDebugList, ingamekeys_listbox_layout) 297 | end 298 | end, 299 | 300 | ShowHideIngameListbox = function(this) 301 | this.bShowBox = not this.bShowBox 302 | end, 303 | -- use this in the event listeners (example below) 304 | AddToList = function(this, newString) 305 | 306 | if gInGameDebugList then 307 | 308 | table.insert(gInGameDebugList, { ShowStr = newString }) 309 | 310 | -- move cursor to newest event 311 | local numEntries = table.getn(gInGameDebugList) 312 | numEntries = numEntries or 1 313 | 314 | ingamekeys_listbox_layout.FirstShownIdx = numEntries 315 | ingamekeys_listbox_layout.SelectedIdx = numEntries 316 | ingamekeys_listbox_layout.CursorIdx = numEntries 317 | 318 | --will be repainted in the Update function 319 | end 320 | 321 | end 322 | } 323 | 324 | -- read PC keyboard inputs 325 | if gPlatformStr == "PC" then 326 | ifs_ingame_log.Input_KeyDown = function(this, iKey) 327 | 328 | if not ifs_ingame_log.fromPauseMenu then 329 | --from in-game 330 | 331 | if isKey(iKey, "i") then 332 | --scroll up 333 | ListManager_fnNavUp(ifs_ingame_log.listbox, gInGameDebugList, ingamekeys_listbox_layout) 334 | elseif isKey(iKey, "k") then 335 | --scroll down 336 | ListManager_fnNavDown(ifs_ingame_log.listbox, gInGameDebugList, ingamekeys_listbox_layout) 337 | elseif isKey(iKey, "l") then 338 | ifs_ingame_log:ShowHideIngameListbox() 339 | end 340 | 341 | elseif ifs_ingame_log.fromPauseMenu then 342 | -- from pause menu 343 | 344 | if iKey == 27 and ScriptCB_CheckProfanity == nil then 345 | -- do not do this for Aspyr's game version 346 | --pressed ESC, exit the screen 347 | -- go back to pause menu 348 | ScriptCB_SndPlaySound("shell_menu_exit"); 349 | ScriptCB_PopScreen() 350 | end 351 | end 352 | 353 | --can add code for a selecting one of the items if you want to 354 | end 355 | end 356 | 357 | function ifs_ingamekeys_fnBuildScreen(this) 358 | ingamekeys_listbox_layout.fontheight = ScriptCB_GetFontHeight(ingamekeys_listbox_layout.font) 359 | ingamekeys_listbox_layout.yHeight = ingamekeys_listbox_layout.fontheight 360 | 361 | this.listbox = NewButtonWindow { 362 | ZPos = 200, x = 0, y = 0, 363 | --ScreenRelativeX = 0.9, -- mostly right 364 | --ScreenRelativeY = 0.45, -- upper part 365 | 366 | ScreenRelativeX = 0.5, -- mostly left? 367 | ScreenRelativeY = 0.45, -- upper part? 368 | 369 | width = ingamekeys_listbox_layout.width + 35, 370 | height = ingamekeys_listbox_layout.showcount * (ingamekeys_listbox_layout.yHeight + ingamekeys_listbox_layout.ySpacing) + 30, 371 | } 372 | ListManager_fnInitList(this.listbox, ingamekeys_listbox_layout) 373 | end 374 | 375 | ifs_ingamekeys_fnBuildScreen(ifs_ingame_log) 376 | 377 | AddIFScreen(ifs_ingame_log, "ifs_ingame_log") 378 | --function for debugging, will insert JSON strings to the given table 379 | function tableToJsonLines(tbl, outputTable, tblName, indent) 380 | indent = indent or 0 381 | outputTable = outputTable or {} 382 | tblName = tblName or "" 383 | 384 | local function append_line(str) 385 | table.insert(outputTable, { ShowStr = string.rep(" ", indent) .. str }) 386 | end 387 | 388 | if next(tbl) == nil then 389 | append_line(tblName .. "{}") 390 | return 391 | end 392 | 393 | append_line(tblName .. "{") 394 | local isFirst = true 395 | for key, value in pairs(tbl) do 396 | if not isFirst then 397 | append_line(",") 398 | end 399 | isFirst = false 400 | 401 | local keyStr 402 | if type(key) == "number" then 403 | keyStr = "[" .. key .. "]" 404 | elseif type(key) == "string" then 405 | keyStr = '["' .. key .. '"]' 406 | else 407 | keyStr = "[" .. tostring(key) .. "]" 408 | end 409 | 410 | if type(value) == "table" then 411 | append_line(keyStr .. " = ") 412 | tableToJsonLines(value, outputTable, "", indent + 4) 413 | else 414 | if type(value) == "string" then 415 | value = '"' .. value .. '"' 416 | end 417 | append_line(keyStr .. " = " .. tostring(value)) 418 | end 419 | end 420 | append_line("}") 421 | end 422 | 423 | 424 | 425 | 426 | 427 | -- the function that you call after ingame.lvl is loaded 428 | -- place any new event listeners in this function 429 | -- add new items to gInGameDebugList 430 | -- use ifs_ingame_log:AddToList("New Item") 431 | function addInGameKeyScreen() 432 | 433 | local screenTimer = CreateTimer("screenTimer") 434 | --SetTimerValue(screenTimer, 0.1) 435 | 436 | -- if I just tried to push the screen with OnCharacterSpawn, then it exits right away 437 | -- start it after a small timer it stays open 438 | local myTimerResponse = OnTimerElapse( 439 | function(timer) 440 | --print("pushing screen after timer ===============") 441 | --ifs_ingame_log.fromPauseMenu = nil 442 | ScriptCB_PushScreen("ifs_ingame_log") 443 | end, 444 | screenTimer 445 | ) 446 | 447 | --start timer when player spawns, then calls the elapse function 448 | local charSpawn = OnCharacterSpawn(function(character) 449 | if IsCharacterHuman(character) 450 | then 451 | --print("player spawned =============") 452 | SetTimerValue(screenTimer, 0.1) 453 | StartTimer(screenTimer) 454 | end 455 | end) 456 | 457 | -- EXAMPLE 458 | -- add a string to the listbox every time player attacks something 459 | local onHumanDamage = OnObjectDamage(function(object, damager) 460 | 461 | if GetCharacterUnit(damager) and IsCharacterHuman(damager) then 462 | 463 | ifs_ingame_log:AddToList("player attacked something") 464 | 465 | end 466 | end) 467 | 468 | end 469 | 470 | -- Add the debug log to the end of the pause menu 471 | function IGK_addButtonToPauseMenu() 472 | 473 | local IGK_originalAddIFScreen = AddIFScreen 474 | AddIFScreen = function(screenTable, screenName) 475 | 476 | print("IGK: AddIFScreen " .. tostring(screenTable) .. " " .. tostring(screenName)) 477 | 478 | if screenName == "ifs_pausemenu" then 479 | print("IGK: overriding pauseMenu") 480 | 481 | -- add new button without remaking the whole pause menu 482 | local newButton = { tag = "debugLog", string = "Debug Log", } 483 | -- but the button before the options button, or at the end of the list 484 | local newButtonIndex = table.getn(ifspausemenu_vbutton_layout.buttonlist) 485 | for index, button in ifspausemenu_vbutton_layout.buttonlist do 486 | if button.tag == "opts" then 487 | newButtonIndex = index 488 | break 489 | end 490 | end 491 | table.insert(ifspausemenu_vbutton_layout.buttonlist, newButtonIndex, newButton) 492 | screenTable.CurButton = AddVerticalButtons(screenTable.buttons, ifspausemenu_vbutton_layout) 493 | local originalInputAccept = ifs_pausemenu_fnInput_Accept 494 | function ifs_pausemenu_fnInput_Accept(this) 495 | originalInputAccept(this) 496 | 497 | if this.CurButton == newButton.tag then 498 | 499 | ifs_movietrans_PushScreen(ifs_ingame_log) 500 | end 501 | end 502 | 503 | IGK_originalAddIFScreen(screenTable, screenName) 504 | elseif screenName == "ifs_pc_SpawnSelect" then 505 | 506 | local originalExit = ifs_pc_SpawnSelect.Exit 507 | ifs_pc_SpawnSelect.Exit = function(this, bFwd) 508 | print("10X: ifs_pc_SpawnSelect.Exit") 509 | print("10X: bFwd is " .. tostring(bFwd)) 510 | 511 | if originalExit then 512 | print("10X: calling original exit") 513 | originalExit(this, bFwd) 514 | else 515 | print("10X: spawn select exit not originally defined") 516 | end 517 | 518 | if not ScriptCB_IsScreenInStack("ifs_pausemenu") then 519 | if not ScriptCB_IsScreenInStack("ifs_ingame_log") then 520 | print("10X: pushing to ingame screen") 521 | ifs_movietrans_PushScreen(ifs_ingame_log) 522 | elseif ScriptCB_IsScreenInStack("ifs_ingame_log") then 523 | print("10X: ingame screen already in stack") 524 | --ScriptCB_PopScreen("ifs_ingame_log") 525 | end 526 | end 527 | print("10X: exit function done") 528 | end 529 | 530 | IGK_originalAddIFScreen(screenTable, screenName) 531 | else 532 | IGK_originalAddIFScreen(screenTable, screenName) 533 | end 534 | end 535 | 536 | end -------------------------------------------------------------------------------- /src/scripts/ifs_mod_menu_tree.lua: -------------------------------------------------------------------------------- 1 | 2 | -- BAD_AL 3 | -- ifs_mod_menu_tree 4 | 5 | if( ifs_mod_menu_tree ~= nil ) then 6 | print("ifs_mod_menu_tree is already defined; exiting") 7 | return 8 | end 9 | 10 | -- Helper function. Given a layout (x,y,width, height), returns a 11 | -- fully-built item. 12 | function ifs_mod_menu_tree_listbox_CreateItem(layout) 13 | -- Make a coordinate system pegged to the top-left of where the cursor would go. 14 | local Temp = NewIFContainer { 15 | x = layout.x - 0.5 * layout.width, 16 | y=layout.y - 10, 17 | bHotspot = true, 18 | fHotspotH = layout.height, 19 | fHotspotW = layout.width, 20 | fHotspotX = 0, 21 | fHotspotY = -layout.height /2, 22 | } 23 | 24 | Temp.NameStr = NewIFText{ 25 | x = 10, y = 0, halign = "left", 26 | font = ifs_mod_menu_tree_listbox_layout.Font, 27 | --textw = 220, 28 | textw = ifs_mod_menu_tree_listbox_layout.width - 20, 29 | nocreatebackground = 1, startdelay=math.random()*0.5, 30 | } 31 | return Temp 32 | end 33 | 34 | -- Helper function. For a destination item (previously created w/ 35 | -- CreateItem), fills it in with data, which may be nil (==blank it) 36 | function ifs_mod_menu_tree_listbox_PopulateItem(Dest, Data, bSelected, iColorR, iColorG, iColorB, fAlpha) 37 | 38 | if(Data) then 39 | --print("ifs_mod_menu_tree_listbox_PopulateItem Data:"); tprint(Data) 40 | -- Show this entry 41 | --IFText_fnSetUString(Dest.NameStr,Data.showstr) 42 | local theText = "" 43 | if(type(Data.showstr) == "function") then 44 | theText = Data.showstr() 45 | else 46 | theText = Data.showstr 47 | end 48 | if( Data.action ~= nil and type(Data.action) == "table") then 49 | IFText_fnSetString(Dest.NameStr, "[+] " .. theText) 50 | IFObj_fnSetColor(Dest.NameStr, 200, 150, 0) -- mustard yellow 51 | else 52 | IFText_fnSetString(Dest.NameStr,theText) 53 | IFObj_fnSetColor(Dest.NameStr, iColorR, iColorG, iColorB) 54 | end 55 | IFObj_fnSetAlpha(Dest.NameStr, fAlpha) 56 | else 57 | --print("ifs_mod_menu_tree_listbox_PopulateItem empty item") 58 | -- Blank this entry 59 | IFText_fnSetString(Dest.NameStr,"") 60 | end 61 | 62 | IFObj_fnSetVis(Dest.NameStr, Data) 63 | end 64 | 65 | 66 | local ifs_mod_menu_tree_listbox_contents = { 67 | --{id="fontTest", showstr= "Font Test", action="ifs_fonttest"}, 68 | --{id="campaignList", showstr = "ifs.sp.campaign", action="ifs_sp_briefing"}, 69 | } 70 | local scrnW, scrnH = ScriptCB_GetScreenInfo() 71 | ifs_mod_menu_tree_listbox_layout = { 72 | showcount = 10, 73 | bCreateSlider = true, -- can't figure out how to make slider mouse sensitive :( 74 | yHeight = 34, 75 | ySpacing = 0, 76 | --width = 260, 77 | width = scrnW *0.7, 78 | x = 0, 79 | slider = 1, 80 | CreateFn = ifs_mod_menu_tree_listbox_CreateItem, 81 | PopulateFn = ifs_mod_menu_tree_listbox_PopulateItem, 82 | Font = gListboxItemFont, 83 | fNumberWidth = 40, -- width, in pixels, for the XBox's file # 84 | } 85 | 86 | -- Sets the hilight on the listbox, create button given a hilight 87 | function ifs_mod_menu_tree_SetHilight(this,aListIndex) 88 | if(gNoNewProfiles or aListIndex) then 89 | -- Deactivate 'create' button, if applicable. 90 | if(gCurHiliteButton) then 91 | IFButton_fnSelect(gCurHiliteButton,nil) -- Deactivate old button 92 | gCurHiliteButton = nil 93 | this.CurButton = nil 94 | end 95 | else 96 | -- Not in listindex. Focus is on the create buttons 97 | this.CurButton = "new" 98 | gCurHiliteButton = this.buttons[this.CurButton] 99 | IFButton_fnSelect(gCurHiliteButton,1) -- Activate button 100 | end 101 | 102 | IFObj_fnSetVis(this.Helptext_Accept,1) 103 | 104 | ifs_mod_menu_tree_listbox_layout.SelectedIdx = aListIndex 105 | ifs_mod_menu_tree_listbox_layout.CursorIdx = aListIndex 106 | end 107 | 108 | 109 | function ifs_mod_menu_tree_fnListFullOk() 110 | local this = ifs_mod_menu_tree 111 | ifs_mod_menu_tree_fnSetPieceVis(this, 1) 112 | end 113 | 114 | 115 | -- Helper function: turns pieces on/off as requested 116 | function ifs_mod_menu_tree_fnSetPieceVis(this,bNormalVis) 117 | IFObj_fnSetVis(this.listbox,bNormalVis) 118 | IFObj_fnSetVis(this.buttons,bNormalVis and (not gNoNewProfiles)) 119 | 120 | IFObj_fnSetVis(this.Helptext_Accept,bNormalVis) 121 | IFObj_fnSetVis(this.Helptext_Back,bNormalVis ) 122 | 123 | if(bNormalVis) then 124 | ifs_mod_menu_tree_fnRegetListbox(this) 125 | end 126 | end 127 | 128 | ifs_mod_menu_tree = NewIFShellScreen { 129 | bAcceptIsSelect = 1, 130 | buttons = NewIFContainer { 131 | ScreenRelativeX = 0.5, -- center 132 | ScreenRelativeY = 0.5, -- center 133 | y = 80, 134 | }, 135 | menuStack ={}, -- should be a list of lists; used for menu nav 136 | menuStateList ={}, 137 | currentMenu = nil, 138 | timeOfLastAction=0, 139 | 140 | movieIntro = nil, 141 | movieBackground = "shell_main", 142 | bg_texture = nil, -- "single_player_option", 143 | enterSound = "", 144 | exitSound = "", 145 | GetTitleString = function(this) 146 | local retVal = "Mod Menu Tree" 147 | local count = table.getn(this.menuStateList) 148 | if(count > 0) then 149 | retVal = this.menuStateList[count].Title 150 | end 151 | if( type(retVal) == "function") then 152 | retVal = retVal() -- this looks so crazy 153 | end 154 | print("GetTitleString: " .. retVal) 155 | return retVal 156 | end, 157 | UpdateTitle = function(this) 158 | IFText_fnSetString(this.listbox.titleBarElement, this.GetTitleString(this)) 159 | end, 160 | HandleMouse = function(this, x, y) 161 | gHandleMouse(this,x,y) 162 | end, 163 | Input_KeyDown = function (this, iKey) 164 | --[[ 165 | b 98 The b key. 166 | Back 8 The BACKSPACE key. 167 | Escape 27 The ESC key. 168 | PageDown 34 The PAGE DOWN key. 169 | PageUp 33 The PAGE UP key. 170 | Space 32 The Space bar key. 171 | Tab 9 The Tab Key 172 | ]] 173 | print("gInput_KeyDown: " .. iKey) 174 | if (iKey == 8 or iKey == 98) or (iKey == 27 and ScriptCB_CheckProfanity == nil) then 175 | -- do not handle 'esc' for Aspyr's game version here 176 | this.Input_Back(this) 177 | elseif(iKey == 13 or iKey == 32) then 178 | this.Input_Accept(this) 179 | elseif(iKey == 33) then 180 | this.Input_LTrigger(this) 181 | elseif(iKey == 34) then 182 | this.Input_RTrigger(this) 183 | end 184 | end, 185 | -- Push a list of items to show 186 | Push_List = function(this, the_list, the_title) 187 | table.insert(this.menuStack, the_list) 188 | local state = { 189 | FirstShownIdx = ifs_mod_menu_tree_listbox_layout.FirstShownIdx, 190 | SelectedIdx = ifs_mod_menu_tree_listbox_layout.SelectedIdx, 191 | CursorIdx = ifs_mod_menu_tree_listbox_layout.CursorIdx, 192 | Title = the_title or "MOD MENU TREE", 193 | } 194 | table.insert(this.menuStateList, state ) 195 | this.currentMenu = the_list 196 | print("mod_menu.Push_List with item[1].id = " .. this.currentMenu[1].id ) 197 | 198 | this.Populate_List(this) 199 | end, 200 | -- Remove the list of items last pushed. 201 | -- If there is a list of items to show left on the menu stack, show it; 202 | -- if there are no more lists of items to show, pop the screen 203 | Pop_List = function(this) 204 | print("mod_menu.Pop_List") 205 | local count = table.getn(this.menuStack) 206 | local state = nil 207 | if( count > 0) then 208 | table.remove(this.menuStack, count) 209 | state = table.remove(this.menuStateList, count) 210 | end 211 | count = table.getn(this.menuStack) 212 | if(count > 0) then 213 | this.currentMenu = this.menuStack[count] 214 | print("mod_menu.Pop_List with item[1].id = " .. this.currentMenu[1].id ) 215 | this.Populate_List(this, state) 216 | else 217 | this.currentMenu = nil 218 | print("mod_menu.Pop_List no more to show, exiting...") 219 | ScriptCB_PopScreen() 220 | if(this.SaveSettings) then 221 | this:SaveSettings() 222 | end 223 | end 224 | end, 225 | Enter = function(this, bFwd) 226 | print("ifs_mod_menu_tree.Enter" ) 227 | this.timeOfLastAction = ScriptCB_GetMissionTime() 228 | gIFShellScreenTemplate_fnEnter(this, bFwd) -- call default enter function 229 | this.Push_List(this,ifs_mod_menu_tree_listbox_contents, "Mod Menu Tree") 230 | print("ifs_mod_menu_tree.Enter this.listbox= " .. tostring(this.listbox) ) 231 | --tprint(this.listbox) 232 | end, 233 | Populate_List = function(this, state) 234 | print("ifs_mod_menu_tree.Populate_List: size = " .. tostring(table.getn(this.currentMenu))) 235 | --for _, item in this.currentMenu do -- debug print stuff 236 | -- print(string.format("id:'%s' '%s' action: %s", item.id, item.showstr, tostring(item.action))) 237 | --end 238 | this.UpdateTitle(this) 239 | if(state) then 240 | ifs_mod_menu_tree_listbox_layout.FirstShownIdx = state.FirstShownIdx 241 | ifs_mod_menu_tree_listbox_layout.SelectedIdx = state.SelectedIdx 242 | ifs_mod_menu_tree_listbox_layout.CursorIdx = state.CursorIdx 243 | else 244 | ifs_mod_menu_tree_listbox_layout.FirstShownIdx = 1 245 | ifs_mod_menu_tree_listbox_layout.SelectedIdx = 1 246 | ifs_mod_menu_tree_listbox_layout.CursorIdx = 1 247 | end 248 | ListManager_fnFillContents(this.listbox,this.currentMenu,ifs_mod_menu_tree_listbox_layout) 249 | end, 250 | Input_Back = function(this) 251 | print("ifs_mod_menu_tree.Back ", tostring(ScriptCB_IsScreenInStack("ifs_mod_menu_tree") ) ) 252 | this.Pop_List(this) 253 | end, 254 | 255 | Input_GeneralUp = function(this) 256 | -- Fix for 11032 - if we're headed to another screen already, 257 | -- ignore inputs - NM 8/20/05 258 | if(ifs_mod_menu_tree.bNoInputs) then 259 | return 260 | end 261 | 262 | ListManager_fnNavUp(this.listbox,this.currentMenu,ifs_mod_menu_tree_listbox_layout) 263 | end, 264 | 265 | Input_LTrigger = function(this) 266 | ListManager_fnPageUp(this.listbox,this.currentMenu,ifs_mod_menu_tree_listbox_layout) 267 | end, 268 | 269 | Input_GeneralDown = function(this) 270 | -- Fix for 11032 - if we're headed to another screen already, 271 | -- ignore inputs - NM 8/20/05 272 | if(ifs_mod_menu_tree.bNoInputs) then 273 | return 274 | end 275 | 276 | ListManager_fnNavDown(this.listbox,this.currentMenu,ifs_mod_menu_tree_listbox_layout) 277 | end, 278 | 279 | Input_RTrigger = function(this) 280 | ListManager_fnPageDown(this.listbox,this.currentMenu,ifs_mod_menu_tree_listbox_layout) 281 | end, 282 | 283 | -- Not possible on this screen 284 | Input_GeneralLeft = function(this) 285 | end, 286 | Input_GeneralRight = function(this) 287 | end, 288 | 289 | Input_Accept = function(this) 290 | 291 | -- If base class handled this work, then we're done 292 | if(gShellScreen_fnDefaultInputAccept(this,1)) then 293 | -- this code chunk helps/makes slider function 294 | return 295 | end 296 | 297 | print("ifs_mod_menu_tree.Accept CurButton", tostring(this.CurButton) ) 298 | if(gMouseListBox) then 299 | print( string.format("ifs_mod_menu_tree: Got Mouse click, update selected idx from %s to %s", 300 | tostring(gMouseListBox.Layout.SelectedIdx) , tostring(gMouseListBox.Layout.CursorIdx) ) ) 301 | -- set the selection 302 | --gMouseListBox.Layout.SelectedIdx = gMouseListBox.Layout.CursorIdx 303 | if( ( gMouseListBox.Layout.SelectedIdx == gMouseListBox.Layout.CursorIdx ) and 304 | ( this.lastDoubleClickTime ) and 305 | ( ScriptCB_GetMissionTime(), menu_table[optional sub menu table] }) 435 | -- displayStr can be a string or function to call to get the display string. 436 | -- == string -> launch a screen named 'name'; 437 | -- == function -> call the function name(id); 438 | -- == table -> push ui and show it 439 | -- menu_table - if nil, push to the initial menu list; otherwise push the item to 'menu_table'. 440 | AddModMenuItem = function(id, displayStr, action, menu_table ) 441 | print(string.format("AddModMenuItem: Adding item { %s %s %s }", id, tostring(displayStr), tostring(action))) 442 | local newItem = {id= id, showstr= displayStr, action= action } 443 | if(menu_table ~= nil) then 444 | table.insert( menu_table, newItem ) 445 | else 446 | table.insert( ifs_mod_menu_tree_listbox_contents, newItem ) 447 | end 448 | end 449 | 450 | -- adds 'the_menu' to the menu tree; inserts the menu into 'menu_table' or the main menu if 'menu_table' is nil 451 | AddModMenu = function(the_menu, menu_table ) 452 | print(string.format("AddModMenu: Adding item { %s %s %s }", the_menu.id, tostring(the_menu.showstr), tostring(the_menu.action))) 453 | if(menu_table ~= nil) then 454 | table.insert( menu_table, the_menu ) 455 | else 456 | table.insert( ifs_mod_menu_tree_listbox_contents, the_menu ) 457 | end 458 | end 459 | 460 | 461 | local help_CreateOptionsSetting = [[ 462 | 'CreateOptionsSetting()' will return an item that can be added to a menu with 'AddModMenu' 463 | expects data in a form like : 464 | { 465 | default = 9, -- > used when target_table[property_name] is nil 466 | target_table = zero_patch_data, -- > the table we'll set the data on 467 | property_name = "my_setting", -- > saved to --> target_table.my_setting 468 | title = "My Setting", 469 | callback = nil, -- > Callback once the data is set 470 | options = {0,1,2,3,4,5,6,7,8,9} -- > List of options to show 471 | }]] 472 | CreateOptionsSetting = function(options_data) 473 | if( options_data == nil or options_data.target_table == nil or 474 | options_data.property_name == nil or options_data.options == nil ) then 475 | print("error! CreateOptionsSetting invalid options_data. " .. help_CreateOptionsSetting) 476 | return nil 477 | end 478 | local selections = {} 479 | local update_function = function(option_id) 480 | options_data.target_table[options_data.property_name] = tonumber(option_id) or option_id 481 | -- menu title text gets updated when after calling a function 482 | if(options_data.callback ~= nil) then 483 | options_data.callback(options_data.property_name) 484 | end 485 | end 486 | for _, value in options_data.options do 487 | AddModMenuItem(tostring(value), tostring(value),update_function, selections) 488 | end 489 | 490 | local GetOptionTitleText = function() 491 | -- Parent item shows the title: value 492 | local currentValue = options_data.target_table[options_data.property_name] or options_data.default 493 | local itemTitle = string.format("%s: %s",options_data.title, tostring(currentValue)) 494 | return itemTitle 495 | end 496 | 497 | local retVal = { id=options_data.property_name, showstr=GetOptionTitleText, action=selections} 498 | return retVal 499 | end 500 | -- ======================================== PUBLIC API END =========================================================== -------------------------------------------------------------------------------- /src/scripts/patch_ingame.lua: -------------------------------------------------------------------------------- 1 | 2 | -- first things game_interface does: 3 | -- gPlatformStr = ScriptCB_GetPlatform() 4 | -- gOnlineServiceStr = ScriptCB_GetConnectType() 5 | -- gLangStr,gLangEnum = ScriptCB_GetLanguage() 6 | -- ScriptCB_DoFile("globals") -- our 'patch' is called from here 7 | 8 | -- last thing game_interface does: ScriptCB_DoFile("ifs_mpgs_friends") 9 | 10 | print("Patch ingame start") 11 | 12 | -- added 'ingame_messages' table to keep track of errors befroe we re-direct 'print' 13 | local ingame_messages ={} 14 | local function addIngameMessage(str) 15 | table.insert(ingame_messages, str) 16 | end 17 | 18 | if(printf == nil) then 19 | function printf (...) print(string.format(unpack(arg))) end 20 | end 21 | 22 | if( tprint == nil ) then 23 | function getn(v) 24 | local v_type = type(v); 25 | if v_type == "table" then 26 | return table.getn(v); 27 | elseif v_type == "string" then 28 | return string.len(v); 29 | else 30 | return; 31 | end 32 | end 33 | 34 | function string.starts(str, Start) 35 | return string.sub(str, 1, getn(Start)) == Start; 36 | end 37 | 38 | function tprint(t, indent) 39 | if not indent then indent = 1, print(tostring(t) .. " {") end 40 | if t then 41 | for key,value in pairs(t) do 42 | if not string.starts(tostring(key), "__") then 43 | local formatting = string.rep(" ", indent) .. tostring(key) .. "= "; 44 | if value and type(value) == "table" then 45 | print(formatting .. --[[tostring(value) ..]] " {") 46 | tprint(value, indent+1); 47 | else 48 | if(type(value) == "string") then 49 | --print(formatting .."'" .. tostring(value) .."'" ..",") 50 | printf("%s'%s',",formatting, tostring(value)) 51 | else 52 | print(formatting .. tostring(value) ..",") 53 | end 54 | end 55 | end 56 | end 57 | print(string.rep(" ", indent - 1) .. "},") 58 | end 59 | end 60 | end 61 | 62 | 63 | if IsFileExist == nil then 64 | print("patch_ingame: defining IsFileExist") 65 | IsFileExist = function(path) 66 | local testPath = "..\\..\\".. path 67 | return ScriptCB_IsFileExist(testPath) 68 | end 69 | end 70 | 71 | -- this will get the Fake console & Free Cam buttons to show 72 | gFinalBuild = false 73 | 74 | print("gFinalBuild: " .. tostring(gFinalBuild)) 75 | 76 | local function load_fs() 77 | ScriptCB_DoFile("zero_patch_fs") 78 | end 79 | 80 | local status, err = pcall(load_fs) 81 | if not status then 82 | local msg = "Caught an error in load_fs: " .. err 83 | addIngameMessage(msg) 84 | print(msg) 85 | else 86 | -- Successful execution 87 | end 88 | 89 | ScriptCB_DoFile("utility_functions2") 90 | local target = "..\\..\\addon\\0\\patch_scripts\\v1.3patch_strings.lvl" 91 | if(ScriptCB_IsFileExist(target) == 1) then 92 | print("info: read in old 1.3 patch strings") 93 | ReadDataFile(target) 94 | end 95 | 96 | target = "..\\..\\addon\\0\\hud-options\\hud_texture_pack.lvl" 97 | if(ScriptCB_IsFileExist(target) == 1) then 98 | print("info: read in hud_texture_pack") 99 | ReadDataFile(target) 100 | end 101 | 102 | -- trim "path\\to\\file.lvl" to "file" 103 | function trimToFileName(filePath) 104 | -- Find the last directory separator 105 | local nameStart = 1 106 | local sepStart, sepEnd = string.find(filePath, "[/\\]", nameStart) 107 | while sepStart do 108 | nameStart = sepEnd + 1 109 | sepStart, sepEnd = string.find(filePath, "[/\\]", nameStart) 110 | end 111 | 112 | -- Extract the file name part 113 | local fileName = string.sub(filePath, nameStart) 114 | 115 | -- Attempt to remove the extension 116 | local dotPosition = string.find(fileName, ".[^.]*$") 117 | if dotPosition then 118 | fileName = string.sub(fileName, 1, dotPosition - 1) 119 | end 120 | 121 | return fileName 122 | end 123 | 124 | zero_patch_data ={} 125 | 126 | 127 | function LoadZeroPatchSettings() 128 | print("info: LoadZeroPatchSettings begin") 129 | if ScriptCB_IsMissionSetupSaved() then 130 | local missionSetup = ScriptCB_LoadMissionSetup() 131 | if (missionSetup ~= nil ) then 132 | --print("Mission Setup:"); tprint(missionSetup) 133 | if(missionSetup.zero_patch_data ~= nil) then 134 | for key, value in string.gfind(missionSetup.zero_patch_data, "([A-Za-z0-9_]+):([A-Za-z0-9_]+);") do 135 | print(string.format("info:zero_patch setting: %s:%s",key, tostring(value))) 136 | addIngameMessage(string.format("info:zero_patch setting: %s:%s",key, tostring(value))) 137 | zero_patch_data[key] = tonumber(value) or value 138 | end 139 | end 140 | end 141 | else 142 | print("info: LoadZeroPatchSettings: no mission setup saved") 143 | end 144 | print("info: LoadZeroPatchSettings end") 145 | end 146 | 147 | LoadZeroPatchSettings() 148 | 149 | 150 | function Run_uop_UserScripts() 151 | local scriptName = "" 152 | local files = zero_patch_fs.getFiles("user_script_", {".lvl", ".script"}) 153 | 154 | for i, value in ipairs(files) do 155 | if( ScriptCB_IsFileExist(value) == 1) then 156 | addIngameMessage("info: running " .. value) 157 | ReadDataFile(value) 158 | scriptName = trimToFileName(value) 159 | ScriptCB_DoFile(scriptName) 160 | end 161 | end 162 | print("Run_uop_UserScripts() End") 163 | end 164 | Run_uop_UserScripts() 165 | 166 | if(ScriptCB_IsFileExist("..\\..\\addon\\0\\debug.txt") == 1) then 167 | gZeroPatchPrintAllDebug = true 168 | end 169 | -- filter debug messages to messages with 'info:', 'error' or 'warn' in them. 170 | function zero_patch_is_worthy_for_debug(str) 171 | local test = string.lower(str) 172 | if (gZeroPatchPrintAllDebug or string.find(test, 'error') or string.find(test, 'warn') or string.find(test, 'info:') or 173 | string.find(test, 'debug')) then 174 | return true 175 | end 176 | return false 177 | end 178 | 179 | -- sets up 'ifs_ingame_log' 180 | function SetupIngamelog() 181 | print("SetupIngamelog") 182 | ScriptCB_DoFile("ifs_ingame_log") 183 | local oldPrint = print 184 | print = function(...) 185 | if( ifs_ingame_log ~= nil and zero_patch_is_worthy_for_debug(arg[1])) then 186 | ifs_ingame_log:AddToList(arg[1]) 187 | end 188 | oldPrint(unpack(arg)) 189 | end 190 | 191 | if(ingame_messages~= nil and table.getn(ingame_messages) > 0) then 192 | for i, err in ipairs(ingame_messages) do 193 | print(err) 194 | end 195 | ingame_messages = nil 196 | end 197 | end 198 | 199 | local function SetupAddIfScreenCatching() 200 | --[[ 201 | for patching ifs_ menu screens, we'll want to 'Catch' when they are passed to 'AddIfScreen'. 202 | We can manipulate/adjust the screen structure at that time. Adding buttons, adjusting location... 203 | But after 'AddIfScreen' is called, messing with the screen will make it look all janky or 204 | it won't work at all. 205 | 206 | For replacing a screen we can override 'ScriptCB_PushScreen' and/or 'ScriptCB_SetIFScreen'. 207 | 'ScriptCB_SetIFScreen' is used infrequently though. 208 | ]] 209 | print("Setup 'catch' point for ifs_fakeconsole") 210 | 211 | local old_AddIFScreen = AddIFScreen 212 | AddIFScreen = function(...) 213 | print("AddIFScreen: " .. arg[2]) 214 | if arg[2] == "ifs_pausemenu" then 215 | SetupIngamelog() 216 | print("IGK: adding debug log button to pauseMenu") 217 | print("Platform: ".. tostring( ScriptCB_GetPlatform() )) 218 | 219 | -- add new button without remaking the whole pause menu 220 | local newButton = { tag = "debugLog", string = "Debug Log", } 221 | local index = table.getn(ifspausemenu_vbutton_layout.buttonlist) -1 222 | table.insert(ifspausemenu_vbutton_layout.buttonlist, index, newButton) 223 | arg[1].CurButton = AddVerticalButtons(arg[1].buttons, ifspausemenu_vbutton_layout) 224 | local originalInputAccept = ifs_pausemenu_fnInput_Accept 225 | function ifs_pausemenu_fnInput_Accept(this) 226 | originalInputAccept(this) 227 | if this.CurButton == newButton.tag then 228 | ifs_movietrans_PushScreen(ifs_ingame_log) 229 | end 230 | end 231 | -- not sure why, but the code below that hides the fakecamera button is crashing the steamdeck. 232 | -- disabling that for now. 233 | --print("info: GAME_VERSION: ".. GAME_VERSION) 234 | --if(GAME_VERSION == "SWBF2_CC") then 235 | -- -- Freecam is no-worky on BF CC; but don't hide it for 'OG' BF2 236 | -- local old_ifs_pausemenu_fnEnter = ifs_pausemenu_fnEnter 237 | -- ifs_pausemenu_fnEnter = function(this, bFwd, iInstance) 238 | -- old_ifs_pausemenu_fnEnter(this, bFwd, iInstance) 239 | -- this.buttons.freecam.hidden = true 240 | -- this.CurButton = ShowHideVerticalButtons(this.buttons, ifspausemenu_vbutton_layout) 241 | -- SetCurButton(this.CurButton, this) 242 | -- end 243 | --end 244 | 245 | end 246 | return old_AddIFScreen(unpack(arg)) 247 | end 248 | end 249 | 250 | 251 | local function uop_do_files() 252 | if( ifs_saveop == nil ) then 253 | -- 'ifs_saveop' (for some reason) is by default only run on the PC platform 254 | ScriptCB_DoFile("ifs_saveop") 255 | end 256 | ScriptCB_DoFile("fakeconsole_functions") 257 | ScriptCB_DoFile("popup_prompt") 258 | end 259 | 260 | local old_ScriptCB_DoFile = ScriptCB_DoFile 261 | ScriptCB_DoFile = function(...) 262 | print("ScriptCB_DoFile: " .. arg[1]) 263 | if(arg[1] == "ifs_pausemenu") then 264 | uop_do_files() -- do these before pause menu 265 | elseif(arg[1] == "ifs_fakeconsole")then 266 | arg[1] = "ifs_fakeconsole_zero_patch" 267 | elseif(arg[1] == "ifs_opt_top")then 268 | SetupAddIfScreenCatching() 269 | end 270 | return old_ScriptCB_DoFile(unpack(arg)) 271 | end 272 | -------------------------------------------------------------------------------- /src/scripts/patch_paths.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: deprecated 2 | -- STAR WARS BATTLEFRONT CLASSIC COLLECTION - Old Mod Patcher - Redirects an all.lvl load to all1 and all2, redirects an addon load to addon2 3 | -- Greetings from Kenny 4 | 5 | local __scriptName__ = "[zero_patch: patch_ingame]: " 6 | print("Start: " .. __scriptName__) 7 | 8 | THE_ORIGINAL_ScriptCB_IsFileExist = ScriptCB_IsFileExist 9 | 10 | if IsFileExist == nil then 11 | print("defining IsFileExist") 12 | IsFileExist = function(path) 13 | local testPath = "..\\..\\".. path 14 | return THE_ORIGINAL_ScriptCB_IsFileExist(testPath) 15 | end 16 | end 17 | 18 | 19 | if string.starts == nil then 20 | function string.starts(str, Start) 21 | return string.sub(str, 1, string.len(Start)) == Start; 22 | end 23 | end 24 | 25 | if( string.endsWith == nil ) then 26 | function string.endsWith(str, ending) 27 | return ending == "" or string.sub(str, -string.len(ending)) == ending 28 | end 29 | end 30 | 31 | function replaceAllLVL(func) 32 | -- backup old function 33 | local oldFunc = func 34 | 35 | -- wrap function 36 | return function(...) 37 | -- use string.sub and not find to make sure that it is not dc:SIDE 38 | local test = string.lower(arg[1]) 39 | test = string.gsub(test, "/", "\\") -- normalize the path separator 40 | if test == "side\\all.lvl" then 41 | arg[1] = "side\\all1.lvl" 42 | print("info:" .. __scriptName__ .. "FOUND side\\all.lvl call. redirecting -> " .. arg[1]) 43 | -- run ReadDataFile on all1, then on all2 to cover the whole side. 44 | oldFunc(unpack(arg)) 45 | arg[1] = "side\\all2.lvl" 46 | end 47 | 48 | local retval = {oldFunc(unpack(arg))} 49 | return unpack(retval) 50 | end 51 | end 52 | 53 | function redirectBF2Path(func) 54 | -- backup old function 55 | local oldFunc = func 56 | 57 | -- wrap function 58 | return function(...) 59 | -- Use "addon\\" to ensure that it isnt already addon2 60 | local arg_1 = string.lower(arg[1]) 61 | local msg = nil 62 | 63 | if string.find(arg_1, "addon\\") then 64 | arg[1] = string.gsub(arg_1, "addon\\", "addon2\\") 65 | msg = " redirect -> " .. arg[1] 66 | end 67 | 68 | -- We'll re-direct calls to the _lvl_common folder when the asset exists in _lvl_common 69 | -- and not in _lvl_pc 70 | arg_1 = string.lower(arg[1]) 71 | if string.find(arg_1, "\\_lvl_pc\\") then 72 | local commonTest = string.gsub(arg_1, "\\_lvl_pc\\", "\\_lvl_common\\") 73 | if( THE_ORIGINAL_ScriptCB_IsFileExist(arg_1) == 0 and THE_ORIGINAL_ScriptCB_IsFileExist(commonTest) == 1) then 74 | arg[1] = commonTest 75 | msg = "redirect -> " .. arg[1] 76 | end 77 | end 78 | if(msg) then 79 | print(msg) 80 | end 81 | 82 | -- let the original function happen 83 | local retval = {oldFunc(unpack(arg))} 84 | 85 | -- return unmodified return values 86 | return unpack(retval) 87 | end 88 | end 89 | 90 | 91 | ---------------------------------------------------------------------------------------- 92 | 93 | if(ScriptCB_IsFileExist("..\\..\\addon2\\0\\patch_scripts\\patch_paths.script") == 1) then 94 | print("This is BF Classic Collection; patching file paths") 95 | -- patch paths 96 | if ReadDataFile then 97 | ReadDataFile = redirectBF2Path(ReadDataFile) 98 | ReadDataFile = replaceAllLVL(ReadDataFile) 99 | end 100 | 101 | if ReadDataFileInGame then 102 | ReadDataFileInGame = redirectBF2Path(ReadDataFileInGame) 103 | ReadDataFileInGame = replaceAllLVL(ReadDataFileInGame) 104 | end 105 | 106 | if ScriptCB_IsFileExist then 107 | ScriptCB_IsFileExist = redirectBF2Path(ScriptCB_IsFileExist) 108 | end 109 | 110 | if ScriptCB_OpenMovie then 111 | ScriptCB_OpenMovie = redirectBF2Path(ScriptCB_OpenMovie) 112 | end 113 | -- not needed, ScriptCB_DoFile 'does' a file that has already been read into memory. 114 | --if ScriptCB_DoFile then 115 | -- ScriptCB_DoFile = redirectBF2Path(ScriptCB_DoFile) 116 | --end 117 | 118 | if PlayAudioStream then 119 | PlayAudioStream = redirectBF2Path(PlayAudioStream) 120 | end 121 | 122 | -- I do not know who came up with the idea to make this a whole other function, but screw you 123 | if PlayAudioStreamUsingProperties then 124 | PlayAudioStreamUsingProperties = redirectBF2Path(PlayAudioStreamUsingProperties) 125 | end 126 | 127 | if OpenAudioStream then 128 | OpenAudioStream = redirectBF2Path(OpenAudioStream) 129 | end 130 | 131 | if AudioStreamAppendSegments then 132 | AudioStreamAppendSegments = redirectBF2Path(AudioStreamAppendSegments) 133 | end 134 | 135 | if SetMissionEndMovie then 136 | SetMissionEndMovie = redirectBF2Path(SetMissionEndMovie) 137 | end 138 | 139 | if CreateEffect then 140 | CreateEffect = redirectBF2Path(CreateEffect) 141 | end 142 | end 143 | 144 | ---- debug logging 145 | --local old_ReadDataFile = ReadDataFile 146 | --ReadDataFile = function(...) 147 | -- print("info: enter ReadDataFile " .. arg[1]) 148 | -- return old_ReadDataFile(unpack(arg)) 149 | --end 150 | 151 | --local old_ScriptCB_PushScreen = ScriptCB_PushScreen 152 | --ScriptCB_PushScreen = function(screenName) 153 | -- print("ScriptCB_PushScreen: " .. screenName) 154 | -- old_ScriptCB_PushScreen(screenName) 155 | --end 156 | --local old_ScriptCB_SetIFScreen = ScriptCB_SetIFScreen 157 | --ScriptCB_SetIFScreen = function(screenName) 158 | -- print("ScriptCB_SetIFScreen: ".. screenName) 159 | -- return old_ScriptCB_SetIFScreen(screenName) 160 | --end 161 | 162 | -- This does not work. Find a better solution asap. 163 | --[[ load core again for custom localization 164 | if ScriptCB_IsFileExist("dc:core.lvl") == 1 then 165 | ReadDataFile("dc:core.lvl") 166 | end]]-- 167 | 168 | print("End: " .. __scriptName__) -------------------------------------------------------------------------------- /src/scripts/patch_shell.lua: -------------------------------------------------------------------------------- 1 | print("patch_shell start") 2 | 3 | --[[ 4 | for patching ifs_ shell menu screens, we'll want to 'Catch' when they are passed to 'AddIfScreen'. 5 | We can manipulate/adjust the screen structure at that time. Adding buttons, adjusting location... 6 | But after 'AddIfScreen' is called, messing with the screen will make it look all janky or 7 | it won't work at all. 8 | 9 | 10 | For replacing a screen we can override 'ScriptCB_PushScreen' and/or 'ScriptCB_SetIFScreen'. 11 | 'ScriptCB_SetIFScreen' is used infrequently though. 12 | (Prefer to do this in the '0' addme though) 13 | ]] 14 | 15 | if (printf == nil) then 16 | function printf(...) 17 | print(string.format(unpack(arg))) 18 | end 19 | end 20 | 21 | if (tprint == nil) then 22 | function getn(v) 23 | local v_type = type(v); 24 | if v_type == "table" then 25 | return table.getn(v); 26 | elseif v_type == "string" then 27 | return string.len(v); 28 | else 29 | return; 30 | end 31 | end 32 | 33 | if string.starts == nil then 34 | function string.starts(str, Start) 35 | return string.sub(str, 1, string.len(Start)) == Start; 36 | end 37 | end 38 | 39 | function tprint(t, indent) 40 | if not indent then 41 | indent = 1, print(tostring(t) .. " {") 42 | end 43 | if t then 44 | for key, value in pairs(t) do 45 | if not string.starts(tostring(key), "__") then 46 | local formatting = string.rep(" ", indent) .. tostring(key) .. "= "; 47 | if value and type(value) == "table" then 48 | print(formatting .. --[[tostring(value) ..]] " {") 49 | tprint(value, indent + 1); 50 | else 51 | if (type(value) == "string") then 52 | -- print(formatting .."'" .. tostring(value) .."'" ..",") 53 | printf("%s'%s',", formatting, tostring(value)) 54 | else 55 | print(formatting .. tostring(value) .. ",") 56 | end 57 | end 58 | end 59 | end 60 | print(string.rep(" ", indent - 1) .. "},") 61 | end 62 | end 63 | end 64 | 65 | if IsFileExist == nil then 66 | print("patch_shell: defining IsFileExist") 67 | IsFileExist = function(path) 68 | local testPath = "..\\..\\" .. path 69 | return ScriptCB_IsFileExist(testPath) 70 | end 71 | end 72 | 73 | if(ScriptCB_IsFileExist("..\\..\\addon\\0\\patch_scripts\\v1.3patch_strings.lvl") == 1) then 74 | print("info: read in old 1.3 patch strings") 75 | ReadDataFile("..\\..\\addon\\0\\patch_scripts\\v1.3patch_strings.lvl") 76 | end 77 | 78 | ScriptCB_DoFile("zero_patch_fs") 79 | 80 | -- trim "path\\to\\file.lvl" to "file" 81 | function trimToFileName(filePath) 82 | -- Find the last directory separator 83 | local nameStart = 1 84 | local sepStart, sepEnd = string.find(filePath, "[/\\]", nameStart) 85 | while sepStart do 86 | nameStart = sepEnd + 1 87 | sepStart, sepEnd = string.find(filePath, "[/\\]", nameStart) 88 | end 89 | 90 | -- Extract the file name part 91 | local fileName = string.sub(filePath, nameStart) 92 | 93 | -- Attempt to remove the extension 94 | local dotPosition = string.find(fileName, ".[^.]*$") 95 | if dotPosition then 96 | fileName = string.sub(fileName, 1, dotPosition - 1) 97 | end 98 | 99 | return fileName 100 | end 101 | 102 | 103 | --================== CUSTOM Galactic Conquest Framework Begin ==================== 104 | 105 | function custom_PressedGCButton(tag) 106 | print("custom_PressedGCButton()") 107 | 108 | -- if we didn't handle this, return false 109 | return false 110 | end 111 | 112 | function custom_GetGCButtonList() 113 | print("custom_GetGCButtonList()") 114 | return { 115 | -- Custom GC list starts out Empty in 'zero patch' 116 | } 117 | end 118 | 119 | function RunCustomGCFiles() 120 | print("info: RunCustomGCFiles START") 121 | local scriptName = "" 122 | local files = zero_patch_fs.getFiles("custom_gc", {".lvl", ".script"}) 123 | 124 | for i, value in ipairs(files) do 125 | if (string.find(value, "custom_gc_10.lvl")) then 126 | -- skip custom_gc_10, because that was a special one 127 | -- from zerted that loaded more custom_gc lvls 128 | else 129 | print("info: running " .. value) 130 | scriptName = trimToFileName(value) 131 | ReadDataFile(value) 132 | ScriptCB_DoFile(scriptName) 133 | end 134 | end 135 | print("info: RunCustomGCFiles END") 136 | end 137 | --================== CUSTOM Galactic Conquest Framework End ==================== 138 | 139 | -- good point of entry for the zero patch is just after the basic interface stuff is defined and 140 | -- just before the 'ifs_' screens are defined so we give a 'modify' oppurtunity to the gc/interface scripts. 141 | local old_ScriptCB_DoFile = ScriptCB_DoFile 142 | ScriptCB_DoFile = function(...) 143 | -- print("ScriptCB_DoFile: " .. arg[1]) 144 | if (arg[1] == "ifs_movietrans") then 145 | SetupZeroPatchDebugLog() 146 | local status, err = pcall(RunCustomGCFiles) 147 | if not status then 148 | local msg = "error caught in 'RunCustomGCFiles': " .. err 149 | print(msg) 150 | else 151 | -- Successful execution 152 | end 153 | end 154 | return old_ScriptCB_DoFile(unpack(arg)) 155 | end 156 | 157 | if(ScriptCB_IsFileExist("..\\..\\addon\\0\\debug.txt") == 1) then 158 | gZeroPatchPrintAllDebug = true 159 | end 160 | 161 | -- filter debug messages to messages with 'info:', 'error' or 'warn' in them. 162 | function zero_patch_is_worthy_for_debug(str) 163 | local test = string.lower(str) 164 | if (gZeroPatchPrintAllDebug or string.find(test, 'error') or string.find(test, 'warn') or string.find(test, 'info:') or 165 | string.find(test, 'debug')) then 166 | return true 167 | end 168 | return false 169 | end 170 | 171 | function SetupZeroPatchDebugLog() 172 | print("info: SetupZeroPatchDebugLog start") 173 | ScriptCB_DoFile("ifs_ingame_log") 174 | local oldPrint = print 175 | print = function(...) 176 | if (ifs_ingame_log ~= nil and zero_patch_is_worthy_for_debug(arg[1])) then 177 | ifs_ingame_log:AddToList(arg[1]) 178 | end 179 | oldPrint(unpack(arg)) 180 | end 181 | print("info: SetupZeroPatchDebugLog end") 182 | end 183 | 184 | print("patch_shell end") 185 | -------------------------------------------------------------------------------- /src/scripts/popup_prompt.lua: -------------------------------------------------------------------------------- 1 | -- popup_prompt.lua (zerted PC v1.3 r129 patch ) 2 | -- verified decompile by cbadal 3 | 4 | 5 | function gPopup_Prompt_fnActivate(this,vis) 6 | this:fnDefaultActivate(vis) 7 | if(vis) then 8 | if( gCurScreenTable and gCurScreenTable.Helptext_Back) then 9 | this.EntryScreenTable = gCurScreenTable 10 | this.bBackVis = IFObj_fnGetVis(gCurScreenTable.Helptext_Back) 11 | IFObj_fnSetVis(gCurScreenTable.Helptext_Back,nil) 12 | else 13 | this.EntryScreenTable = nil -- 26 14 | end 15 | 16 | local tagOfFirst = nil 17 | local maxItemWidth = nil 18 | tagOfFirst, maxItemWidth = ShowHideVerticalButtons(this.buttons,Vertical_YesNoButtons_layout) 19 | 20 | this.fMinWidth = maxItemWidth + 20 21 | 22 | -- Default: "yes" is selected, if not already set 23 | this.CurButton = this.CurButton or "yes" 24 | 25 | -- A little extra work, but shouldn't be too bad: deselect all 26 | -- buttons on entry 27 | IFButton_fnSelect(this.buttons.no,nil) 28 | IFButton_fnSelect(this.buttons.yes,nil) 29 | 30 | -- Select right button. 31 | IFButton_fnSelect(this.buttons[this.CurButton],1) 32 | gCurHiliteButton = this.buttons[this.CurButton] 33 | 34 | -- accept input from all controllers 35 | if (ifs_saveop and ifs_saveop.saveProfileNum) then 36 | this.iOnlyJoystick = ifs_saveop.saveProfileNum 37 | end 38 | if(this.iOnlyJoystick) then 39 | this.wasRead = ScriptCB_SetHotController(this.iOnlyJoystick) 40 | end 41 | else 42 | -- restore previous input state 43 | if(this.iOnlyJoystick) then 44 | ScriptCB_SetHotController(this.wasRead) 45 | this.wasRead = nil 46 | this.iOnlyJoystick = nil 47 | end 48 | if(this.EntryScreenTable) then 49 | IFObj_fnSetVis(this.EntryScreenTable.Helptext_Back, this.bBackVis) 50 | this.EntryScreenTable = nil 51 | end 52 | 53 | end 54 | 55 | end 56 | 57 | 58 | 59 | 60 | 61 | 62 | function gPopup_Prompt_fnInput_Accept(this, iJoystick) 63 | if (this.CurButton) then 64 | gPopup_Prompt_fnActivate(this, nil) 65 | ifelm_shellscreen_fnPlaySound("shell_menu_accept") 66 | 67 | if( this.fnDone) then 68 | if ( this.CurButton == "no") then 69 | this.fnDone(nil) 70 | return 71 | end 72 | this.fnDone(IFEditbox_fnGetString(this.box.input)) 73 | end 74 | end 75 | end 76 | 77 | function gPopup_Prompt_fnInput_GeneralUp(this, bFromAI) 78 | gDefault_Input_GeneralUp(this, bFromAI) 79 | end 80 | 81 | function gPopup_Prompt_fnInput_GeneralDown(this, bFromAI) 82 | gDefault_Input_GeneralDown(this, bFromAI) 83 | end 84 | 85 | Popup_Prompt = NewPopup { 86 | ScreenRelativeX = 0.5, 87 | ScreenRelativeY = 0.5, 88 | height = 220, 89 | width = 440, 90 | ButtonHeightHint = 70, 91 | ZPos = 50, 92 | title = NewIFText { 93 | font = gPopupTextFont, 94 | textw = 410, 95 | texth = 160, 96 | y2 = -100, 97 | flashy = 0 98 | }, 99 | buttons = NewIFContainer { 100 | y = 40 101 | }, 102 | box = NewIFContainer { 103 | ScreenRelativeX = 0.5, 104 | ScreenRelativeY = 0, 105 | y = 80, 106 | input = NewEditbox { 107 | width = 600, 108 | height = 100, 109 | font = "gamefont_medium", 110 | string = "500", 111 | MaxLen = 2000, 112 | MaxChars = 2000, 113 | bKeepsFocus = 1 114 | } 115 | }, 116 | fnSetMode = gPopup_YesNo_fnSetMode, 117 | 118 | fnActivate = gPopup_Prompt_fnActivate, 119 | 120 | Input_Accept = function(this, r1) 121 | gPopup_Prompt_fnInput_Accept(this, r1) 122 | end, 123 | 124 | Input_GeneralUp = gPopup_Prompt_fnInput_GeneralUp, 125 | 126 | Input_GeneralDown = gPopup_Prompt_fnInput_GeneralDown, 127 | 128 | Input_Back = function(this) 129 | -- Blank on purpose 130 | end, 131 | --[[ 132 | Keys that are handled in the ifs scripts: 133 | 8: Backspace 134 | 9: Tab 135 | 10: Newline 136 | 13: Carriage Return 137 | 27: Esc 138 | 32: Space 139 | 43: * 140 | 44: , 141 | 45: - 142 | 61: = 143 | 95: _ (underscore) 144 | -59: F1 145 | -211: Delete 146 | ]]-- 147 | Input_KeyDown = function(this, iKey) 148 | if((iKey == 10) or (iKey == 13)) then 149 | -- handle enter 150 | this.CurButton = "yes" 151 | gPopup_Prompt_fnInput_Accept(this, nil) 152 | return 153 | elseif ( iKey == 27 ) then 154 | -- handle ESC 155 | this.CurButton = "no" 156 | gPopup_Prompt_fnInput_Accept(this, nil) 157 | return 158 | end 159 | IFEditbox_fnAddChar( this.box.input, iKey) 160 | end 161 | } 162 | 163 | AddVerticalButtons(Popup_Prompt.buttons, Vertical_YesNoButtons_layout) 164 | Popup_Prompt.buttons.x2 = Popup_Prompt.buttons.x 165 | CreatePopupInC(Popup_Prompt, "Popup_Prompt") 166 | Popup_Prompt = DoPostDelete(Popup_Prompt) 167 | -------------------------------------------------------------------------------- /src/scripts/user_script_ai_hero_no_turret.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- AIHeroScriptNoTurret by Rayman1103 3 | -- Description: Prevents the AI Hero from entering turrets/vehicles. 4 | -- 5 | 6 | AIHeroScriptNoTurret = nil --Force garbage collection (just in case) 7 | 8 | AIHeroScriptNoTurret = 9 | { 10 | numTeams = 2, 11 | noVehicleHeroes = 12 | { 13 | rep_hero_yoda = true, 14 | cis_hero_grievous = true, 15 | }, 16 | heroClassName = {}, 17 | heroClassIndex = {}, 18 | hasStarted = false, 19 | } 20 | 21 | function AIHeroScriptNoTurret:New(o) 22 | o = o or {} 23 | setmetatable(o, self) 24 | self.__index = self 25 | return o 26 | end 27 | 28 | function AIHeroScriptNoTurret:Start() 29 | if self.hasStarted then return end 30 | 31 | local initAIHeroSupport = function(teamPtr) 32 | OnCharacterSpawnTeam( 33 | function(character) 34 | local charClass = GetCharacterClass(character) 35 | local heroIndex = self.heroClassIndex[teamPtr] 36 | 37 | if charClass == heroIndex then 38 | if IsCharacterHuman(character) then 39 | if not self.noVehicleHeroes[self.heroClassName[teamPtr]] then 40 | SetClassProperty(self.heroClassName[teamPtr], "NoEnterVehicles", "0") 41 | end 42 | else 43 | SetClassProperty(self.heroClassName[teamPtr], "NoEnterVehicles", "1") 44 | end 45 | end 46 | end, 47 | teamPtr 48 | ) 49 | end 50 | 51 | for i = 1, self.numTeams do 52 | if self.heroClassName[i] then 53 | self.heroClassIndex[i] = (GetTeamClassCount(i) - 1) 54 | initAIHeroSupport(i) 55 | end 56 | end 57 | 58 | self.hasStarted = true 59 | end 60 | 61 | gAIHeroNoTurretClasses = {} 62 | gAIHeroNoTurretNumTeams = 0 63 | 64 | --attempt to take control of (or listen to the calls of) the ScriptPostLoad function 65 | if ScriptPostLoad and not AIHeroScriptNoTurret_ScriptPostLoad then 66 | --backup the current ScriptPostLoad function 67 | local AIHeroScriptNoTurret_ScriptPostLoad = ScriptPostLoad 68 | 69 | --this is our new ScriptPostLoad function 70 | ScriptPostLoad = function(...) 71 | -- let the original function happen and catch the return value 72 | local AIHeroScriptNoTurret_SPLreturn = {AIHeroScriptNoTurret_ScriptPostLoad(unpack(arg))} 73 | 74 | if not IsCampaign() then 75 | local GameData = ScriptCB_GetNetGameDefaults() 76 | 77 | if GameData.bHeroesEnabled then 78 | if gAIHeroNoTurretNumTeams > 0 then 79 | AIHeroScriptNoTurret:New{heroClassName = gAIHeroNoTurretClasses, numTeams = 2,}:Start() 80 | end 81 | end 82 | end 83 | 84 | -- return the unmanipulated values 85 | return unpack(AIHeroScriptNoTurret_SPLreturn) 86 | end 87 | end 88 | 89 | --attempt to take control of (or listen to the calls of) the SetHeroClass function 90 | if SetHeroClass and not AIHeroScriptNoTurret_SetHeroClass then 91 | --backup the current SetHeroClass function 92 | local AIHeroScriptNoTurret_SetHeroClass = SetHeroClass 93 | 94 | --this is our new SetHeroClass function 95 | SetHeroClass = function(teamPtr, heroClassName, ...) 96 | -- let the original function happen and catch the return value 97 | local AIHeroScriptNoTurret_SHCreturn = {AIHeroScriptNoTurret_SetHeroClass(teamPtr, heroClassName, unpack(arg))} 98 | 99 | if heroClassName ~= "" then 100 | gAIHeroNoTurretClasses[teamPtr] = heroClassName 101 | 102 | if teamPtr > gAIHeroNoTurretNumTeams then 103 | gAIHeroNoTurretNumTeams = teamPtr 104 | end 105 | end 106 | 107 | -- return the unmanipulated values 108 | return unpack(AIHeroScriptNoTurret_SHCreturn) 109 | end 110 | end -------------------------------------------------------------------------------- /src/scripts/user_script_cc_music_fix.lua: -------------------------------------------------------------------------------- 1 | -- user_script_cc_music_fix.lua 2 | -- Fixes the music in mod levels due to the addition of sound\\music.lvl (Aspyr patch 3) 3 | -- author: BAD-AL 4 | 5 | -- OpenAudioStream("sound\\global.lvl", "cw_music") 6 | -- OpenAudioStream("sound\\global.lvl", "gcw_music") 7 | 8 | -- OpenAudioStream("sound\\music.lvl","cw_music") 9 | -- OpenAudioStream("sound\\music.lvl","gcw_music") 10 | 11 | if( ScriptCB_IsFileExist("sound\\music.lvl") == 1) then 12 | print("info: sound/music.lvl exists, define special fix") 13 | 14 | local oldOpenAudioStream = OpenAudioStream 15 | OpenAudioStream = function(...) 16 | 17 | if( table.getn(arg) > 1 18 | and string.find(string.lower(arg[1]), "^sound[\\/]global.lvl") ~= nil ) 19 | and (arg[2] == "cw_music" or arg[2] == "gcw_music" ) then 20 | print("info: (g)cw_music global.lvl -> music.lvl ") 21 | arg[1] = "sound\\music.lvl" 22 | end 23 | 24 | return oldOpenAudioStream(unpack(arg)) 25 | end 26 | else 27 | print("info: sound/music.lvl does not exist, no special fix") 28 | end -------------------------------------------------------------------------------- /src/scripts/user_script_cis_tread_tank.lua: -------------------------------------------------------------------------------- 1 | -- user_script_snailtank_fix.lua 2 | -- Description: Fixes issue where cis snailtank treads don't move. 3 | -- 4 | local oldReadDataFile = ReadDataFile 5 | local function LoadSnailtank() 6 | print("info: LoadSnailtank()") 7 | -- use the zero_fs to find the right file; 'cis_tread_tank.msh' file munged with the proper options 8 | local result = zero_patch_fs.getFiles("cis_tread_tank", {".lvl"}) 9 | if( table.getn(result) > 0) then 10 | ReadDataFile(result[1]) 11 | print("info: run fix for 'cis_tread_tank' ") 12 | else 13 | print("info: Oopsie! 'cis_tread_tank.lvl' not found") 14 | end 15 | end 16 | 17 | ReadDataFile = function(...) 18 | if(string.upper(arg[1]) == "SIDE\\CIS.LVL") then 19 | local index = 2 20 | local length = table.getn(arg) 21 | while index < length do 22 | if( string.lower(arg[index]) == "cis_tread_snailtank") then 23 | LoadSnailtank() 24 | break 25 | end 26 | index = index + 1 27 | end 28 | end 29 | return oldReadDataFile(unpack(arg)) 30 | end -------------------------------------------------------------------------------- /src/scripts/user_script_han_fix.lua: -------------------------------------------------------------------------------- 1 | -- user_script_han_fix.lua 2 | -- HanFix by bad_al_, Rayman1103 3 | -- Description: Fixes issue where Han Solo crashes the game. 4 | -- 5 | 6 | -- need to set 7 | -- MedalsTypeToLock = -1 in "all_weap_hero_hanpistol.odf" 8 | 9 | local oldReadDataFile = ReadDataFile 10 | 11 | local function LoadHanPistol() 12 | print("info: LoadHanPistol()") 13 | -- use the zero_fs to find the right file 14 | local result = zero_patch_fs.getFiles("all_weap_hero_hanpistol", {".lvl"}) 15 | if( table.getn(result) > 0) then 16 | ReadDataFile(result[1], "all_weap_hero_hanpistol") 17 | print("info: run fix for 'all_weap_hero_hanpistol' ") 18 | else 19 | print("info: Oopsie! 'all_weap_hero_hanpistol.lvl' not found ") 20 | end 21 | end 22 | 23 | ReadDataFile = function(...) 24 | if(string.upper(arg[1]) == "SIDE\\ALL1.LVL") then 25 | local index = 2 26 | local length = table.getn(arg) 27 | while index < length do 28 | if( string.lower(arg[index]) == "all_hero_hansolo_tat") then 29 | LoadHanPistol() 30 | break 31 | end 32 | index = index + 1 33 | end 34 | end 35 | return oldReadDataFile(unpack(arg)) 36 | end 37 | -------------------------------------------------------------------------------- /src/scripts/user_script_hero_vo.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- HeroVOScript by Rayman1103 3 | -- Description: Plays voicelines when Heroes enter the battlefield or have been defeated. 4 | -- 5 | 6 | HeroVOScript = nil --Force garbage collection (just in case) 7 | 8 | HeroVOScript = 9 | { 10 | numTeams = 2, 11 | heroVOList = 12 | { 13 | [1] = -- Alliance 14 | { 15 | Us = 16 | { 17 | all_hero_luke_jedi = "all_hero_luke_", 18 | all_hero_hansolo_tat = "all_hero_solo_", 19 | all_hero_leia = "all_hero_leia_", 20 | all_hero_chewbacca = "all_hero_chewbacca_", 21 | all_hero_luke_pilot = "all_hero_luke_", 22 | all_hero_luke_storm = "all_hero_luke_", 23 | all_hero_luke_tat = "all_hero_luke_", 24 | all_hero_hansolo_storm = "all_hero_solo_", 25 | rep_hero_obiwan = "all_hero_obiwan_", 26 | rep_hero_obiwan_old = "all_hero_obiwan_", 27 | all_hero_obiwan = "all_hero_obiwan_", 28 | }, 29 | Them = 30 | { 31 | imp_hero_darthvader = "all_hero_vader_", 32 | imp_hero_emperor = "all_hero_emperor_", 33 | imp_hero_bobafett = "all_hero_boba_", 34 | rep_hero_anakin = "all_hero_vader_", 35 | rep_hero_cloakedanakin = "all_hero_vader_", 36 | }, 37 | }, 38 | [2] = -- Empire 39 | { 40 | Us = 41 | { 42 | imp_hero_darthvader = "imp_hero_vader_", 43 | imp_hero_emperor = "imp_hero_emperor_", 44 | imp_hero_bobafett = "imp_hero_boba_", 45 | rep_hero_anakin = "imp_hero_vader_", 46 | rep_hero_cloakedanakin = "imp_hero_vader_", 47 | }, 48 | Them = 49 | { 50 | all_hero_luke_jedi = "imp_hero_luke_", 51 | all_hero_hansolo_tat = "imp_hero_solo_", 52 | all_hero_leia = "imp_hero_leia_", 53 | all_hero_chewbacca = "imp_hero_chewbacca_", 54 | all_hero_luke_pilot = "imp_hero_luke_", 55 | all_hero_luke_storm = "imp_hero_luke_", 56 | all_hero_luke_tat = "imp_hero_luke_", 57 | all_hero_hansolo_storm = "imp_hero_solo_", 58 | rep_hero_obiwan = "imp_hero_obiwan_", 59 | rep_hero_obiwan_old = "imp_hero_obiwan_", 60 | all_hero_obiwan = "imp_hero_obiwan_", 61 | }, 62 | }, 63 | [3] = -- Republic 64 | { 65 | Us = 66 | { 67 | rep_hero_yoda = "rep_hero_yoda_", 68 | rep_hero_macewindu = "rep_hero_windu_", 69 | rep_hero_anakin = "rep_hero_anakin_", 70 | rep_hero_aalya = "rep_hero_aayla_", 71 | rep_hero_kiyadimundi = "rep_hero_mundi_", 72 | rep_hero_obiwan = "rep_hero_kenobi_", 73 | }, 74 | Them = 75 | { 76 | cis_hero_grievous = "rep_hero_grievous_", 77 | cis_hero_darthmaul = "rep_hero_maul_", 78 | cis_hero_countdooku = "rep_hero_dooku_", 79 | cis_hero_jangofett = "rep_hero_jango_", 80 | }, 81 | }, 82 | [4] = -- CIS 83 | { 84 | Us = 85 | { 86 | cis_hero_grievous = "cis_hero_grievous_", 87 | cis_hero_darthmaul = "cis_hero_maul_", 88 | cis_hero_countdooku = "cis_hero_dooku_", 89 | cis_hero_jangofett = "cis_hero_jango_", 90 | }, 91 | Them = 92 | { 93 | rep_hero_yoda = "cis_hero_yoda_", 94 | rep_hero_macewindu = "cis_hero_windu_", 95 | rep_hero_anakin = "cis_hero_anakin_", 96 | rep_hero_aalya = "cis_hero_aayla_", 97 | rep_hero_kiyadimundi = "cis_hero_mundi_", 98 | rep_hero_obiwan = "cis_hero_kenobi_", 99 | }, 100 | }, 101 | }, 102 | otherTeam = {}, 103 | heroClassName = {}, 104 | heroClassIndex = {}, 105 | hasStarted = false, 106 | } 107 | 108 | function HeroVOScript:New(o) 109 | o = o or {} 110 | setmetatable(o, self) 111 | self.__index = self 112 | return o 113 | end 114 | 115 | function HeroVOScript:Start() 116 | if self.hasStarted then return end 117 | 118 | local getTeamName = function(teamPtr) 119 | local teamName = nil 120 | 121 | if GetTeamFactionId(teamPtr) == 1 then 122 | teamName = "all" 123 | elseif GetTeamFactionId(teamPtr) == 2 then 124 | teamName = "imp" 125 | elseif GetTeamFactionId(teamPtr) == 3 then 126 | teamName = "rep" 127 | elseif GetTeamFactionId(teamPtr) == 4 then 128 | teamName = "cis" 129 | end 130 | 131 | return teamName 132 | end 133 | 134 | local initHeroVO = function(teamPtr) 135 | OnCharacterDeathTeam( 136 | function(character, killer) 137 | local charClass = GetCharacterClass(character) 138 | if charClass == self.heroClassIndex[teamPtr] then 139 | local usVO = self.heroVOList[GetTeamFactionId(teamPtr)].Us[self.heroClassName[teamPtr]] 140 | local themVO = self.heroVOList[GetTeamFactionId(self.otherTeam[teamPtr])].Them[self.heroClassName[teamPtr]] 141 | 142 | if usVO then 143 | BroadcastVoiceOver(usVO .. "exit", teamPtr) 144 | end 145 | if themVO then 146 | BroadcastVoiceOver(themVO .. "exit", self.otherTeam[teamPtr]) 147 | end 148 | end 149 | end, 150 | teamPtr 151 | ) 152 | 153 | OnCharacterSpawnTeam( 154 | function(character) 155 | local charClass = GetCharacterClass(character) 156 | local heroIndex = self.heroClassIndex[teamPtr] 157 | 158 | if charClass == heroIndex then 159 | local usVO = self.heroVOList[GetTeamFactionId(teamPtr)].Us[self.heroClassName[teamPtr]] 160 | local themVO = self.heroVOList[GetTeamFactionId(self.otherTeam[teamPtr])].Them[self.heroClassName[teamPtr]] 161 | 162 | if usVO then 163 | BroadcastVoiceOver(usVO .. "enter", teamPtr) 164 | else 165 | if getTeamName(teamPtr) then 166 | BroadcastVoiceOver("team_bonus_leader_us_" .. getTeamName(teamPtr), teamPtr) 167 | end 168 | end 169 | if themVO then 170 | BroadcastVoiceOver(themVO .. "enter", self.otherTeam[teamPtr]) 171 | else 172 | if getTeamName(self.otherTeam[teamPtr]) then 173 | BroadcastVoiceOver("team_bonus_leader_them_" .. getTeamName(self.otherTeam[teamPtr]), self.otherTeam[teamPtr]) 174 | end 175 | end 176 | end 177 | end, 178 | teamPtr 179 | ) 180 | end 181 | 182 | for i = 1, self.numTeams do 183 | if self.heroClassName[i] then 184 | self.heroClassIndex[i] = (GetTeamClassCount(i) - 1) 185 | self.otherTeam[i] = (i == 2 and 1 or 2) 186 | initHeroVO(i) 187 | end 188 | end 189 | 190 | self.hasStarted = true 191 | end 192 | 193 | gHeroVOClasses = {} 194 | gHeroVONumTeams = 0 195 | 196 | --attempt to take control of (or listen to the calls of) the ScriptPostLoad function 197 | if ScriptPostLoad and not HeroVOScript_ScriptPostLoad then 198 | --backup the current ScriptPostLoad function 199 | local HeroVOScript_ScriptPostLoad = ScriptPostLoad 200 | 201 | --this is our new ScriptPostLoad function 202 | ScriptPostLoad = function(...) 203 | -- let the original function happen and catch the return value 204 | local heroVOScript_SPLreturn = {HeroVOScript_ScriptPostLoad(unpack(arg))} 205 | 206 | if not IsCampaign() then 207 | local GameData = ScriptCB_GetNetGameDefaults() 208 | 209 | if GameData.bHeroesEnabled then 210 | if GameData.bHeroesEnabled then 211 | if gHeroVONumTeams > 0 then 212 | HeroVOScript:New{heroClassName = gHeroVOClasses, numTeams = 2,}:Start() 213 | end 214 | end 215 | end 216 | end 217 | 218 | -- return the unmanipulated values 219 | return unpack(heroVOScript_SPLreturn) 220 | end 221 | end 222 | 223 | --attempt to take control of (or listen to the calls of) the SetHeroClass function 224 | if SetHeroClass and not HeroVOScript_SetHeroClass then 225 | --backup the current SetHeroClass function 226 | local HeroVOScript_SetHeroClass = SetHeroClass 227 | 228 | --this is our new SetHeroClass function 229 | SetHeroClass = function(teamPtr, heroClassName, ...) 230 | -- let the original function happen and catch the return value 231 | local heroVOScript_SHCreturn = {HeroVOScript_SetHeroClass(teamPtr, heroClassName, unpack(arg))} 232 | 233 | if heroClassName ~= "" then 234 | gHeroVOClasses[teamPtr] = heroClassName 235 | 236 | if teamPtr > gHeroVONumTeams then 237 | gHeroVONumTeams = teamPtr 238 | end 239 | end 240 | 241 | -- return the unmanipulated values 242 | return unpack(heroVOScript_SHCreturn) 243 | end 244 | end -------------------------------------------------------------------------------- /src/scripts/user_script_loc_fix.lua: -------------------------------------------------------------------------------- 1 | -- user_script_loc_fix.lua 2 | 3 | -- Hopefully Aspyr will fix the bf2 loc issue and this can be disgarded after the next update. 4 | 5 | -- summary: 6 | -- Loads 'dc:core.lvl' once the script calls ReadDataFile on a path starting with 'dc:'. 7 | -- Should work in most cases. 8 | -- will crash if a mod does not have a core.lvl file (this is rare; work around would be to put a core.lvl one in the mod that doean't have one). 9 | 10 | zz_dc_core_loaded = false 11 | 12 | local oldReadDataFile = ReadDataFile 13 | ReadDataFile = function(...) 14 | if( string.find( string.lower(arg[1]), "dc:" ) == 1 and not zz_dc_core_loaded ) then 15 | print("info: loading 'dc:core.lvl' [to fix mod loc issue]") 16 | oldReadDataFile("dc:core.lvl") 17 | zz_dc_core_loaded = true 18 | end 19 | return oldReadDataFile(unpack(arg)) 20 | end -------------------------------------------------------------------------------- /src/scripts/user_script_locals_yellow_team.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- LocalsYellowTeamScript by Rayman1103 3 | -- Description: Makes the locals team have yellow CP and unit icons. 4 | -- 5 | 6 | gLocalsYellowTeamScriptHasLocals = false 7 | gLocalsYellowTeamScriptInPostLoad = false 8 | gLocalsYellowTeamScriptLocalsTeams = {} 9 | gLocalsYellowTeamScriptTeam = { [1] = {}, [2] = {} } 10 | 11 | --attempt to take control of (or listen to the calls of) the ScriptPostLoad function 12 | if ScriptPostLoad and not LocalsYellowTeamScript_ScriptPostLoad then 13 | --backup the current ScriptPostLoad function 14 | local LocalsYellowTeamScript_ScriptPostLoad = ScriptPostLoad 15 | 16 | --this is our new ScriptPostLoad function 17 | ScriptPostLoad = function(...) 18 | gLocalsYellowTeamScriptInPostLoad = true 19 | -- let the original function happen and catch the return value 20 | local LocalsYellowTeamScript_SPLreturn = {LocalsYellowTeamScript_ScriptPostLoad(unpack(arg))} 21 | 22 | SetTeamAsEnemy = LocalsYellowTeamScript_SetTeamAsEnemy 23 | SetTeamAsFriend = LocalsYellowTeamScript_SetTeamAsFriend 24 | SetTeamName = LocalsYellowTeamScript_SetTeamName 25 | 26 | if gLocalsYellowTeamScriptHasLocals then 27 | for i, team in pairs(gLocalsYellowTeamScriptLocalsTeams) do 28 | for u = 1, 2, 1 do 29 | if gLocalsYellowTeamScriptTeam[u][team] == "friend" then 30 | SetTeamAsFriend(u, team) 31 | else 32 | SetTeamAsEnemy(u, team) 33 | end 34 | end 35 | end 36 | end 37 | 38 | -- return the unmanipulated values 39 | return unpack(LocalsYellowTeamScript_SPLreturn) 40 | end 41 | end 42 | 43 | --attempt to take control of (or listen to the calls of) the SetTeamAsEnemy function 44 | if SetTeamAsEnemy and not LocalsYellowTeamScript_SetTeamAsEnemy then 45 | --backup the current SetTeamAsEnemy function 46 | LocalsYellowTeamScript_SetTeamAsEnemy = SetTeamAsEnemy 47 | 48 | --this is our new SetTeamAsEnemy function 49 | SetTeamAsEnemy = function(teamPtr1, teamPtr2, ...) 50 | -- let the original function happen and catch the return value 51 | local LocalsYellowTeamScript_STAEreturn = {LocalsYellowTeamScript_SetTeamAsEnemy(teamPtr1, teamPtr2, unpack(arg))} 52 | 53 | if teamPtr1 < 3 and teamPtr2 > 2 then 54 | gLocalsYellowTeamScriptTeam[teamPtr1][teamPtr2] = "enemy" 55 | if not gLocalsYellowTeamScriptInPostLoad and GetTeamFactionId(teamPtr2) == 5 then 56 | SetTeamAsNeutral(teamPtr1, teamPtr2) 57 | end 58 | end 59 | 60 | -- return the unmanipulated values 61 | return unpack(LocalsYellowTeamScript_STAEreturn) 62 | end 63 | end 64 | 65 | --attempt to take control of (or listen to the calls of) the SetTeamAsFriend function 66 | if SetTeamAsFriend and not LocalsYellowTeamScript_SetTeamAsFriend then 67 | --backup the current SetTeamAsFriend function 68 | LocalsYellowTeamScript_SetTeamAsFriend = SetTeamAsFriend 69 | 70 | --this is our new SetTeamAsFriend function 71 | SetTeamAsFriend = function(teamPtr1, teamPtr2, ...) 72 | -- let the original function happen and catch the return value 73 | local LocalsYellowTeamScript_STAEreturn = {LocalsYellowTeamScript_SetTeamAsFriend(teamPtr1, teamPtr2, unpack(arg))} 74 | 75 | if teamPtr1 < 3 and teamPtr2 > 2 then 76 | gLocalsYellowTeamScriptTeam[teamPtr1][teamPtr2] = "friend" 77 | if not gLocalsYellowTeamScriptInPostLoad and GetTeamFactionId(teamPtr2) == 5 then 78 | SetTeamAsNeutral(teamPtr1, teamPtr2) 79 | end 80 | end 81 | 82 | -- return the unmanipulated values 83 | return unpack(LocalsYellowTeamScript_STAEreturn) 84 | end 85 | end 86 | 87 | --attempt to take control of (or listen to the calls of) the SetTeamName function 88 | if SetTeamName and not LocalsYellowTeamScript_SetTeamName then 89 | --backup the current SetTeamName function 90 | LocalsYellowTeamScript_SetTeamName = SetTeamName 91 | 92 | --this is our new SetTeamName function 93 | SetTeamName = function(teamPtr, teamName, ...) 94 | -- let the original function happen and catch the return value 95 | local LocalsYellowTeamScript_STNreturn = {LocalsYellowTeamScript_SetTeamName(teamPtr, teamName, unpack(arg))} 96 | 97 | if teamPtr > 2 and GetTeamFactionId(teamPtr) == 5 then 98 | gLocalsYellowTeamScriptHasLocals = true 99 | table.insert(gLocalsYellowTeamScriptLocalsTeams, teamPtr) 100 | SetTeamAsNeutral(1, teamPtr) 101 | SetTeamAsNeutral(2, teamPtr) 102 | end 103 | 104 | -- return the unmanipulated values 105 | return unpack(LocalsYellowTeamScript_STNreturn) 106 | end 107 | end -------------------------------------------------------------------------------- /src/scripts/user_script_rhenvar2_cp_fix.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- RhenVar2CPFix by Rayman1103 3 | -- Description: Fixes issue where Command Posts 1 and 4 aren't counted as valid CPs for Rhen Var Citadel Conquest. 4 | -- 5 | 6 | --attempt to take control of (or listen to the calls of) the ScriptPostLoad function 7 | if ScriptPostLoad and not RhenVar2CPFix_ScriptPostLoad then 8 | --backup the current ScriptPostLoad function 9 | local RhenVar2CPFix_ScriptPostLoad = ScriptPostLoad 10 | 11 | --this is our new ScriptPostLoad function 12 | ScriptPostLoad = function(...) 13 | -- let the original function happen and catch the return value 14 | local RhenVar2CPFix_SPLreturn = {RhenVar2CPFix_ScriptPostLoad(unpack(arg))} 15 | 16 | --check if the map's Rhen Var Citadel and if the Conquest objective is defined 17 | if GetWorldFilename() == "rhenvar2" and ObjectiveConquest ~= nil then 18 | --if cp6 is undefined, add the missing CPs 19 | if cp6 == nil then 20 | cp6 = CommandPost:New{name = "CP1"} 21 | cp7 = CommandPost:New{name = "CP4"} 22 | conquest:AddCommandPost(cp6) 23 | conquest:AddCommandPost(cp7) 24 | conquest:Start() 25 | end 26 | end 27 | 28 | -- return the unmanipulated values 29 | return unpack(RhenVar2CPFix_SPLreturn) 30 | end 31 | end -------------------------------------------------------------------------------- /src/scripts/zero_patch_fs.lua: -------------------------------------------------------------------------------- 1 | 2 | local function ReadFileSystemLuaFile() 3 | local checkFile = "addon2\\0\\patch_scripts\\fs.lua" 4 | local prefix = "addon2\\" 5 | if(IsFileExist("addon2\\0\\addme.script") == 0 ) then 6 | -- make compatible with OG version too 7 | checkFile = "addon\\0\\patch_scripts\\fs.lua" 8 | end 9 | if(IsFileExist(checkFile) == 1) then 10 | dofile(checkFile) 11 | print("info: ReadFileSystemLuaFile; read in fs.lua") 12 | return true 13 | end 14 | return false 15 | end 16 | 17 | -- The switch (NX) version does not support 'dofile()' 18 | local function ReadFileSystemScriptFile() 19 | local checkFile = "..\\..\\addon\\0\\patch_scripts\\fs.script" 20 | if( ScriptCB_IsFileExist(checkFile) == 1 ) then 21 | ReadDataFile(checkFile) 22 | ScriptCB_DoFile("fs") 23 | print("info: ReadFileSystemScriptFile; read in fs.script") 24 | return true 25 | end 26 | return false 27 | end 28 | 29 | 30 | local function ReadInFileSystem() 31 | print("info: ReadInFileSystem()") 32 | local prefix = "addon2\\" 33 | if(IsFileExist("addon2\\0\\addme.script") == 0 ) then 34 | -- make compatible with OG version too 35 | prefix = "addon\\" 36 | end 37 | 38 | zero_patch_files_string = "" 39 | if( ReadFileSystemScriptFile() or ReadFileSystemLuaFile() ) then 40 | -- the zero_patch_files_string string should exist now 41 | local function splitStringByNewline(str) 42 | local t = {} 43 | local start = 1 44 | local splitStart, splitEnd = string.find(str, "\r?\n", start) 45 | while splitStart do 46 | table.insert(t, string.sub(str, start, splitStart - 1)) 47 | start = splitEnd + 1 48 | splitStart, splitEnd = string.find(str, "\r?\n", start) 49 | end 50 | table.insert(t, string.sub(str, start)) 51 | return t 52 | end 53 | zero_patch_files_string = string.gsub(zero_patch_files_string, "/", "\\") -- normalize path sep 54 | local myList = splitStringByNewline(string.lower(zero_patch_files_string)) 55 | 56 | local allFiles = { } 57 | 58 | for i, line in ipairs(myList) do 59 | local startPos, endPos = string.find(line, prefix) 60 | -- TODO: discuss if we want to disable scripts/files this way. 61 | if startPos and string.find(line, "disabled") == nil then 62 | -- Extract and print part of the line from "addon2\" to the end 63 | local part = "..\\..\\" .. string.sub(line, startPos) 64 | table.insert( allFiles, part) 65 | --print(part) 66 | end 67 | end 68 | -- clean out memory 69 | zero_patch_files_string = nil 70 | 71 | return allFiles 72 | end 73 | end 74 | 75 | local function hasExtension(path, ext_list) 76 | if ext_list == nil then 77 | return true 78 | end 79 | -- Iterate through each extension in the list 80 | for _, ext in ipairs(ext_list) do 81 | -- Check if the path ends with the current extension 82 | if string.sub(path, -string.len(ext)) == ext then 83 | return true 84 | end 85 | end 86 | return false 87 | end 88 | zero_patch_fs = { 89 | 90 | -- Get files given a lua pattern. For get all files, pass no argument 91 | -- Lua pattern matching is a bit on the 'weak' side, it is not 'POSIX' compliant. 92 | -- Lua pattern matching https://riptutorial.com/lua/example/20315/lua-pattern-matching 93 | -- pattern - A Lua pattern 94 | -- ext_list - extensions you need the file to have; example: { ".lvl", ".script"} 95 | getFiles = function(pattern, ext_list) 96 | local matchedFiles = {} 97 | local lowerPattern = pattern and string.lower(pattern) 98 | local file_system = ReadInFileSystem() -- let this get cleaned out after use, how many times would it get called anyway? 99 | for i, v in ipairs(file_system) do 100 | if( pattern == nil ) then 101 | table.insert(matchedFiles, v) 102 | elseif string.find(v, lowerPattern) and hasExtension(v, ext_list) then 103 | table.insert(matchedFiles, v) 104 | end 105 | end 106 | if(table.getn(matchedFiles) == 0 and not gFinalBuild ) then 107 | -- print out a help message if using the debugger 108 | print(string.format("zero_patch_fs.getFiles('%s') -> Matched 0 files.\n If you need help with lua patterns check the help message:", tostring(pattern))) 109 | zero_patch_fs.printHelp() 110 | end 111 | table.sort(matchedFiles) 112 | return matchedFiles 113 | end, 114 | 115 | printHelp = function() 116 | local msg = [[ 117 | ====================================================== 118 | zero_patch_fs.print_help() 119 | getFiles(pattern, extension_list) 120 | ====================================================== 121 | Quick Reminders: 122 | ^ = Beginning of string/line 123 | $ = End of string/line 124 | .* = match any number of characters 125 | or = doesn't exist in Lua patterns 126 | 127 | Example: 128 | Get all .lvl files under the 'abc' addon folder: 129 | abc_lvls = zero_patch_fs.getFiles(".*abc.*lvl") 130 | user_scripts = zero_patch_fs.getFiles("user_script_", {".lvl", ".script"}) 131 | 132 | Or use the following guide for lua pattern matching: 133 | https://riptutorial.com/lua/example/20315/lua-pattern-matching 134 | ]] 135 | print(msg) 136 | end 137 | } -------------------------------------------------------------------------------- /src/textures/border_3_pieces.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/src/textures/border_3_pieces.tga -------------------------------------------------------------------------------- /src/textures/border_3a_pieces.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/src/textures/border_3a_pieces.tga -------------------------------------------------------------------------------- /src/textures/border_popup.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/src/textures/border_popup.tga -------------------------------------------------------------------------------- /src/textures/opaque_rect.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gametoast/ClassicCollectionModPatch/9757908133fed30902ef422c59bd723b125a05f1/src/textures/opaque_rect.tga -------------------------------------------------------------------------------- /switch_notes.txt: -------------------------------------------------------------------------------- 1 | 2 | Notes on Switch (1st release; no patches): 3 | 1. All files seem to need to be lower case. 4 | 2. 'dofile()' doesn't work. 5 | 3. 'ScriptCB_IsFileExist("side\\rep.lvl")' Works properly only when the target file is in the 'data2\\_lvl_common' folder. 6 | --------------------------------------------------------------------------------