-
-; *** Buttons
-ButtonBack=< Na&zaj
-ButtonNext=&Naprej >
-ButtonInstall=&Namesti
-ButtonOK=V redu
-ButtonCancel=Preklii
-ButtonYes=&Da
-ButtonYesToAll=Da za &vse
-ButtonNo=&Ne
-ButtonNoToAll=N&e za vse
-ButtonFinish=&Konaj
-ButtonBrowse=Pre&brskaj...
-ButtonWizardBrowse=Pre&brskaj...
-ButtonNewFolder=&Ustvari novo mapo
-
-; *** "Select Language" dialog messages
-SelectLanguageTitle=Izbira jezika namestitve
-SelectLanguageLabel=Izberite jezik, ki ga elite uporabljati med namestitvijo.
-
-; *** Common wizard text
-ClickNext=Kliknite Naprej za nadaljevanje namestitve ali Preklii za prekinitev namestitve.
-BeveledLabel=
-BrowseDialogTitle=Izbira mape
-BrowseDialogLabel=Izberite mapo s spiska, nato kliknite V redu.
-NewFolderName=Nova mapa
-
-; *** "Welcome" wizard page
-WelcomeLabel1=Dobrodoli v namestitev programa [name].
-WelcomeLabel2=V raunalnik boste namestili program [name/ver].%n%nPriporoljivo je, da pred zaetkom namestitve zaprete vse odprte programe.
-
-; *** "Password" wizard page
-WizardPassword=Geslo
-PasswordLabel1=Namestitev je zaitena z geslom.
-PasswordLabel3=Vnesite geslo, nato kliknite Naprej za nadaljevanje. Pri vnaanju pazite na male in velike rke.
-PasswordEditLabel=&Geslo:
-IncorrectPassword=Vneseno geslo ni pravilno. Poizkusite ponovno.
-
-; *** "License Agreement" wizard page
-WizardLicense=Licenna pogodba
-LicenseLabel=Pred nadaljevanjem preberite licenno pogodbo za uporabo programa.
-LicenseLabel3=Preberite licenno pogodbo za uporabo programa. Program lahko namestite le, e se s pogodbo v celoti strinjate.
-LicenseAccepted=&Da, sprejemam vse pogoje licenne pogodbe
-LicenseNotAccepted=N&e, pogojev licenne pogodbe ne sprejmem
-
-; *** "Information" wizard pages
-WizardInfoBefore=Informacije
-InfoBeforeLabel=Pred nadaljevanjem preberite naslednje pomembne informacije.
-InfoBeforeClickLabel=Ko boste pripravljeni na nadaljevanje namestitve, kliknite Naprej.
-WizardInfoAfter=Informacije
-InfoAfterLabel=Pred nadaljevanjem preberite naslednje pomembne informacije.
-InfoAfterClickLabel=Ko boste pripravljeni na nadaljevanje namestitve, kliknite Naprej.
-
-; *** "User Information" wizard page
-WizardUserInfo=Podatki o uporabniku
-UserInfoDesc=Vnesite svoje podatke.
-UserInfoName=&Ime:
-UserInfoOrg=&Podjetje:
-UserInfoSerial=&Serijska tevilka:
-UserInfoNameRequired=Vnos imena je obvezen.
-
-; *** "Select Destination Location" wizard page
-WizardSelectDir=Izbira ciljnega mesta
-SelectDirDesc=Kam elite namestiti program [name]?
-SelectDirLabel3=Program [name] bo nameen v naslednjo mapo.
-SelectDirBrowseLabel=Za nadaljevanje kliknite Naprej. e elite izbrati drugo mapo, kliknite Prebrskaj.
-DiskSpaceGBLabel=Na disku mora biti vsaj [gb] GB prostora.
-DiskSpaceMBLabel=Na disku mora biti vsaj [mb] MB prostora.
-CannotInstallToNetworkDrive=Programa ni mogoe namestiti na mreni pogon.
-CannotInstallToUNCPath=Programa ni mogoe namestiti v UNC pot.
-InvalidPath=Vpisati morate polno pot vkljuno z oznako pogona. Primer:%n%nC:\PROGRAM%n%nali UNC pot v obliki:%n%n\\strenik\mapa_skupne_rabe
-InvalidDrive=Izbrani pogon ali omreno sredstvo UNC ne obstaja ali ni dostopno. Izberite drugega.
-DiskSpaceWarningTitle=Na disku ni dovolj prostora
-DiskSpaceWarning=Namestitev potrebuje vsaj %1 KB prostora, toda na izbranem pogonu je na voljo le %2 KB.%n%nelite kljub temu nadaljevati?
-DirNameTooLong=Ime mape ali poti je predolgo.
-InvalidDirName=Ime mape ni veljavno.
-BadDirName32=Ime mape ne sme vsebovati naslednjih znakov:%n%n%1
-DirExistsTitle=Mapa e obstaja
-DirExists=Mapa%n%n%1%n%ne obstaja. elite program vseeno namestiti v to mapo?
-DirDoesntExistTitle=Mapa ne obstaja
-DirDoesntExist=Mapa %n%n%1%n%nne obstaja. Ali jo elite ustvariti?
-
-; *** "Select Components" wizard page
-WizardSelectComponents=Izbira komponent
-SelectComponentsDesc=Katere komponente elite namestiti?
-SelectComponentsLabel2=Oznaite komponente, ki jih elite namestiti; odznaite komponente, ki jih ne elite namestiti. Kliknite Naprej, ko boste pripravljeni za nadaljevanje.
-FullInstallation=Popolna namestitev
-; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
-CompactInstallation=Osnovna namestitev
-CustomInstallation=Namestitev po meri
-NoUninstallWarningTitle=Komponente e obstajajo
-NoUninstallWarning=Namestitveni program je ugotovil, da so naslednje komponente e nameene v raunalniku:%n%n%1%n%nNamestitveni program teh e nameenih komponent ne bo odstranil.%n%nelite vseeno nadaljevati?
-ComponentSize1=%1 KB
-ComponentSize2=%1 MB
-ComponentsDiskSpaceGBLabel=Za izbrano namestitev potrebujete vsaj [gb] GB prostora na disku.
-ComponentsDiskSpaceMBLabel=Za izbrano namestitev potrebujete vsaj [mb] MB prostora na disku.
-
-; *** "Select Additional Tasks" wizard page
-WizardSelectTasks=Izbira dodatnih opravil
-SelectTasksDesc=Katera dodatna opravila elite izvesti?
-SelectTasksLabel2=Izberite dodatna opravila, ki jih bo namestitveni program opravil med namestitvijo programa [name], nato kliknite Naprej.
-
-; *** "Select Start Menu Folder" wizard page
-WizardSelectProgramGroup=Izbira mape v meniju Zaetek
-SelectStartMenuFolderDesc=Kje naj namestitveni program ustvari blinjice?
-SelectStartMenuFolderLabel3=Namestitveni program bo ustvaril blinjice v naslednji mapi v meniju Start.
-SelectStartMenuFolderBrowseLabel=Za nadaljevanje kliknite Naprej. e elite izbrati drugo mapo, kliknite Prebrskaj.
-MustEnterGroupName=Ime skupine mora biti vpisano.
-GroupNameTooLong=Ime mape ali poti je predolgo.
-InvalidGroupName=Ime mape ni veljavno.
-BadGroupName=Ime skupine ne sme vsebovati naslednjih znakov:%n%n%1
-NoProgramGroupCheck2=&Ne ustvari mape v meniju Start
-
-; *** "Ready to Install" wizard page
-WizardReady=Pripravljen za namestitev
-ReadyLabel1=Namestitveni program je pripravljen za namestitev programa [name] v va raunalnik.
-ReadyLabel2a=Kliknite Namesti za zaetek nameanja. Kliknite Nazaj, e elite pregledati ali spremeniti katerokoli nastavitev.
-ReadyLabel2b=Kliknite Namesti za zaetek nameanja.
-ReadyMemoUserInfo=Podatki o uporabniku:
-ReadyMemoDir=Ciljno mesto:
-ReadyMemoType=Vrsta namestitve:
-ReadyMemoComponents=Izbrane komponente:
-ReadyMemoGroup=Mapa v meniju Zaetek:
-ReadyMemoTasks=Dodatna opravila:
-
-; *** TDownloadWizardPage wizard page and DownloadTemporaryFile
-DownloadingLabel=Prenaam dodatne datoteke...
-ButtonStopDownload=Prekini preno&s
-StopDownload=Ali res elite prekiniti prenos?
-ErrorDownloadAborted=Prenos prekinjen
-ErrorDownloadFailed=Prenos ni uspel: %1 %2
-ErrorDownloadSizeFailed=Pridobivanje velikosti ni uspelo: %1 %2
-ErrorFileHash1=Pridobivanje zgoene vrednosti ni uspelo: %1
-ErrorFileHash2=Neveljavna zgoena vrednost: priakovana %1, dobljena %2
-ErrorProgress=Neveljaven potek: %1 od %2
-ErrorFileSize=Neveljavna velikost datoteke: priakovana %1, dobljena %2
-
-; *** "Preparing to Install" wizard page
-WizardPreparing=Pripravljam za namestitev
-PreparingDesc=Namestitveni program je pripravljen za namestitev programa [name] v va raunalnik.
-PreviousInstallNotCompleted=Namestitev ali odstranitev prejnjega programa ni bila konana. Da bi jo dokonali, morate raunalnik znova zagnati.%n%nPo ponovnem zagonu raunalnika znova zaenite namestitveni program, da boste konali namestitev programa [name].
-CannotContinue=Namestitveni program ne more nadaljevati. Pritisnite Preklii za izhod.
-
-; *** "Installing" wizard page
-ApplicationsFound=Naslednji programi uporabljajo datoteke, ki jih mora namestitveni program posodobiti. Priporoljivo je, da namestitvenemu programu dovolite, da te programe kona.
-ApplicationsFound2=Naslednji programi uporabljajo datoteke, ki jih mora namestitveni program posodobiti. Priporoljivo je, da namestitvenemu programu dovolite, da te programe kona. Po koncu namestitve bo namestitveni program poizkusil znova zagnati te programe.
-CloseApplications=S&amodejno zapri programe
-DontCloseApplications=&Ne zapri programov
-ErrorCloseApplications=Namestitvenemu programu ni uspelo samodejno zapreti vseh programov. Priporoljivo je, da pred nadaljevanjem zaprete vse programe, ki uporabljajo datoteke, katere mora namestitev posodobiti.
-PrepareToInstallNeedsRestart=Namestitveni program mora znova zagnati va raunalnik. Za dokonanje namestitve programa [name], po ponovnem zagonu znova zaenite namestitveni program.%n%nAli elite zdaj znova zagnati raunalnik?
-
-WizardInstalling=Nameanje
-InstallingLabel=Poakajte, da bo program [name] nameen v va raunalnik.
-
-; *** "Setup Completed" wizard page
-FinishedHeadingLabel=Zakljuek namestitve programa [name]
-FinishedLabelNoIcons=Program [name] je nameen v va raunalnik.
-FinishedLabel=Program [name] je nameen v va raunalnik. Program zaenete tako, da odprete pravkar ustvarjene programske ikone.
-ClickFinish=Kliknite tipko Konaj za zakljuek namestitve.
-FinishedRestartLabel=Za dokonanje namestitve programa [name] morate raunalnik znova zagnati. Ali ga elite znova zagnati zdaj?
-FinishedRestartMessage=Za dokonanje namestitve programa [name] morate raunalnik znova zagnati. %n%nAli ga elite znova zagnati zdaj?
-ShowReadmeCheck=elim prebrati datoteko BERIME
-YesRadio=&Da, raunalnik znova zaeni zdaj
-NoRadio=&Ne, raunalnik bom znova zagnal pozneje
-
-; used for example as 'Run MyProg.exe'
-RunEntryExec=Zaeni %1
-; used for example as 'View Readme.txt'
-RunEntryShellExec=Preglej %1
-
-; *** "Setup Needs the Next Disk" stuff
-ChangeDiskTitle=Namestitveni program potrebuje naslednji disk
-SelectDiskLabel2=Vstavite disk %1 in kliknite V redu.%n%ne se datoteke s tega diska nahajajo v drugi mapi kot je navedena spodaj, vnesite pravilno pot ali kliknite Prebrskaj.
-PathLabel=&Pot:
-FileNotInDir2=Datoteke %1 ni v mapi %2. Vstavite pravilni disk ali izberite drugo mapo.
-SelectDirectoryLabel=Vnesite mesto naslednjega diska.
-
-; *** Installation phase messages
-SetupAborted=Namestitev ni bila konana.%n%nOdpravite teavo in znova odprite namestitveni program.
-AbortRetryIgnoreSelectAction=Izberite dejanje
-AbortRetryIgnoreRetry=Poizkusi &znova
-AbortRetryIgnoreIgnore=&Prezri napako in nadaljuj
-AbortRetryIgnoreCancel=Preklii namestitev
-
-; *** Installation status messages
-StatusClosingApplications=Zapiranje programov...
-StatusCreateDirs=Ustvarjanje map...
-StatusExtractFiles=Razirjanje datotek...
-StatusCreateIcons=Ustvarjanje blinjic...
-StatusCreateIniEntries=Vpisovanje v INI datoteke...
-StatusCreateRegistryEntries=Ustvarjanje vnosov v register...
-StatusRegisterFiles=Registriranje datotek...
-StatusSavingUninstall=Zapisovanje podatkov za odstranitev...
-StatusRunProgram=Zakljuevanje namestitve...
-StatusRestartingApplications=Zaganjanje programov...
-StatusRollback=Obnavljanje prvotnega stanja...
-
-; *** Misc. errors
-ErrorInternal2=Interna napaka: %1
-ErrorFunctionFailedNoCode=%1 ni uspel(a)
-ErrorFunctionFailed=%1 ni uspel(a); koda %2
-ErrorFunctionFailedWithMessage=%1 ni uspela; koda %2.%n%3
-ErrorExecutingProgram=Ne morem zagnati programa:%n%1
-
-; *** Registry errors
-ErrorRegOpenKey=Napaka pri odpiranju kljua v registru:%n%1\%2
-ErrorRegCreateKey=Napaka pri ustvarjanju kljua v registru:%n%1\%2
-ErrorRegWriteKey=Napaka pri pisanju kljua v registru:%n%1\%2
-
-; *** INI errors
-ErrorIniEntry=Napaka pri vpisu v INI datoteko %1.
-
-; *** File copying errors
-FileAbortRetryIgnoreSkipNotRecommended=Pre&skoi to datoteko (ni priporoeno)
-FileAbortRetryIgnoreIgnoreNotRecommended=Prezr&i napako in nadaljuj (ni priporoeno)
-SourceIsCorrupted=Izvorna datoteka je okvarjena
-SourceDoesntExist=Izvorna datoteka %1 ne obstaja
-ExistingFileReadOnly2=Obstojee datoteke ni mogoe nadomestiti, ker ima oznako samo za branje.
-ExistingFileReadOnlyRetry=Odst&rani oznako samo za branje in poizkusi ponovno
-ExistingFileReadOnlyKeepExisting=&Ohrani obstojeo datoteko
-ErrorReadingExistingDest=Pri branju obstojee datoteke je prilo do napake:
-FileExistsSelectAction=Izberite dejanje
-FileExists2=Datoteka e obstaja.
-FileExistsOverwriteExisting=&Prepii obstojeo datoteko
-FileExistsKeepExisting=&Ohrani trenutno datoteko
-FileExistsOverwriteOrKeepAll=&To naredite za preostale spore
-ExistingFileNewerSelectAction=Izberite dejanje
-ExistingFileNewer2=Obstojea datoteka je noveja, kot datoteka, ki se namea.
-ExistingFileNewerOverwriteExisting=&Prepii obstojeo datoteko
-ExistingFileNewerKeepExisting=&Ohrani trenutno datoteko (priporoeno)
-ExistingFileNewerOverwriteOrKeepAll=&To naredite za preostale spore
-ErrorChangingAttr=Pri poskusu spremembe lastnosti datoteke je prilo do napake:
-ErrorCreatingTemp=Pri ustvarjanju datoteke v ciljni mapi je prilo do napake:
-ErrorReadingSource=Pri branju izvorne datoteke je prilo do napake:
-ErrorCopying=Pri kopiranju datoteke je prilo do napake:
-ErrorReplacingExistingFile=Pri poskusu zamenjave obstojee datoteke je prilo do napake:
-ErrorRestartReplace=Napaka RestartReplace:
-ErrorRenamingTemp=Pri poskusu preimenovanja datoteke v ciljni mapi je prilo do napake:
-ErrorRegisterServer=Registracija DLL/OCX ni uspela: %1
-ErrorRegSvr32Failed=RegSvr32 ni uspel s kodo napake %1
-ErrorRegisterTypeLib=Registracija TypeLib ni uspela: %1
-
-; *** Uninstall display name markings
-UninstallDisplayNameMark=%1 (%2)
-UninstallDisplayNameMarks=%1 (%2, %3)
-UninstallDisplayNameMark32Bit=32-bitno
-UninstallDisplayNameMark64Bit=64-bitno
-UninstallDisplayNameMarkAllUsers=vsi uporabniki
-UninstallDisplayNameMarkCurrentUser=trenutni uporabnik
-
-; *** Post-installation errors
-ErrorOpeningReadme=Pri odpiranju datoteke BERIME je prilo do napake.
-ErrorRestartingComputer=Namestitvenemu programu ni uspelo znova zagnati raunalnika. Sami znova zaenite raunalnik.
-
-; *** Uninstaller messages
-UninstallNotFound=Datoteka %1 ne obstaja. Odstranitev ni mogoa.
-UninstallOpenError=Datoteke %1 ne morem odpreti. Ne morem odstraniti
-UninstallUnsupportedVer=Dnevnika datoteka %1 je v obliki, ki je ta razliica odstranitvenega programa ne razume. Programa ni mogoe odstraniti
-UninstallUnknownEntry=V dnevniki datoteki je bil najden neznani vpis (%1)
-ConfirmUninstall=Ste prepriani, da elite v celoti odstraniti program %1 in pripadajoe komponente?
-UninstallOnlyOnWin64=To namestitev je mogoe odstraniti le v 64-bitni razliici sistema Windows.
-OnlyAdminCanUninstall=Za odstranitev tega programa morate imeti skrbnike pravice.
-UninstallStatusLabel=Poakajte, da se program %1 odstrani iz vaega raunalnika.
-UninstalledAll=Program %1 je bil uspeno odstranjen iz vaega raunalnika.
-UninstalledMost=Odstranjevanje programa %1 je konano.%n%nNekatere datoteke niso bile odstranjene in jih lahko odstranite rono.
-UninstalledAndNeedsRestart=Za dokonanje odstranitve programa %1 morate raunalnik znova zagnati.%n%nAli ga elite znova zagnati zdaj?
-UninstallDataCorrupted=Datoteka %1 je okvarjena. Odstranitev ni mona
-
-; *** Uninstallation phase messages
-ConfirmDeleteSharedFileTitle=elite odstraniti datoteko v skupni rabi?
-ConfirmDeleteSharedFile2=Spodaj izpisane datoteke v skupni rabi ne uporablja ve noben program. elite odstraniti to datoteko?%n%ne jo uporablja katerikoli program in jo boste odstranili, ta program verjetno ne bo ve deloval pravilno. e niste prepriani, kliknite Ne. e boste datoteko ohranili v raunalniku, ne bo ni narobe.
-SharedFileNameLabel=Ime datoteke:
-SharedFileLocationLabel=Mesto:
-WizardUninstalling=Odstranjevanje programa
-StatusUninstalling=Odstranjujem %1...
-
-ShutdownBlockReasonInstallingApp=Nameam %1.
-ShutdownBlockReasonUninstallingApp=Odstranjujem %1.
-
-[CustomMessages]
-
-NameAndVersion=%1 razliica %2
-AdditionalIcons=Dodatne ikone:
-CreateDesktopIcon=Ustvari ikono na &namizju
-CreateQuickLaunchIcon=Ustvari ikono za &hitri zagon
-ProgramOnTheWeb=%1 na spletu
-UninstallProgram=Odstrani %1
-LaunchProgram=Odpri %1
-AssocFileExtension=&Povei %1 s pripono %2
-AssocingFileExtension=Povezujem %1 s pripono %2...
-AutoStartProgramGroupDescription=Zagon:
-AutoStartProgram=Samodejno zaeni %1
-AddonHostProgramNotFound=Programa %1 ni bilo mogoe najti v izbrani mapi.%n%nAli elite vseeno nadaljevati?
diff --git a/Dependencies/Inno/Languages/Spanish.isl b/Dependencies/Inno/Languages/Spanish.isl
deleted file mode 100644
index 0bd5df2f..00000000
--- a/Dependencies/Inno/Languages/Spanish.isl
+++ /dev/null
@@ -1,383 +0,0 @@
-; *** Inno Setup version 6.1.0+ Spanish messages ***
-;
-; Maintained by Jorge Andres Brugger (jbrugger@ideaworks.com.ar)
-; Spanish.isl version 1.5 (20200727)
-; Default.isl version 6.1.0
-;
-; Thanks to Germn Giraldo, Jordi Latorre, Ximo Tamarit, Emiliano Llano,
-; Ramn Verduzco, Graciela Garca, Carles Millan and Rafael Barranco-Droege
-
-[LangOptions]
-; The following three entries are very important. Be sure to read and
-; understand the '[LangOptions] section' topic in the help file.
-LanguageName=Espa<00F1>ol
-LanguageID=$0c0a
-LanguageCodePage=1252
-; If the language you are translating to requires special font faces or
-; sizes, uncomment any of the following entries and change them accordingly.
-;DialogFontName=
-;DialogFontSize=8
-;WelcomeFontName=Verdana
-;WelcomeFontSize=12
-;TitleFontName=Arial
-;TitleFontSize=29
-;CopyrightFontName=Arial
-;CopyrightFontSize=8
-
-[Messages]
-
-; *** Application titles
-SetupAppTitle=Instalar
-SetupWindowTitle=Instalar - %1
-UninstallAppTitle=Desinstalar
-UninstallAppFullTitle=Desinstalar - %1
-
-; *** Misc. common
-InformationTitle=Informacin
-ConfirmTitle=Confirmar
-ErrorTitle=Error
-
-; *** SetupLdr messages
-SetupLdrStartupMessage=Este programa instalar %1. Desea continuar?
-LdrCannotCreateTemp=Imposible crear archivo temporal. Instalacin interrumpida
-LdrCannotExecTemp=Imposible ejecutar archivo en la carpeta temporal. Instalacin interrumpida
-HelpTextNote=
-
-; *** Startup error messages
-LastErrorMessage=%1.%n%nError %2: %3
-SetupFileMissing=El archivo %1 no se encuentra en la carpeta de instalacin. Por favor, solucione el problema u obtenga una copia nueva del programa.
-SetupFileCorrupt=Los archivos de instalacin estn daados. Por favor, obtenga una copia nueva del programa.
-SetupFileCorruptOrWrongVer=Los archivos de instalacin estn daados o son incompatibles con esta versin del programa de instalacin. Por favor, solucione el problema u obtenga una copia nueva del programa.
-InvalidParameter=Se ha utilizado un parmetro no vlido en la lnea de comandos:%n%n%1
-SetupAlreadyRunning=El programa de instalacin an est ejecutndose.
-WindowsVersionNotSupported=Este programa no es compatible con la versin de Windows de su equipo.
-WindowsServicePackRequired=Este programa requiere %1 Service Pack %2 o posterior.
-NotOnThisPlatform=Este programa no se ejecutar en %1.
-OnlyOnThisPlatform=Este programa debe ejecutarse en %1.
-OnlyOnTheseArchitectures=Este programa slo puede instalarse en versiones de Windows diseadas para las siguientes arquitecturas de procesadores:%n%n%1
-WinVersionTooLowError=Este programa requiere %1 versin %2 o posterior.
-WinVersionTooHighError=Este programa no puede instalarse en %1 versin %2 o posterior.
-AdminPrivilegesRequired=Debe iniciar la sesin como administrador para instalar este programa.
-PowerUserPrivilegesRequired=Debe iniciar la sesin como administrador o como miembro del grupo de Usuarios Avanzados para instalar este programa.
-SetupAppRunningError=El programa de instalacin ha detectado que %1 est ejecutndose.%n%nPor favor, cirrelo ahora, luego haga clic en Aceptar para continuar o en Cancelar para salir.
-UninstallAppRunningError=El desinstalador ha detectado que %1 est ejecutndose.%n%nPor favor, cirrelo ahora, luego haga clic en Aceptar para continuar o en Cancelar para salir.
-
-; *** Startup questions
-PrivilegesRequiredOverrideTitle=Seleccin del Modo de Instalacin
-PrivilegesRequiredOverrideInstruction=Seleccione el modo de instalacin
-PrivilegesRequiredOverrideText1=%1 puede ser instalado para todos los usuarios (requiere privilegios administrativos), o solo para Ud.
-PrivilegesRequiredOverrideText2=%1 puede ser instalado solo para Ud, o para todos los usuarios (requiere privilegios administrativos).
-PrivilegesRequiredOverrideAllUsers=Instalar para &todos los usuarios
-PrivilegesRequiredOverrideAllUsersRecommended=Instalar para &todos los usuarios (recomendado)
-PrivilegesRequiredOverrideCurrentUser=Instalar para &m solamente
-PrivilegesRequiredOverrideCurrentUserRecommended=Instalar para &m solamente (recomendado)
-
-; *** Misc. errors
-ErrorCreatingDir=El programa de instalacin no pudo crear la carpeta "%1"
-ErrorTooManyFilesInDir=Imposible crear un archivo en la carpeta "%1" porque contiene demasiados archivos
-
-; *** Setup common messages
-ExitSetupTitle=Salir de la Instalacin
-ExitSetupMessage=La instalacin no se ha completado an. Si cancela ahora, el programa no se instalar.%n%nPuede ejecutar nuevamente el programa de instalacin en otra ocasin para completarla.%n%nSalir de la instalacin?
-AboutSetupMenuItem=&Acerca de Instalar...
-AboutSetupTitle=Acerca de Instalar
-AboutSetupMessage=%1 versin %2%n%3%n%n%1 sitio Web:%n%4
-AboutSetupNote=
-TranslatorNote=Spanish translation maintained by Jorge Andres Brugger (jbrugger@gmx.net)
-
-; *** Buttons
-ButtonBack=< &Atrs
-ButtonNext=&Siguiente >
-ButtonInstall=&Instalar
-ButtonOK=Aceptar
-ButtonCancel=Cancelar
-ButtonYes=&S
-ButtonYesToAll=S a &Todo
-ButtonNo=&No
-ButtonNoToAll=N&o a Todo
-ButtonFinish=&Finalizar
-ButtonBrowse=&Examinar...
-ButtonWizardBrowse=&Examinar...
-ButtonNewFolder=&Crear Nueva Carpeta
-
-; *** "Select Language" dialog messages
-SelectLanguageTitle=Seleccione el Idioma de la Instalacin
-SelectLanguageLabel=Seleccione el idioma a utilizar durante la instalacin.
-
-; *** Common wizard text
-ClickNext=Haga clic en Siguiente para continuar o en Cancelar para salir de la instalacin.
-BeveledLabel=
-BrowseDialogTitle=Buscar Carpeta
-BrowseDialogLabel=Seleccione una carpeta y luego haga clic en Aceptar.
-NewFolderName=Nueva Carpeta
-
-; *** "Welcome" wizard page
-WelcomeLabel1=Bienvenido al asistente de instalacin de [name]
-WelcomeLabel2=Este programa instalar [name/ver] en su sistema.%n%nSe recomienda cerrar todas las dems aplicaciones antes de continuar.
-
-; *** "Password" wizard page
-WizardPassword=Contrasea
-PasswordLabel1=Esta instalacin est protegida por contrasea.
-PasswordLabel3=Por favor, introduzca la contrasea y haga clic en Siguiente para continuar. En las contraseas se hace diferencia entre maysculas y minsculas.
-PasswordEditLabel=&Contrasea:
-IncorrectPassword=La contrasea introducida no es correcta. Por favor, intntelo nuevamente.
-
-; *** "License Agreement" wizard page
-WizardLicense=Acuerdo de Licencia
-LicenseLabel=Es importante que lea la siguiente informacin antes de continuar.
-LicenseLabel3=Por favor, lea el siguiente acuerdo de licencia. Debe aceptar las clusulas de este acuerdo antes de continuar con la instalacin.
-LicenseAccepted=A&cepto el acuerdo
-LicenseNotAccepted=&No acepto el acuerdo
-
-; *** "Information" wizard pages
-WizardInfoBefore=Informacin
-InfoBeforeLabel=Es importante que lea la siguiente informacin antes de continuar.
-InfoBeforeClickLabel=Cuando est listo para continuar con la instalacin, haga clic en Siguiente.
-WizardInfoAfter=Informacin
-InfoAfterLabel=Es importante que lea la siguiente informacin antes de continuar.
-InfoAfterClickLabel=Cuando est listo para continuar, haga clic en Siguiente.
-
-; *** "User Information" wizard page
-WizardUserInfo=Informacin de Usuario
-UserInfoDesc=Por favor, introduzca sus datos.
-UserInfoName=Nombre de &Usuario:
-UserInfoOrg=&Organizacin:
-UserInfoSerial=Nmero de &Serie:
-UserInfoNameRequired=Debe introducir un nombre.
-
-; *** "Select Destination Location" wizard page
-WizardSelectDir=Seleccione la Carpeta de Destino
-SelectDirDesc=Dnde debe instalarse [name]?
-SelectDirLabel3=El programa instalar [name] en la siguiente carpeta.
-SelectDirBrowseLabel=Para continuar, haga clic en Siguiente. Si desea seleccionar una carpeta diferente, haga clic en Examinar.
-DiskSpaceGBLabel=Se requieren al menos [gb] GB de espacio libre en el disco.
-DiskSpaceMBLabel=Se requieren al menos [mb] MB de espacio libre en el disco.
-CannotInstallToNetworkDrive=El programa de instalacin no puede realizar la instalacin en una unidad de red.
-CannotInstallToUNCPath=El programa de instalacin no puede realizar la instalacin en una ruta de acceso UNC.
-InvalidPath=Debe introducir una ruta completa con la letra de la unidad; por ejemplo:%n%nC:\APP%n%no una ruta de acceso UNC de la siguiente forma:%n%n\\servidor\compartido
-InvalidDrive=La unidad o ruta de acceso UNC que seleccion no existe o no es accesible. Por favor, seleccione otra.
-DiskSpaceWarningTitle=Espacio Insuficiente en Disco
-DiskSpaceWarning=La instalacin requiere al menos %1 KB de espacio libre, pero la unidad seleccionada slo cuenta con %2 KB disponibles.%n%nDesea continuar de todas formas?
-DirNameTooLong=El nombre de la carpeta o la ruta son demasiado largos.
-InvalidDirName=El nombre de la carpeta no es vlido.
-BadDirName32=Los nombres de carpetas no pueden incluir los siguientes caracteres:%n%n%1
-DirExistsTitle=La Carpeta Ya Existe
-DirExists=La carpeta:%n%n%1%n%nya existe. Desea realizar la instalacin en esa carpeta de todas formas?
-DirDoesntExistTitle=La Carpeta No Existe
-DirDoesntExist=La carpeta:%n%n%1%n%nno existe. Desea crear esa carpeta?
-
-; *** "Select Components" wizard page
-WizardSelectComponents=Seleccione los Componentes
-SelectComponentsDesc=Qu componentes deben instalarse?
-SelectComponentsLabel2=Seleccione los componentes que desea instalar y desmarque los componentes que no desea instalar. Haga clic en Siguiente cuando est listo para continuar.
-FullInstallation=Instalacin Completa
-; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
-CompactInstallation=Instalacin Compacta
-CustomInstallation=Instalacin Personalizada
-NoUninstallWarningTitle=Componentes Encontrados
-NoUninstallWarning=El programa de instalacin ha detectado que los siguientes componentes ya estn instalados en su sistema:%n%n%1%n%nDesmarcar estos componentes no los desinstalar.%n%nDesea continuar de todos modos?
-ComponentSize1=%1 KB
-ComponentSize2=%1 MB
-ComponentsDiskSpaceGBLabel=La seleccin actual requiere al menos [gb] GB de espacio en disco.
-ComponentsDiskSpaceMBLabel=La seleccin actual requiere al menos [mb] MB de espacio en disco.
-
-; *** "Select Additional Tasks" wizard page
-WizardSelectTasks=Seleccione las Tareas Adicionales
-SelectTasksDesc=Qu tareas adicionales deben realizarse?
-SelectTasksLabel2=Seleccione las tareas adicionales que desea que se realicen durante la instalacin de [name] y haga clic en Siguiente.
-
-; *** "Select Start Menu Folder" wizard page
-WizardSelectProgramGroup=Seleccione la Carpeta del Men Inicio
-SelectStartMenuFolderDesc=Dnde deben colocarse los accesos directos del programa?
-SelectStartMenuFolderLabel3=El programa de instalacin crear los accesos directos del programa en la siguiente carpeta del Men Inicio.
-SelectStartMenuFolderBrowseLabel=Para continuar, haga clic en Siguiente. Si desea seleccionar una carpeta distinta, haga clic en Examinar.
-MustEnterGroupName=Debe proporcionar un nombre de carpeta.
-GroupNameTooLong=El nombre de la carpeta o la ruta son demasiado largos.
-InvalidGroupName=El nombre de la carpeta no es vlido.
-BadGroupName=El nombre de la carpeta no puede incluir ninguno de los siguientes caracteres:%n%n%1
-NoProgramGroupCheck2=&No crear una carpeta en el Men Inicio
-
-; *** "Ready to Install" wizard page
-WizardReady=Listo para Instalar
-ReadyLabel1=Ahora el programa est listo para iniciar la instalacin de [name] en su sistema.
-ReadyLabel2a=Haga clic en Instalar para continuar con el proceso o haga clic en Atrs si desea revisar o cambiar alguna configuracin.
-ReadyLabel2b=Haga clic en Instalar para continuar con el proceso.
-ReadyMemoUserInfo=Informacin del usuario:
-ReadyMemoDir=Carpeta de Destino:
-ReadyMemoType=Tipo de Instalacin:
-ReadyMemoComponents=Componentes Seleccionados:
-ReadyMemoGroup=Carpeta del Men Inicio:
-ReadyMemoTasks=Tareas Adicionales:
-
-; *** TDownloadWizardPage wizard page and DownloadTemporaryFile
-DownloadingLabel=Descargando archivos adicionales...
-ButtonStopDownload=&Detener descarga
-StopDownload=Est seguiro que desea detener la descarga?
-ErrorDownloadAborted=Descarga cancelada
-ErrorDownloadFailed=Fall descarga: %1 %2
-ErrorDownloadSizeFailed=Fall obtencin de tamao: %1 %2
-ErrorFileHash1=Fall hash del archivo: %1
-ErrorFileHash2=Hash de archivo no vlido: esperado %1, encontrado %2
-ErrorProgress=Progreso no vlido: %1 de %2
-ErrorFileSize=Tamao de archivo no vlido: esperado %1, encontrado %2
-
-; *** "Preparing to Install" wizard page
-WizardPreparing=Preparndose para Instalar
-PreparingDesc=El programa de instalacin est preparndose para instalar [name] en su sistema.
-PreviousInstallNotCompleted=La instalacin/desinstalacin previa de un programa no se complet. Deber reiniciar el sistema para completar esa instalacin.%n%nUna vez reiniciado el sistema, ejecute el programa de instalacin nuevamente para completar la instalacin de [name].
-CannotContinue=El programa de instalacin no puede continuar. Por favor, presione Cancelar para salir.
-ApplicationsFound=Las siguientes aplicaciones estn usando archivos que necesitan ser actualizados por el programa de instalacin. Se recomienda que permita al programa de instalacin cerrar automticamente estas aplicaciones.
-ApplicationsFound2=Las siguientes aplicaciones estn usando archivos que necesitan ser actualizados por el programa de instalacin. Se recomienda que permita al programa de instalacin cerrar automticamente estas aplicaciones. Al completarse la instalacin, el programa de instalacin intentar reiniciar las aplicaciones.
-CloseApplications=&Cerrar automticamente las aplicaciones
-DontCloseApplications=&No cerrar las aplicaciones
-ErrorCloseApplications=El programa de instalacin no pudo cerrar de forma automtica todas las aplicaciones. Se recomienda que, antes de continuar, cierre todas las aplicaciones que utilicen archivos que necesitan ser actualizados por el programa de instalacin.
-PrepareToInstallNeedsRestart=El programa de instalacin necesita reiniciar el sistema. Una vez que se haya reiniciado ejecute nuevamente el programa de instalacin para completar la instalacin de [name].%n%nDesea reiniciar el sistema ahora?
-
-; *** "Installing" wizard page
-WizardInstalling=Instalando
-InstallingLabel=Por favor, espere mientras se instala [name] en su sistema.
-
-; *** "Setup Completed" wizard page
-FinishedHeadingLabel=Completando la instalacin de [name]
-FinishedLabelNoIcons=El programa complet la instalacin de [name] en su sistema.
-FinishedLabel=El programa complet la instalacin de [name] en su sistema. Puede ejecutar la aplicacin utilizando los accesos directos creados.
-ClickFinish=Haga clic en Finalizar para salir del programa de instalacin.
-FinishedRestartLabel=Para completar la instalacin de [name], su sistema debe reiniciarse. Desea reiniciarlo ahora?
-FinishedRestartMessage=Para completar la instalacin de [name], su sistema debe reiniciarse.%n%nDesea reiniciarlo ahora?
-ShowReadmeCheck=S, deseo ver el archivo LAME
-YesRadio=&S, deseo reiniciar el sistema ahora
-NoRadio=&No, reiniciar el sistema ms tarde
-; used for example as 'Run MyProg.exe'
-RunEntryExec=Ejecutar %1
-; used for example as 'View Readme.txt'
-RunEntryShellExec=Ver %1
-
-; *** "Setup Needs the Next Disk" stuff
-ChangeDiskTitle=El Programa de Instalacin Necesita el Siguiente Disco
-SelectDiskLabel2=Por favor, inserte el Disco %1 y haga clic en Aceptar.%n%nSi los archivos pueden ser hallados en una carpeta diferente a la indicada abajo, introduzca la ruta correcta o haga clic en Examinar.
-PathLabel=&Ruta:
-FileNotInDir2=El archivo "%1" no se ha podido hallar en "%2". Por favor, inserte el disco correcto o seleccione otra carpeta.
-SelectDirectoryLabel=Por favor, especifique la ubicacin del siguiente disco.
-
-; *** Installation phase messages
-SetupAborted=La instalacin no se ha completado.%n%nPor favor solucione el problema y ejecute nuevamente el programa de instalacin.
-AbortRetryIgnoreSelectAction=Seleccione accin
-AbortRetryIgnoreRetry=&Reintentar
-AbortRetryIgnoreIgnore=&Ignorar el error y continuar
-AbortRetryIgnoreCancel=Cancelar instalacin
-
-; *** Installation status messages
-StatusClosingApplications=Cerrando aplicaciones...
-StatusCreateDirs=Creando carpetas...
-StatusExtractFiles=Extrayendo archivos...
-StatusCreateIcons=Creando accesos directos...
-StatusCreateIniEntries=Creando entradas INI...
-StatusCreateRegistryEntries=Creando entradas de registro...
-StatusRegisterFiles=Registrando archivos...
-StatusSavingUninstall=Guardando informacin para desinstalar...
-StatusRunProgram=Terminando la instalacin...
-StatusRestartingApplications=Reiniciando aplicaciones...
-StatusRollback=Deshaciendo cambios...
-
-; *** Misc. errors
-ErrorInternal2=Error interno: %1
-ErrorFunctionFailedNoCode=%1 fall
-ErrorFunctionFailed=%1 fall; cdigo %2
-ErrorFunctionFailedWithMessage=%1 fall; cdigo %2.%n%3
-ErrorExecutingProgram=Imposible ejecutar el archivo:%n%1
-
-; *** Registry errors
-ErrorRegOpenKey=Error al abrir la clave del registro:%n%1\%2
-ErrorRegCreateKey=Error al crear la clave del registro:%n%1\%2
-ErrorRegWriteKey=Error al escribir la clave del registro:%n%1\%2
-
-; *** INI errors
-ErrorIniEntry=Error al crear entrada INI en el archivo "%1".
-
-; *** File copying errors
-FileAbortRetryIgnoreSkipNotRecommended=&Omitir este archivo (no recomendado)
-FileAbortRetryIgnoreIgnoreNotRecommended=&Ignorar el error y continuar (no recomendado)
-SourceIsCorrupted=El archivo de origen est daado
-SourceDoesntExist=El archivo de origen "%1" no existe
-ExistingFileReadOnly2=El archivo existente no puede ser reemplazado debido a que est marcado como slo-lectura.
-ExistingFileReadOnlyRetry=&Elimine el atributo de slo-lectura y reintente
-ExistingFileReadOnlyKeepExisting=&Mantener el archivo existente
-ErrorReadingExistingDest=Ocurri un error mientras se intentaba leer el archivo:
-FileExistsSelectAction=Seleccione accin
-FileExists2=El archivo ya existe.
-FileExistsOverwriteExisting=&Sobreescribir el archivo existente
-FileExistsKeepExisting=&Mantener el archivo existente
-FileExistsOverwriteOrKeepAll=&Hacer lo mimso para lo siguientes conflictos
-ExistingFileNewerSelectAction=Seleccione accin
-ExistingFileNewer2=El archivo existente es ms reciente que el que se est tratando de instalar.
-ExistingFileNewerOverwriteExisting=&Sobreescribir el archivo existente
-ExistingFileNewerKeepExisting=&Mantener el archivo existente (recomendado)
-ExistingFileNewerOverwriteOrKeepAll=&Hacer lo mimso para lo siguientes conflictos
-ErrorChangingAttr=Ocurri un error al intentar cambiar los atributos del archivo:
-ErrorCreatingTemp=Ocurri un error al intentar crear un archivo en la carpeta de destino:
-ErrorReadingSource=Ocurri un error al intentar leer el archivo de origen:
-ErrorCopying=Ocurri un error al intentar copiar el archivo:
-ErrorReplacingExistingFile=Ocurri un error al intentar reemplazar el archivo existente:
-ErrorRestartReplace=Fall reintento de reemplazar:
-ErrorRenamingTemp=Ocurri un error al intentar renombrar un archivo en la carpeta de destino:
-ErrorRegisterServer=Imposible registrar el DLL/OCX: %1
-ErrorRegSvr32Failed=RegSvr32 fall con el cdigo de salida %1
-ErrorRegisterTypeLib=Imposible registrar la librera de tipos: %1
-
-; *** Uninstall display name markings
-; used for example as 'My Program (32-bit)'
-UninstallDisplayNameMark=%1 (%2)
-; used for example as 'My Program (32-bit, All users)'
-UninstallDisplayNameMarks=%1 (%2, %3)
-UninstallDisplayNameMark32Bit=32-bit
-UninstallDisplayNameMark64Bit=64-bit
-UninstallDisplayNameMarkAllUsers=Todos los usuarios
-UninstallDisplayNameMarkCurrentUser=Usuario actual
-
-; *** Post-installation errors
-ErrorOpeningReadme=Ocurri un error al intentar abrir el archivo LAME.
-ErrorRestartingComputer=El programa de instalacin no pudo reiniciar el equipo. Por favor, hgalo manualmente.
-
-; *** Uninstaller messages
-UninstallNotFound=El archivo "%1" no existe. Imposible desinstalar.
-UninstallOpenError=El archivo "%1" no pudo ser abierto. Imposible desinstalar
-UninstallUnsupportedVer=El archivo de registro para desinstalar "%1" est en un formato no reconocido por esta versin del desinstalador. Imposible desinstalar
-UninstallUnknownEntry=Se encontr una entrada desconocida (%1) en el registro de desinstalacin
-ConfirmUninstall=Est seguro que desea desinstalar completamente %1 y todos sus componentes?
-UninstallOnlyOnWin64=Este programa slo puede ser desinstalado en Windows de 64-bits.
-OnlyAdminCanUninstall=Este programa slo puede ser desinstalado por un usuario con privilegios administrativos.
-UninstallStatusLabel=Por favor, espere mientras %1 es desinstalado de su sistema.
-UninstalledAll=%1 se desinstal satisfactoriamente de su sistema.
-UninstalledMost=La desinstalacin de %1 ha sido completada.%n%nAlgunos elementos no pudieron eliminarse, pero podr eliminarlos manualmente si lo desea.
-UninstalledAndNeedsRestart=Para completar la desinstalacin de %1, su sistema debe reiniciarse.%n%nDesea reiniciarlo ahora?
-UninstallDataCorrupted=El archivo "%1" est daado. No puede desinstalarse
-
-; *** Uninstallation phase messages
-ConfirmDeleteSharedFileTitle=Eliminar Archivo Compartido?
-ConfirmDeleteSharedFile2=El sistema indica que el siguiente archivo compartido no es utilizado por ningn otro programa. Desea eliminar este archivo compartido?%n%nSi elimina el archivo y hay programas que lo utilizan, esos programas podran dejar de funcionar correctamente. Si no est seguro, elija No. Dejar el archivo en su sistema no producir ningn dao.
-SharedFileNameLabel=Archivo:
-SharedFileLocationLabel=Ubicacin:
-WizardUninstalling=Estado de la Desinstalacin
-StatusUninstalling=Desinstalando %1...
-
-; *** Shutdown block reasons
-ShutdownBlockReasonInstallingApp=Instalando %1.
-ShutdownBlockReasonUninstallingApp=Desinstalando %1.
-
-; The custom messages below aren't used by Setup itself, but if you make
-; use of them in your scripts, you'll want to translate them.
-
-[CustomMessages]
-
-NameAndVersion=%1 versin %2
-AdditionalIcons=Accesos directos adicionales:
-CreateDesktopIcon=Crear un acceso directo en el &escritorio
-CreateQuickLaunchIcon=Crear un acceso directo en &Inicio Rpido
-ProgramOnTheWeb=%1 en la Web
-UninstallProgram=Desinstalar %1
-LaunchProgram=Ejecutar %1
-AssocFileExtension=&Asociar %1 con la extensin de archivo %2
-AssocingFileExtension=Asociando %1 con la extensin de archivo %2...
-AutoStartProgramGroupDescription=Inicio:
-AutoStartProgram=Iniciar automticamente %1
-AddonHostProgramNotFound=%1 no pudo ser localizado en la carpeta seleccionada.%n%nDesea continuar de todas formas?
diff --git a/Dependencies/Inno/Languages/Turkish.isl b/Dependencies/Inno/Languages/Turkish.isl
deleted file mode 100644
index 932c705b..00000000
--- a/Dependencies/Inno/Languages/Turkish.isl
+++ /dev/null
@@ -1,384 +0,0 @@
-; *** Inno Setup version 6.1.0+ Turkish messages ***
-; Language "Turkce" Turkish Translate by "Ceviren" Kaya Zeren translator@zeron.net
-; To download user-contributed translations of this file, go to:
-; https://www.jrsoftware.org/files/istrans/
-;
-; Note: When translating this text, do not add periods (.) to the end of
-; messages that didn't have them already, because on those messages Inno
-; Setup adds the periods automatically (appending a period would result in
-; two periods being displayed).
-
-[LangOptions]
-; The following three entries are very important. Be sure to read and
-; understand the '[LangOptions] section' topic in the help file.
-LanguageName=T<00FC>rk<00E7>e
-LanguageID=$041f
-LanguageCodePage=1254
-; If the language you are translating to requires special font faces or
-; sizes, uncomment any of the following entries and change them accordingly.
-;DialogFontName=
-;DialogFontSize=8
-;WelcomeFontName=Verdana
-;WelcomeFontSize=12
-;TitleFontName=Arial
-;TitleFontSize=29
-;CopyrightFontName=Arial
-;CopyrightFontSize=8
-
-[Messages]
-
-; *** Uygulama balklar
-SetupAppTitle=Kurulum Yardmcs
-SetupWindowTitle=%1 - Kurulum Yardmcs
-UninstallAppTitle=Kaldrma Yardmcs
-UninstallAppFullTitle=%1 Kaldrma Yardmcs
-
-; *** eitli ortak metinler
-InformationTitle=Bilgi
-ConfirmTitle=Onay
-ErrorTitle=Hata
-
-; *** Kurulum ykleyici iletileri
-SetupLdrStartupMessage=%1 uygulamas kurulacak. Devam etmek istiyor musunuz?
-LdrCannotCreateTemp=Geici dosya oluturulamadndan kurulum iptal edildi
-LdrCannotExecTemp=Geici klasrdeki dosya altrlamadndan kurulum iptal edildi
-HelpTextNote=
-
-; *** Balang hata iletileri
-LastErrorMessage=%1.%n%nHata %2: %3
-SetupFileMissing=Kurulum klasrnde %1 dosyas eksik. Ltfen sorunu zn ya da uygulamann yeni bir kopyasyla yeniden deneyin.
-SetupFileCorrupt=Kurulum dosyalar bozulmu. Ltfen uygulamann yeni bir kopyasyla yeniden kurmay deneyin.
-SetupFileCorruptOrWrongVer=Kurulum dosyalar bozulmu ya da bu kurulum yardmcs srm ile uyumlu deil. Ltfen sorunu zn ya da uygulamann yeni bir kopyasyla yeniden kurmay deneyin.
-InvalidParameter=Komut satrnda geersiz bir parametre yazlm:%n%n%1
-SetupAlreadyRunning=Kurulum yardmcs zaten alyor.
-WindowsVersionNotSupported=Bu uygulama, bilgisayarnzda ykl olan Windows srm ile uyumlu deil.
-WindowsServicePackRequired=Bu uygulama, %1 Hizmet Paketi %2 ve zerindeki srmler ile alr.
-NotOnThisPlatform=Bu uygulama, %1 zerinde almaz.
-OnlyOnThisPlatform=Bu uygulama, %1 zerinde altrlmaldr.
-OnlyOnTheseArchitectures=Bu uygulama, yalnz u ilemci mimarileri iin tasarlanm Windows srmleriyle alr:%n%n%1
-WinVersionTooLowError=Bu uygulama iin %1 srm %2 ya da zeri gereklidir.
-WinVersionTooHighError=Bu uygulama, '%1' srm '%2' ya da zerine kurulamaz.
-AdminPrivilegesRequired=Bu uygulamay kurmak iin Ynetici olarak oturum alm olmas gereklidir.
-PowerUserPrivilegesRequired=Bu uygulamay kurarken, Ynetici ya da Gl Kullanclar grubunun bir yesi olarak oturum alm olmas gereklidir.
-SetupAppRunningError=Kurulum yardmcs %1 uygulamasnn almakta olduunu alglad.%n%nLtfen uygulamann alan tm kopyalarn kapatp, devam etmek iin Tamam, kurulum yardmcsndan kmak iin ptal zerine tklayn.
-UninstallAppRunningError=Kaldrma yardmcs, %1 uygulamasnn almakta olduunu alglad.%n%nLtfen uygulamann alan tm kopyalarn kapatp, devam etmek iin Tamam ya da kaldrma yardmcsndan kmak iin ptal zerine tklayn.
-
-; *** Balang sorular
-PrivilegesRequiredOverrideTitle=Kurulum Kipini Sein
-PrivilegesRequiredOverrideInstruction=Kurulum kipini sein
-PrivilegesRequiredOverrideText1=%1 tm kullanclar iin (ynetici izinleri gerekir) ya da yalnz sizin hesabnz iin kurulabilir.
-PrivilegesRequiredOverrideText2=%1 yalnz sizin hesabnz iin ya da tm kullanclar iin (ynetici izinleri gerekir) kurulabilir.
-PrivilegesRequiredOverrideAllUsers=&Tm kullanclar iin kurulsun
-PrivilegesRequiredOverrideAllUsersRecommended=&Tm kullanclar iin kurulsun (nerilir)
-PrivilegesRequiredOverrideCurrentUser=&Yalnz benim kullancm iin kurulsun
-PrivilegesRequiredOverrideCurrentUserRecommended=&Yalnz benim kullancm iin kurulsun (nerilir)
-
-; *** eitli hata metinleri
-ErrorCreatingDir=Kurulum yardmcs "%1" klasrn oluturamad.
-ErrorTooManyFilesInDir="%1" klasr iinde ok sayda dosya olduundan bir dosya oluturulamad
-
-; *** Ortak kurulum iletileri
-ExitSetupTitle=Kurulum Yardmcsndan k
-ExitSetupMessage=Kurulum tamamlanmad. imdi karsanz, uygulama kurulmayacak.%n%nKurulumu tamamlamak iin istediiniz zaman kurulum yardmcsn yeniden altrabilirsiniz.%n%nKurulum yardmcsndan klsn m?
-AboutSetupMenuItem=Kurulum H&akknda...
-AboutSetupTitle=Kurulum Hakknda
-AboutSetupMessage=%1 %2 srm%n%3%n%n%1 ana sayfa:%n%4
-AboutSetupNote=
-TranslatorNote=
-
-; *** Dmeler
-ButtonBack=< &nceki
-ButtonNext=&Sonraki >
-ButtonInstall=&Kur
-ButtonOK=Tamam
-ButtonCancel=ptal
-ButtonYes=E&vet
-ButtonYesToAll=&Tmne Evet
-ButtonNo=&Hayr
-ButtonNoToAll=Tmne Ha&yr
-ButtonFinish=&Bitti
-ButtonBrowse=&Gzat...
-ButtonWizardBrowse=Gza&t...
-ButtonNewFolder=Ye&ni Klasr Olutur
-
-; *** "Kurulum Dilini Sein" sayfas iletileri
-SelectLanguageTitle=Kurulum Yardmcs Dilini Sein
-SelectLanguageLabel=Kurulum sresince kullanlacak dili sein.
-
-; *** Ortak metinler
-ClickNext=Devam etmek iin Sonraki, kmak iin ptal zerine tklayn.
-BeveledLabel=
-BrowseDialogTitle=Klasre Gzat
-BrowseDialogLabel=Aadaki listeden bir klasr seip, Tamam zerine tklayn.
-NewFolderName=Yeni Klasr
-
-; *** "Ho geldiniz" sayfas
-WelcomeLabel1=[name] Kurulum Yardmcsna Hogeldiniz.
-WelcomeLabel2=Bilgisayarnza [name/ver] uygulamas kurulacak.%n%nDevam etmeden nce alan dier tm uygulamalar kapatmanz nerilir.
-
-; *** "Parola" sayfas
-WizardPassword=Parola
-PasswordLabel1=Bu kurulum parola korumaldr.
-PasswordLabel3=Ltfen parolay yazn ve devam etmek iin Sonraki zerine tklayn. Parolalar byk kk harflere duyarldr.
-PasswordEditLabel=&Parola:
-IncorrectPassword=Yazdnz parola doru deil. Ltfen yeniden deneyin.
-
-; *** "Lisans Anlamas" sayfas
-WizardLicense=Lisans Anlamas
-LicenseLabel=Ltfen devam etmeden nce aadaki nemli bilgileri okuyun.
-LicenseLabel3=Ltfen Aadaki Lisans Anlamasn okuyun. Kuruluma devam edebilmek iin bu anlamay kabul etmelisiniz.
-LicenseAccepted=Anlamay kabul &ediyorum.
-LicenseNotAccepted=Anlamay kabul et&miyorum.
-
-; *** "Bilgiler" sayfas
-WizardInfoBefore=Bilgiler
-InfoBeforeLabel=Ltfen devam etmeden nce aadaki nemli bilgileri okuyun.
-InfoBeforeClickLabel=Kuruluma devam etmeye hazr olduunuzda Sonraki zerine tklayn.
-WizardInfoAfter=Bilgiler
-InfoAfterLabel=Ltfen devam etmeden nce aadaki nemli bilgileri okuyun.
-InfoAfterClickLabel=Kuruluma devam etmeye hazr olduunuzda Sonraki zerine tklayn.
-
-; *** "Kullanc Bilgileri" sayfas
-WizardUserInfo=Kullanc Bilgileri
-UserInfoDesc=Ltfen bilgilerinizi yazn.
-UserInfoName=K&ullanc Ad:
-UserInfoOrg=Ku&rum:
-UserInfoSerial=&Seri Numaras:
-UserInfoNameRequired=Bir ad yazmalsnz.
-
-; *** "Hedef Konumunu Sein" sayfas
-WizardSelectDir=Hedef Konumunu Sein
-SelectDirDesc=[name] nereye kurulsun?
-SelectDirLabel3=[name] uygulamas u klasre kurulacak.
-SelectDirBrowseLabel=Devam etmek icin Sonraki zerine tklayn. Farkl bir klasr semek iin Gzat zerine tklayn.
-DiskSpaceGBLabel=En az [gb] GB bo disk alan gereklidir.
-DiskSpaceMBLabel=En az [mb] MB bo disk alan gereklidir.
-CannotInstallToNetworkDrive=Uygulama bir a srcs zerine kurulamaz.
-CannotInstallToUNCPath=Uygulama bir UNC yolu zerine (\\yol gibi) kurulamaz.
-InvalidPath=Src ad ile tam yolu yazmalsnz; rnein: %n%nC:\APP%n%n ya da u ekilde bir UNC yolu:%n%n\\sunucu\paylam
-InvalidDrive=Src ya da UNC paylam yok ya da eriilemiyor. Ltfen baka bir tane sein.
-DiskSpaceWarningTitle=Yeterli Bo Disk Alan Yok
-DiskSpaceWarning=Kurulum iin %1 KB bo alan gerekli, ancak seilmi srcde yalnz %2 KB bo alan var.%n%nGene de devam etmek istiyor musunuz?
-DirNameTooLong=Klasr ad ya da yol ok uzun.
-InvalidDirName=Klasr ad geersiz.
-BadDirName32=Klasr adlarnda u karakterler bulunamaz:%n%n%1
-DirExistsTitle=Klasr Zaten Var"
-DirExists=Klasr:%n%n%1%n%zaten var. Kurulum iin bu klasr kullanmak ister misiniz?
-DirDoesntExistTitle=Klasr Bulunamad
-DirDoesntExist=Klasr:%n%n%1%n%nbulunamad.Klasrn oluturmasn ister misiniz?
-
-; *** "Bileenleri Sein" sayfas
-WizardSelectComponents=Bileenleri Sein
-SelectComponentsDesc=Hangi bileenler kurulacak?
-SelectComponentsLabel2=Kurmak istediiniz bileenleri sein; kurmak istemediiniz bileenlerin iaretini kaldrn. Devam etmeye hazr olduunuzda Sonraki zerine tklayn.
-FullInstallation=Tam Kurulum
-; Mmknse 'Compact' ifadesini kendi dilinizde 'Minimal' anlamnda evirmeyin
-CompactInstallation=Normal kurulum
-CustomInstallation=zel kurulum
-NoUninstallWarningTitle=Bileenler Zaten Var
-NoUninstallWarning=u bileenlerin bilgisayarnzda zaten kurulu olduu algland:%n%n%1%n%n Bu bileenlerin iaretlerinin kaldrlmas bileenleri kaldrmaz.%n%nGene de devam etmek istiyor musunuz?
-ComponentSize1=%1 KB
-ComponentSize2=%1 MB
-ComponentsDiskSpaceGBLabel=Seili bileenler iin diskte en az [gb] GB bo alan bulunmas gerekli.
-ComponentsDiskSpaceMBLabel=Seili bileenler iin diskte en az [mb] MB bo alan bulunmas gerekli.
-
-; *** "Ek lemleri Sein" sayfas
-WizardSelectTasks=Ek lemleri Sein
-SelectTasksDesc=Baka hangi ilemler yaplsn?
-SelectTasksLabel2=[name] kurulumu srasnda yaplmasn istediiniz ek ileri sein ve Sonraki zerine tklayn.
-
-; *** "Balat Mens Klasrn Sein" sayfas
-WizardSelectProgramGroup=Balat Mens Klasrn Sein
-SelectStartMenuFolderDesc=Uygulamann ksayollar nereye eklensin?
-SelectStartMenuFolderLabel3=Kurulum yardmcs uygulama ksayollarn aadaki Balat Mens klasrne ekleyecek.
-SelectStartMenuFolderBrowseLabel=Devam etmek iin Sonraki zerine tklayn. Farkl bir klasr semek iin Gzat zerine tklayn.
-MustEnterGroupName=Bir klasr ad yazmalsnz.
-GroupNameTooLong=Klasr ad ya da yol ok uzun.
-InvalidGroupName=Klasr ad geersiz.
-BadGroupName=Klasr adnda u karakterler bulunamaz:%n%n%1
-NoProgramGroupCheck2=Balat Mens klasr &oluturulmasn
-
-; *** "Kurulmaya Hazr" sayfas
-WizardReady=Kurulmaya Hazr
-ReadyLabel1=[name] bilgisayarnza kurulmaya hazr.
-ReadyLabel2a=Kuruluma devam etmek iin Sonraki zerine, ayarlar gzden geirip deitirmek iin nceki zerine tklayn.
-ReadyLabel2b=Kuruluma devam etmek iin Sonraki zerine tklayn.
-ReadyMemoUserInfo=Kullanc bilgileri:
-ReadyMemoDir=Hedef konumu:
-ReadyMemoType=Kurulum tr:
-ReadyMemoComponents=Seilmi bileenler:
-ReadyMemoGroup=Balat Mens klasr:
-ReadyMemoTasks=Ek ilemler:
-
-; *** TDownloadWizardPage wizard page and DownloadTemporaryFile
-DownloadingLabel=Ek dosyalar indiriliyor...
-ButtonStopDownload=ndirmeyi &durdur
-StopDownload=ndirmeyi durdurmak istediinize emin misiniz?
-ErrorDownloadAborted=ndirme durduruldu
-ErrorDownloadFailed=ndirilemedi: %1 %2
-ErrorDownloadSizeFailed=Boyut alnamad: %1 %2
-ErrorFileHash1=Dosya karmas dorulanamad: %1
-ErrorFileHash2=Dosya karmas geersiz: %1 olmas gerekirken %2
-ErrorProgress=Adm geersiz: %1 / %2
-ErrorFileSize=Dosya boyutu geersiz: %1 olmas gerekirken %2
-
-; *** "Kuruluma Hazrlanlyor" sayfas
-WizardPreparing=Kuruluma Hazrlanlyor
-PreparingDesc=[name] bilgisayarnza kurulmaya hazrlanyor.
-PreviousInstallNotCompleted=nceki uygulama kurulumu ya da kaldrlmas tamamlanmam. Bu kurulumun tamamlanmas iin bilgisayarnz yeniden balatmalsnz.%n%nBilgisayarnz yeniden balattktan sonra ilemi tamamlamak iin [name] kurulum yardmcsn yeniden altrn.
-CannotContinue=Kuruluma devam edilemiyor. kmak iin ptal zerine tklayn.
-ApplicationsFound=Kurulum yardmcs tarafndan gncellenmesi gereken dosyalar, u uygulamalar tarafndan kullanyor. Kurulum yardmcsnn bu uygulamalar otomatik olarak kapatmasna izin vermeniz nerilir.
-ApplicationsFound2=Kurulum yardmcs tarafndan gncellenmesi gereken dosyalar, u uygulamalar tarafndan kullanyor. Kurulum yardmcsnn bu uygulamalar otomatik olarak kapatmasna izin vermeniz nerilir. Kurulum tamamlandktan sonra, uygulamalar yeniden balatlmaya allacak.
-CloseApplications=&Uygulamalar kapatlsn
-DontCloseApplications=Uygulamalar &kapatlmasn
-ErrorCloseApplications=Kurulum yardmcs uygulamalar kapatamad. Kurulum yardmcs tarafndan gncellenmesi gereken dosyalar kullanan uygulamalar el ile kapatmanz nerilir.
-PrepareToInstallNeedsRestart=Kurulum iin bilgisayarn yeniden balatlmas gerekiyor. Bilgisayar yeniden balattktan sonra [name] kurulumunu tamamlamak iin kurulum yardmcsn yeniden altrn.%n%nBilgisayar imdi yeniden balatmak ister misiniz?
-
-; *** "Kuruluyor" sayfas
-WizardInstalling=Kuruluyor
-InstallingLabel=Ltfen [name] bilgisayarnza kurulurken bekleyin.
-
-; *** "Kurulum Tamamland" sayfas
-FinishedHeadingLabel=[name] kurulum yardmcs tamamlanyor
-FinishedLabelNoIcons=Bilgisayarnza [name] kurulumu tamamland.
-FinishedLabel=Bilgisayarnza [name] kurulumu tamamland. Simgeleri yklemeyi setiyseniz, simgelere tklayarak uygulamay balatabilirsiniz.
-ClickFinish=Kurulum yardmcsndan kmak iin Bitti zerine tklayn.
-FinishedRestartLabel=[name] kurulumunun tamamlanmas iin, bilgisayarnz yeniden balatlmal. imdi yeniden balatmak ister misiniz?
-FinishedRestartMessage=[name] kurulumunun tamamlanmas iin, bilgisayarnz yeniden balatlmal.%n%nimdi yeniden balatmak ister misiniz?
-ShowReadmeCheck=Evet README dosyas grntlensin
-YesRadio=&Evet, bilgisayar imdi yeniden balatlsn
-NoRadio=&Hayr, bilgisayar daha sonra yeniden balatacam
-; used for example as 'Run MyProg.exe'
-RunEntryExec=%1 altrlsn
-; used for example as 'View Readme.txt'
-RunEntryShellExec=%1 grntlensin
-
-; *** "Kurulum iin Sradaki Disk Gerekli" iletileri
-ChangeDiskTitle=Kurulum Yardmcs Sradaki Diske Gerek Duyuyor
-SelectDiskLabel2=Ltfen %1 numaral diski takp Tamam zerine tklayn.%n%nDiskteki dosyalar aadakinden farkl bir klasrde bulunuyorsa, doru yolu yazn ya da Gzat zerine tklayarak doru klasr sein.
-PathLabel=&Yol:
-FileNotInDir2="%1" dosyas "%2" iinde bulunamad. Ltfen doru diski takn ya da baka bir klasr sein.
-SelectDirectoryLabel=Ltfen sonraki diskin konumunu belirtin.
-
-; *** Kurulum aamas iletileri
-SetupAborted=Kurulum tamamlanamad.%n%nLtfen sorunu dzelterek kurulum yardmcsn yeniden altrn.
-AbortRetryIgnoreSelectAction=Yaplacak ilemi sein
-AbortRetryIgnoreRetry=&Yeniden denensin
-AbortRetryIgnoreIgnore=&Sorun yok saylp devam edilsin
-AbortRetryIgnoreCancel=Kurulum iptal edilsin
-
-; *** Kurulum durumu iletileri
-StatusClosingApplications=Uygulamalar kapatlyor...
-StatusCreateDirs=Klasrler oluturuluyor...
-StatusExtractFiles=Dosyalar ayklanyor...
-StatusCreateIcons=Ksayollar oluturuluyor...
-StatusCreateIniEntries=INI kaytlar oluturuluyor...
-StatusCreateRegistryEntries=Kayt Defteri kaytlar oluturuluyor...
-StatusRegisterFiles=Dosyalar kaydediliyor...
-StatusSavingUninstall=Kaldrma bilgileri kaydediliyor...
-StatusRunProgram=Kurulum tamamlanyor...
-StatusRestartingApplications=Uygulamalar yeniden balatlyor...
-StatusRollback=Deiiklikler geri alnyor...
-
-; *** eitli hata iletileri
-ErrorInternal2= hata: %1
-ErrorFunctionFailedNoCode=%1 tamamlanamad.
-ErrorFunctionFailed=%1 tamamlanamad; kod %2
-ErrorFunctionFailedWithMessage=%1 tamamlanamad; kod %2.%n%3
-ErrorExecutingProgram=u dosya yrtlemedi:%n%1
-
-; *** Kayt defteri hatalar
-ErrorRegOpenKey=Kayt defteri anahtar alrken bir sorun kt:%n%1%2
-ErrorRegCreateKey=Kayt defteri anahtar eklenirken bir sorun kt:%n%1%2
-ErrorRegWriteKey=Kayt defteri anahtar yazlrken bir sorun kt:%n%1%2
-
-; *** INI hatalar
-ErrorIniEntry="%1" dosyasna INI kayd eklenirken bir sorun kt.
-
-; *** Dosya kopyalama hatalar
-FileAbortRetryIgnoreSkipNotRecommended=&Bu dosya atlansn (nerilmez)
-FileAbortRetryIgnoreIgnoreNotRecommended=&Sorun yok saylp devam edilsin (nerilmez)
-SourceIsCorrupted=Kaynak dosya bozulmu
-SourceDoesntExist="%1" kaynak dosyas bulunamad
-ExistingFileReadOnly2=Var olan dosya salt okunabilir olarak iaretlenmi olduundan zerine yazlamad.
-ExistingFileReadOnlyRetry=&Salt okunur iareti kaldrlp yeniden denensin
-ExistingFileReadOnlyKeepExisting=&Var olan dosya korunsun
-ErrorReadingExistingDest=Var olan dosya okunmaya allrken bir sorun kt.
-FileExistsSelectAction=Yaplacak ilemi sein
-FileExists2=Dosya zaten var.
-FileExistsOverwriteExisting=&Var olan dosyann zerine yazlsn
-FileExistsKeepExisting=Var &olan dosya korunsun
-FileExistsOverwriteOrKeepAll=&Sonraki akmalarda da bu ilem yaplsn
-ExistingFileNewerSelectAction=Yaplacak ilemi sein
-ExistingFileNewer2=Var olan dosya, kurulum yardmcs tarafndan yazlmaya allandan daha yeni.
-ExistingFileNewerOverwriteExisting=&Var olan dosyann zerine yazlsn
-ExistingFileNewerKeepExisting=Var &olan dosya korunsun (nerilir)
-ExistingFileNewerOverwriteOrKeepAll=&Sonraki akmalarda bu ilem yaplsn
-ErrorChangingAttr=Var olan dosyann znitelikleri deitirilirken bir sorun kt:
-ErrorCreatingTemp=Hedef klasrde bir dosya oluturulurken bir sorun kt:
-ErrorReadingSource=Kaynak dosya okunurken bir sorun kt:
-ErrorCopying=Dosya kopyalanrken bir sorun kt:
-ErrorReplacingExistingFile=Var olan dosya deitirilirken bir sorun kt:
-ErrorRestartReplace=Yeniden balatmada zerine yazlamad:
-ErrorRenamingTemp=Hedef klasrdeki bir dosyann ad deitirilirken sorun kt:
-ErrorRegisterServer=DLL/OCX kayt edilemedi: %1
-ErrorRegSvr32Failed=RegSvr32 ilemi u kod ile tamamlanamad: %1
-ErrorRegisterTypeLib=Tr kitapl kayt defterine eklenemedi: %1
-
-; *** Kaldrma srasnda grntlenecek ad iaretleri
-; used for example as 'My Program (32-bit)'
-UninstallDisplayNameMark=%1 (%2)
-; used for example as 'My Program (32-bit, All users)'
-UninstallDisplayNameMarks=%1 (%2, %3)
-UninstallDisplayNameMark32Bit=32 bit
-UninstallDisplayNameMark64Bit=64 bit
-UninstallDisplayNameMarkAllUsers=Tm kullanclar
-UninstallDisplayNameMarkCurrentUser=Geerli kullanc
-
-; *** Kurulum sonras hatalar
-ErrorOpeningReadme=README dosyas alrken bir sorun kt.
-ErrorRestartingComputer=Kurulum yardmcs bilgisayarnz yeniden balatamyor. Ltfen bilgisayarnz yeniden balatn.
-
-; *** Kaldrma yardmcs iletileri
-UninstallNotFound="%1" dosyas bulunamad. Uygulama kaldrlamyor.
-UninstallOpenError="%1" dosyas alamad. Uygulama kaldrlamyor.
-UninstallUnsupportedVer="%1" uygulama kaldrma gnlk dosyasnn biimi, bu kaldrma yardmcs srm tarafndan anlalamad. Uygulama kaldrlamyor.
-UninstallUnknownEntry=Kaldrma gnlnde bilinmeyen bir kayt (%1) bulundu.
-ConfirmUninstall=%1 uygulamasn tm bileenleri ile birlikte tamamen kaldrmak istediinize emin misiniz?
-UninstallOnlyOnWin64=Bu kurulum yalnz 64 bit Windows zerinden kaldrlabilir.
-OnlyAdminCanUninstall=Bu kurulum yalnz ynetici haklarna sahip bir kullanc tarafndan kaldrlabilir.
-UninstallStatusLabel=Ltfen %1 uygulamas bilgisayarnzdan kaldrlrken bekleyin.
-UninstalledAll=%1 uygulamas bilgisayarnzdan kaldrld.
-UninstalledMost=%1 uygulamas kaldrld.%n%nBaz bileenler kaldrlamad. Bunlar el ile silebilirsiniz.
-UninstalledAndNeedsRestart=%1 kaldrma ileminin tamamlanmas iin bilgisayarnzn yeniden balatlmas gerekli.%n%nimdi yeniden balatmak ister misiniz?
-UninstallDataCorrupted="%1" dosyas bozulmu. Kaldrlamyor.
-
-; *** Kaldrma aamas iletileri
-ConfirmDeleteSharedFileTitle=Paylalan Dosya Silinsin mi?
-ConfirmDeleteSharedFile2=Sisteme gre, paylalan u dosya baka bir uygulama tarafndan kullanlmyor ve kaldrlabilir. Bu paylalm dosyay silmek ister misiniz?%n%nBu dosya, baka herhangi bir uygulama tarafndan kullanlyor ise, silindiinde dier uygulama dzgn almayabilir. Emin deilseniz Hayr zerine tklayn. Dosyay sisteminizde brakmann bir zarar olmaz.
-SharedFileNameLabel=Dosya ad:
-SharedFileLocationLabel=Konum:
-WizardUninstalling=Kaldrma Durumu
-StatusUninstalling=%1 kaldrlyor...
-
-; *** Kapatmay engelleme nedenleri
-ShutdownBlockReasonInstallingApp=%1 kuruluyor.
-ShutdownBlockReasonUninstallingApp=%1 kaldrlyor.
-
-; The custom messages below aren't used by Setup itself, but if you make
-; use of them in your scripts, you'll want to translate them.
-
-[CustomMessages]
-
-NameAndVersion=%1 %2 srm
-AdditionalIcons=Ek simgeler:
-CreateDesktopIcon=Masast simg&esi oluturulsun
-CreateQuickLaunchIcon=Hzl Balat simgesi &oluturulsun
-ProgramOnTheWeb=%1 Web Sitesi
-UninstallProgram=%1 Uygulamasn Kaldr
-LaunchProgram=%1 Uygulamasn altr
-AssocFileExtension=%1 &uygulamas ile %2 dosya uzants ilikilendirilsin
-AssocingFileExtension=%1 uygulamas ile %2 dosya uzants ilikilendiriliyor...
-AutoStartProgramGroupDescription=Balang:
-AutoStartProgram=%1 otomatik olarak balatlsn
-AddonHostProgramNotFound=%1 setiiniz klasrde bulunamad.%n%nYine de devam etmek istiyor musunuz?
\ No newline at end of file
diff --git a/Dependencies/Inno/Languages/Ukrainian.isl b/Dependencies/Inno/Languages/Ukrainian.isl
deleted file mode 100644
index c12c6ebf..00000000
--- a/Dependencies/Inno/Languages/Ukrainian.isl
+++ /dev/null
@@ -1,385 +0,0 @@
-; *** Inno Setup version 6.1.0+ Ukrainian messages ***
-; Author: Dmytro Onyshchuk
-; E-Mail: mrlols3@gmail.com
-; Please report all spelling/grammar errors, and observations.
-; Version 2020.08.04
-
-; *** Inno Setup 6.1.0 ***
-; :
-; E-Mail: mrlols3@gmail.com
-; , .
-; 2020.08.04
-
-[LangOptions]
-; The following three entries are very important. Be sure to read and
-; understand the '[LangOptions] section' topic in the help file.
-LanguageName=<0423><043A><0440><0430><0457><043D><0441><044C><043A><0430>
-LanguageID=$0422
-LanguageCodePage=1251
-; If the language you are translating to requires special font faces or
-; sizes, uncomment any of the following entries and change them accordingly.
-;DialogFontName=
-;DialogFontSize=8
-;WelcomeFontName=Verdana
-;WelcomeFontSize=12
-;TitleFontName=Arial
-;TitleFontSize=29
-;CopyrightFontName=Arial
-;CopyrightFontSize=8
-
-[Messages]
-
-; ***
-SetupAppTitle=
-SetupWindowTitle= %1
-UninstallAppTitle=
-UninstallAppFullTitle= %1
-
-; *** Misc. common
-InformationTitle=
-ConfirmTitle=ϳ
-ErrorTitle=
-
-; *** SetupLdr messages
-SetupLdrStartupMessage= %1 ', ?
-LdrCannotCreateTemp= .
-LdrCannotExecTemp= .
-HelpTextNote=
-
-; *** Startup error messages
-LastErrorMessage=%1.%n%n %2: %3
-SetupFileMissing= %1 . , .
-SetupFileCorrupt= . , .
-SetupFileCorruptOrWrongVer= . , .
-InvalidParameter= :%n%n%1
-SetupAlreadyRunning= .
-WindowsVersionNotSupported= Windows, '.
-WindowsServicePackRequired= %1 Service Pack %2 .
-NotOnThisPlatform= %1.
-OnlyOnThisPlatform= %1.
-OnlyOnTheseArchitectures= ' Windows :%n%n%1
-WinVersionTooLowError= %1 %2 .
-WinVersionTooHighError= %1 %2 .
-AdminPrivilegesRequired= .
-PowerUserPrivilegesRequired= .
-SetupAppRunningError=, %1 .%n%n , ﳿ OK , .
-UninstallAppRunningError=, %1 .%n%n , ﳿ OK , .
-
-; *** Startup questions
-PrivilegesRequiredOverrideTitle=
-PrivilegesRequiredOverrideInstruction=
-PrivilegesRequiredOverrideText1=%1 ( ), .
-PrivilegesRequiredOverrideText2=%1 , ( ).
-PrivilegesRequiredOverrideAllUsers= &
-PrivilegesRequiredOverrideAllUsersRecommended= & ()
-PrivilegesRequiredOverrideCurrentUser=
-PrivilegesRequiredOverrideCurrentUserRecommended= & ()
-
-; *** г
-ErrorCreatingDir= "%1"
-ErrorTooManyFilesInDir= "%1",
-
-; ***
-ExitSetupTitle=
-ExitSetupMessage= . , .%n%n .%n%n ?
-AboutSetupMenuItem=& ...
-AboutSetupTitle=
-AboutSetupMessage=%1 %2%n%3%n%n%1 :%n%4
-AboutSetupNote=
-TranslatorNote=Ukrainian translation by Dmytro Onyshchuk
-
-; ***
-ButtonBack=< &
-ButtonNext=& >
-ButtonInstall=&
-ButtonOK=OK
-ButtonCancel=
-ButtonYes=&
-ButtonYesToAll= &
-ButtonNo=&ͳ
-ButtonNoToAll=&
-ButtonFinish=&
-ButtonBrowse=&...
-ButtonWizardBrowse=&...
-ButtonNewFolder=&
-
-; *** ij " "
-SelectLanguageTitle=
-SelectLanguageLabel= , .
-
-; ***
-ClickNext= 볻, , .
-BeveledLabel=
-BrowseDialogTitle=
-BrowseDialogLabel= ʻ.
-NewFolderName=
-
-; *** ""
-WelcomeLabel1= [name].
-WelcomeLabel2= [name/ver] .%n%n .
-
-; *** ""
-WizardPassword=
-PasswordLabel1= .
-PasswordLabel3= , 볻, . .
-PasswordEditLabel=&:
-IncorrectPassword= . , .
-
-; *** "˳ "
-WizardLicense=˳
-LicenseLabel= , .
-LicenseLabel3= , . , .
-LicenseAccepted= &
-LicenseNotAccepted= &
-
-; *** ""
-WizardInfoBefore=
-InfoBeforeLabel= , , .
-InfoBeforeClickLabel= , 볻.
-WizardInfoAfter=
-InfoAfterLabel= , , .
-InfoAfterClickLabel= , 볻.
-
-; *** " "
-WizardUserInfo=
-UserInfoDesc= , .
-UserInfoName=& :
-UserInfoOrg=&:
-UserInfoSerial=& :
-UserInfoNameRequired= '.
-
-; *** " "
-WizardSelectDir=
-SelectDirDesc= [name]?
-SelectDirLabel3= [name] .
-SelectDirBrowseLabel= 볻, . , .
-DiskSpaceGBLabel= [gb] .
-DiskSpaceMBLabel= [mb] M .
-CannotInstallToNetworkDrive= .
-CannotInstallToUNCPath= .
-InvalidPath= , :%n%nC:\APP%n%n UNC:%n%n\\\
-InvalidDrive= , . , .
-DiskSpaceWarningTitle=
-DiskSpaceWarning= %1 , %2 .%n%n ?
-DirNameTooLong=' .
-InvalidDirName= .
-BadDirName32=' :%n%n%1
-DirExistsTitle=
-DirExists=:%n%n%1%n%n . ?
-DirDoesntExistTitle=
-DirDoesntExist=:%n%n%1%n%n . ?
-
-; *** " "
-WizardSelectComponents=
-SelectComponentsDesc= ?
-SelectComponentsLabel2= ; . 볻, .
-FullInstallation=
-; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
-CompactInstallation=
-CustomInstallation=
-NoUninstallWarningTitle=
-NoUninstallWarning=, :%n%n%1%n%n³ .%n%n ?
-ComponentSize1=%1 K
-ComponentSize2=%1 M
-ComponentsDiskSpaceGBLabel= [gb] .
-ComponentsDiskSpaceMBLabel= [mb] M .
-
-; *** " "
-WizardSelectTasks=
-SelectTasksDesc= ?
-SelectTasksLabel2= [name] , 볻.
-
-; *** " "
-WizardSelectProgramGroup=
-SelectStartMenuFolderDesc= ?
-SelectStartMenuFolderLabel3= .
-SelectStartMenuFolderBrowseLabel= 볻, . , .
-MustEnterGroupName= ' .
-GroupNameTooLong= .
-InvalidGroupName= .
-BadGroupName=' :%n%n%1
-NoProgramGroupCheck2=&
-
-; *** " "
-WizardReady=
-ReadyLabel1= [name] .
-ReadyLabel2a= , , .
-ReadyLabel2b= .
-ReadyMemoUserInfo= :
-ReadyMemoDir= :
-ReadyMemoType= :
-ReadyMemoComponents= :
-ReadyMemoGroup= :
-ReadyMemoTasks= :
-
-; *** TDownloadWizardPage wizard page and DownloadTemporaryFile
-DownloadingLabel= ...
-ButtonStopDownload=&
-StopDownload= ?
-ErrorDownloadAborted=
-ErrorDownloadFailed= : %1 %2
-ErrorDownloadSizeFailed= : %1 %2
-ErrorFileHash1= : %1
-ErrorFileHash2= : %1, %2
-ErrorProgress= : %1 %2
-ErrorFileSize= : %1, %2
-
-; *** "ϳ "
-WizardPreparing=ϳ
-PreparingDesc= [name] .
-PreviousInstallNotCompleted= . .%n%nϳ , [name].
-CannotContinue= . , .
-ApplicationsFound= , . .
-ApplicationsFound2= , . . ϳ , .
-CloseApplications=&
-DontCloseApplications=&
-ErrorCloseApplications= . , , , .
-PrepareToInstallNeedsRestart= . ϳ , [name]%n%n ?
-
-; *** ""
-WizardInstalling=
-InstallingLabel= , , [name] '.
-
-; *** " "
-FinishedHeadingLabel= [name]
-FinishedLabelNoIcons= [name] .
-FinishedLabel= [name] . .
-ClickFinish= .
-FinishedRestartLabel= [name] . ?
-FinishedRestartMessage= [name] .%n%n ?
-ShowReadmeCheck=, README
-YesRadio=&,
-NoRadio=&ͳ,
-; used for example as 'Run MyProg.exe'
-RunEntryExec=³ %1
-; used for example as 'View Readme.txt'
-RunEntryShellExec= %1
-
-; *** "Setup Needs the Next Disk" stuff
-ChangeDiskTitle=
-SelectDiskLabel2= , %1 OK.%n%n , , .
-PathLabel=&:
-FileNotInDir2= "%1" "%2". , .
-SelectDirectoryLabel= , .
-
-; *** Installation phase messages
-SetupAborted= .%n%n , .
-AbortRetryIgnoreSelectAction=
-AbortRetryIgnoreRetry=&
-AbortRetryIgnoreIgnore=&
-AbortRetryIgnoreCancel=³
-
-; ***
-StatusClosingApplications= ...
-StatusCreateDirs= ...
-StatusExtractFiles= ...
-StatusCreateIcons= ...
-StatusCreateIniEntries= INI ...
-StatusCreateRegistryEntries= ...
-StatusRegisterFiles= ...
-StatusSavingUninstall= ...
-StatusRunProgram= ...
-StatusRestartingApplications= ...
-StatusRollback= ...
-
-; *** г
-ErrorInternal2= : %1
-ErrorFunctionFailedNoCode=%1
-ErrorFunctionFailed=%1 ; %2
-ErrorFunctionFailedWithMessage=%1 ; %2.%n%3
-ErrorExecutingProgram= :%n%1
-
-; ***
-ErrorRegOpenKey= :%n%1\%2
-ErrorRegCreateKey= :%n%1\%2
-ErrorRegWriteKey= :%n%1\%2
-
-; *** INI
-ErrorIniEntry= INI- "%1".
-
-; ***
-FileAbortRetryIgnoreSkipNotRecommended=& ( )
-FileAbortRetryIgnoreIgnoreNotRecommended=& ( )
-SourceIsCorrupted=
-SourceDoesntExist= "%1"
-ExistingFileReadOnly2= , .
-ExistingFileReadOnlyRetry=& " "
-ExistingFileReadOnlyKeepExisting=&
-ErrorReadingExistingDest= :
-FileExistsSelectAction=
-FileExists2= .
-FileExistsOverwriteExisting=&
-FileExistsKeepExisting=&
-FileExistsOverwriteOrKeepAll=&
-ExistingFileNewerSelectAction=
-ExistingFileNewer2= , .
-ExistingFileNewerOverwriteExisting=&
-ExistingFileNewerKeepExisting=& ()
-ExistingFileNewerOverwriteOrKeepAll=&
-ErrorChangingAttr= :
-ErrorCreatingTemp= :
-ErrorReadingSource= :
-ErrorCopying= :
-ErrorReplacingExistingFile= :
-ErrorRestartReplace= RestartReplace:
-ErrorRenamingTemp= :
-ErrorRegisterServer= DLL/OCX: %1
-ErrorRegSvr32Failed= RegSvr32, %1
-ErrorRegisterTypeLib= : %1
-
-; *** Uninstall display name markings
-UninstallDisplayNameMark=%1 (%2)
-UninstallDisplayNameMarks=%1 (%2, %3)
-UninstallDisplayNameMark32Bit=32-
-UninstallDisplayNameMark64Bit=64-
-UninstallDisplayNameMarkAllUsers=
-UninstallDisplayNameMarkCurrentUser=
-
-; *** Post-installation errors
-ErrorOpeningReadme= README.
-ErrorRestartingComputer= '. , .
-
-; ***
-UninstallNotFound= "%1" , .
-UninstallOpenError= "%1".
-UninstallUnsupportedVer= "%1" .
-UninstallUnknownEntry= (%1)
-ConfirmUninstall= , %1 ?
-UninstallOnlyOnWin64= 64- Windows.
-OnlyAdminCanUninstall= .
-UninstallStatusLabel= , , %1 '.
-UninstalledAll=%1 '.
-UninstalledMost= %1 .%n%n . .
-UninstalledAndNeedsRestart= %1 .%n%n ?
-UninstallDataCorrupted= "%1" .
-
-; *** Uninstallation phase messages
-ConfirmDeleteSharedFileTitle= ?
-ConfirmDeleteSharedFile2= , . ?%n%n , . , ͳ. .
-SharedFileNameLabel=' :
-SharedFileLocationLabel=:
-WizardUninstalling=
-StatusUninstalling= %1...
-
-
-; ***
-ShutdownBlockReasonInstallingApp= %1.
-ShutdownBlockReasonUninstallingApp= %1.
-
-; The custom messages below aren't used by Setup itself, but if you make
-; use of them in your scripts, you'll want to translate them.
-
-[CustomMessages]
-
-NameAndVersion=%1, %2
-AdditionalIcons= :
-CreateDesktopIcon= &
-CreateQuickLaunchIcon= &
-ProgramOnTheWeb= %1
-UninstallProgram= %1
-LaunchProgram=³ %1
-AssocFileExtension=& %1 %2
-AssocingFileExtension= %1 %2...
-AutoStartProgramGroupDescription=:
-AutoStartProgram= %1
-AddonHostProgramNotFound=%1 %n%n ?
diff --git a/Dependencies/Inno/Setup.e32 b/Dependencies/Inno/Setup.e32
deleted file mode 100644
index 7fcf47cd..00000000
Binary files a/Dependencies/Inno/Setup.e32 and /dev/null differ
diff --git a/Dependencies/Inno/SetupLdr.e32 b/Dependencies/Inno/SetupLdr.e32
deleted file mode 100644
index 9bf36bab..00000000
Binary files a/Dependencies/Inno/SetupLdr.e32 and /dev/null differ
diff --git a/Dependencies/Inno/WizModernImage-IS.bmp b/Dependencies/Inno/WizModernImage-IS.bmp
deleted file mode 100644
index cf844e09..00000000
Binary files a/Dependencies/Inno/WizModernImage-IS.bmp and /dev/null differ
diff --git a/Dependencies/Inno/WizModernImage.bmp b/Dependencies/Inno/WizModernImage.bmp
deleted file mode 100644
index cb05a063..00000000
Binary files a/Dependencies/Inno/WizModernImage.bmp and /dev/null differ
diff --git a/Dependencies/Inno/WizModernSmallImage-IS.bmp b/Dependencies/Inno/WizModernSmallImage-IS.bmp
deleted file mode 100644
index 1e8e4979..00000000
Binary files a/Dependencies/Inno/WizModernSmallImage-IS.bmp and /dev/null differ
diff --git a/Dependencies/Inno/WizModernSmallImage.bmp b/Dependencies/Inno/WizModernSmallImage.bmp
deleted file mode 100644
index 63f42104..00000000
Binary files a/Dependencies/Inno/WizModernSmallImage.bmp and /dev/null differ
diff --git a/Dependencies/Inno/isbunzip.dll b/Dependencies/Inno/isbunzip.dll
deleted file mode 100644
index 814e8680..00000000
Binary files a/Dependencies/Inno/isbunzip.dll and /dev/null differ
diff --git a/Dependencies/Inno/isbzip.dll b/Dependencies/Inno/isbzip.dll
deleted file mode 100644
index 1afeefd5..00000000
Binary files a/Dependencies/Inno/isbzip.dll and /dev/null differ
diff --git a/Dependencies/Inno/isfaq.url b/Dependencies/Inno/isfaq.url
deleted file mode 100644
index 91054550..00000000
--- a/Dependencies/Inno/isfaq.url
+++ /dev/null
@@ -1,2 +0,0 @@
-[InternetShortcut]
-URL=https://jrsoftware.org/isfaq.php
diff --git a/Dependencies/Inno/islzma.dll b/Dependencies/Inno/islzma.dll
deleted file mode 100644
index 81fd05ac..00000000
Binary files a/Dependencies/Inno/islzma.dll and /dev/null differ
diff --git a/Dependencies/Inno/islzma32.exe b/Dependencies/Inno/islzma32.exe
deleted file mode 100644
index 7562645e..00000000
Binary files a/Dependencies/Inno/islzma32.exe and /dev/null differ
diff --git a/Dependencies/Inno/islzma64.exe b/Dependencies/Inno/islzma64.exe
deleted file mode 100644
index fd58a59e..00000000
Binary files a/Dependencies/Inno/islzma64.exe and /dev/null differ
diff --git a/Dependencies/Inno/isscint.dll b/Dependencies/Inno/isscint.dll
deleted file mode 100644
index 5f8ef49b..00000000
Binary files a/Dependencies/Inno/isscint.dll and /dev/null differ
diff --git a/Dependencies/Inno/isunzlib.dll b/Dependencies/Inno/isunzlib.dll
deleted file mode 100644
index 8c4ed510..00000000
Binary files a/Dependencies/Inno/isunzlib.dll and /dev/null differ
diff --git a/Dependencies/Inno/iszlib.dll b/Dependencies/Inno/iszlib.dll
deleted file mode 100644
index b326e3a7..00000000
Binary files a/Dependencies/Inno/iszlib.dll and /dev/null differ
diff --git a/Dependencies/Inno/license.txt b/Dependencies/Inno/license.txt
deleted file mode 100644
index 17b2e1b7..00000000
--- a/Dependencies/Inno/license.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-Inno Setup License
-==================
-
-Except where otherwise noted, all of the documentation and software included in the Inno Setup
-package is copyrighted by Jordan Russell.
-
-Copyright (C) 1997-2020 Jordan Russell. All rights reserved.
-Portions Copyright (C) 2000-2020 Martijn Laan. All rights reserved.
-
-This software is provided "as-is," without any express or implied warranty. In no event shall the
-author be held liable for any damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial
-applications, and to alter and redistribute it, provided that the following conditions are met:
-
-1. All redistributions of source code files must retain all copyright notices that are currently in
- place, and this list of conditions without modification.
-
-2. All redistributions in binary form must retain all occurrences of the above copyright notice and
- web site addresses that are currently in place (for example, in the About boxes).
-
-3. The origin of this software must not be misrepresented; you must not claim that you wrote the
- original software. If you use this software to distribute a product, an acknowledgment in the
- product documentation would be appreciated but is not required.
-
-4. Modified versions in source or binary form must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
-
-Jordan Russell
-jr-2020 AT jrsoftware.org
-https://jrsoftware.org/
\ No newline at end of file
diff --git a/Dependencies/Inno/unins000.dat b/Dependencies/Inno/unins000.dat
deleted file mode 100644
index 409a1aa0..00000000
Binary files a/Dependencies/Inno/unins000.dat and /dev/null differ
diff --git a/Dependencies/Inno/unins000.exe b/Dependencies/Inno/unins000.exe
deleted file mode 100644
index b8b4f910..00000000
Binary files a/Dependencies/Inno/unins000.exe and /dev/null differ
diff --git a/Dependencies/Inno/unins000.msg b/Dependencies/Inno/unins000.msg
deleted file mode 100644
index 24de8ec9..00000000
Binary files a/Dependencies/Inno/unins000.msg and /dev/null differ
diff --git a/Dependencies/Inno/whatsnew.htm b/Dependencies/Inno/whatsnew.htm
deleted file mode 100644
index 9add84e1..00000000
--- a/Dependencies/Inno/whatsnew.htm
+++ /dev/null
@@ -1,172 +0,0 @@
-
-
-
-Inno Setup 6 Revision History
-
-
-
-
-
-Inno Setup 6
Revision History
-
-Copyright © 1997-2020 Jordan Russell. All rights reserved.
-Portions Copyright © 2000-2020 Martijn Laan. All rights reserved.
-For conditions of distribution and use, see LICENSE.TXT.
-
-
-Want to be notified by e-mail of new Inno Setup releases? Subscribe to the Inno Setup Mailing List!
-
-6.1.2 (2020-11-15)
-
- - Compiler IDE change: Added new Print... (Ctrl+P) menu item to the File menu.
- - Minor tweaks.
-
-
-6.1.1-beta (2020-10-29)
-Compiler IDE updates
-Various improvements have been made to the Compiler IDE:
-
-Other changes
-
- - The /PORTABLE=1 command line parameter accepted by Inno Setup's own installers has been improved to allow side-by-side installations. For example, to quickly install a new version to the current user's desktop without affecting already installed versions use the following command line parameters: /portable=1 /silent /currentuser.
- - Setup's and Uninstall's Back and Next buttons no longer display outdated "<" and ">" markers.
- - Added new topic to the help file listing the additional Compiler IDE keyboard commands which are not listed in the menus. Added new Keyboard Commands menu item to the Compiler IDE's Help menu to open this topic.
- - ISPP change: Added new SaveStringToFile support function.
- - Fix: Calling DLL functions which return a 64-bit integer now gives correct result values.
- - Minor tweaks.
-
-
-All official translations have now been updated for the changes in the previous version. Thanks to the maintainers for their time.
-
-
6.1.0-beta (2020-09-13)
-Per-user fonts
-Per-user fonts are now supported if Setup is running on Windows 10 Version 1803 and later:
-
- - [Files] section parameter FontInstall can now be used in non administrative installs. Requires Windows 10 Version 1803 or later to successfully install a font.
- - The {fonts} constant has been renamed to {commonfonts}. The old name is still supported, but it is recommended to update your scripts to the new names and the compiler will issue a warning if you don't.
-
- Added new {userfonts} constant. Only Windows 10 Version 1803 and later supports {userfonts}. Same directory as {localappdata}\Microsoft\Windows\Fonts.
- - Added new {autofonts} constant which automatically maps to {commonfonts} unless the installation is running in non administrative install mode, in which case it maps to {userfonts}. It is recommended to update your scripts to use {autofonts} as much as possible to avoid mistakes.
- - Updated all examples to use {autofonts} instead of {fonts}.
- - Pascal Scripting change: The UnregisterFont support function now has an extra parameter.
-
-Compiler IDE updates
-Various improvements have been made to the Compiler IDE:
-
- - If the script uses Inno Setup Preprocessor (ISPP) functionality, the Compiler IDE now automatically shows the preprocessor output in a tab so you can check it. This can be turned off in the options.
- - The Compiler IDE now automatically opens (up to 10) #include files in tabs which allow you to edit and debug these files from within the Compiler IDE. The list of #include files is updated after opening a new main file and after each compilation. This can be turned off in the options. If the option is not turned off, a new Save All menu item is added to the File menu.
- - If #include files are modified since last compile, the script is now automatically re-compiled before running it. This works even if the option to automatically open #include files is turned off.
- - Added new Next Tab and Previous Tab menu items to the View menu.
- - Added new topic to the help file explaining the various integrated debugger menu items in the Run menu which can be used to debug your [Code] section.
- - Improved highlighting for the [CustomMessages] and [Messages] sections.
- - Added new MessageBox Designer menu item to the Tools menu to design and insert MsgBox or TaskDialogMsgBox calls for the [Code] section.
- - Added new Step Out menu item to the Run menu to unpause Setup until it reaches the end of the current function, then pause it on the next line.
- - Added buttons to the Welcome dialog to Donate to support Inno Setup (Thank you!) and to Subscribe to the Inno Setup Mailing List to be notified by e-mail of new Inno Setup releases.
- - The Run Parameters dialog now shows a list of most recently used parameters.
-
-Built-in download support for [Code]
-Pascal Scripting now supports downloading files and checking SHA-256 hashes:
-
- - Added new DownloadTemporaryFile support function to download files without using a third-party tool:
-
- - Supports HTTPS (but not expired or self-signed certificates) and HTTP.
- - Redirects are automatically followed and proxy settings are automatically used.
- - Safe to use from services unlike existing third-party tools.
- - Supports SHA-256 hash checking of the downloaded file.
- - Supports basic authentication.
-
-
- - Added new CreateDownloadPage support function to easily show the download progress to the user. See the new CodeDownloadFiles.iss example script for an example.
- - Added new DownloadTemporaryFileSize support function to get the size of a file without downloading it.
- - Added new GetSHA256OfFile, GetSHA256OfString, and GetSHA256OfUnicodeString support functions to calculate SHA-256 hashes.
- - Change in default behavior: Setup no longer disables itself entirely while PrepareToInstall is running. Instead only the Cancel button is disabled.
-
-Inno Setup Preprocessor (ISPP) updates
-ISPP now uses 64-bit integers and has new functions to more easily compare version numbers:
-
- - ISPP's int type is now a signed 64-bit integer type.
-
- Support function FileSize now supports 64-bit file sizes.
- - Added new GetPackedVersion, PackVersionNumbers, PackVersionComponents, ComparePackedVersion, SamePackedVersion, UnpackVersionNumbers, UnpackVersionComponents, and VersionToStr support functions.
- - Support function GetFileVersion and ParseVersion have been renamed to GetVersionNumbersString and GetVersionComponents respectively. The old names are still supported, but it is recommended to update your scripts to the new names and the compiler will issue a warning if you don't.
-
-Similar Pascal Scripting changes have been done for [Code]:
-
- - Added new FileSize64 support function.
- - Added new GetPackedVersion, PackVersionNumbers, PackVersionComponents, ComparePackedVersion, SamePackedVersion, UnpackVersionNumbers, UnpackVersionComponents, GetVersionComponents, and VersionToStr support functions. This makes ISPP and [Code] support the same list of version related functions.
-
-Other changes
-
- - Fix: Inno Setup 6.0.5 no longer supported Windows Vista.
- - Change in default behavior: [Setup] section directive MinVersion now defaults to 6.1sp1, so by default Setup will not run on Windows Vista or on versions of Windows 7 and Windows Server 2008 R2 which have not been updated. Setting MinVersion to 6.0 to allow Setup to run on Windows Vista is supported but not recommended: Windows Vista doesn't support some of Setup's security measures against potential DLL preloading attacks so these have to be removed by the compiler if MinVersion is below 6.1 making your installer less secure on all versions of Windows.
- - Inno Setup's version number doesn't display "(u)" at the end anymore since the Unicode version has been the only version for quite some time now.
- - Added new [Run] and [UninstallRun] sections flag: dontlogparameters. If this flag is specified, the command line parameters for the program will not be included in the log file.
- - If there are [UninstallRun] section entries without a RunOnceId parameter the compiler will now warn you about this. By assigning a string to RunOnceId, you can ensure that a particular [UninstallRun] entry will only be executed once during uninstallation. The warning can be disabled using new [Setup] section directive MissingRunOnceIdsWarning.
- - Added new [Icons] section parameter: AppUserModelToastActivatorCLSID. Specifies the Windows 10 Application User Model Toast Activator CLSID for the shortcut. Ignored on earlier Windows versions.
- - Setup's prompts to overwrite or keep existing files have been made more user friendly:
-
-
- - Console-mode compiler (ISCC) change: Warnings and errors are now colorized.
- - Pascal Scripting changes:
-
- - Added new CalculateButtonWidth function to the TSetupForm support class.
- - The ACaption and ADescription parameters of the various Create...Page support functions may now specify Setup messages containing shorthands like [name].
- - Fix: Support function WizardSelectComponents now also updates component sizes and the current selection's required disk space.
-
-
- - ISPP changes:
-
- - Using #pragma verboselevel now automatically turns on verbose mode.
- - Added new Message, Warning, and Error support functions.
- - ISPP's output is now cleaner and warnings are colorized.
-
- - Various documentation improvements.
- - Minor tweaks.
-
-
-Contributions via GitHub: Thanks to Gavin Lambert and Sergii Leonov for their contributions.
-
-Some messages have been added and changed in this version: (View differences in Default.isl).
-
- - New messages:
-
- - DownloadingLabel, ButtonStopDownload, StopDownload, ErrorDownloadAborted, ErrorDownloadFailed, ErrorDownloadSizeFailed, ErrorFileHash1, ErrorFileHash2, ErrorProgress, ErrorFileSize.
- - ExistingFileNewerSelectAction, ExistingFileNewer2, ExistingFileNewerOverwriteExisting, ExistingFileNewerKeepExisting, ExistingFileNewerOverwriteOrKeepAll.
- - FileExistsSelectAction, FileExists2, FileExistsOverwriteExisting, FileExistsKeepExisting, FileExistsOverwriteOrKeepAll.
-
-
- - Previously optional messages which must now always be set:
-
- - ComponentsDiskSpaceGBLabel, DiskSpaceGBLabel, PrepareToInstallNeedsRestart.
-
- - Removed messages:
-
- - ExistingFileNewer, FileExists.
-
-
-
-
-Note: Not all official translations have been updated for these changes at this moment.
-
-
Inno Setup 6.0 Revision History
-
-
-
diff --git a/Dependencies/VclStylesinno.dll b/Dependencies/VclStylesinno.dll
deleted file mode 100644
index 8a55dfb5..00000000
Binary files a/Dependencies/VclStylesinno.dll and /dev/null differ
diff --git a/Example mod/Example mod.csproj b/Example mod/Example mod.csproj
new file mode 100644
index 00000000..fcc8f479
--- /dev/null
+++ b/Example mod/Example mod.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+
+ SN.STABLE
+ AnyCPU
+ {C8FB0981-77D2-47C7-BBEF-A3A9EBACACBF}
+ Library
+ Properties
+ Example_mod
+ Example mod
+ v4.7.2
+ 512
+ true
+
+
+
+ bin\SN.STABLE\
+ SUBNAUTICA;SUBNAUTICA_STABLE
+ true
+ true
+ AnyCPU
+ 8.0
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ false
+
+
+ bin\SN.EXP\
+ SUBNAUTICA;SUBNAUTICA_EXP
+ true
+ true
+ AnyCPU
+ 8.0
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ false
+
+
+ bin\BZ.STABLE\
+ BELOWZERO;BELOWZERO_STABLE
+ true
+ true
+ AnyCPU
+ 8.0
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ false
+
+
+ bin\BZ.EXP\
+ BELOWZERO;BELOWZERO_EXP
+ true
+ true
+ AnyCPU
+ 8.0
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ false
+
+
+
+
+ ..\packages\HarmonyX.2.9.0\lib\net45\0Harmony.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Mdb.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Pdb.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Rocks.dll
+ False
+
+
+ ..\packages\MonoMod.RuntimeDetour.22.1.29.1\lib\net452\MonoMod.RuntimeDetour.dll
+ False
+
+
+ ..\packages\MonoMod.Utils.22.1.29.1\lib\net452\MonoMod.Utils.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ {da63f59d-4676-4726-afec-bd9d3682733f}
+ QModManager
+ False
+
+
+ {418502dd-372d-4ef9-8021-b262552dfede}
+ SMLHelper
+ False
+
+
+
+
\ No newline at end of file
diff --git a/Example mod/Mod.cs b/Example mod/Mod.cs
new file mode 100644
index 00000000..17611b8e
--- /dev/null
+++ b/Example mod/Mod.cs
@@ -0,0 +1,320 @@
+using HarmonyLib;
+using QModManager.API.ModLoading;
+using SMLHelper.V2.Commands;
+using SMLHelper.V2.Handlers;
+using SMLHelper.V2.Interfaces;
+using SMLHelper.V2.Json;
+using SMLHelper.V2.Json.Attributes;
+using SMLHelper.V2.Options;
+using SMLHelper.V2.Options.Attributes;
+using System;
+using UnityEngine;
+using UnityEngine.UI;
+using Logger = QModManager.Utility.Logger;
+
+namespace SMLHelper.V2.Examples
+{
+ [QModCore]
+ public static class ExampleMod
+ {
+ ///
+ /// A simple SaveDataCache implementation, intended to save the players current position to disk.
+ ///
+ [FileName("player_position")]
+ internal class SaveData : SaveDataCache
+ {
+ public Vector3 PlayerPosition { get; set; }
+ }
+
+ [QModPatch]
+ public static void Patch()
+ {
+ /// Here, we are setting up a instance of , which will automatically generate an
+ /// options menu using Attributes. The values in this instance will be updated whenever the user changes
+ /// the corresponding option in the menu.
+ Config config = OptionsPanelHandler.Main.RegisterModOptions();
+
+ /// In a similar manner to the above, here we set up an instance of , which will
+ /// automatically be saved and loaded to and from disk appropriately.
+ /// The values in this instance will be updated automatically whenever the user switches between save slots.
+ SaveData saveData = SaveDataHandler.Main.RegisterSaveDataCache();
+
+ // Simply display the recorded player position whenever the save data is loaded
+ saveData.OnFinishedLoading += (object sender, JsonFileEventArgs e) =>
+ {
+ SaveData data = e.Instance as SaveData; // e.Instance is the instance of your SaveData stored as a JsonFile.
+ // We can use polymorphism to convert it back into a SaveData
+ // instance, and access its members, such as PlayerPosition.
+
+ Logger.Log(Logger.Level.Info,
+ $"loaded player position from save slot: {data.PlayerPosition}",
+ showOnScreen: true);
+ };
+
+ // Update the player position before saving it
+ saveData.OnStartedSaving += (object sender, JsonFileEventArgs e) =>
+ {
+ SaveData data = e.Instance as SaveData;
+ data.PlayerPosition = Player.main.transform.position;
+ };
+
+ // Simply display the position we recorded to the save file whenever the save data it is saved
+ saveData.OnFinishedSaving += (object sender, JsonFileEventArgs e) =>
+ {
+ SaveData data = e.Instance as SaveData;
+ Logger.Log(Logger.Level.Info,
+ $"saved player position to save slot: {data.PlayerPosition}",
+ showOnScreen: true);
+ };
+
+ /// Here we are registering a console command by use of a delegate. The delegate will respond to the "delegatecommand"
+ /// command from the dev console, passing values following "delegatecommand" as the correct types, provided they can be
+ /// parsed to that type. For example, "delegatecommand foo 3 true" would be a valid command for the
+ /// delegate signature. You can also use Func or Action to define your delegate signatures
+ /// if you prefer, and you can also pass a reference to a method that matches this signature.
+ ///
+ /// Registered commands must be unique. If another mod has already added the command, your command will be rejected.
+ ///
+ /// If the user enters incorrect parameters for a command, they will be notified of the expected parameter types,
+ /// both on-screen and in the log.
+ ///
+ /// Note that a command can have a return type, but it is not necessary. If it does return any type, it will be printed
+ /// both on-screen and in the log.
+ ConsoleCommandsHandler.Main.RegisterConsoleCommand("delegatecommand", (myString, myInt, myBool) =>
+ {
+ return $"Parameters: {myString} {myInt} {myBool}";
+ });
+
+ /// Here we are registering all console commands defined in , defined by decorating them
+ /// with the . See below
+ /// for an example.
+ ConsoleCommandsHandler.Main.RegisterConsoleCommands(typeof(ExampleMod));
+
+ Logger.Log(Logger.Level.Info, "Patched successfully!");
+ }
+
+ private delegate string MyCommand(string myString, int myInt, bool myBool);
+
+ ///
+ /// Here, we are using the to define a custom console command, which is
+ /// registered via our use of above.
+ ///
+ /// This method will respond to the "attributedcommand" command from the dev console. The command will respect the method
+ /// signature of the decorated method, passing values following "attributedcommand" as the correct types, as long as they can be
+ /// parsed to that type. For example, "attributedcommand foo 3 true" would be a valid command for this method signature.
+ ///
+ /// The decorated method must be both and , or the attribute will
+ /// be ignored. allows for
+ /// targeting non- methods (must still be ), and uses a
+ /// similar syntax to for defining the target method.
+ ///
+ ///
+ ///
+ ///
+ ///
+ [ConsoleCommand("attributedcommand")]
+ public static string MyAttributedCommand(string myString, int myInt, bool myBool = false)
+ {
+ return $"Parameters: {myString} {myInt} {myBool}";
+ }
+ }
+
+ public enum CustomChoice { One, Two, Three }
+
+ ///
+ /// The allows us to set the title of our options menu in the "Mods" tab.
+ ///
+ /// Optionally, we can set the or properties to
+ /// customise when the values are saved to or loaded from disk respectively. By default, the values will be saved whenever
+ /// they change, and loaded from disk when the game is registered to the options menu, which in this example happens on game
+ /// launch and is the recommended setting.
+ ///
+ /// Both of these values allow for bitwise combinations of their options, so
+ /// [Menu("SMLHelper Example Mod", LoadOn = MenuAttribute.LoadEvents.MenuRegistered | MenuAttribute.LoadEvents.MenuOpened)]
+ /// is valid and will result in the values being loaded both on game start and also whenever the menu is opened.
+ ///
+ /// We could also specify a here to customise the name of the config file
+ /// (defaults to "config") and an optional subfolder for the config file to reside in.
+ ///
+ [Menu("SMLHelper Example Mod")]
+ public class Config : ConfigFile
+ {
+ ///
+ /// A is represented by a group of options where only one can be selected at a time,
+ /// similar in usage to a dropdown or radial button group.
+ ///
+ /// Here, we are defining a with the label "My index-based choice", where the underlying
+ /// field represents the index in an array of choices, where the values "One", "Two" and "Three" make
+ /// up that array. As we are not specifiying a default value, the index will by 0 by default.
+ ///
+ /// The is optional and allows us to specify the name of a method in the Config class to
+ /// call when the value has been changed via the options menu. Note that in many cases, you won't need to specify an OnChange
+ /// event, as the values are automatically saved to disk for you as specified by the , and are
+ /// updated in the instance of returned when registering it to the options menu.
+ ///
+ /// Here, we are specifying the name of a method which can handle any OnChange event, for the purposes of demonstrating
+ /// its usage. See for an example usage.
+ ///
+ [Choice("My index-based choice", "One", "Two", "Three"), OnChange(nameof(MyGenericValueChangedEvent))]
+ public int ChoiceIndex;
+
+ ///
+ /// Here, we are defining a which uses a as its backing field,
+ /// where the value represents which option is currently selected, and are specifying "Foo" as the default.
+ ///
+ [Choice("My string-based choice", "Foo", "Bar"), OnChange(nameof(MyGenericValueChangedEvent))]
+ public string ChoiceValue = "Foo";
+
+ ///
+ /// Here, we are defining a which uses an -type as its backing field,
+ /// and the string values for each value defined in the will be used to represent each option,
+ /// so we don't need to specify them. The options will be "One", "Two" and "Three".
+ ///
+ [Choice("My enum-based choice"), OnChange(nameof(MyGenericValueChangedEvent))]
+ public CustomChoice ChoiceEnum;
+
+ ///
+ /// Here, we are again defining a with a -type as its backing field,
+ /// however we are also specifying custom strings which will be used to represent each option in the menu.
+ ///
+ /// An option of will be represented by the "1",
+ /// will be represented by the "2", and so on.
+ ///
+ [Choice("My customised enum-based choice", "1", "2", "3"), OnChange(nameof(MyGenericValueChangedEvent))]
+ public CustomChoice ChoiceCustomEnum;
+
+ ///
+ /// A is represented in the mod options menu as a customistable keybind, where the
+ /// user clicks the box to set the binding, stored as a .
+ ///
+ /// Here, we are not specifying a default, so by default this keybind will not be set.
+ ///
+ [Keybind("My keybind"), OnChange(nameof(MyGenericValueChangedEvent))]
+ public KeyCode KeybindKey;
+
+ ///
+ /// A is used to represent a numeric value as a slider in the options menu, with a
+ /// minimum and maximum value. By default, the minimum value is 0 and maximum is 100.
+ ///
+ /// Here, we are setting an initial value of 25 for the slider. We are also setting the
+ /// property so that this default can be represented by a notch in the slider.
+ ///
+ ///
+ [Slider("My slider", 0, 50, DefaultValue = 25), OnChange(nameof(MyGenericValueChangedEvent))]
+ public int SliderValue = 25;
+
+ ///
+ /// Here, we are defining a with a step of 10, meaning that the value will only
+ /// increment/decrement by 10, resulting in possible values of 0, 10, 20, 30, and so on up to 100.
+ ///
+ /// By default, an has a step of 1, and a or
+ /// has a step of 0.05.
+ ///
+ [Slider("My stepped slider", Step = 10), OnChange(nameof(MyGenericValueChangedEvent))]
+ public int SteppedSliderValue;
+
+ ///
+ /// Here, we are defining a with a format string which defines how the numeric value
+ /// is displayed.
+ /// See for more
+ /// info on numeric format strings.
+ ///
+ [Slider("My float-based slider", Format = "{0:F2}"), OnChange(nameof(MyGenericValueChangedEvent))]
+ public float FloatSliderValue;
+
+ ///
+ /// A is used to represent a checkbox in the options menu
+ /// and is backed by a .
+ ///
+ /// Note that here we are defining two s which correspond to different methods
+ /// in the class. They will both be fired when the value changes, but one of them is specific to this value only. See
+ /// for an example usage.
+ ///
+ [Toggle("My checkbox"), OnChange(nameof(MyCheckboxToggleEvent)), OnChange(nameof(MyGenericValueChangedEvent))]
+ public bool ToggleValue;
+
+ ///
+ /// A is used to represent a button in the options menu, and is different to the other
+ /// attributes above in that it is backed by a public method rather than a field or property, and the method that is
+ /// decorated with the will be called when the corresponding button is clicked, allowing you
+ /// to perform some custom action on demand from the options menu.
+ ///
+ /// Here, we are also defining an to demonstrate its usage. In most cases,
+ /// this attribute will not be needed, but in some cases if you would like to perform some special action with the
+ /// it can be useful. See for an
+ /// example usage.
+ ///
+ /// The passed from the button click event, containing the id of the
+ /// button as a string. It is worth mentioning that it is not necessary to define the
+ /// if you don't need it.
+ [Button("My button"), OnGameObjectCreated(nameof(MyGameObjectCreatedEvent))]
+ public void MyButtonClickEvent(ButtonClickedEventArgs e)
+ {
+ Logger.Log(Logger.Level.Info, "Button was clicked!");
+ Logger.Log(Logger.Level.Info, $"{e.Id}");
+ }
+
+ ///
+ /// This method will be called whenever a value with an referencing it by name is changed.
+ /// In this example, only the field references it, so it will only be called whenever this value
+ /// is changed by the user via the options menu.
+ ///
+ /// The passed from the onchange event, containing the id of the field
+ /// as a string as well as the new value. As with the other events in this example, it is not necessary to define the
+ /// parameter if you do not need the data it contains.
+ private void MyCheckboxToggleEvent(ToggleChangedEventArgs e)
+ {
+ Logger.Log(Logger.Level.Info, "Checkbox value was changed!");
+ Logger.Log(Logger.Level.Info, $"{e.Value}");
+ }
+
+ ///
+ /// This method will be called whenever a value with an referencing it by name is changed.
+ /// In this example, every field above references it, so it will be called whenever any value in this class is changed by the
+ /// user via the options menu.
+ ///
+ /// The data from the onchange event, passed as the interface .
+ ///
+ /// As this particular method is being used as an onchange event for various field types, the usage of the
+ /// interface here enables coercion to its original data type for correct handling, as
+ /// demonstrated by the statement below.
+ ///
+ /// As with the other events in this example, it is not necessary to define the parameter if you do not need the data
+ /// it contains.
+ private void MyGenericValueChangedEvent(IModOptionEventArgs e)
+ {
+ Logger.Log(Logger.Level.Info, "Generic value changed!");
+ Logger.Log(Logger.Level.Info, $"{e.Id}: {e.GetType()}");
+
+ switch (e)
+ {
+ case KeybindChangedEventArgs keybindChangedEventArgs:
+ Logger.Log(Logger.Level.Info, keybindChangedEventArgs.KeyName);
+ break;
+ case ChoiceChangedEventArgs choiceChangedEventArgs:
+ Logger.Log(Logger.Level.Info, $"{choiceChangedEventArgs.Index}: {choiceChangedEventArgs.Value}");
+ break;
+ case SliderChangedEventArgs sliderChangedEventArgs:
+ Logger.Log(Logger.Level.Info, sliderChangedEventArgs.Value.ToString());
+ break;
+ case ToggleChangedEventArgs toggleChangedEventArgs:
+ Logger.Log(Logger.Level.Info, toggleChangedEventArgs.Value.ToString());
+ break;
+ }
+ }
+
+ ///
+ /// The method will be called whenever the for a member with a
+ /// referencing it by name is created. In this example, only the
+ /// button is referencing it, so it will only be called whenever
+ /// this button is created.
+ ///
+ /// The passed from the event, containing the id of the field
+ /// as a string as well as the newly created .
+ private void MyGameObjectCreatedEvent(GameObjectCreatedEventArgs e)
+ {
+ Logger.Log(Logger.Level.Info, "GameObject was created");
+ Logger.Log(Logger.Level.Info, $"{e.Id}: {e.GameObject}");
+ }
+ }
+}
diff --git a/Dependencies/Inno/Examples/MyDll/C#/Properties/AssemblyInfo.cs b/Example mod/Properties/AssemblyInfo.cs
similarity index 74%
rename from Dependencies/Inno/Examples/MyDll/C#/Properties/AssemblyInfo.cs
rename to Example mod/Properties/AssemblyInfo.cs
index 93bbef2e..11142b07 100644
--- a/Dependencies/Inno/Examples/MyDll/C#/Properties/AssemblyInfo.cs
+++ b/Example mod/Properties/AssemblyInfo.cs
@@ -2,35 +2,35 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("MyDll")]
+[assembly: AssemblyTitle("Example mod")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MyDll")]
-[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyProduct("Example mod")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("711cc3c2-07db-46ca-b34b-ba06f4edcbcd")]
+[assembly: Guid("c8fb0981-77d2-47c7-bbef-a3a9ebacacbf")]
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("2.12.1.1")]
+[assembly: AssemblyFileVersion("2.12.1.1")]
diff --git a/Example mod/app.config b/Example mod/app.config
new file mode 100644
index 00000000..2a4bf252
--- /dev/null
+++ b/Example mod/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Example mod/mod.json b/Example mod/mod.json
new file mode 100644
index 00000000..b964cadf
--- /dev/null
+++ b/Example mod/mod.json
@@ -0,0 +1,12 @@
+{
+ "Id": "SMLHelperExampleMod",
+ "DisplayName": "Example Mod For SMLHelper",
+ "Author": "The SMLHelper Dev Team",
+ "Version": "2.12.1.1",
+ "Enable": true,
+ "AssemblyName": "Example mod.dll",
+ "VersionDependencies": {
+ "SMLHelper": "2.13"
+ },
+ "Game": "Both"
+}
diff --git a/Example mod/packages.config b/Example mod/packages.config
new file mode 100644
index 00000000..e3cfd7ec
--- /dev/null
+++ b/Example mod/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Executable/CleanUp.cs b/Executable/CleanUp.cs
deleted file mode 100644
index 60dd3225..00000000
--- a/Executable/CleanUp.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using Mono.Cecil;
-using System;
-using System.IO;
-
-namespace QModManager
-{
- internal static class CleanUp
- {
- private static bool IsChildOf(this DirectoryInfo child, DirectoryInfo parent, bool recursive = false)
- {
- if (child.Parent == null)
- return false;
-
- return child.Parent.FullName == parent.FullName || (recursive && child.Parent.IsChildOf(parent, recursive));
- }
-
- private static bool IsChildOf(this FileInfo child, DirectoryInfo parent, bool recursive = true)
- {
- if (child.Directory == null)
- return false;
-
- return child.Directory.FullName == parent.FullName || (recursive && child.Directory.IsChildOf(parent, recursive));
- }
- private static bool IsChildOf(this FileInfo child, string parentPath, bool recursive = true)
- => child.IsChildOf(new DirectoryInfo(parentPath), recursive);
-
- internal static void Initialize(string gameRootDirectory, string managedDirectory)
- {
- string qmodsDirectory = Path.Combine(gameRootDirectory, "QMods");
- string bepinexCoreDirectory = Path.Combine(gameRootDirectory, "BepInEx", "core");
-
- string[] pathsToCheck = new[] { managedDirectory, qmodsDirectory };
-
- foreach (var path in pathsToCheck)
- {
- foreach (var file in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories))
- {
- var fileInfo = new FileInfo(file);
- if (fileInfo.FullName.Contains("system32") || fileInfo.FullName.Contains("Windows") || fileInfo.IsChildOf(bepinexCoreDirectory, true))
- {
- Console.WriteLine($"Path is unsafe! {path}");
- continue;
- }
-
- try
- {
- using (var stream = new MemoryStream(File.ReadAllBytes(file)))
- {
- if (AssemblyDefinition.ReadAssembly(stream).MainModule.Name == "0Harmony" && File.Exists(file))
- {
- File.Delete(file);
- Console.WriteLine($"Deleted {new DirectoryInfo(file).FullName}...");
- }
- }
- }
- catch (BadImageFormatException)
- {
- if (Path.GetFileName(file).StartsWith("0Harmony") && File.Exists(file))
- {
- File.Delete(file);
- Console.WriteLine($"Deleted {new DirectoryInfo(file).FullName}...");
- }
- }
- }
- }
- }
- }
-}
diff --git a/Executable/Executable.csproj b/Executable/Executable.csproj
deleted file mode 100644
index 3d133575..00000000
--- a/Executable/Executable.csproj
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {E00B7FE8-0F1D-4AE6-9E47-4BFD81537F14}
- Exe
- QModManager
- QModManager
- v4.7.2
- 512
- {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 4
- true
-
-
-
- ..\Build\$(Configuration)\
- SUBNAUTICA;SUBNAUTICA_STABLE
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
-
-
- ..\Build\$(Configuration)\
- SUBNAUTICA;SUBNAUTICA_EXP
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
-
-
- ..\Build\$(Configuration)\
- BELOWZERO;BELOWZERO_STABLE
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
-
-
- ..\Build\$(Configuration)\
- BELOWZERO;BELOWZERO_EXP
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
-
-
- QModManager.Executable
-
-
- icon.ico
-
-
-
- ..\Dependencies\BepInEx\BepInEx\core\Mono.Cecil.dll
- False
-
-
-
-
-
-
-
- Code
-
-
-
-
-
-
-
-
- {ca99f076-7459-4682-a41d-2850aee28566}
- QModManager.UnityAudioFixer
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Executable/Program.cs b/Executable/Program.cs
deleted file mode 100644
index 38095240..00000000
--- a/Executable/Program.cs
+++ /dev/null
@@ -1,305 +0,0 @@
-namespace QModManager
-{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using System.Runtime.InteropServices;
-
- internal enum Action
- {
- Install,
- Uninstall,
- RunByUser,
- CleanUp
- }
-
- [Flags]
- internal enum OS
- {
- None = 0b00,
- Windows = 0b01,
- Mac = 0b10,
- Both = Windows | Mac,
- }
-
- internal static class Executable
- {
- internal static Action action = Action.RunByUser;
- internal static OS os;
-
- internal static void Main(string[] args)
- {
- try
- {
- InitAssemblyResolver();
-
- var parsedArgs = new Dictionary();
-
- foreach (string arg in args)
- {
- if (arg.Contains("="))
- {
- parsedArgs = args.Select(s => s.Split(new[] { '=' }, 1)).ToDictionary(s => s[0], s => s[1]);
- }
- else if (arg == "-i")
- action = Action.Install;
- else if (arg == "-u")
- action = Action.Uninstall;
- else if (arg == "-c")
- action = Action.CleanUp;
- }
-
- string gameRootDirectory = Path.Combine(Environment.CurrentDirectory, "../../..");
- string snManagedDirectory = Path.Combine(gameRootDirectory, "Subnautica_Data", "Managed");
- string bzManagedDirectory = Path.Combine(gameRootDirectory, "SubnauticaZero_Data", "Managed");
- string managedDirectory;
- if (Directory.Exists(snManagedDirectory))
- {
- managedDirectory = snManagedDirectory;
- }
- else if (Directory.Exists(bzManagedDirectory))
- {
- managedDirectory = bzManagedDirectory;
- }
- else
- {
- Console.WriteLine("Could not find Managed directory.");
- Console.WriteLine("Please make sure you have installed QModManager in the right folder.");
- Console.WriteLine("If the problem persists, open a bug report on NexusMods or an issue on GitHub");
- Console.WriteLine();
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
- Environment.Exit(1);
- return;
- }
- string globalgamemanagers = Path.Combine(managedDirectory, "../globalgamemanagers");
-
- os = GetOS();
-
- if (os == OS.Both)
- {
- // This runs if both windows and mac files were detected, but it should NEVER happen.
- Console.WriteLine("An unexpected error has occurred.");
- Console.WriteLine("Both Windows and Mac files detected!");
- Console.WriteLine("Is this a Windows or a Mac environment?");
- Console.WriteLine();
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
- Environment.Exit(1);
- return;
- }
- else if (os == OS.None)
- {
- Console.WriteLine("Could not find any game to patch!");
- Console.WriteLine("An assembly file was found, but no executable was detected.");
- Console.WriteLine("Please make sure you have installed QModManager in the right folder.");
- Console.WriteLine("If the problem persists, open a bug report on NexusMods or an issue on GitHub");
- Console.WriteLine();
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
- Environment.Exit(1);
- return;
- }
-
- if (!File.Exists(Path.Combine(managedDirectory, "Assembly-CSharp.dll")))
- {
- Console.WriteLine("Could not find the assembly file.");
- Console.WriteLine("Please make sure you have installed QModManager in the right folder.");
- Console.WriteLine("If the problem persists, open a bug report on NexusMods or an issue on GitHub");
- Console.WriteLine();
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
- Environment.Exit(1);
- }
-
- if (action == Action.Install)
- {
- Console.WriteLine("Attempting to enable Unity audio...");
- UnityAudioFixer.EnableUnityAudio();
- Console.WriteLine("Unity audio enabled.");
- Environment.Exit(0);
- }
- else if (action == Action.Uninstall)
- {
- Console.WriteLine("Attempting to disable Unity audio...");
- UnityAudioFixer.DisableUnityAudio();
- Console.WriteLine("Unity audio disabled.");
- Environment.Exit(0);
- }
- else if (action == Action.CleanUp)
- {
- Console.WriteLine("Attempting to clean up Nitrox and previous QMM installs...");
- CleanUp.Initialize(gameRootDirectory, managedDirectory);
- Console.WriteLine("Clean-up complete.");
- Environment.Exit(0);
- }
- else
- {
- Console.Write("Clean up install? [Y/N] > ");
- ConsoleKey key = Console.ReadKey().Key;
- Console.WriteLine();
-
- if (key == ConsoleKey.Y)
- {
- Console.WriteLine("Attempting to clean up Nitrox and previous QMM installs...");
- CleanUp.Initialize(gameRootDirectory, managedDirectory);
- Console.WriteLine("Clean-up complete.");
- Console.WriteLine();
- }
-
- Console.Write("Enable Unity sound? [Y/N] > ");
- key = Console.ReadKey().Key;
- Console.WriteLine();
-
- if (key == ConsoleKey.Y)
- {
- Console.WriteLine("Attempting to enable Unity audio...");
- UnityAudioFixer.EnableUnityAudio();
- Console.WriteLine("Unity audio enabled.");
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
- Environment.Exit(0);
- }
- else if (key == ConsoleKey.N)
- {
- Console.WriteLine("Attempting to disable Unity audio...");
- UnityAudioFixer.DisableUnityAudio();
- Console.WriteLine("Unity audio disabled.");
- Console.WriteLine("Press any key to exit...");
- Console.ReadKey();
- Environment.Exit(0);
- }
- }
- }
- catch (Exception e)
- {
- Console.WriteLine("EXCEPTION CAUGHT!");
- Console.WriteLine(e.ToString());
- Console.WriteLine();
- Console.ReadKey();
- Environment.Exit(2);
- }
- }
-
- private static OS GetOS()
- {
- string windowsDirectory = Path.Combine(Environment.CurrentDirectory, "../../..");
- string macDirectory = Path.Combine(Environment.CurrentDirectory, "../../../../../..");
-
- bool subnautica = false, belowzero = false;
-
- // Check if the device is running Windows OS
- bool onWindows = false, onWindowsSN, onWindowsBZ;
- if (Directory.Exists(windowsDirectory))
- {
- try
- {
- // Try to get the Subnautica executable
- // This method throws a lot of exceptions
- onWindowsSN = Directory.GetFiles(windowsDirectory, "Subnautica.exe", SearchOption.TopDirectoryOnly).Length > 0;
- onWindowsBZ = Directory.GetFiles(windowsDirectory, "SubnauticaZero.exe", SearchOption.TopDirectoryOnly).Length > 0;
-
- onWindows = onWindowsSN || onWindowsBZ;
-
- subnautica = subnautica || onWindowsSN;
- belowzero = belowzero || onWindowsBZ;
- }
- catch (Exception)
- {
- // If an exception was thrown, the file probably isn't there
- onWindows = false;
- }
- }
-
- // Check if the device is running Mac OS
- bool onMac = false, onMacSN, onMacBZ;
- if (Directory.Exists(macDirectory))
- {
- try
- {
- // Try to get the Subnautica executable
- // This method throws a lot of exceptions
- // On mac, .app files act as files and folders at the same time, but they are detected as folders.
- onMacSN = Directory.GetDirectories(macDirectory, "Subnautica.app", SearchOption.TopDirectoryOnly).Length > 0;
- onMacBZ = Directory.GetDirectories(macDirectory, "SubnauticaZero.app", SearchOption.TopDirectoryOnly).Length > 0;
-
- onMac = onMacSN || onMacBZ;
-
- subnautica = subnautica || onMacSN;
- belowzero = belowzero || onMacBZ;
- }
- catch (Exception)
- {
- // If an exception was thrown, the file probably isn't there
- onMac = false;
- }
- }
-
- var os = OS.None;
- if (onWindows)
- {
- os |= OS.Windows;
- }
- if (onMac)
- {
- os |= OS.Mac;
- }
- return os;
- }
-
- private static void InitAssemblyResolver()
- {
- AppDomain.CurrentDomain.AssemblyResolve += ResolveAssemblies;
- }
-
- internal static string GameRootDirectory => Path.Combine(Environment.CurrentDirectory, "../../..");
- internal static string BepInExRootDirectory => Path.Combine(GameRootDirectory, "BepInEx");
- internal static string BepInExAssemblyDirectory => Path.Combine(BepInExRootDirectory, "core");
- internal static string QModManagerPluginDirectory => Path.Combine(BepInExRootDirectory, "plugins", "QModManager");
- internal static string QModManagerPatcherDirectory => Path.Combine(BepInExRootDirectory, "patchers", "QModManager");
-
- private static Assembly ResolveAssemblies(object sender, ResolveEventArgs e)
- {
- var name = new AssemblyName(e.Name);
-
- if (Utility.TryResolveDllAssembly(name, BepInExRootDirectory, out var assembly) || // First try BepInEx assemblies
- Utility.TryResolveDllAssembly(name, QModManagerPluginDirectory, out assembly) || // Then QModManager plugins
- Utility.TryResolveDllAssembly(name, QModManagerPatcherDirectory, out assembly)) // Then QModManager patchers
- {
-
- return assembly;
- }
- else
- {
- return null;
- }
- }
-
- #region Disable exit
-
- internal static void DisableExit()
- {
- DisableExitButton();
- Console.CancelKeyPress += CancelKeyPress;
- Console.TreatControlCAsInput = true;
- }
-
- private static void DisableExitButton()
- {
- EnableMenuItem(GetSystemMenu(GetConsoleWindow(), false), 0xF060, 0x1);
- }
-
- private static void CancelKeyPress(object sender, ConsoleCancelEventArgs e)
- {
- e.Cancel = true;
- }
-
- [DllImport("user32.dll")] private static extern int EnableMenuItem(IntPtr tMenu, int targetItem, int targetStatus);
- [DllImport("user32.dll")] private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
- [DllImport("kernel32.dll", ExactSpelling = true)] private static extern IntPtr GetConsoleWindow();
-
- #endregion
- }
-}
diff --git a/Executable/Properties/AssemblyInfo.cs b/Executable/Properties/AssemblyInfo.cs
deleted file mode 100644
index 1b7e0557..00000000
--- a/Executable/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-[assembly: AssemblyTitle("QModManagerExecutable")]
-[assembly: AssemblyDescription("Config based patch management for Subnautica and Subnautica: Below Zero")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("QModManager")]
-[assembly: AssemblyProduct("QModManagerExecutable")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-[assembly: ComVisible(false)]
-
-[assembly: AssemblyVersion("4.3.0")]
-[assembly: AssemblyFileVersion("4.3.0")]
diff --git a/Executable/Utility.cs b/Executable/Utility.cs
deleted file mode 100644
index 36f1503c..00000000
--- a/Executable/Utility.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Reflection;
-
-namespace QModManager
-{
- public static class Utility
- {
- ///
- /// Try to resolve and load the given assembly DLL.
- ///
- /// Name of the assembly, of the type .
- /// Directory to search the assembly from.
- /// The loaded assembly.
- /// True, if the assembly was found and loaded. Otherwise, false.
- private static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, Func loader, out T assembly) where T : class
- {
- assembly = null;
-
- var potentialDirectories = new List { directory };
-
- potentialDirectories.AddRange(Directory.GetDirectories(directory, "*", SearchOption.AllDirectories));
-
- foreach (string subDirectory in potentialDirectories)
- {
- string path = Path.Combine(subDirectory, $"{assemblyName.Name}.dll");
-
- if (!File.Exists(path))
- continue;
-
- try
- {
- assembly = loader(path);
- }
- catch (Exception)
- {
- continue;
- }
-
- return true;
- }
-
- return false;
- }
-
- ///
- /// Try to resolve and load the given assembly DLL.
- ///
- /// Name of the assembly, of the type .
- /// Directory to search the assembly from.
- /// The loaded assembly.
- /// True, if the assembly was found and loaded. Otherwise, false.
- public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly)
- {
- return TryResolveDllAssembly(assemblyName, directory, Assembly.LoadFile, out assembly);
- }
- }
-}
diff --git a/Executable/app.config b/Executable/app.config
deleted file mode 100644
index 312bb3f2..00000000
--- a/Executable/app.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/Executable/icon.ico b/Executable/icon.ico
deleted file mode 100644
index 0d39dee8..00000000
Binary files a/Executable/icon.ico and /dev/null differ
diff --git a/Installer/BZ.EXP.iss b/Installer/BZ.EXP.iss
deleted file mode 100644
index 04b9d9f1..00000000
--- a/Installer/BZ.EXP.iss
+++ /dev/null
@@ -1,606 +0,0 @@
-; Throws an error if the version used to compile this script is not unicode
-; This ensures that the application is built correctly
-#if !Defined(UNICODE)
- #error A unicode version of Inno Setup is required to compile this script
-#endif
-
-#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.3.0"
-#define Author "QModManager"
-#define URL "https://github.com/QModManager/QModManager"
-#define SupportURL "https://discord.gg/UpWuWwq"
-#define UpdatesURL "https://nexusmods.com" ; The link to the mod will be added after it
-
-[Setup]
-AllowNetworkDrive=no
-AllowUNCPath=no
-AlwaysShowDirOnReadyPage=yes
-AppendDefaultDirName=no
-AppId={code:GetGUID}
-AppName={#Name}
-AppPublisher={#Author}
-AppPublisherURL={#URL}
-AppSupportURL={#SupportURL}
-AppUpdatesURL={code:GetURL}
-AppVerName={#Name} {#Version}
-AppVersion={#Version}
-Compression=lzma
-DefaultDirName=.
-DirExistsWarning=no
-DisableDirPage=no
-DisableProgramGroupPage=yes
-DisableWelcomePage=no
-EnableDirDoesntExistWarning=yes
-OutputBaseFilename=QModManager_{#Version}_BelowZero_Experimental_Setup
-OutputDir=.\
-PrivilegesRequired=admin
-SetupIconFile=..\..\Assets\Icon.ico
-SolidCompression=yes
-UninstallDisplayIcon={app}\BepInEx\patchers\QModManager\QModManager.exe
-UninstallDisplayName={code:GetName}
-UsePreviousAppDir=no
-UsePreviousLanguage=no
-WizardImageFile=..\..\Assets\InstallerImage.bmp
-WizardSmallImageFile=..\..\Assets\InstallerSmallImage.bmp
-UsePreviousSetupType=False
-UsePreviousTasks=False
-CloseApplications=False
-
-[Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
-
-[Files]
-; Files used by the installer but not required by QModManager itself
-; Installer theme
-Source: "..\..\Dependencies\VclStylesinno.dll"; Flags: DontCopy
-Source: "..\..\Dependencies\Carbon.vsf"; Flags: DontCopy
-; Installer extensions
-Source: "InstallerExtensions.dll"; Flags: DontCopy
-
-; Files required by QModManager itself
-; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\Oculus.Newtonsoft.Json.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; QMM
-Source: "QModInstaller.dll"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModInstaller.xml"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModManager.exe"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx patchers
-Source: "QModManager.OculusNewtonsoftRedirect.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.QModPluginGenerator.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx
-Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
-Source: "..\..\Dependencies\BZ.EXP\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
-
-[Dirs]
-Name: "{app}\QMods"
-
-[Run]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-c"; Tasks: cleanup
-
-[UninstallRun]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-u";
-
-[Messages]
-; BeveledLabel={#Name} {#Version}
-WizardSelectDir=Select install location
-SelectDirLabel3=Please select the install folder of the game.
-SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.%nIf you have the game on steam, you can also use the buttons on the bottom left to auto-complete the install path for the chosen game.
-ReadyLabel2a=By installing, you agree to allow QModManager to send external web requests, most often to check for updates. You can disable this option at any time in the Mods tab of the Subnautica options menu.
-ExitSetupMessage=Setup is not complete. If you exit now, {#Name} will not be installed.%nExit Setup?
-WizardSelectComponents=Review Install
-SelectComponentsDesc=
-SelectComponentsLabel2=
-
-[Types]
-; Used to disable the three Full, Compact and Custom types
-Name: "select"; Description: "QModManager"; Flags: IsCustom;
-
-[Components]
-Name: "qmm"; Description: "QModManager"; Flags: fixed; Types: select;
-Name: "qmm\bz"; Description: "Install for Below Zero"; Flags: exclusive fixed;
-
-[Tasks]
-Name: "cleanup"; Description: "(Recommended) Clean up after previous Nitrox and QMM installs";
-
-[Code]
-// Import stuff from InstallerExtensions.dll
-function PathsEqual(pathone, pathtwo: WideString): Boolean; external 'PathsEqual@files:InstallerExtensions.dll stdcall setuponly delayload';
-
-function IsBelowZero(path: String): Boolean;
-begin
- if (FileExists(path + '\SubnauticaZero.exe')) and (FileExists(path + '\SubnauticaZero_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := true
- Exit
- end
- else
- begin
- Result := false
- Exit
- end
-end;
-function IsBelowZeroApp(): Boolean;
-begin
- Result := IsBelowZero(ExpandConstant('{app}'));
-end;
-
-function GetName(def: string): String;
-begin
- if (IsBelowZeroApp()) then
- begin
- Result := '{#Name} (Below Zero)'
- end
- else
- begin
- Result := ExpandConstant('{app}')
- end
-end;
-
-function GetURL(def: string): String;
-begin
- if (IsBelowZeroApp()) then
- begin
- Result := '{#UpdatesURL}/subnauticabelowzero/mods/1'
- end
- else
- begin
- Result := '{#UpdatesURL}'
- end
-end;
-
-function CurPageChanged_SelectComponents(CurPageID: Integer): Boolean;
-var
- Index: Integer;
- app: String;
-begin
- if CurPageID = wpSelectComponents then
- begin
- try
- app := ExpandConstant('{app}')
- except
- app := 'null'
- end;
- if not IsBelowZero(app) then
- begin
- WizardForm.SelectComponentsLabel.Caption := 'No game detected in this folder, cannot install'
- Exit
- end;
- Index := WizardForm.ComponentsList.Items.IndexOf('Install for Below Zero')
- if Index <> -1 then
- begin
- if IsBelowZero(app) then
- begin
- WizardForm.ComponentsList.Checked[Index] := true
- WizardForm.SelectComponentsLabel.Caption := 'Install QModManager for Below Zero'
- end
- end
- end
-end;
-
-function GetDir(folder: String; name: String): String;
-var
-I : Integer;
-P : Integer;
-steamInstallPath : String;
-configFile : String;
-fileLines: TArrayOfString;
-begin
- steamInstallPath := ''
- RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\WOW6432Node\Valve\Steam', 'InstallPath', steamInstallPath)
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- else
- begin
- configFile := steamInstallPath + '\config\config.vdf'
- if FileExists(configFile) then
- begin
- if LoadStringsFromFile(configFile, FileLines) then
- begin
- for I := 0 to GetArrayLength(FileLines) - 1 do
- begin
- P := Pos('BaseInstallFolder_', FileLines[I])
- if P > 0 then
- begin
- steamInstallPath := Copy(FileLines[I], P + 23, 3) + Copy(FileLines[I], P + 27, Length(FileLines[I]) - P - 27);
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then // If the folder is correct
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- end
- end
- end
- end
- end;
- Result := 'none'
- Exit
-end;
-
-var ACLabel: TLabel;
-var BelowZeroButton: TNewRadioButton;
-
-procedure BelowZeroButtonOnClick(Sender: TObject);
-begin
- WizardForm.DirEdit.Text := GetDir('SubnauticaZero', 'SubnauticaZero')
- BelowZeroButton.Checked := true
-end;
-
-function InitializeWizard_AddButtons(): Boolean;
-begin
- ACLabel := TLabel.Create(WizardForm)
- with ACLabel do
- begin
- Parent := WizardForm
- Caption := 'Get path from Steam for:'
- Left := WizardForm.SelectDirLabel.Left / 3
- Top := WizardForm.BackButton.Top - WizardForm.BackButton.Top / 90
- end;
-
- BelowZeroButton := TNewRadioButton.Create(WizardForm)
- with BelowZeroButton do
- begin
- Parent := WizardForm
- Caption := 'Below Zero'
- OnClick := @BelowZeroButtonOnClick
- Left := WizardForm.SelectDirLabel.Left + WizardForm.SelectDirLabel.Left / 30
- Top := WizardForm.BackButton.Top + 10
- Height := WizardForm.BackButton.Height
- Enabled := True
- end;
-end;
-
-function CurPageChanged_AddButtons(CurPageID: Integer): Boolean;
-begin
- if CurPageID = wpSelectDir then
- begin
- WizardForm.DirEdit.Text := ''
- if GetDir('SubnauticaZero', 'SubnauticaZero') = 'none' then
- begin
- BelowZeroButton.Enabled := false
- end;
-
- if BelowZeroButton.Enabled then
- begin
- WizardForm.DirEdit.Text := GetDir('SubnauticaZero', 'SubnauticaZero')
- BelowZeroButton.Checked := true
- end;
- end;
- BelowZeroButton.Visible := CurPageID = wpSelectDir
- ACLabel.Visible := CurPageID = wpSelectDir
-end;
-
-var DirEditOnChangePrev: TNotifyEvent;
-
-procedure DirEditOnChange(Sender: TObject);
-var
- S: String;
-begin
- if Pos('subnauticazero', LowerCase(WizardForm.DirEdit.Text)) <> 0 then
- begin
- if PathsEqual(WizardForm.DirEdit.Text, GetDir('SubnauticaZero', 'SubnauticaZero')) then
- begin
- BelowZeroButton.Checked := true
- end
- else
- begin
- BelowZeroButton.Checked := false;
- end
- end
- else
- begin
- BelowZeroButton.Checked := false;
- end;
-
- if (Pos('://', WizardForm.DirEdit.Text) <> 0) or (Pos(':\\', WizardForm.DirEdit.Text) <> 0) then
- begin
- S := WizardForm.DirEdit.Text;
- StringChangeEx(S, '://', ':/', true);
- StringChangeEx(S, ':\\', ':\', true);
- WizardForm.DirEdit.Text := S;
- end
-end;
-
-function InitializeWizard_DirOnChange(): Boolean;
-begin
- DirEditOnChangePrev := WizardForm.DirEdit.OnChange
- WizardForm.DirEdit.OnChange := @DirEditOnChange
-end;
-
-var appIsSet: Boolean;
-
-function GetGUID(def: String): String;
-begin
- if not appIsSet then // The installer tries to get the GUID at startup to use previous options such as install path or install settings. As QModManager's GUID is defined AFTER the path is selected, it doesn't need to provide a value
- begin
- Result := ''
- Exit
- end;
- if IsBelowZero(ExpandConstant('{app}')) then
- begin
- Result := '{A535470D-3403-46A2-8D44-28AD4B90C9A3}'
- Exit
- end
-end;
-
-function IsAppRunning(const FileName : string): Boolean;
-var
- FSWbemLocator: Variant;
- FWMIService : Variant;
- FWbemObjectSet: Variant;
-begin
- Result := false;
- FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
- FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
- FWbemObjectSet :=
- FWMIService.ExecQuery(
- Format('SELECT Name FROM Win32_Process Where Name="%s"', [FileName]));
- Result := (FWbemObjectSet.Count > 0);
- FWbemObjectSet := Unassigned;
- FWMIService := Unassigned;
- FSWbemLocator := Unassigned;
-end;
-
-// Imports some stuff from VclStylesInno.dll
-procedure LoadVCLStyle(VClStyleFile: String); external 'LoadVCLStyleW@files:VclStylesInno.dll stdcall';
-procedure UnLoadVCLStyles; external 'UnLoadVCLStyles@files:VclStylesInno.dll stdcall';
-
-// Check for .NET version -- code from http://www.kynosarges.de/DotNetVersion.html
-function IsDotNetDetected(version: string; service: cardinal): boolean;
-// Indicates whether the specified version and service pack of the .NET Framework is installed.
-//
-// version -- Specify one of these strings for the required .NET Framework version:
-// 'v1.1' .NET Framework 1.1
-// 'v2.0' .NET Framework 2.0
-// 'v3.0' .NET Framework 3.0
-// 'v3.5' .NET Framework 3.5
-// 'v4\Client' .NET Framework 4.0 Client Profile
-// 'v4\Full' .NET Framework 4.0 Full Installation
-// 'v4.5' .NET Framework 4.5
-// 'v4.5.1' .NET Framework 4.5.1
-// 'v4.5.2' .NET Framework 4.5.2
-// 'v4.6' .NET Framework 4.6
-// 'v4.6.1' .NET Framework 4.6.1
-// 'v4.6.2' .NET Framework 4.6.2
-// 'v4.7' .NET Framework 4.7
-//
-// service -- Specify any non-negative integer for the required service pack level:
-// 0 No service packs required
-// 1, 2, etc. Service pack 1, 2, etc. required
-var
- key, versionKey: string;
- install, release, serviceCount, versionRelease: cardinal;
- success: boolean;
-begin
- versionKey := version;
- versionRelease := 0;
-
- // .NET 1.1 and 2.0 embed release number in version key
- if version = 'v1.1' then begin
- versionKey := 'v1.1.4322';
- end else if version = 'v2.0' then begin
- versionKey := 'v2.0.50727';
- end
-
- // .NET 4.5 and newer install as update to .NET 4.0 Full
- else if Pos('v4.', version) = 1 then begin
- versionKey := 'v4\Full';
- case version of
- 'v4.5': versionRelease := 378389;
- 'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
- 'v4.5.2': versionRelease := 379893;
- 'v4.6': versionRelease := 393295; // 393297 on Windows 8.1 and older
- 'v4.6.1': versionRelease := 394254; // 394271 before Win10 November Update
- 'v4.6.2': versionRelease := 394802; // 394806 before Win10 Anniversary Update
- 'v4.7': versionRelease := 460798; // 460805 before Win10 Creators Update
- end;
- end;
-
- // installation key group for all .NET versions
- key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;
-
- // .NET 3.0 uses value InstallSuccess in subkey Setup
- if Pos('v3.0', version) = 1 then begin
- success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
- end else begin
- success := RegQueryDWordValue(HKLM, key, 'Install', install);
- end;
-
- // .NET 4.0 and newer use value Servicing instead of SP
- if Pos('v4', version) = 1 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
- end else begin
- success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
- end;
-
- // .NET 4.5 and newer use additional value Release
- if versionRelease > 0 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
- success := success and (release >= versionRelease);
- end;
-
- result := success and (install = 1) and (serviceCount >= service);
-end;
-
-function InitializeSetup(): Boolean;
-var
- ErrCode: Integer;
-begin
- if not IsDotNetDetected('v4\Full', 0) then
- begin
- if MsgBox('QModManager requires Microsoft .NET Framework 4.0' + #13#10 + 'Would you like to install it now?', mbCriticalError, MB_YESNO) = IDYES then
- begin
- if not ShellExec('open', 'https://dotnet.microsoft.com/download/dotnet-framework/net40', '', '', SW_SHOW, ewNoWait, ErrCode) then
- begin
- SysErrorMessage(ErrCode);
- end
- end;
- result := false;
- Exit
- end;
- appIsSet := false
- if IsAppRunning('SubnauticaZero.exe') then
- begin
- MsgBox('You need to close Below Zero before installing QModManager.' + #13#10 + 'If the game is not running, please reboot your computer.', mbError, MB_OK);
- Result := false
- end
- else
- begin
- // Load skin
- ExtractTemporaryFile('Carbon.vsf');
- LoadVCLStyle(ExpandConstant('{tmp}\Carbon.vsf'));
- Result := true
- end
-end;
-
-function IsPreviousVersionInstalled: Boolean;
-var
- uninstallRegKey: String;
- previousVersion: String;
-begin
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- previousVersion := '';
- Result := (RegKeyExists(HKLM, uninstallRegKey) or RegKeyExists(HKCU, uninstallRegKey));
-end;
-
-function GetUninstallString: string;
-var
- uninstallRegKey: String;
- uninstallString: String;
-begin
- Result := '';
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- uninstallString := '';
- if not RegQueryStringValue(HKLM, uninstallRegKey, 'UninstallString', uninstallString) then
- RegQueryStringValue(HKCU, uninstallRegKey, 'UninstallString', uninstallString);
- Result := uninstallString;
-end;
-
-function IsUpgrade: Boolean;
-begin
- Result := (GetUninstallString() <> '');
-end;
-
-function NextButtonClick(CurPageID: Integer): Boolean;
-var
- uninstallString: String;
- resultCode: Integer;
-begin
- if CurPageID = wpSelectComponents then
- appIsSet := true;
-
- Result := true;
-end;
-
-function PrepareToInstall(var NeedsRestart: boolean): string;
-var
- uninstallString: string;
- resultCode: integer;
-begin
- NeedsRestart := false;
-
- if IsPreviousVersionInstalled() then
- begin
- uninstallString := RemoveQuotes(GetUninstallString());
- if FileExists(uninstallString) then
- begin
- Exec(uninstallString, '/SILENT', '', SW_SHOW, ewWaitUntilTerminated, resultCode);
- if IsPreviousVersionInstalled() then
- Result := 'Previous installation must be uninstalled to continue.';
- end;
- end;
-end;
-
-var TypesComboOnChangePrev: TNotifyEvent;
-
-procedure ComponentsListCheckChanges;
-begin
- WizardForm.NextButton.Enabled := (WizardSelectedComponents(false) <> '')
-end;
-
-procedure ComponentsListClickCheck(Sender: TObject);
-begin
- ComponentsListCheckChanges
-end;
-
-procedure TypesComboOnChange(Sender: TObject);
-begin
- TypesComboOnChangePrev(Sender)
- ComponentsListCheckChanges
-end;
-
-procedure CurPageChanged(CurPageID: Integer);
-begin
- CurPageChanged_SelectComponents(CurPageID)
- CurPageChanged_AddButtons(CurPageID)
- if CurPageID = wpSelectComponents then
- begin
- ComponentsListCheckChanges;
- end
-end;
-
-procedure InitializeWizard();
-begin
- WizardForm.ComponentsList.OnClickCheck := @ComponentsListClickCheck
- TypesComboOnChangePrev := WizardForm.TypesCombo.OnChange
- WizardForm.TypesCombo.OnChange := @TypesComboOnChange
- InitializeWizard_AddButtons
- InitializeWizard_DirOnChange
-end;
-
-procedure UnloadInstallerExtensions();
- var
- FilePath: string;
- BatchPath: string;
- S: TArrayOfString;
- ResultCode: Integer;
-begin
- FilePath := ExpandConstant('{tmp}\InstallerExtensions.dll');
- if not FileExists(FilePath) then
- begin
- Log(Format('File %s does not exist', [FilePath]));
- end
- else
- begin
- BatchPath :=
- ExpandConstant('{%TEMP}\') +
- 'delete_' + ExtractFileName(ExpandConstant('{tmp}')) + '.bat';
- SetArrayLength(S, 7);
- S[0] := ':loop';
- S[1] := 'del "' + FilePath + '"';
- S[2] := 'if not exist "' + FilePath + '" goto end';
- S[3] := 'goto loop';
- S[4] := ':end';
- S[5] := 'rd "' + ExpandConstant('{tmp}') + '"';
- S[6] := 'del "' + BatchPath + '"';
- if not SaveStringsToFile(BatchPath, S, False) then
- begin
- Log(Format('Error creating batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- if not Exec(BatchPath, '', '', SW_HIDE, ewNoWait, ResultCode) then
- begin
- Log(Format('Error executing batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- begin
- Log(Format('Executed batch file %s to delete %s', [BatchPath, FilePath]));
- end;
- end;
-end;
-
-procedure DeinitializeSetup();
-begin
- // Unload skin
- UnLoadVCLStyles;
- UnloadInstallerExtensions;
-end;
diff --git a/Installer/BZ.STABLE.iss b/Installer/BZ.STABLE.iss
deleted file mode 100644
index a111918d..00000000
--- a/Installer/BZ.STABLE.iss
+++ /dev/null
@@ -1,606 +0,0 @@
-; Throws an error if the version used to compile this script is not unicode
-; This ensures that the application is built correctly
-#if !Defined(UNICODE)
- #error A unicode version of Inno Setup is required to compile this script
-#endif
-
-#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.3.0"
-#define Author "QModManager"
-#define URL "https://github.com/QModManager/QModManager"
-#define SupportURL "https://discord.gg/UpWuWwq"
-#define UpdatesURL "https://nexusmods.com" ; The link to the mod will be added after it
-
-[Setup]
-AllowNetworkDrive=no
-AllowUNCPath=no
-AlwaysShowDirOnReadyPage=yes
-AppendDefaultDirName=no
-AppId={code:GetGUID}
-AppName={#Name}
-AppPublisher={#Author}
-AppPublisherURL={#URL}
-AppSupportURL={#SupportURL}
-AppUpdatesURL={code:GetURL}
-AppVerName={#Name} {#Version}
-AppVersion={#Version}
-Compression=lzma
-DefaultDirName=.
-DirExistsWarning=no
-DisableDirPage=no
-DisableProgramGroupPage=yes
-DisableWelcomePage=no
-EnableDirDoesntExistWarning=yes
-OutputBaseFilename=QModManager_{#Version}_BelowZero_Setup
-OutputDir=.\
-PrivilegesRequired=admin
-SetupIconFile=..\..\Assets\Icon.ico
-SolidCompression=yes
-UninstallDisplayIcon={app}\BepInEx\patchers\QModManager\QModManager.exe
-UninstallDisplayName={code:GetName}
-UsePreviousAppDir=no
-UsePreviousLanguage=no
-WizardImageFile=..\..\Assets\InstallerImage.bmp
-WizardSmallImageFile=..\..\Assets\InstallerSmallImage.bmp
-UsePreviousSetupType=False
-UsePreviousTasks=False
-CloseApplications=False
-
-[Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
-
-[Files]
-; Files used by the installer but not required by QModManager itself
-; Installer theme
-Source: "..\..\Dependencies\VclStylesinno.dll"; Flags: DontCopy
-Source: "..\..\Dependencies\Carbon.vsf"; Flags: DontCopy
-; Installer extensions
-Source: "InstallerExtensions.dll"; Flags: DontCopy
-
-; Files required by QModManager itself
-; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\Oculus.Newtonsoft.Json.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; QMM
-Source: "QModInstaller.dll"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModInstaller.xml"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModManager.exe"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx patchers
-Source: "QModManager.OculusNewtonsoftRedirect.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.QModPluginGenerator.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx
-Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
-Source: "..\..\Dependencies\BZ.STABLE\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
-
-[Dirs]
-Name: "{app}\QMods"
-
-[Run]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-c"; Tasks: cleanup
-
-[UninstallRun]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-u";
-
-[Messages]
-; BeveledLabel={#Name} {#Version}
-WizardSelectDir=Select install location
-SelectDirLabel3=Please select the install folder of the game.
-SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.%nIf you have the game on steam, you can also use the buttons on the bottom left to auto-complete the install path for the chosen game.
-ReadyLabel2a=By installing, you agree to allow QModManager to send external web requests, most often to check for updates. You can disable this option at any time in the Mods tab of the Subnautica options menu.
-ExitSetupMessage=Setup is not complete. If you exit now, {#Name} will not be installed.%nExit Setup?
-WizardSelectComponents=Review Install
-SelectComponentsDesc=
-SelectComponentsLabel2=
-
-[Types]
-; Used to disable the three Full, Compact and Custom types
-Name: "select"; Description: "QModManager"; Flags: IsCustom;
-
-[Components]
-Name: "qmm"; Description: "QModManager"; Flags: fixed; Types: select;
-Name: "qmm\bz"; Description: "Install for Below Zero"; Flags: exclusive fixed;
-
-[Tasks]
-Name: "cleanup"; Description: "(Recommended) Clean up after previous Nitrox and QMM installs";
-
-[Code]
-// Import stuff from InstallerExtensions.dll
-function PathsEqual(pathone, pathtwo: WideString): Boolean; external 'PathsEqual@files:InstallerExtensions.dll stdcall setuponly delayload';
-
-function IsBelowZero(path: String): Boolean;
-begin
- if (FileExists(path + '\SubnauticaZero.exe')) and (FileExists(path + '\SubnauticaZero_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := true
- Exit
- end
- else
- begin
- Result := false
- Exit
- end
-end;
-function IsBelowZeroApp(): Boolean;
-begin
- Result := IsBelowZero(ExpandConstant('{app}'));
-end;
-
-function GetName(def: string): String;
-begin
- if (IsBelowZeroApp()) then
- begin
- Result := '{#Name} (Below Zero)'
- end
- else
- begin
- Result := ExpandConstant('{app}')
- end
-end;
-
-function GetURL(def: string): String;
-begin
- if (IsBelowZeroApp()) then
- begin
- Result := '{#UpdatesURL}/subnauticabelowzero/mods/1'
- end
- else
- begin
- Result := '{#UpdatesURL}'
- end
-end;
-
-function CurPageChanged_SelectComponents(CurPageID: Integer): Boolean;
-var
- Index: Integer;
- app: String;
-begin
- if CurPageID = wpSelectComponents then
- begin
- try
- app := ExpandConstant('{app}')
- except
- app := 'null'
- end;
- if not IsBelowZero(app) then
- begin
- WizardForm.SelectComponentsLabel.Caption := 'No game detected in this folder, cannot install'
- Exit
- end;
- Index := WizardForm.ComponentsList.Items.IndexOf('Install for Below Zero')
- if Index <> -1 then
- begin
- if IsBelowZero(app) then
- begin
- WizardForm.ComponentsList.Checked[Index] := true
- WizardForm.SelectComponentsLabel.Caption := 'Install QModManager for Below Zero'
- end
- end
- end
-end;
-
-function GetDir(folder: String; name: String): String;
-var
-I : Integer;
-P : Integer;
-steamInstallPath : String;
-configFile : String;
-fileLines: TArrayOfString;
-begin
- steamInstallPath := ''
- RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\WOW6432Node\Valve\Steam', 'InstallPath', steamInstallPath)
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- else
- begin
- configFile := steamInstallPath + '\config\config.vdf'
- if FileExists(configFile) then
- begin
- if LoadStringsFromFile(configFile, FileLines) then
- begin
- for I := 0 to GetArrayLength(FileLines) - 1 do
- begin
- P := Pos('BaseInstallFolder_', FileLines[I])
- if P > 0 then
- begin
- steamInstallPath := Copy(FileLines[I], P + 23, 3) + Copy(FileLines[I], P + 27, Length(FileLines[I]) - P - 27);
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then // If the folder is correct
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- end
- end
- end
- end
- end;
- Result := 'none'
- Exit
-end;
-
-var ACLabel: TLabel;
-var BelowZeroButton: TNewRadioButton;
-
-procedure BelowZeroButtonOnClick(Sender: TObject);
-begin
- WizardForm.DirEdit.Text := GetDir('SubnauticaZero', 'SubnauticaZero')
- BelowZeroButton.Checked := true
-end;
-
-function InitializeWizard_AddButtons(): Boolean;
-begin
- ACLabel := TLabel.Create(WizardForm)
- with ACLabel do
- begin
- Parent := WizardForm
- Caption := 'Get path from Steam for:'
- Left := WizardForm.SelectDirLabel.Left / 3
- Top := WizardForm.BackButton.Top - WizardForm.BackButton.Top / 90
- end;
-
- BelowZeroButton := TNewRadioButton.Create(WizardForm)
- with BelowZeroButton do
- begin
- Parent := WizardForm
- Caption := 'Below Zero'
- OnClick := @BelowZeroButtonOnClick
- Left := WizardForm.SelectDirLabel.Left + WizardForm.SelectDirLabel.Left / 30
- Top := WizardForm.BackButton.Top + 10
- Height := WizardForm.BackButton.Height
- Enabled := True
- end;
-end;
-
-function CurPageChanged_AddButtons(CurPageID: Integer): Boolean;
-begin
- if CurPageID = wpSelectDir then
- begin
- WizardForm.DirEdit.Text := ''
- if GetDir('SubnauticaZero', 'SubnauticaZero') = 'none' then
- begin
- BelowZeroButton.Enabled := false
- end;
-
- if BelowZeroButton.Enabled then
- begin
- WizardForm.DirEdit.Text := GetDir('SubnauticaZero', 'SubnauticaZero')
- BelowZeroButton.Checked := true
- end;
- end;
- BelowZeroButton.Visible := CurPageID = wpSelectDir
- ACLabel.Visible := CurPageID = wpSelectDir
-end;
-
-var DirEditOnChangePrev: TNotifyEvent;
-
-procedure DirEditOnChange(Sender: TObject);
-var
- S: String;
-begin
- if Pos('subnauticazero', LowerCase(WizardForm.DirEdit.Text)) <> 0 then
- begin
- if PathsEqual(WizardForm.DirEdit.Text, GetDir('SubnauticaZero', 'SubnauticaZero')) then
- begin
- BelowZeroButton.Checked := true
- end
- else
- begin
- BelowZeroButton.Checked := false;
- end
- end
- else
- begin
- BelowZeroButton.Checked := false;
- end;
-
- if (Pos('://', WizardForm.DirEdit.Text) <> 0) or (Pos(':\\', WizardForm.DirEdit.Text) <> 0) then
- begin
- S := WizardForm.DirEdit.Text;
- StringChangeEx(S, '://', ':/', true);
- StringChangeEx(S, ':\\', ':\', true);
- WizardForm.DirEdit.Text := S;
- end
-end;
-
-function InitializeWizard_DirOnChange(): Boolean;
-begin
- DirEditOnChangePrev := WizardForm.DirEdit.OnChange
- WizardForm.DirEdit.OnChange := @DirEditOnChange
-end;
-
-var appIsSet: Boolean;
-
-function GetGUID(def: String): String;
-begin
- if not appIsSet then // The installer tries to get the GUID at startup to use previous options such as install path or install settings. As QModManager's GUID is defined AFTER the path is selected, it doesn't need to provide a value
- begin
- Result := ''
- Exit
- end;
- if IsBelowZero(ExpandConstant('{app}')) then
- begin
- Result := '{A535470D-3403-46A2-8D44-28AD4B90C9A3}'
- Exit
- end
-end;
-
-function IsAppRunning(const FileName : string): Boolean;
-var
- FSWbemLocator: Variant;
- FWMIService : Variant;
- FWbemObjectSet: Variant;
-begin
- Result := false;
- FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
- FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
- FWbemObjectSet :=
- FWMIService.ExecQuery(
- Format('SELECT Name FROM Win32_Process Where Name="%s"', [FileName]));
- Result := (FWbemObjectSet.Count > 0);
- FWbemObjectSet := Unassigned;
- FWMIService := Unassigned;
- FSWbemLocator := Unassigned;
-end;
-
-// Imports some stuff from VclStylesInno.dll
-procedure LoadVCLStyle(VClStyleFile: String); external 'LoadVCLStyleW@files:VclStylesInno.dll stdcall';
-procedure UnLoadVCLStyles; external 'UnLoadVCLStyles@files:VclStylesInno.dll stdcall';
-
-// Check for .NET version -- code from http://www.kynosarges.de/DotNetVersion.html
-function IsDotNetDetected(version: string; service: cardinal): boolean;
-// Indicates whether the specified version and service pack of the .NET Framework is installed.
-//
-// version -- Specify one of these strings for the required .NET Framework version:
-// 'v1.1' .NET Framework 1.1
-// 'v2.0' .NET Framework 2.0
-// 'v3.0' .NET Framework 3.0
-// 'v3.5' .NET Framework 3.5
-// 'v4\Client' .NET Framework 4.0 Client Profile
-// 'v4\Full' .NET Framework 4.0 Full Installation
-// 'v4.5' .NET Framework 4.5
-// 'v4.5.1' .NET Framework 4.5.1
-// 'v4.5.2' .NET Framework 4.5.2
-// 'v4.6' .NET Framework 4.6
-// 'v4.6.1' .NET Framework 4.6.1
-// 'v4.6.2' .NET Framework 4.6.2
-// 'v4.7' .NET Framework 4.7
-//
-// service -- Specify any non-negative integer for the required service pack level:
-// 0 No service packs required
-// 1, 2, etc. Service pack 1, 2, etc. required
-var
- key, versionKey: string;
- install, release, serviceCount, versionRelease: cardinal;
- success: boolean;
-begin
- versionKey := version;
- versionRelease := 0;
-
- // .NET 1.1 and 2.0 embed release number in version key
- if version = 'v1.1' then begin
- versionKey := 'v1.1.4322';
- end else if version = 'v2.0' then begin
- versionKey := 'v2.0.50727';
- end
-
- // .NET 4.5 and newer install as update to .NET 4.0 Full
- else if Pos('v4.', version) = 1 then begin
- versionKey := 'v4\Full';
- case version of
- 'v4.5': versionRelease := 378389;
- 'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
- 'v4.5.2': versionRelease := 379893;
- 'v4.6': versionRelease := 393295; // 393297 on Windows 8.1 and older
- 'v4.6.1': versionRelease := 394254; // 394271 before Win10 November Update
- 'v4.6.2': versionRelease := 394802; // 394806 before Win10 Anniversary Update
- 'v4.7': versionRelease := 460798; // 460805 before Win10 Creators Update
- end;
- end;
-
- // installation key group for all .NET versions
- key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;
-
- // .NET 3.0 uses value InstallSuccess in subkey Setup
- if Pos('v3.0', version) = 1 then begin
- success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
- end else begin
- success := RegQueryDWordValue(HKLM, key, 'Install', install);
- end;
-
- // .NET 4.0 and newer use value Servicing instead of SP
- if Pos('v4', version) = 1 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
- end else begin
- success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
- end;
-
- // .NET 4.5 and newer use additional value Release
- if versionRelease > 0 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
- success := success and (release >= versionRelease);
- end;
-
- result := success and (install = 1) and (serviceCount >= service);
-end;
-
-function InitializeSetup(): Boolean;
-var
- ErrCode: Integer;
-begin
- if not IsDotNetDetected('v4\Full', 0) then
- begin
- if MsgBox('QModManager requires Microsoft .NET Framework 4.0' + #13#10 + 'Would you like to install it now?', mbCriticalError, MB_YESNO) = IDYES then
- begin
- if not ShellExec('open', 'https://dotnet.microsoft.com/download/dotnet-framework/net40', '', '', SW_SHOW, ewNoWait, ErrCode) then
- begin
- SysErrorMessage(ErrCode);
- end
- end;
- result := false;
- Exit
- end;
- appIsSet := false
- if IsAppRunning('SubnauticaZero.exe') then
- begin
- MsgBox('You need to close Below Zero before installing QModManager.' + #13#10 + 'If the game is not running, please reboot your computer.', mbError, MB_OK);
- Result := false
- end
- else
- begin
- // Load skin
- ExtractTemporaryFile('Carbon.vsf');
- LoadVCLStyle(ExpandConstant('{tmp}\Carbon.vsf'));
- Result := true
- end
-end;
-
-function IsPreviousVersionInstalled: Boolean;
-var
- uninstallRegKey: String;
- previousVersion: String;
-begin
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- previousVersion := '';
- Result := (RegKeyExists(HKLM, uninstallRegKey) or RegKeyExists(HKCU, uninstallRegKey));
-end;
-
-function GetUninstallString: string;
-var
- uninstallRegKey: String;
- uninstallString: String;
-begin
- Result := '';
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- uninstallString := '';
- if not RegQueryStringValue(HKLM, uninstallRegKey, 'UninstallString', uninstallString) then
- RegQueryStringValue(HKCU, uninstallRegKey, 'UninstallString', uninstallString);
- Result := uninstallString;
-end;
-
-function IsUpgrade: Boolean;
-begin
- Result := (GetUninstallString() <> '');
-end;
-
-function NextButtonClick(CurPageID: Integer): Boolean;
-var
- uninstallString: String;
- resultCode: Integer;
-begin
- if CurPageID = wpSelectComponents then
- appIsSet := true;
-
- Result := true;
-end;
-
-function PrepareToInstall(var NeedsRestart: boolean): string;
-var
- uninstallString: string;
- resultCode: integer;
-begin
- NeedsRestart := false;
-
- if IsPreviousVersionInstalled() then
- begin
- uninstallString := RemoveQuotes(GetUninstallString());
- if FileExists(uninstallString) then
- begin
- Exec(uninstallString, '/SILENT', '', SW_SHOW, ewWaitUntilTerminated, resultCode);
- if IsPreviousVersionInstalled() then
- Result := 'Previous installation must be uninstalled to continue.';
- end;
- end;
-end;
-
-var TypesComboOnChangePrev: TNotifyEvent;
-
-procedure ComponentsListCheckChanges;
-begin
- WizardForm.NextButton.Enabled := (WizardSelectedComponents(false) <> '')
-end;
-
-procedure ComponentsListClickCheck(Sender: TObject);
-begin
- ComponentsListCheckChanges
-end;
-
-procedure TypesComboOnChange(Sender: TObject);
-begin
- TypesComboOnChangePrev(Sender)
- ComponentsListCheckChanges
-end;
-
-procedure CurPageChanged(CurPageID: Integer);
-begin
- CurPageChanged_SelectComponents(CurPageID)
- CurPageChanged_AddButtons(CurPageID)
- if CurPageID = wpSelectComponents then
- begin
- ComponentsListCheckChanges;
- end
-end;
-
-procedure InitializeWizard();
-begin
- WizardForm.ComponentsList.OnClickCheck := @ComponentsListClickCheck
- TypesComboOnChangePrev := WizardForm.TypesCombo.OnChange
- WizardForm.TypesCombo.OnChange := @TypesComboOnChange
- InitializeWizard_AddButtons
- InitializeWizard_DirOnChange
-end;
-
-procedure UnloadInstallerExtensions();
- var
- FilePath: string;
- BatchPath: string;
- S: TArrayOfString;
- ResultCode: Integer;
-begin
- FilePath := ExpandConstant('{tmp}\InstallerExtensions.dll');
- if not FileExists(FilePath) then
- begin
- Log(Format('File %s does not exist', [FilePath]));
- end
- else
- begin
- BatchPath :=
- ExpandConstant('{%TEMP}\') +
- 'delete_' + ExtractFileName(ExpandConstant('{tmp}')) + '.bat';
- SetArrayLength(S, 7);
- S[0] := ':loop';
- S[1] := 'del "' + FilePath + '"';
- S[2] := 'if not exist "' + FilePath + '" goto end';
- S[3] := 'goto loop';
- S[4] := ':end';
- S[5] := 'rd "' + ExpandConstant('{tmp}') + '"';
- S[6] := 'del "' + BatchPath + '"';
- if not SaveStringsToFile(BatchPath, S, False) then
- begin
- Log(Format('Error creating batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- if not Exec(BatchPath, '', '', SW_HIDE, ewNoWait, ResultCode) then
- begin
- Log(Format('Error executing batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- begin
- Log(Format('Executed batch file %s to delete %s', [BatchPath, FilePath]));
- end;
- end;
-end;
-
-procedure DeinitializeSetup();
-begin
- // Unload skin
- UnLoadVCLStyles;
- UnloadInstallerExtensions;
-end;
diff --git a/Installer/Extensions.cs b/Installer/Extensions.cs
deleted file mode 100644
index 03cb4a84..00000000
--- a/Installer/Extensions.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using RGiesecke.DllExport;
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-
-namespace QModManager.Installer.Extensions
-{
- public static class Extensions
- {
- [DllExport]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static bool PathsEqual([MarshalAs(UnmanagedType.BStr)] string path1, [MarshalAs(UnmanagedType.BStr)] string path2)
- {
- string path1parsed = Path.GetFullPath(path1.Trim('/', '\\'));
- string path2parsed = Path.GetFullPath(path2.Trim('/', '\\'));
-
- return string.Equals(path1parsed, path2parsed, StringComparison.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/Installer/InstallerExtensions.csproj b/Installer/InstallerExtensions.csproj
deleted file mode 100644
index 5042885a..00000000
--- a/Installer/InstallerExtensions.csproj
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {92726127-A08F-4843-BF96-4989CE1BF422}
- Library
- Properties
- QModManager.Installer.Extensions
- InstallerExtensions
- v3.5
- 512
- true
-
-
- ..\Build\$(Configuration)\
- SUBNAUTICA;SUBNAUTICA_STABLE
- true
- none
- x86
- 7.3
- prompt
-
-
- ..\Build\$(Configuration)\
- SUBNAUTICA;SUBNAUTICA_EXP
- true
- none
- x86
- 7.3
- prompt
-
-
- ..\Build\$(Configuration)\
- BELOWZERO;BELOWZERO_STABLE
- true
- none
- x86
- 7.3
- prompt
-
-
- ..\Build\$(Configuration)\
- BELOWZERO;BELOWZERO_EXP
- true
- none
- x86
- 7.3
- prompt
-
-
-
- ..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Installer/Properties/AssemblyInfo.cs b/Installer/Properties/AssemblyInfo.cs
deleted file mode 100644
index 82875126..00000000
--- a/Installer/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-[assembly: AssemblyTitle("QModManagerInstallerExtensions")]
-[assembly: AssemblyDescription("Config based patch management for Subnautica and Subnautica: Below Zero")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("QModManager")]
-[assembly: AssemblyProduct("QModManagerInstallerExtensions")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-[assembly: ComVisible(true)]
-
-[assembly: Guid("8c6c9a0b-80c4-43d2-89f2-749e6f09fdda")]
-
-[assembly: AssemblyVersion("4.3.0")]
-[assembly: AssemblyFileVersion("4.3.0")]
diff --git a/Installer/SN.EXP.iss b/Installer/SN.EXP.iss
deleted file mode 100644
index 2532cf9d..00000000
--- a/Installer/SN.EXP.iss
+++ /dev/null
@@ -1,608 +0,0 @@
-; Throws an error if the version used to compile this script is not unicode
-; This ensures that the application is built correctly
-#if !Defined(UNICODE)
- #error A unicode version of Inno Setup is required to compile this script
-#endif
-
-#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.3.0"
-#define Author "QModManager"
-#define URL "https://github.com/QModManager/QModManager"
-#define SupportURL "https://discord.gg/UpWuWwq"
-#define UpdatesURL "https://nexusmods.com" ; The link to the mod will be added after it
-
-[Setup]
-AllowNetworkDrive=no
-AllowUNCPath=no
-AlwaysShowDirOnReadyPage=yes
-AppendDefaultDirName=no
-AppId={code:GetGUID}
-AppName={#Name}
-AppPublisher={#Author}
-AppPublisherURL={#URL}
-AppSupportURL={#SupportURL}
-AppUpdatesURL={code:GetURL}
-AppVerName={#Name} {#Version}
-AppVersion={#Version}
-Compression=lzma
-DefaultDirName=.
-DirExistsWarning=no
-DisableDirPage=no
-DisableProgramGroupPage=yes
-DisableWelcomePage=no
-EnableDirDoesntExistWarning=yes
-OutputBaseFilename=QModManager_{#Version}_Subnautica_Experimental_Setup
-OutputDir=.\
-PrivilegesRequired=admin
-SetupIconFile=..\..\Assets\Icon.ico
-SolidCompression=yes
-UninstallDisplayIcon={app}\BepInEx\patchers\QModManager\QModManager.exe
-UninstallDisplayName={code:GetName}
-UsePreviousAppDir=no
-UsePreviousLanguage=no
-WizardImageFile=..\..\Assets\InstallerImage.bmp
-WizardSmallImageFile=..\..\Assets\InstallerSmallImage.bmp
-UsePreviousSetupType=False
-UsePreviousTasks=False
-CloseApplications=False
-
-[Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
-
-[Files]
-; Files used by the installer but not required by QModManager itself
-; Installer theme
-Source: "..\..\Dependencies\VclStylesinno.dll"; Flags: DontCopy
-Source: "..\..\Dependencies\Carbon.vsf"; Flags: DontCopy
-; Installer extensions
-Source: "InstallerExtensions.dll"; Flags: DontCopy
-
-; Files required by QModManager itself
-; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\Oculus.Newtonsoft.Json.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; QMM
-Source: "QModInstaller.dll"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModInstaller.xml"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModManager.exe"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx patchers
-Source: "QModManager.OculusNewtonsoftRedirect.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.QModPluginGenerator.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx
-Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
-Source: "..\..\Dependencies\SN.EXP\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
-
-[Dirs]
-Name: "{app}\QMods"
-
-[Run]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-c"; Tasks: cleanup
-
-[UninstallRun]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-u";
-
-[Messages]
-; BeveledLabel={#Name} {#Version}
-WizardSelectDir=Select install location
-SelectDirLabel3=Please select the install folder of the game.
-SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.%nIf you have the game on steam, you can also use the buttons on the bottom left to auto-complete the install path for the chosen game.
-ReadyLabel2a=By installing, you agree to allow QModManager to send external web requests, most often to check for updates. You can disable this option at any time in the Mods tab of the Subnautica options menu.
-ExitSetupMessage=Setup is not complete. If you exit now, {#Name} will not be installed.%nExit Setup?
-WizardSelectComponents=Review Install
-SelectComponentsDesc=
-SelectComponentsLabel2=
-
-[Types]
-; Used to disable the three Full, Compact and Custom types
-Name: "select"; Description: "QModManager"; Flags: IsCustom;
-
-[Components]
-Name: "qmm"; Description: "QModManager"; Flags: fixed; Types: select;
-Name: "qmm\sn"; Description: "Install for Subnautica"; Flags: exclusive fixed;
-
-[Tasks]
-Name: "cleanup"; Description: "(Recommended) Clean up after previous Nitrox and QMM installs";
-
-[Code]
-// Import stuff from InstallerExtensions.dll
-function PathsEqual(pathone, pathtwo: WideString): Boolean; external 'PathsEqual@files:InstallerExtensions.dll stdcall setuponly delayload';
-
-function IsSubnautica(path: String): Boolean;
-begin
- if (FileExists(path + '\Subnautica.exe')) and (FileExists(path + '\Subnautica_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := true
- Exit
- end
- else
- begin
- Result := false
- Exit
- end
-end;
-function IsSubnauticaApp(): Boolean;
-begin
- Result := IsSubnautica(ExpandConstant('{app}'));
-end;
-
-function GetName(def: string): String;
-begin
- if (IsSubnauticaApp()) then
- begin
- Result := '{#Name} (Subnautica)'
- end
- else
- begin
- Result := ExpandConstant('{app}')
- end
-end;
-
-function GetURL(def: string): String;
-begin
- if (IsSubnauticaApp()) then
- begin
- Result := '{#UpdatesURL}/subnautica/mods/201'
- end
- else
- begin
- Result := '{#UpdatesURL}'
- end
-end;
-
-function CurPageChanged_SelectComponents(CurPageID: Integer): Boolean;
-var
- Index: Integer;
- app: String;
-begin
- if CurPageID = wpSelectComponents then
- begin
- try
- app := ExpandConstant('{app}')
- except
- app := 'null'
- end;
- if not IsSubnautica(app) then
- begin
- WizardForm.SelectComponentsLabel.Caption := 'Game not detected in this folder, cannot install'
- Exit
- end;
- Index := WizardForm.ComponentsList.Items.IndexOf('Install for Subnautica')
- if Index <> -1 then
- begin
- if IsSubnautica(app) then
- begin
- WizardForm.ComponentsList.Checked[Index] := true
- WizardForm.SelectComponentsLabel.Caption := 'Install QModManager for Subnautica'
- end
- end;
- end
-end;
-
-function GetDir(folder: String; name: String): String;
-var
-I : Integer;
-P : Integer;
-steamInstallPath : String;
-configFile : String;
-fileLines: TArrayOfString;
-begin
- steamInstallPath := ''
- RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\WOW6432Node\Valve\Steam', 'InstallPath', steamInstallPath)
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- else
- begin
- configFile := steamInstallPath + '\config\config.vdf'
- if FileExists(configFile) then
- begin
- if LoadStringsFromFile(configFile, FileLines) then
- begin
- for I := 0 to GetArrayLength(FileLines) - 1 do
- begin
- P := Pos('BaseInstallFolder_', FileLines[I])
- if P > 0 then
- begin
- steamInstallPath := Copy(FileLines[I], P + 23, 3) + Copy(FileLines[I], P + 27, Length(FileLines[I]) - P - 27);
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then // If the folder is correct
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- end
- end
- end
- end
- end;
- Result := 'none'
- Exit
-end;
-
-var ACLabel: TLabel;
-var SubnauticaButton: TNewRadioButton;
-
-procedure SubnauticaButtonOnClick(Sender: TObject);
-begin
- WizardForm.DirEdit.Text := GetDir('Subnautica', 'Subnautica')
- SubnauticaButton.Checked := true
-end;
-
-function InitializeWizard_AddButtons(): Boolean;
-begin
- ACLabel := TLabel.Create(WizardForm)
- with ACLabel do
- begin
- Parent := WizardForm
- Caption := 'Get path from Steam for:'
- Left := WizardForm.SelectDirLabel.Left / 3
- Top := WizardForm.BackButton.Top - WizardForm.BackButton.Top / 90
- end;
-
- SubnauticaButton := TNewRadioButton.Create(WizardForm)
- with SubnauticaButton do
- begin
- Parent := WizardForm
- Caption := 'Subnautica'
- OnClick := @SubnauticaButtonOnClick
- Left := WizardForm.SelectDirLabel.Left + WizardForm.SelectDirLabel.Left / 30
- Top := WizardForm.BackButton.Top + 10
- Height := WizardForm.BackButton.Height
- Enabled := True
- end;
-
-end;
-
-function CurPageChanged_AddButtons(CurPageID: Integer): Boolean;
-begin
- if CurPageID = wpSelectDir then
- begin
- WizardForm.DirEdit.Text := ''
- if GetDir('Subnautica', 'Subnautica') = 'none' then
- begin
- SubnauticaButton.Enabled := false
- end;
-
- if SubnauticaButton.Enabled then
- begin
- WizardForm.DirEdit.Text := GetDir('Subnautica', 'Subnautica')
- SubnauticaButton.Checked := true
- end
-
- end;
- SubnauticaButton.Visible := CurPageID = wpSelectDir
- ACLabel.Visible := CurPageID = wpSelectDir
-end;
-
-var DirEditOnChangePrev: TNotifyEvent;
-
-procedure DirEditOnChange(Sender: TObject);
-var
- S: String;
-begin
- if Pos('subnautica', LowerCase(WizardForm.DirEdit.Text)) <> 0 then
- begin
- if PathsEqual(WizardForm.DirEdit.Text, GetDir('Subnautica', 'Subnautica')) then
- begin
- SubnauticaButton.Checked := true
- end
- else
- begin
- SubnauticaButton.Checked := false;
- end
- end
- else
- begin
- SubnauticaButton.Checked := false;
- end;
-
- if (Pos('://', WizardForm.DirEdit.Text) <> 0) or (Pos(':\\', WizardForm.DirEdit.Text) <> 0) then
- begin
- S := WizardForm.DirEdit.Text;
- StringChangeEx(S, '://', ':/', true);
- StringChangeEx(S, ':\\', ':\', true);
- WizardForm.DirEdit.Text := S;
- end
-end;
-
-function InitializeWizard_DirOnChange(): Boolean;
-begin
- DirEditOnChangePrev := WizardForm.DirEdit.OnChange
- WizardForm.DirEdit.OnChange := @DirEditOnChange
-end;
-
-var appIsSet: Boolean;
-
-function GetGUID(def: String): String;
-begin
- if not appIsSet then // The installer tries to get the GUID at startup to use previous options such as install path or install settings. As QModManager's GUID is defined AFTER the path is selected, it doesn't need to provide a value
- begin
- Result := ''
- Exit
- end;
- if IsSubnautica(ExpandConstant('{app}')) then
- begin
- Result := '{52CC87AA-645D-40FB-8411-510142191678}'
- Exit
- end;
-end;
-
-function IsAppRunning(const FileName : string): Boolean;
-var
- FSWbemLocator: Variant;
- FWMIService : Variant;
- FWbemObjectSet: Variant;
-begin
- Result := false;
- FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
- FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
- FWbemObjectSet :=
- FWMIService.ExecQuery(
- Format('SELECT Name FROM Win32_Process Where Name="%s"', [FileName]));
- Result := (FWbemObjectSet.Count > 0);
- FWbemObjectSet := Unassigned;
- FWMIService := Unassigned;
- FSWbemLocator := Unassigned;
-end;
-
-// Imports some stuff from VclStylesInno.dll
-procedure LoadVCLStyle(VClStyleFile: String); external 'LoadVCLStyleW@files:VclStylesInno.dll stdcall';
-procedure UnLoadVCLStyles; external 'UnLoadVCLStyles@files:VclStylesInno.dll stdcall';
-
-// Check for .NET version -- code from http://www.kynosarges.de/DotNetVersion.html
-function IsDotNetDetected(version: string; service: cardinal): boolean;
-// Indicates whether the specified version and service pack of the .NET Framework is installed.
-//
-// version -- Specify one of these strings for the required .NET Framework version:
-// 'v1.1' .NET Framework 1.1
-// 'v2.0' .NET Framework 2.0
-// 'v3.0' .NET Framework 3.0
-// 'v3.5' .NET Framework 3.5
-// 'v4\Client' .NET Framework 4.0 Client Profile
-// 'v4\Full' .NET Framework 4.0 Full Installation
-// 'v4.5' .NET Framework 4.5
-// 'v4.5.1' .NET Framework 4.5.1
-// 'v4.5.2' .NET Framework 4.5.2
-// 'v4.6' .NET Framework 4.6
-// 'v4.6.1' .NET Framework 4.6.1
-// 'v4.6.2' .NET Framework 4.6.2
-// 'v4.7' .NET Framework 4.7
-//
-// service -- Specify any non-negative integer for the required service pack level:
-// 0 No service packs required
-// 1, 2, etc. Service pack 1, 2, etc. required
-var
- key, versionKey: string;
- install, release, serviceCount, versionRelease: cardinal;
- success: boolean;
-begin
- versionKey := version;
- versionRelease := 0;
-
- // .NET 1.1 and 2.0 embed release number in version key
- if version = 'v1.1' then begin
- versionKey := 'v1.1.4322';
- end else if version = 'v2.0' then begin
- versionKey := 'v2.0.50727';
- end
-
- // .NET 4.5 and newer install as update to .NET 4.0 Full
- else if Pos('v4.', version) = 1 then begin
- versionKey := 'v4\Full';
- case version of
- 'v4.5': versionRelease := 378389;
- 'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
- 'v4.5.2': versionRelease := 379893;
- 'v4.6': versionRelease := 393295; // 393297 on Windows 8.1 and older
- 'v4.6.1': versionRelease := 394254; // 394271 before Win10 November Update
- 'v4.6.2': versionRelease := 394802; // 394806 before Win10 Anniversary Update
- 'v4.7': versionRelease := 460798; // 460805 before Win10 Creators Update
- end;
- end;
-
- // installation key group for all .NET versions
- key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;
-
- // .NET 3.0 uses value InstallSuccess in subkey Setup
- if Pos('v3.0', version) = 1 then begin
- success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
- end else begin
- success := RegQueryDWordValue(HKLM, key, 'Install', install);
- end;
-
- // .NET 4.0 and newer use value Servicing instead of SP
- if Pos('v4', version) = 1 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
- end else begin
- success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
- end;
-
- // .NET 4.5 and newer use additional value Release
- if versionRelease > 0 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
- success := success and (release >= versionRelease);
- end;
-
- result := success and (install = 1) and (serviceCount >= service);
-end;
-
-function InitializeSetup(): Boolean;
-var
- ErrCode: Integer;
-begin
- if not IsDotNetDetected('v4\Full', 0) then
- begin
- if MsgBox('QModManager requires Microsoft .NET Framework 4.0' + #13#10 + 'Would you like to install it now?', mbCriticalError, MB_YESNO) = IDYES then
- begin
- if not ShellExec('open', 'https://dotnet.microsoft.com/download/dotnet-framework/net40', '', '', SW_SHOW, ewNoWait, ErrCode) then
- begin
- SysErrorMessage(ErrCode);
- end
- end;
- result := false;
- Exit
- end;
- appIsSet := false
- if IsAppRunning('Subnautica.exe') then
- begin
- MsgBox('You need to close Subnautica before installing QModManager.' + #13#10 + 'If the game is not running, please reboot your computer.', mbError, MB_OK);
- Result := false
- end
- else
- begin
- // Load skin
- ExtractTemporaryFile('Carbon.vsf');
- LoadVCLStyle(ExpandConstant('{tmp}\Carbon.vsf'));
- Result := true
- end
-end;
-
-function IsPreviousVersionInstalled: Boolean;
-var
- uninstallRegKey: String;
- previousVersion: String;
-begin
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- previousVersion := '';
- Result := (RegKeyExists(HKLM, uninstallRegKey) or RegKeyExists(HKCU, uninstallRegKey));
-end;
-
-function GetUninstallString: string;
-var
- uninstallRegKey: String;
- uninstallString: String;
-begin
- Result := '';
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- uninstallString := '';
- if not RegQueryStringValue(HKLM, uninstallRegKey, 'UninstallString', uninstallString) then
- RegQueryStringValue(HKCU, uninstallRegKey, 'UninstallString', uninstallString);
- Result := uninstallString;
-end;
-
-function IsUpgrade: Boolean;
-begin
- Result := (GetUninstallString() <> '');
-end;
-
-function NextButtonClick(CurPageID: Integer): Boolean;
-var
- uninstallString: String;
- resultCode: Integer;
-begin
- if CurPageID = wpSelectComponents then
- appIsSet := true;
-
- Result := true;
-end;
-
-function PrepareToInstall(var NeedsRestart: boolean): string;
-var
- uninstallString: string;
- resultCode: integer;
-begin
- NeedsRestart := false;
-
- if IsPreviousVersionInstalled() then
- begin
- uninstallString := RemoveQuotes(GetUninstallString());
- if FileExists(uninstallString) then
- begin
- Exec(uninstallString, '/SILENT', '', SW_SHOW, ewWaitUntilTerminated, resultCode);
- if IsPreviousVersionInstalled() then
- Result := 'Previous installation must be uninstalled to continue.';
- end;
- end;
-end;
-
-var TypesComboOnChangePrev: TNotifyEvent;
-
-procedure ComponentsListCheckChanges;
-begin
- WizardForm.NextButton.Enabled := (WizardSelectedComponents(false) <> '')
-end;
-
-procedure ComponentsListClickCheck(Sender: TObject);
-begin
- ComponentsListCheckChanges
-end;
-
-procedure TypesComboOnChange(Sender: TObject);
-begin
- TypesComboOnChangePrev(Sender)
- ComponentsListCheckChanges
-end;
-
-procedure CurPageChanged(CurPageID: Integer);
-begin
- CurPageChanged_SelectComponents(CurPageID)
- CurPageChanged_AddButtons(CurPageID)
- if CurPageID = wpSelectComponents then
- begin
- ComponentsListCheckChanges;
- end
-end;
-
-procedure InitializeWizard();
-begin
- WizardForm.ComponentsList.OnClickCheck := @ComponentsListClickCheck
- TypesComboOnChangePrev := WizardForm.TypesCombo.OnChange
- WizardForm.TypesCombo.OnChange := @TypesComboOnChange
- InitializeWizard_AddButtons
- InitializeWizard_DirOnChange
-end;
-
-procedure UnloadInstallerExtensions();
- var
- FilePath: string;
- BatchPath: string;
- S: TArrayOfString;
- ResultCode: Integer;
-begin
- FilePath := ExpandConstant('{tmp}\InstallerExtensions.dll');
- if not FileExists(FilePath) then
- begin
- Log(Format('File %s does not exist', [FilePath]));
- end
- else
- begin
- BatchPath :=
- ExpandConstant('{%TEMP}\') +
- 'delete_' + ExtractFileName(ExpandConstant('{tmp}')) + '.bat';
- SetArrayLength(S, 7);
- S[0] := ':loop';
- S[1] := 'del "' + FilePath + '"';
- S[2] := 'if not exist "' + FilePath + '" goto end';
- S[3] := 'goto loop';
- S[4] := ':end';
- S[5] := 'rd "' + ExpandConstant('{tmp}') + '"';
- S[6] := 'del "' + BatchPath + '"';
- if not SaveStringsToFile(BatchPath, S, False) then
- begin
- Log(Format('Error creating batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- if not Exec(BatchPath, '', '', SW_HIDE, ewNoWait, ResultCode) then
- begin
- Log(Format('Error executing batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- begin
- Log(Format('Executed batch file %s to delete %s', [BatchPath, FilePath]));
- end;
- end;
-end;
-
-procedure DeinitializeSetup();
-begin
- // Unload skin
- UnLoadVCLStyles;
- UnloadInstallerExtensions;
-end;
diff --git a/Installer/SN.STABLE.iss b/Installer/SN.STABLE.iss
deleted file mode 100644
index e2c08f9d..00000000
--- a/Installer/SN.STABLE.iss
+++ /dev/null
@@ -1,606 +0,0 @@
-; Throws an error if the version used to compile this script is not unicode
-; This ensures that the application is built correctly
-#if !Defined(UNICODE)
- #error A unicode version of Inno Setup is required to compile this script
-#endif
-
-#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.3.0"
-#define Author "QModManager"
-#define URL "https://github.com/QModManager/QModManager"
-#define SupportURL "https://discord.gg/UpWuWwq"
-#define UpdatesURL "https://nexusmods.com" ; The link to the mod will be added after it
-
-[Setup]
-AllowNetworkDrive=no
-AllowUNCPath=no
-AlwaysShowDirOnReadyPage=yes
-AppendDefaultDirName=no
-AppId={code:GetGUID}
-AppName={#Name}
-AppPublisher={#Author}
-AppPublisherURL={#URL}
-AppSupportURL={#SupportURL}
-AppUpdatesURL={code:GetURL}
-AppVerName={#Name} {#Version}
-AppVersion={#Version}
-Compression=lzma
-DefaultDirName=.
-DirExistsWarning=no
-DisableDirPage=no
-DisableProgramGroupPage=yes
-DisableWelcomePage=no
-EnableDirDoesntExistWarning=yes
-OutputBaseFilename=QModManager_{#Version}_Subnautica_Setup
-OutputDir=.\
-PrivilegesRequired=admin
-SetupIconFile=..\..\Assets\Icon.ico
-SolidCompression=yes
-UninstallDisplayIcon={app}\BepInEx\patchers\QModManager\QModManager.exe
-UninstallDisplayName={code:GetName}
-UsePreviousAppDir=no
-UsePreviousLanguage=no
-WizardImageFile=..\..\Assets\InstallerImage.bmp
-WizardSmallImageFile=..\..\Assets\InstallerSmallImage.bmp
-UsePreviousSetupType=False
-UsePreviousTasks=False
-CloseApplications=False
-
-[Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
-
-[Files]
-; Files used by the installer but not required by QModManager itself
-; Installer theme
-Source: "..\..\Dependencies\VclStylesinno.dll"; Flags: DontCopy
-Source: "..\..\Dependencies\Carbon.vsf"; Flags: DontCopy
-; Installer extensions
-Source: "InstallerExtensions.dll"; Flags: DontCopy
-
-; Files required by QModManager itself
-; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; QMM
-Source: "QModInstaller.dll"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModInstaller.xml"; DestDir: "{app}\BepInEx\plugins\QModManager"; Flags: ignoreversion;
-Source: "QModManager.exe"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx patchers
-Source: "QModManager.QModPluginGenerator.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
-
-; BepInEx
-Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
-Source: "..\..\Dependencies\SN.STABLE\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
-
-[Dirs]
-Name: "{app}\QMods"
-
-[Run]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-c"; Tasks: cleanup
-
-[UninstallRun]
-Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-u";
-
-[Messages]
-; BeveledLabel={#Name} {#Version}
-WizardSelectDir=Select install location
-SelectDirLabel3=Please select the install folder of the game.
-SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.%nIf you have the game on steam, you can also use the buttons on the bottom left to auto-complete the install path for the chosen game.
-ReadyLabel2a=By installing, you agree to allow QModManager to send external web requests, most often to check for updates. You can disable this option at any time in the Mods tab of the Subnautica options menu.
-ExitSetupMessage=Setup is not complete. If you exit now, {#Name} will not be installed.%nExit Setup?
-WizardSelectComponents=Review Install
-SelectComponentsDesc=
-SelectComponentsLabel2=
-
-[Types]
-; Used to disable the three Full, Compact and Custom types
-Name: "select"; Description: "QModManager"; Flags: IsCustom;
-
-[Components]
-Name: "qmm"; Description: "QModManager"; Flags: fixed; Types: select;
-Name: "qmm\sn"; Description: "Install for Subnautica"; Flags: exclusive fixed;
-
-[Tasks]
-Name: "cleanup"; Description: "(Recommended) Clean up after previous Nitrox and QMM installs";
-
-[Code]
-// Import stuff from InstallerExtensions.dll
-function PathsEqual(pathone, pathtwo: WideString): Boolean; external 'PathsEqual@files:InstallerExtensions.dll stdcall setuponly delayload';
-
-function IsSubnautica(path: String): Boolean;
-begin
- if (FileExists(path + '\Subnautica.exe')) and (FileExists(path + '\Subnautica_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := true
- Exit
- end
- else
- begin
- Result := false
- Exit
- end
-end;
-function IsSubnauticaApp(): Boolean;
-begin
- Result := IsSubnautica(ExpandConstant('{app}'));
-end;
-
-function GetName(def: string): String;
-begin
- if (IsSubnauticaApp()) then
- begin
- Result := '{#Name} (Subnautica)'
- end
- else
- begin
- Result := ExpandConstant('{app}')
- end
-end;
-
-function GetURL(def: string): String;
-begin
- if (IsSubnauticaApp()) then
- begin
- Result := '{#UpdatesURL}/subnautica/mods/201'
- end
- else
- begin
- Result := '{#UpdatesURL}'
- end
-end;
-
-function CurPageChanged_SelectComponents(CurPageID: Integer): Boolean;
-var
- Index: Integer;
- app: String;
-begin
- if CurPageID = wpSelectComponents then
- begin
- try
- app := ExpandConstant('{app}')
- except
- app := 'null'
- end;
- if not IsSubnautica(app) then
- begin
- WizardForm.SelectComponentsLabel.Caption := 'Game not detected in this folder, cannot install'
- Exit
- end;
- Index := WizardForm.ComponentsList.Items.IndexOf('Install for Subnautica')
- if Index <> -1 then
- begin
- if IsSubnautica(app) then
- begin
- WizardForm.ComponentsList.Checked[Index] := true
- WizardForm.SelectComponentsLabel.Caption := 'Install QModManager for Subnautica'
- end
- end;
- end
-end;
-
-function GetDir(folder: String; name: String): String;
-var
-I : Integer;
-P : Integer;
-steamInstallPath : String;
-configFile : String;
-fileLines: TArrayOfString;
-begin
- steamInstallPath := ''
- RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\WOW6432Node\Valve\Steam', 'InstallPath', steamInstallPath)
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- else
- begin
- configFile := steamInstallPath + '\config\config.vdf'
- if FileExists(configFile) then
- begin
- if LoadStringsFromFile(configFile, FileLines) then
- begin
- for I := 0 to GetArrayLength(FileLines) - 1 do
- begin
- P := Pos('BaseInstallFolder_', FileLines[I])
- if P > 0 then
- begin
- steamInstallPath := Copy(FileLines[I], P + 23, 3) + Copy(FileLines[I], P + 27, Length(FileLines[I]) - P - 27);
- if (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '.exe')) and (FileExists(steamInstallPath + '\steamapps\common\' + folder + '\' + name + '_Data\Managed\Assembly-CSharp.dll')) then // If the folder is correct
- begin
- Result := steamInstallPath + '\steamapps\common\' + folder
- Exit
- end
- end
- end
- end
- end
- end;
- Result := 'none'
- Exit
-end;
-
-var ACLabel: TLabel;
-var SubnauticaButton: TNewRadioButton;
-
-procedure SubnauticaButtonOnClick(Sender: TObject);
-begin
- WizardForm.DirEdit.Text := GetDir('Subnautica', 'Subnautica')
- SubnauticaButton.Checked := true
-end;
-
-function InitializeWizard_AddButtons(): Boolean;
-begin
- ACLabel := TLabel.Create(WizardForm)
- with ACLabel do
- begin
- Parent := WizardForm
- Caption := 'Get path from Steam for:'
- Left := WizardForm.SelectDirLabel.Left / 3
- Top := WizardForm.BackButton.Top - WizardForm.BackButton.Top / 90
- end;
-
- SubnauticaButton := TNewRadioButton.Create(WizardForm)
- with SubnauticaButton do
- begin
- Parent := WizardForm
- Caption := 'Subnautica'
- OnClick := @SubnauticaButtonOnClick
- Left := WizardForm.SelectDirLabel.Left + WizardForm.SelectDirLabel.Left / 30
- Top := WizardForm.BackButton.Top + 10
- Height := WizardForm.BackButton.Height
- Enabled := True
- end;
-
-end;
-
-function CurPageChanged_AddButtons(CurPageID: Integer): Boolean;
-begin
- if CurPageID = wpSelectDir then
- begin
- WizardForm.DirEdit.Text := ''
- if GetDir('Subnautica', 'Subnautica') = 'none' then
- begin
- SubnauticaButton.Enabled := false
- end;
-
- if SubnauticaButton.Enabled then
- begin
- WizardForm.DirEdit.Text := GetDir('Subnautica', 'Subnautica')
- SubnauticaButton.Checked := true
- end
-
- end;
- SubnauticaButton.Visible := CurPageID = wpSelectDir
- ACLabel.Visible := CurPageID = wpSelectDir
-end;
-
-var DirEditOnChangePrev: TNotifyEvent;
-
-procedure DirEditOnChange(Sender: TObject);
-var
- S: String;
-begin
- if Pos('subnautica', LowerCase(WizardForm.DirEdit.Text)) <> 0 then
- begin
- if PathsEqual(WizardForm.DirEdit.Text, GetDir('Subnautica', 'Subnautica')) then
- begin
- SubnauticaButton.Checked := true
- end
- else
- begin
- SubnauticaButton.Checked := false;
- end
- end
- else
- begin
- SubnauticaButton.Checked := false;
- end;
-
- if (Pos('://', WizardForm.DirEdit.Text) <> 0) or (Pos(':\\', WizardForm.DirEdit.Text) <> 0) then
- begin
- S := WizardForm.DirEdit.Text;
- StringChangeEx(S, '://', ':/', true);
- StringChangeEx(S, ':\\', ':\', true);
- WizardForm.DirEdit.Text := S;
- end
-end;
-
-function InitializeWizard_DirOnChange(): Boolean;
-begin
- DirEditOnChangePrev := WizardForm.DirEdit.OnChange
- WizardForm.DirEdit.OnChange := @DirEditOnChange
-end;
-
-var appIsSet: Boolean;
-
-function GetGUID(def: String): String;
-begin
- if not appIsSet then // The installer tries to get the GUID at startup to use previous options such as install path or install settings. As QModManager's GUID is defined AFTER the path is selected, it doesn't need to provide a value
- begin
- Result := ''
- Exit
- end;
- if IsSubnautica(ExpandConstant('{app}')) then
- begin
- Result := '{52CC87AA-645D-40FB-8411-510142191678}'
- Exit
- end;
-end;
-
-function IsAppRunning(const FileName : string): Boolean;
-var
- FSWbemLocator: Variant;
- FWMIService : Variant;
- FWbemObjectSet: Variant;
-begin
- Result := false;
- FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
- FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
- FWbemObjectSet :=
- FWMIService.ExecQuery(
- Format('SELECT Name FROM Win32_Process Where Name="%s"', [FileName]));
- Result := (FWbemObjectSet.Count > 0);
- FWbemObjectSet := Unassigned;
- FWMIService := Unassigned;
- FSWbemLocator := Unassigned;
-end;
-
-// Imports some stuff from VclStylesInno.dll
-procedure LoadVCLStyle(VClStyleFile: String); external 'LoadVCLStyleW@files:VclStylesInno.dll stdcall';
-procedure UnLoadVCLStyles; external 'UnLoadVCLStyles@files:VclStylesInno.dll stdcall';
-
-// Check for .NET version -- code from http://www.kynosarges.de/DotNetVersion.html
-function IsDotNetDetected(version: string; service: cardinal): boolean;
-// Indicates whether the specified version and service pack of the .NET Framework is installed.
-//
-// version -- Specify one of these strings for the required .NET Framework version:
-// 'v1.1' .NET Framework 1.1
-// 'v2.0' .NET Framework 2.0
-// 'v3.0' .NET Framework 3.0
-// 'v3.5' .NET Framework 3.5
-// 'v4\Client' .NET Framework 4.0 Client Profile
-// 'v4\Full' .NET Framework 4.0 Full Installation
-// 'v4.5' .NET Framework 4.5
-// 'v4.5.1' .NET Framework 4.5.1
-// 'v4.5.2' .NET Framework 4.5.2
-// 'v4.6' .NET Framework 4.6
-// 'v4.6.1' .NET Framework 4.6.1
-// 'v4.6.2' .NET Framework 4.6.2
-// 'v4.7' .NET Framework 4.7
-//
-// service -- Specify any non-negative integer for the required service pack level:
-// 0 No service packs required
-// 1, 2, etc. Service pack 1, 2, etc. required
-var
- key, versionKey: string;
- install, release, serviceCount, versionRelease: cardinal;
- success: boolean;
-begin
- versionKey := version;
- versionRelease := 0;
-
- // .NET 1.1 and 2.0 embed release number in version key
- if version = 'v1.1' then begin
- versionKey := 'v1.1.4322';
- end else if version = 'v2.0' then begin
- versionKey := 'v2.0.50727';
- end
-
- // .NET 4.5 and newer install as update to .NET 4.0 Full
- else if Pos('v4.', version) = 1 then begin
- versionKey := 'v4\Full';
- case version of
- 'v4.5': versionRelease := 378389;
- 'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
- 'v4.5.2': versionRelease := 379893;
- 'v4.6': versionRelease := 393295; // 393297 on Windows 8.1 and older
- 'v4.6.1': versionRelease := 394254; // 394271 before Win10 November Update
- 'v4.6.2': versionRelease := 394802; // 394806 before Win10 Anniversary Update
- 'v4.7': versionRelease := 460798; // 460805 before Win10 Creators Update
- end;
- end;
-
- // installation key group for all .NET versions
- key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;
-
- // .NET 3.0 uses value InstallSuccess in subkey Setup
- if Pos('v3.0', version) = 1 then begin
- success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
- end else begin
- success := RegQueryDWordValue(HKLM, key, 'Install', install);
- end;
-
- // .NET 4.0 and newer use value Servicing instead of SP
- if Pos('v4', version) = 1 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
- end else begin
- success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
- end;
-
- // .NET 4.5 and newer use additional value Release
- if versionRelease > 0 then begin
- success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
- success := success and (release >= versionRelease);
- end;
-
- result := success and (install = 1) and (serviceCount >= service);
-end;
-
-function InitializeSetup(): Boolean;
-var
- ErrCode: Integer;
-begin
- if not IsDotNetDetected('v4\Full', 0) then
- begin
- if MsgBox('QModManager requires Microsoft .NET Framework 4.0' + #13#10 + 'Would you like to install it now?', mbCriticalError, MB_YESNO) = IDYES then
- begin
- if not ShellExec('open', 'https://dotnet.microsoft.com/download/dotnet-framework/net40', '', '', SW_SHOW, ewNoWait, ErrCode) then
- begin
- SysErrorMessage(ErrCode);
- end
- end;
- result := false;
- Exit
- end;
- appIsSet := false
- if IsAppRunning('Subnautica.exe') then
- begin
- MsgBox('You need to close Subnautica before installing QModManager.' + #13#10 + 'If the game is not running, please reboot your computer.', mbError, MB_OK);
- Result := false
- end
- else
- begin
- // Load skin
- ExtractTemporaryFile('Carbon.vsf');
- LoadVCLStyle(ExpandConstant('{tmp}\Carbon.vsf'));
- Result := true
- end
-end;
-
-function IsPreviousVersionInstalled: Boolean;
-var
- uninstallRegKey: String;
- previousVersion: String;
-begin
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- previousVersion := '';
- Result := (RegKeyExists(HKLM, uninstallRegKey) or RegKeyExists(HKCU, uninstallRegKey));
-end;
-
-function GetUninstallString: string;
-var
- uninstallRegKey: String;
- uninstallString: String;
-begin
- Result := '';
- uninstallRegKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetGuid('') + '_is1';
- uninstallString := '';
- if not RegQueryStringValue(HKLM, uninstallRegKey, 'UninstallString', uninstallString) then
- RegQueryStringValue(HKCU, uninstallRegKey, 'UninstallString', uninstallString);
- Result := uninstallString;
-end;
-
-function IsUpgrade: Boolean;
-begin
- Result := (GetUninstallString() <> '');
-end;
-
-function NextButtonClick(CurPageID: Integer): Boolean;
-var
- uninstallString: String;
- resultCode: Integer;
-begin
- if CurPageID = wpSelectComponents then
- appIsSet := true;
-
- Result := true;
-end;
-
-function PrepareToInstall(var NeedsRestart: boolean): string;
-var
- uninstallString: string;
- resultCode: integer;
-begin
- NeedsRestart := false;
-
- if IsPreviousVersionInstalled() then
- begin
- uninstallString := RemoveQuotes(GetUninstallString());
- if FileExists(uninstallString) then
- begin
- Exec(uninstallString, '/SILENT', '', SW_SHOW, ewWaitUntilTerminated, resultCode);
- if IsPreviousVersionInstalled() then
- Result := 'Previous installation must be uninstalled to continue.';
- end;
- end;
-end;
-
-var TypesComboOnChangePrev: TNotifyEvent;
-
-procedure ComponentsListCheckChanges;
-begin
- WizardForm.NextButton.Enabled := (WizardSelectedComponents(false) <> '')
-end;
-
-procedure ComponentsListClickCheck(Sender: TObject);
-begin
- ComponentsListCheckChanges
-end;
-
-procedure TypesComboOnChange(Sender: TObject);
-begin
- TypesComboOnChangePrev(Sender)
- ComponentsListCheckChanges
-end;
-
-procedure CurPageChanged(CurPageID: Integer);
-begin
- CurPageChanged_SelectComponents(CurPageID)
- CurPageChanged_AddButtons(CurPageID)
- if CurPageID = wpSelectComponents then
- begin
- ComponentsListCheckChanges;
- end
-end;
-
-procedure InitializeWizard();
-begin
- WizardForm.ComponentsList.OnClickCheck := @ComponentsListClickCheck
- TypesComboOnChangePrev := WizardForm.TypesCombo.OnChange
- WizardForm.TypesCombo.OnChange := @TypesComboOnChange
- InitializeWizard_AddButtons
- InitializeWizard_DirOnChange
-end;
-
-procedure UnloadInstallerExtensions();
- var
- FilePath: string;
- BatchPath: string;
- S: TArrayOfString;
- ResultCode: Integer;
-begin
- FilePath := ExpandConstant('{tmp}\InstallerExtensions.dll');
- if not FileExists(FilePath) then
- begin
- Log(Format('File %s does not exist', [FilePath]));
- end
- else
- begin
- BatchPath :=
- ExpandConstant('{%TEMP}\') +
- 'delete_' + ExtractFileName(ExpandConstant('{tmp}')) + '.bat';
- SetArrayLength(S, 7);
- S[0] := ':loop';
- S[1] := 'del "' + FilePath + '"';
- S[2] := 'if not exist "' + FilePath + '" goto end';
- S[3] := 'goto loop';
- S[4] := ':end';
- S[5] := 'rd "' + ExpandConstant('{tmp}') + '"';
- S[6] := 'del "' + BatchPath + '"';
- if not SaveStringsToFile(BatchPath, S, False) then
- begin
- Log(Format('Error creating batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- if not Exec(BatchPath, '', '', SW_HIDE, ewNoWait, ResultCode) then
- begin
- Log(Format('Error executing batch file %s to delete %s', [BatchPath, FilePath]));
- end
- else
- begin
- Log(Format('Executed batch file %s to delete %s', [BatchPath, FilePath]));
- end;
- end;
-end;
-
-procedure DeinitializeSetup();
-begin
- // Unload skin
- UnLoadVCLStyles;
- UnloadInstallerExtensions;
-end;
diff --git a/Installer/packages.config b/Installer/packages.config
deleted file mode 100644
index 79f218d4..00000000
--- a/Installer/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/NuGet.Config b/NuGet.Config
new file mode 100644
index 00000000..2e1990f2
--- /dev/null
+++ b/NuGet.Config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OculusNewtonsoftRedirect/QModManager.OculusNewtonsoftRedirect.csproj b/OculusNewtonsoftRedirect/OculusNewtonsoftRedirect.csproj
similarity index 51%
rename from OculusNewtonsoftRedirect/QModManager.OculusNewtonsoftRedirect.csproj
rename to OculusNewtonsoftRedirect/OculusNewtonsoftRedirect.csproj
index a8e9ff54..0f7e3107 100644
--- a/OculusNewtonsoftRedirect/QModManager.OculusNewtonsoftRedirect.csproj
+++ b/OculusNewtonsoftRedirect/OculusNewtonsoftRedirect.csproj
@@ -13,6 +13,8 @@
512
true
+
+
..\Build\$(Configuration)\
@@ -51,12 +53,36 @@
prompt
-
- ..\Dependencies\BepInEx\BepInEx\core\BepInEx.dll
+
+ ..\packages\HarmonyX.2.7.0\lib\net45\0Harmony.dll
False
-
- ..\Dependencies\BepInEx\BepInEx\core\Mono.Cecil.dll
+
+ ..\packages\BepInEx.BaseLib.5.4.19\lib\net35\BepInEx.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Mdb.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Pdb.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Rocks.dll
+ False
+
+
+ ..\packages\MonoMod.RuntimeDetour.21.12.13.1\lib\net452\MonoMod.RuntimeDetour.dll
+ False
+
+
+ ..\packages\MonoMod.Utils.21.12.13.1\lib\net452\MonoMod.Utils.dll
False
@@ -66,5 +92,16 @@
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
\ No newline at end of file
diff --git a/OculusNewtonsoftRedirect/app.config b/OculusNewtonsoftRedirect/app.config
new file mode 100644
index 00000000..2a4bf252
--- /dev/null
+++ b/OculusNewtonsoftRedirect/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OculusNewtonsoftRedirect/packages.config b/OculusNewtonsoftRedirect/packages.config
new file mode 100644
index 00000000..47859cc9
--- /dev/null
+++ b/OculusNewtonsoftRedirect/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QModManager/QModManager.csproj b/QModManager/QModManager.csproj
index fbc4536c..9e8a6e98 100644
--- a/QModManager/QModManager.csproj
+++ b/QModManager/QModManager.csproj
@@ -13,6 +13,8 @@
512
true
+
+
..\Build\$(Configuration)\
@@ -58,66 +60,41 @@
latest
prompt
-
-
- ..\Dependencies\BepInEx\BepInEx\core\0Harmony.dll
- False
-
-
- $(Dependencies)\Assemblies\Assembly-CSharp-firstpass.dll
- False
-
-
- $(Dependencies)\Assemblies\Assembly-CSharp.dll
- False
-
-
- ..\Dependencies\BepInEx\BepInEx\core\BepInEx.dll
- False
-
-
- $(Dependencies)\Assemblies\Newtonsoft.Json.dll
+
+
+
+ ..\packages\HarmonyX.2.7.0\lib\net45\0Harmony.dll
False
-
-
- $(Dependencies)\Assemblies\UnityEngine.dll
- False
-
-
- $(Dependencies)\Assemblies\UnityEngine.AssetBundleModule.dll
+
+ ..\packages\BepInEx.BaseLib.5.4.19\lib\net35\BepInEx.dll
False
-
- $(Dependencies)\Assemblies\UnityEngine.CoreModule.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.dll
False
-
- $(Dependencies)\Assemblies\UnityEngine.IMGUIModule.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Mdb.dll
False
-
- $(Dependencies)\Assemblies\UnityEngine.InputLegacyModule.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Pdb.dll
False
-
- $(Dependencies)\Assemblies\UnityEngine.InputModule.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Rocks.dll
False
-
- $(Dependencies)\Assemblies\UnityEngine.UI.dll
+
+ ..\packages\MonoMod.RuntimeDetour.21.12.13.1\lib\net452\MonoMod.RuntimeDetour.dll
False
-
- $(Dependencies)\Assemblies\Unity.ResourceManager.dll
- False
-
-
-
-
- $(Dependencies)\Assemblies\Sentry.dll
+
+ ..\packages\MonoMod.Utils.21.12.13.1\lib\net452\MonoMod.Utils.dll
False
+
@@ -175,14 +152,24 @@
-
+
+
+
+
-rmdir "$(Dependencies)\Assemblies" /q /s
-mkdir "$(Dependencies)\Assemblies"
-Echo $(SolutionDir)nstrip.exe -p -cg -cg-exclude-events "$(ManagedDir)" "$(Dependencies)/Assemblies"
-$(SolutionDir)nstrip.exe -p -cg -cg-exclude-events "$(ManagedDir)" "$(Dependencies)/Assemblies"
+ rmdir "$(Dependencies)/$(Configuration)/Assemblies" /q /s
+ mkdir "$(Dependencies)/$(Configuration)/Assemblies"
+ Echo $(SolutionDir)nstrip.exe -p -cg -cg-exclude-events "$(ManagedDir)" "$(Dependencies)/$(Configuration)/Assemblies"
+ $(SolutionDir)nstrip.exe -p -cg -cg-exclude-events "$(ManagedDir)" "$(Dependencies)/$(Configuration)/Assemblies"
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
\ No newline at end of file
diff --git a/QModManager/app.config b/QModManager/app.config
new file mode 100644
index 00000000..2a4bf252
--- /dev/null
+++ b/QModManager/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QModManager/packages.config b/QModManager/packages.config
new file mode 100644
index 00000000..47859cc9
--- /dev/null
+++ b/QModManager/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QModPluginEmulator/QModManager.QModPluginGenerator.csproj b/QModPluginEmulator/QModManager.QModPluginGenerator.csproj
index 7812c02e..ad83e03f 100644
--- a/QModPluginEmulator/QModManager.QModPluginGenerator.csproj
+++ b/QModPluginEmulator/QModManager.QModPluginGenerator.csproj
@@ -19,6 +19,8 @@
AnyCPU
7.3
prompt
+
+
..\Build\$(Configuration)\
@@ -39,41 +41,39 @@
OnBuildSuccess
+
-
- ..\Dependencies\BepInEx\BepInEx\core\0Harmony.dll
- False
+
+ ..\packages\HarmonyX.2.9.0\lib\net45\0Harmony.dll
-
- $(Dependencies)\Assemblies\Assembly-CSharp-firstpass_publicized.dll
+
+ ..\packages\BepInEx.BaseLib.5.4.19\lib\net35\BepInEx.dll
False
-
- $(Dependencies)\Assemblies\Assembly-CSharp_publicized.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.dll
False
-
- ..\Dependencies\BepInEx\BepInEx\core\BepInEx.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Mdb.dll
False
-
- ..\Dependencies\BepInEx\BepInEx\core\Mono.Cecil.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Pdb.dll
False
-
- $(Dependencies)\Assemblies\Newtonsoft.Json.dll
+
+ ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Rocks.dll
False
-
-
-
- $(Dependencies)\Assemblies\UnityEngine.dll
- False
+
+ ..\packages\MonoMod.RuntimeDetour.22.1.29.1\lib\net452\MonoMod.RuntimeDetour.dll
-
- $(Dependencies)\Assemblies\UnityEngine.CoreModule.dll
- False
+
+ ..\packages\MonoMod.Utils.22.1.29.1\lib\net452\MonoMod.Utils.dll
+
+
@@ -84,13 +84,22 @@
{da63f59d-4676-4726-afec-bd9d3682733f}
QModManager
+ True
+
call "$(SolutionDir)\Scripts\QModPluginGenerator-post-build.cmd" "$(SolutionDir)" "$(TargetDir)" "$(ConfigurationName)"
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
\ No newline at end of file
diff --git a/QModPluginEmulator/app.config b/QModPluginEmulator/app.config
new file mode 100644
index 00000000..2a4bf252
--- /dev/null
+++ b/QModPluginEmulator/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QModPluginEmulator/packages.config b/QModPluginEmulator/packages.config
index 09dc1015..9ec11252 100644
--- a/QModPluginEmulator/packages.config
+++ b/QModPluginEmulator/packages.config
@@ -1,4 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/References.targets b/References.targets
new file mode 100644
index 00000000..f86a827c
--- /dev/null
+++ b/References.targets
@@ -0,0 +1,270 @@
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+ False
+
+
+
\ No newline at end of file
diff --git a/SMLHelper/Assets/BelowZeroModSprite.cs b/SMLHelper/Assets/BelowZeroModSprite.cs
new file mode 100644
index 00000000..3888fd8c
--- /dev/null
+++ b/SMLHelper/Assets/BelowZeroModSprite.cs
@@ -0,0 +1,97 @@
+#if BELOWZERO
+namespace SMLHelper.V2.Assets
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// A class that handles a custom sprite and what item it is associated to.
+ ///
+ internal class ModSprite
+ {
+ internal static void Add(SpriteManager.Group group, string name, UnityEngine.Sprite sprite)
+ {
+ if (group == SpriteManager.Group.None)
+ group = SpriteManager.Group.Item;
+ // There are no calls for sprites in the None Group.
+ // All sprite calls for almost anything we don't manually group is in the Item group.
+
+ if (!ModSprites.ContainsKey(group))
+ ModSprites.Add(group, new Dictionary(StringComparer.InvariantCultureIgnoreCase));
+
+ ModSprites[group][name] = sprite;
+ }
+
+ internal static void Add(ModSprite sprite) => Add(sprite.Group, sprite.Id, sprite.Sprite);
+
+ internal static Dictionary> ModSprites
+ = new Dictionary>();
+
+ ///
+ /// The tech type of a specific item associated with this sprite.
+ /// Can be if this sprite is for used on a group.
+ ///
+ public TechType TechType;
+
+ ///
+ /// The actual sprite used in-game when this sprite is references.
+ ///
+ public UnityEngine.Sprite Sprite;
+
+ ///
+ /// The group that this sprite belongs to.
+ /// Can be if this sprite is for used on an item.
+ ///
+ public SpriteManager.Group Group;
+
+ ///
+ /// The internal identifier of this sprite when it isn't associated to an item.
+ ///
+ public string Id;
+
+ ///
+ /// Creates a new ModSprite to be used with a specific TechType.
+ /// Created with an Atlas Sprite.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public ModSprite(TechType type, UnityEngine.Sprite sprite)
+ {
+ TechType = type;
+ Id = type.AsString();
+ Sprite = sprite;
+ Group = SpriteManager.Group.Item;
+ }
+
+ ///
+ /// Creates a new ModSprite to be used with a specific group and internal ID.
+ /// Created with an Atlas Sprite.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ public ModSprite(SpriteManager.Group group, string id, UnityEngine.Sprite sprite)
+ {
+ Group = group;
+ Id = id;
+ Sprite = sprite;
+ TechType = TechType.None;
+ }
+
+ ///
+ /// Creates a new ModSprite to be used with a specific group and internal ID.
+ /// Created with an Atlas Sprite.
+ ///
+ /// The sprite group.
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public ModSprite(SpriteManager.Group group, TechType type, UnityEngine.Sprite sprite)
+ {
+ Group = group;
+ Id = type.AsString();
+ Sprite = sprite;
+ TechType = type;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/SMLHelper/Assets/Buildable.cs b/SMLHelper/Assets/Buildable.cs
new file mode 100644
index 00000000..b0b47856
--- /dev/null
+++ b/SMLHelper/Assets/Buildable.cs
@@ -0,0 +1,35 @@
+namespace SMLHelper.V2.Assets
+{
+ using System;
+
+ ///
+ /// An item that can be built into the game world.
+ ///
+ ///
+ ///
+ public abstract class Buildable : PdaItem
+ {
+ ///
+ /// Obsolete. No longer functional. Handle with a customized HandTarget instead.
+ ///
+ [Obsolete("No longer functional. Handle with a customized HandTarget instead.", true)]
+ public virtual string HandOverText => null;
+
+ ///
+ /// Initializes a new , the basic class for any item that can built using the Habitat Builder Tool.
+ ///
+ /// The main internal identifier for this item. Your item's will be created using this name.
+ /// The name displayed in-game for this item whether in the open world or in the inventory.
+ /// The description for this item; Typically seen in the PDA, inventory, or crafting screens.
+ protected Buildable(string classId, string friendlyName, string description)
+ : base(classId, friendlyName, description)
+ {
+ CorePatchEvents += PatchBuildable;
+ }
+
+ private void PatchBuildable()
+ {
+ this.CraftDataHandler.AddBuildable(this.TechType);
+ }
+ }
+}
diff --git a/SMLHelper/Assets/Craftable.cs b/SMLHelper/Assets/Craftable.cs
new file mode 100644
index 00000000..a55a55eb
--- /dev/null
+++ b/SMLHelper/Assets/Craftable.cs
@@ -0,0 +1,62 @@
+namespace SMLHelper.V2.Assets
+{
+ using SMLHelper.V2.Interfaces;
+
+ ///
+ /// An item that can be crafted into the game world from a fabricator.
+ ///
+ ///
+ ///
+ public abstract class Craftable : PdaItem
+ {
+ internal ICraftTreeHandler CraftTreeHandler { get; set; } = Handlers.CraftTreeHandler.Main;
+
+ ///
+ /// Override with the vanilla fabricator that crafts this item.
+ /// Leave this as if you are manually adding this item to a custom fabricator.
+ ///
+ public virtual CraftTree.Type FabricatorType => CraftTree.Type.None;
+
+ ///
+ /// Override with the tab node steps to take to get to the tab you want the item's blueprint to appear in.
+ /// If not overriden, the item will appear at the craft tree's root.
+ ///
+ public virtual string[] StepsToFabricatorTab => null;
+
+ ///
+ /// Override with a custom crafting time for this item. Normal default crafting time is 1f.
+ /// Any value zero or less will be ignored.
+ ///
+ public virtual float CraftingTime => 0f;
+
+ ///
+ /// Initializes a new , the basic class for any item that can be crafted at a fabricator.
+ ///
+ /// The main internal identifier for this item. Your item's will be created using this name.
+ /// The name displayed in-game for this item whether in the open world or in the inventory.
+ /// The description for this item; Typically seen in the PDA, inventory, or crafting screens.
+ protected Craftable(string classId, string friendlyName, string description)
+ : base(classId, friendlyName, description)
+ {
+ CorePatchEvents += PatchCraftingData;
+ }
+
+ private void PatchCraftingData()
+ {
+ if (this.FabricatorType == CraftTree.Type.None)
+ {
+ Logger.Debug($"Craftable '{this.ClassID}' was not automatically patched into a crafting tree.");
+ }
+ else
+ {
+ if (this.StepsToFabricatorTab == null || this.StepsToFabricatorTab.Length == 0)
+ this.CraftTreeHandler.AddCraftingNode(this.FabricatorType, this.TechType);
+ else
+ this.CraftTreeHandler.AddCraftingNode(this.FabricatorType, this.TechType, this.StepsToFabricatorTab);
+ }
+
+ if (this.CraftingTime > 0f)
+ this.CraftDataHandler.SetCraftingTime(this.TechType, this.CraftingTime);
+ }
+ }
+}
diff --git a/SMLHelper/Assets/CustomFabricator.cs b/SMLHelper/Assets/CustomFabricator.cs
new file mode 100644
index 00000000..d5935a2a
--- /dev/null
+++ b/SMLHelper/Assets/CustomFabricator.cs
@@ -0,0 +1,424 @@
+namespace SMLHelper.V2.Assets
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using SMLHelper.V2.Crafting;
+ using SMLHelper.V2.Handlers;
+ using UnityEngine;
+ using Logger = V2.Logger;
+
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+#elif BELOWZERO
+ using Sprite = UnityEngine.Sprite;
+#endif
+
+ ///
+ /// An asset class inheriting from that streamlines the process of creating a custom fabricator with a custom crafting tree.
+ ///
+ ///
+ public abstract class CustomFabricator : Buildable
+ {
+ ///
+ /// Defines a list of available models for your .
+ ///
+ public enum Models
+ {
+ ///
+ /// The regular fabricator like the one in the life pod.
+ ///
+ Fabricator,
+
+ ///
+ /// The modification station that upgrades your equipment.
+ ///
+ Workbench,
+#if SUBNAUTICA
+ ///
+ /// The style of fabricator found in the Moon Pool and the Cyclops sub.
+ ///
+ MoonPool,
+#endif
+ ///
+ /// Use this option only if you want to provide your own custom model for your fabricator.
+ /// To use this value, you must override the method.
+ ///
+ Custom
+ }
+
+ private const string RootNode = "root";
+ internal readonly Dictionary CraftTreeLinkingNodes = new Dictionary();
+ internal readonly List OrderedCraftTreeActions = new List();
+
+ ///
+ /// Initialized a new based on the asset class.
+ ///
+ /// The main internal identifier for this item. Your item's will be created using this name.
+ /// The name displayed in-game for this item whether in the open world or in the inventory.
+ /// The description for this item; Typically seen in the PDA, inventory, or crafting screens.
+ protected CustomFabricator(string classId, string friendlyName, string description)
+ : base(classId, friendlyName, description)
+ {
+ OnStartedPatching += () =>
+ {
+ CreateCustomCraftTree(out CraftTree.Type craftTreeType);
+ this.TreeTypeID = craftTreeType;
+ };
+ }
+
+ ///
+ /// Override this property to change which model your custom fabricator will use.
+ /// By default, this will be the standard as seen in the lifepod.
+ ///
+ public virtual Models Model => Models.Fabricator;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to true.
+ ///
+ public virtual bool AllowedInBase => true;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to true.
+ ///
+ public virtual bool AllowedInCyclops => true;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to false.
+ ///
+ public virtual bool AllowedOutside => false;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to false.
+ ///
+ public virtual bool AllowedOnCeiling => false;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to true for and false for all others.
+ ///
+ public virtual bool AllowedOnGround => this.Model == Models.Workbench;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to false for and true for all others.
+ ///
+ public virtual bool AllowedOnWall => this.Model != Models.Workbench;
+
+ ///
+ /// Override this property to change the value of .
+ /// Defaults to true for and false for all others.
+ ///
+ public virtual bool RotationEnabled => this.Model == Models.Workbench;
+
+ ///
+ /// Override this value tp true along with to apply a simple tint to your custom fabricator for easy customization.
+ ///
+ public virtual bool UseCustomTint => false;
+
+ ///
+ /// Override this value to your desired along with to apply a simple tint to your custom fabricator for easy customization.
+ ///
+ public virtual Color ColorTint => Color.white;
+
+ ///
+ /// The ID value for your custom craft tree. This is set after this method is invoked.
+ ///
+ public CraftTree.Type TreeTypeID { get; private set; }
+
+ ///
+ /// Gets the root node of the crafting tree. This is set after this method is invoked.
+ ///
+ public ModCraftTreeRoot Root { get; private set; }
+
+ ///
+ /// Override with the category within the group in the PDA blueprints where this item appears.
+ ///
+ public override TechCategory CategoryForPDA => TechCategory.InteriorModule;
+
+ ///
+ /// Override with the main group in the PDA blueprints where this item appears.
+ ///
+ public override TechGroup GroupForPDA => TechGroup.InteriorModules;
+
+ ///
+ /// The in-game .
+ ///
+ ///
+ public override GameObject GetGameObject()
+ {
+#if SUBNAUTICA_EXP || BELOWZERO
+ return null;
+#else
+ GameObject prefab = this.Model switch
+ {
+ Models.Fabricator => GameObject.Instantiate(CraftData.GetPrefabForTechType(TechType.Fabricator)),
+ Models.Workbench => GameObject.Instantiate(CraftData.GetPrefabForTechType(TechType.Workbench)),
+#if SUBNAUTICA
+ Models.MoonPool => GameObject.Instantiate(Resources.Load("Submarine/Build/CyclopsFabricator")),
+#endif
+ Models.Custom => GetCustomCrafterPreFab(),
+ _ => null
+ };
+
+ return PreProcessPrefab(prefab);
+#endif
+ }
+
+ ///
+ /// The in-game , async way.
+ ///
+ ///
+ public override IEnumerator GetGameObjectAsync(IOut gameObject)
+ {
+ GameObject prefab;
+ var taskResult = new TaskResult();
+
+ switch (this.Model)
+ {
+ case Models.Fabricator:
+ default:
+ yield return CraftData.GetPrefabForTechTypeAsync(TechType.Fabricator, false, taskResult);
+ prefab = GameObject.Instantiate(taskResult.Get());
+ break;
+ case Models.Workbench:
+ yield return CraftData.GetPrefabForTechTypeAsync(TechType.Workbench, false, taskResult);
+ prefab = GameObject.Instantiate(taskResult.Get());
+ break;
+#if SUBNAUTICA
+ case Models.MoonPool:
+#pragma warning disable CS0618 // obsolete
+ var request = UWE.PrefabDatabase.GetPrefabForFilenameAsync("Submarine/Build/CyclopsFabricator.prefab");
+#pragma warning restore CS0618
+ yield return request;
+ request.TryGetPrefab(out prefab);
+ prefab = GameObject.Instantiate(prefab);
+ break;
+#endif
+ case Models.Custom:
+ yield return GetCustomCrafterPreFabAsync(taskResult);
+ prefab = taskResult.Get();
+ break;
+ };
+
+ gameObject.Set(PreProcessPrefab(prefab));
+ }
+
+ private GameObject PreProcessPrefab(GameObject prefab)
+ {
+ Constructable constructible = null;
+ GhostCrafter crafter;
+ switch (this.Model)
+ {
+ case Models.Fabricator:
+ default:
+ crafter = prefab.GetComponent();
+ break;
+ case Models.Workbench:
+ crafter = prefab.GetComponent();
+ break;
+#if SUBNAUTICA
+ case Models.MoonPool:
+ crafter = prefab.GetComponent();
+
+ // Add prefab ID because CyclopsFabricator normaly doesn't have one
+ PrefabIdentifier prefabId = prefab.AddComponent();
+ prefabId.ClassId = this.ClassID;
+ prefabId.name = this.FriendlyName;
+
+ // Add tech tag because CyclopsFabricator normaly doesn't have one
+ TechTag techTag = prefab.AddComponent();
+ techTag.type = this.TechType;
+
+ // Retrieve sub game objects
+ GameObject cyclopsFabLight = prefab.FindChild("fabricatorLight");
+ GameObject cyclopsFabModel = prefab.FindChild("submarine_fabricator_03");
+ // Translate CyclopsFabricator model and light
+ prefab.transform.localPosition = new Vector3(cyclopsFabModel.transform.localPosition.x, // Same X position
+ cyclopsFabModel.transform.localPosition.y - 0.8f, // Push towards the wall slightly
+ cyclopsFabModel.transform.localPosition.z); // Same Z position
+ prefab.transform.localPosition = new Vector3(cyclopsFabLight.transform.localPosition.x, // Same X position
+ cyclopsFabLight.transform.localPosition.y - 0.8f, // Push towards the wall slightly
+ cyclopsFabLight.transform.localPosition.z); // Same Z position
+ // Add constructable - This prefab normally isn't constructed.
+ constructible = prefab.AddComponent();
+ constructible.model = cyclopsFabModel;
+ break;
+#endif
+ case Models.Custom:
+ crafter = prefab.EnsureComponent();
+ break;
+ }
+
+ crafter.craftTree = this.TreeTypeID;
+ crafter.handOverText = $"Use {this.FriendlyName}";
+
+ if (constructible is null)
+ constructible = prefab.GetComponent();
+
+ constructible.allowedInBase = this.AllowedInBase;
+ constructible.allowedInSub = this.AllowedInCyclops;
+ constructible.allowedOutside = this.AllowedOutside;
+ constructible.allowedOnCeiling = this.AllowedOnCeiling;
+ constructible.allowedOnGround = this.AllowedOnGround;
+ constructible.allowedOnWall = this.AllowedOnWall;
+ constructible.allowedOnConstructables = false;
+ constructible.controlModelState = true;
+ constructible.rotationEnabled = this.RotationEnabled;
+ constructible.techType = this.TechType; // This was necessary to correctly associate the recipe at building time
+
+ SkyApplier skyApplier = prefab.EnsureComponent();
+ skyApplier.renderers = prefab.GetComponentsInChildren();
+ skyApplier.anchorSky = Skies.Auto;
+
+ if (this.UseCustomTint)
+ {
+ SkinnedMeshRenderer skinnedMeshRenderer = prefab.GetComponentInChildren();
+ skinnedMeshRenderer.material.color = this.ColorTint; // Tint option available
+ }
+
+ crafter.powerRelay = PowerSource.FindRelay(prefab.transform);
+
+ return prefab;
+ }
+
+ ///
+ /// Override this method if you want to provide your own prefab and model for your custom fabricator.
+ ///
+ ///
+ protected virtual GameObject GetCustomCrafterPreFab()
+ {
+ throw new NotImplementedException($"To use a custom fabricator model, the prefab must be created in {nameof(GetCustomCrafterPreFab)}.");
+ }
+
+ ///
+ /// Override this method if you want to provide your own prefab and model for your custom fabricator.
+ ///
+ ///
+ protected virtual IEnumerator GetCustomCrafterPreFabAsync(IOut gameObject)
+ {
+ throw new NotImplementedException($"To use a custom fabricator model, the prefab must be created in {nameof(GetCustomCrafterPreFabAsync)}.");
+ }
+
+ ///
+ /// Override this method if you want full control over how your custom craft tree is built up.
+ /// To use this method's default behavior, you must use the following methods to build up your crafting tree.
+ /// -
+ /// -
+ /// -
+ ///
+ ///
+ internal virtual void CreateCustomCraftTree(out CraftTree.Type craftTreeType)
+ {
+ ModCraftTreeRoot root = CraftTreeHandler.CreateCustomCraftTreeAndType(this.ClassID, out craftTreeType);
+ this.Root = root;
+ CraftTreeLinkingNodes.Add(RootNode, root);
+
+ // Since we shouldn't rely on attached events to be executed in any particular order,
+ // this list of actions will ensure that the craft tree is built up in the order in which nodes were received.
+ foreach (Action action in OrderedCraftTreeActions)
+ action.Invoke();
+ }
+
+ ///
+ /// Adds a new tab node to the custom crafting tree of this fabricator.
+ ///
+ /// The internal ID for the tab node.
+ /// The in-game text shown for the tab node.
+ /// The sprite used for the tab node.
+ /// Optional. The parent tab of this tab.
+ /// When this value is null, the tab will be added to the root of the craft tree.
+ public void AddTabNode(string tabId, string displayText, Sprite tabSprite, string parentTabId = null)
+ {
+ OrderedCraftTreeActions.Add(() =>
+ {
+ ModCraftTreeLinkingNode parentNode = CraftTreeLinkingNodes[parentTabId ?? RootNode];
+ ModCraftTreeTab tab = parentNode.AddTabNode(tabId, displayText, tabSprite);
+ CraftTreeLinkingNodes[tabId] = tab;
+ });
+ }
+
+ ///
+ /// Adds a new crafting node to the custom crafting tree of this fabricator.
+ ///
+ /// The item to craft.
+ /// Optional. The parent tab of this craft node.
+ /// When this value is null, the craft node will be added to the root of the craft tree.
+ public void AddCraftNode(TechType techType, string parentTabId = null)
+ {
+ Logger.Debug($"'{techType.AsString()}' will be added to the custom craft tree '{this.ClassID}'");
+ OrderedCraftTreeActions.Add(() =>
+ {
+ ModCraftTreeLinkingNode parentTab = CraftTreeLinkingNodes[parentTabId ?? RootNode];
+ parentTab.AddCraftingNode(techType);
+ });
+ }
+
+ ///
+ /// Safely attempts to add a new crafting node to the custom crafting tree of this fabricator.
+ /// If the modded TechType is not found, the craft node will not be added.
+ ///
+ /// The modded item to craft.
+ /// Optional. The parent tab of this craft node.
+ /// When this value is null, the craft node will be added to the root of the craft tree.
+ public void AddCraftNode(string moddedTechType, string parentTabId = null)
+ {
+ Logger.Debug($"'{moddedTechType}' will be added to the custom craft tree '{this.ClassID}'");
+ OrderedCraftTreeActions.Add(() =>
+ {
+ if (this.TechTypeHandler.TryGetModdedTechType(moddedTechType, out TechType techType))
+ {
+ ModCraftTreeLinkingNode parentTab = CraftTreeLinkingNodes[parentTabId ?? RootNode];
+ parentTab.AddCraftingNode(techType);
+ }
+ else
+ {
+ Logger.Info($"Did not find a TechType value for '{moddedTechType}' to add to the custom craft tree '{this.ClassID}'");
+ }
+ });
+ }
+
+ ///
+ /// Safely adds a new crafting node to the custom crafting tree of this fabricator.
+ /// If the item has not been patched yet, its method will first be invoked.
+ ///
+ /// The item to craft from this fabricator.
+ /// Optional. The parent tab of this craft node.
+ /// When this value is null, the item's property will be checked instead.
+ /// The craft node will be added to the root of the craft tree if both are null.
+ public void AddCraftNode(Craftable item, string parentTabId = null)
+ {
+ Logger.Debug($"'{item.ClassID}' will be added to the custom craft tree '{this.ClassID}'");
+ OrderedCraftTreeActions.Add(() =>
+ {
+ if (item.TechType == TechType.None)
+ {
+ Logger.Info($"'{item.ClassID} had to be patched early to obtain its TechType value for the custom craft tree '{this.ClassID}'");
+ item.Patch();
+ }
+
+ string[] stepsToParent = item.StepsToFabricatorTab;
+
+ if (parentTabId == null)
+ {
+ if (stepsToParent != null && stepsToParent.Length > 0)
+ {
+ int last = stepsToParent.Length - 1;
+ parentTabId = stepsToParent[last];
+ }
+ else
+ {
+ parentTabId = RootNode;
+ }
+ }
+
+ ModCraftTreeLinkingNode parentTab = CraftTreeLinkingNodes[parentTabId];
+ parentTab.AddCraftingNode(item.TechType);
+ });
+ }
+ }
+}
diff --git a/SMLHelper/Assets/Equipable.cs b/SMLHelper/Assets/Equipable.cs
new file mode 100644
index 00000000..e77ead7a
--- /dev/null
+++ b/SMLHelper/Assets/Equipable.cs
@@ -0,0 +1,39 @@
+namespace SMLHelper.V2.Assets
+{
+ ///
+ /// An item that can be crafted and equipped.
+ ///
+ ///
+ ///
+ public abstract class Equipable: Craftable
+ {
+ ///
+ /// Gets the type of equipment slot this item can fit into.
+ ///
+ ///
+ /// The type of the equipment slot compatible with this item.
+ ///
+ public abstract EquipmentType EquipmentType { get; }
+
+ ///
+ /// Gets the type of equipment slot this item can fit into.
+ ///
+ ///
+ /// The type of the equipment slot compatible with this item.
+ ///
+ public virtual QuickSlotType QuickSlotType => QuickSlotType.None;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The main internal identifier for this item. Your item's will be created using this name.
+ /// The name displayed in-game for this item whether in the open world or in the inventory.
+ /// The description for this item; Typically seen in the PDA, inventory, or crafting screens.
+ protected Equipable(string classId, string friendlyName, string description)
+ : base(classId, friendlyName, description)
+ {
+ CorePatchEvents += () => CraftDataHandler.SetEquipmentType(TechType, EquipmentType);
+ CorePatchEvents += () => CraftDataHandler.SetQuickSlotType(TechType, QuickSlotType);
+ }
+ }
+}
diff --git a/SMLHelper/Assets/Fish.cs b/SMLHelper/Assets/Fish.cs
new file mode 100644
index 00000000..8ae58ff8
--- /dev/null
+++ b/SMLHelper/Assets/Fish.cs
@@ -0,0 +1,58 @@
+namespace SMLHelper.V2.Assets
+{
+ using Handlers;
+ using UnityEngine;
+
+ ///
+ /// "A wrapper class that uses and to quickly create a custom fish
+ ///
+ public class Fish
+ {
+ ///
+ /// The id to use to create the creature. This should be unique
+ ///
+ public string id;
+ ///
+ /// The display name for your fish. This is shown in the inventory, and can be whatever you want
+ ///
+ public string displayName;
+ ///
+ /// The short description of your fish in the inventory
+ ///
+ public string tooltip = "";
+ ///
+ /// The file name of your fish's sprite in the inventory. You can leave this null if your fish isn't pickupable. The file is relative
+ /// from the QMods folder, e.g. "MinecraftFish/Assets/cod.png"
+ ///
+ public string spriteFileName;
+
+ ///
+ /// The model to use to create the creature. This would ideally only have renderer/collider components attached, but will still work if it has behaviours
+ ///
+ public GameObject modelPrefab;
+
+ ///
+ /// Determines whether your creature can be picked up by the player
+ ///
+ public bool isPickupable;
+ ///
+ /// Determines whether the creature moves on land or in water. Default true, which is in water. Land creature AI does not work ATM.
+ ///
+ public bool isWaterCreature = true;
+
+ ///
+ /// The speed at which your creature will swim
+ ///
+ public float swimSpeed;
+
+ ///
+ /// The interval (in seconds) between when your fish will find a new position to swim to
+ ///
+ public float swimInterval = 1f;
+
+ ///
+ /// The area in which your creature will look for a random position when swimming. This should be larger for larger creatures
+ ///
+ public Vector3 swimRadius;
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Assets/FishPrefab.cs b/SMLHelper/Assets/FishPrefab.cs
new file mode 100644
index 00000000..ed2595fe
--- /dev/null
+++ b/SMLHelper/Assets/FishPrefab.cs
@@ -0,0 +1,132 @@
+namespace SMLHelper.V2.Assets
+{
+ using UnityEngine;
+
+ ///
+ /// Class used by CustomFish for constructing a prefab based on the values provided by the user.
+ /// You can use this yourself if you want, but you will need to manually provide a TechType
+ ///
+ public class FishPrefab : ModPrefab
+ {
+ ///
+ /// The model to use to create the creature. This would ideally only have renderer/collider components attached, but will still work if it has behaviours
+ ///
+ public GameObject modelPrefab;
+ ///
+ /// Determines whether your creature can be picked up
+ ///
+ public bool pickupable;
+ ///
+ /// Determines whether your creature walks or swims. Only works for swimming at the moment, land will probably be fixed at a later time
+ ///
+ public bool isWaterCreature = true;
+
+ ///
+ /// The speed at which your creature moves
+ ///
+ public float swimSpeed;
+ ///
+ /// The area in which your creature's AI will look for a new spot to move to
+ ///
+ public Vector3 swimRadius;
+ ///
+ /// The interval in seconds between when your creature finds a new spot to move to
+ ///
+ public float swimInterval;
+
+ ///
+ /// Creates a new with the given values
+ ///
+ ///
+ ///
+ ///
+ public FishPrefab(string classId, string prefabFileName, TechType techType = TechType.None) : base(classId, prefabFileName, techType) { }
+
+ ///
+ /// Gets the prefab game object
+ ///
+ public sealed override GameObject GetGameObject()
+ {
+ V2.Logger.Debug($"[FishFramework] Initializing fish: {this.ClassID}");
+ GameObject mainObj = modelPrefab;
+
+ Renderer[] renderers = mainObj.GetComponentsInChildren();
+ foreach (Renderer rend in renderers)
+ {
+ rend.material.shader = Shader.Find("MarmosetUBER");
+ }
+
+ Rigidbody rb = mainObj.EnsureComponent();
+ rb.useGravity = false;
+ rb.angularDrag = 1f;
+
+ WorldForces forces = mainObj.EnsureComponent();
+ forces.useRigidbody = rb;
+ forces.aboveWaterDrag = 0f;
+ forces.aboveWaterGravity = 9.81f;
+ forces.handleDrag = true;
+ forces.handleGravity = true;
+ forces.underwaterDrag = 1f;
+ forces.underwaterGravity = 0;
+#if BELOWZERO || SUBNAUTICA_EXP
+ forces.waterDepth = Ocean.GetOceanLevel();
+#else
+ forces.waterDepth = Ocean.main.GetOceanLevel();
+#endif
+ forces.enabled = false;
+ forces.enabled = true;
+
+ mainObj.EnsureComponent().slotType = EntitySlot.Type.Creature;
+ mainObj.EnsureComponent().ClassId = this.ClassID;
+ mainObj.EnsureComponent().type = this.TechType;
+ mainObj.EnsureComponent().renderers = renderers;
+ mainObj.EnsureComponent().cellLevel = LargeWorldEntity.CellLevel.Near;
+ mainObj.EnsureComponent().health = 10f;
+
+ Creature creature = mainObj.EnsureComponent();
+ creature.initialCuriosity = AnimationCurve.Linear(0f, 0.5f, 1f, 0.5f);
+ creature.initialFriendliness = AnimationCurve.Linear(0f, 0.5f, 1f, 0.5f);
+ creature.initialHunger = AnimationCurve.Linear(0f, 0.5f, 1f, 0.5f);
+
+ SwimBehaviour behaviour = null;
+ if (isWaterCreature)
+ {
+ behaviour = mainObj.EnsureComponent();
+ SwimRandom swim = mainObj.EnsureComponent();
+ swim.swimVelocity = swimSpeed;
+ swim.swimRadius = swimRadius;
+ swim.swimInterval = swimInterval;
+ }
+ else
+ {
+ behaviour = mainObj.EnsureComponent();
+ WalkOnGround walk = mainObj.EnsureComponent();
+ OnSurfaceMovement move = mainObj.EnsureComponent();
+ move.onSurfaceTracker = mainObj.EnsureComponent();
+ }
+
+ Locomotion loco = mainObj.EnsureComponent();
+ loco.useRigidbody = rb;
+
+ mainObj.EnsureComponent().type = EcoTargetType.Peeper;
+ mainObj.EnsureComponent();
+ mainObj.EnsureComponent();
+
+ SplineFollowing spline = mainObj.EnsureComponent();
+ spline.locomotion = loco;
+ spline.levelOfDetail = mainObj.EnsureComponent();
+ spline.GoTo(mainObj.transform.position + mainObj.transform.forward, mainObj.transform.forward, 5f);
+
+ behaviour.splineFollowing = spline;
+
+ if (pickupable)
+ {
+ mainObj.EnsureComponent();
+ }
+
+ creature.ScanCreatureActions();
+
+ return mainObj;
+ }
+ }
+}
diff --git a/SMLHelper/Assets/ModPrefab.cs b/SMLHelper/Assets/ModPrefab.cs
new file mode 100644
index 00000000..c64e2371
--- /dev/null
+++ b/SMLHelper/Assets/ModPrefab.cs
@@ -0,0 +1,153 @@
+using System.Reflection;
+
+namespace SMLHelper.V2.Assets
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using UnityEngine;
+
+ ///
+ /// The abstract class to inherit when you want to add new PreFabs into the game.
+ ///
+ public abstract class ModPrefab
+ {
+ private static readonly Dictionary FileNameDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
+ private static readonly Dictionary ClassIdDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
+ private static readonly List PreFabsList = new List();
+ internal static bool ModPrefabsPatched = false;
+
+ internal static void Add(ModPrefab prefab)
+ {
+ FileNameDictionary.Add(prefab.PrefabFileName, prefab);
+ ClassIdDictionary.Add(prefab.ClassID, prefab);
+ PreFabsList.Add(prefab);
+ ModPrefabsPatched = false;
+ }
+
+ internal static IEnumerable Prefabs => PreFabsList;
+ internal static bool TryGetFromFileName(string classId, out ModPrefab prefab)
+ {
+ if (string.IsNullOrEmpty(classId))
+ {
+ prefab = null;
+ return false;
+ }
+ return FileNameDictionary.TryGetValue(classId, out prefab);
+ }
+
+ internal static bool TryGetFromClassId(string classId, out ModPrefab prefab)
+ {
+ if (string.IsNullOrEmpty(classId))
+ {
+ prefab = null;
+ return false;
+ }
+ return ClassIdDictionary.TryGetValue(classId, out prefab);
+ }
+
+ internal readonly Assembly Mod;
+
+ ///
+ /// The class identifier used for the component whenever applicable.
+ ///
+ public string ClassID { get; protected set; }
+
+ ///
+ /// Name of the prefab file.
+ ///
+ public string PrefabFileName { get; protected set; }
+
+ ///
+ /// The of the corresponding item.
+ /// Used for , and components whenever applicable.
+ ///
+ public TechType TechType { get; protected set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The class identifier used for the component whenever applicable.
+ /// Name of the prefab file.
+ /// The techtype of the corresponding item.
+ /// Used for the and components whenever applicable.
+ /// Can also be set later in the constructor if it is not yet provided.
+ protected ModPrefab(string classId, string prefabFileName, TechType techType = TechType.None)
+ {
+ this.ClassID = classId;
+ this.PrefabFileName = prefabFileName;
+ this.TechType = techType;
+
+ Mod = GetType().Assembly;
+ }
+
+ internal GameObject GetGameObjectInternal()
+ {
+ GameObject go = GetGameObject();
+ if (go == null)
+ return null;
+
+ ProcessPrefab(go);
+ return go;
+ }
+
+ internal IEnumerator GetGameObjectInternalAsync(IOut gameObject)
+ {
+ var taskResult = new TaskResult();
+ yield return GetGameObjectAsync(taskResult);
+
+ GameObject go = taskResult.Get();
+ if (go == null)
+ yield break;
+
+ ProcessPrefab(go);
+ gameObject.Set(go);
+ }
+
+ ///
+ /// Caches the prefab, then sets its TechType and ClassID to a default set of values applicable to most mods.
+ /// FOR ADVANCED MODDING ONLY. Do not override unless you know exactly what you are doing.
+ ///
+ ///
+ protected virtual void ProcessPrefab(GameObject go)
+ {
+ if (go.activeInHierarchy) // inactive prefabs don't need to be removed by cache
+ ModPrefabCache.AddPrefab(go);
+
+ go.name = this.ClassID;
+
+ if (this.TechType != TechType.None)
+ {
+ if (go.GetComponent() is TechTag tag)
+ {
+ tag.type = this.TechType;
+ }
+
+ if (go.GetComponent() is Constructable cs)
+ {
+ cs.techType = this.TechType;
+ }
+ }
+
+ if (go.GetComponent() is PrefabIdentifier pid)
+ {
+ pid.ClassId = this.ClassID;
+ }
+ }
+
+
+ ///
+ /// Gets the prefab game object. Set up your prefab components here.
+ /// The and ClassID are already handled.
+ ///
+ /// The game object to be instantiated into a new in-game entity.
+ public virtual GameObject GetGameObject() => null;
+
+ ///
+ /// Gets the prefab game object asynchronously. Set up your prefab components here.
+ /// The and ClassID are already handled.
+ ///
+ /// The game object to be instantiated into a new in-game entity.
+ public virtual IEnumerator GetGameObjectAsync(IOut gameObject) => null;
+ }
+}
diff --git a/SMLHelper/Assets/ModPrefabCache.cs b/SMLHelper/Assets/ModPrefabCache.cs
new file mode 100644
index 00000000..a85cd4ef
--- /dev/null
+++ b/SMLHelper/Assets/ModPrefabCache.cs
@@ -0,0 +1,91 @@
+namespace SMLHelper.V2.Assets
+{
+ using System;
+ using System.Collections.Generic;
+ using UnityEngine;
+ using Logger = V2.Logger;
+
+ ///
+ /// Class that used by to store game objects that used as prefabs.
+ /// Also it can be used by mods directly, e.g. in to store prefab before returning.
+ /// Game objects in cache are inactive and will not be on scene.
+ ///
+ public static class ModPrefabCache
+ {
+ private const float cleanDelay = 30.0f; // delay in secs before attempt to remove prefab from cache
+
+ // list of prefabs for removing (Item1 - time of addition, Item2 - prefab gameobject)
+ private readonly static List> prefabs = new List>();
+
+ private static GameObject root; // active root object with CacheCleaner component
+ private static GameObject prefabRoot; // inactive child object, parent for added prefabs
+
+ private class CacheCleaner : MonoBehaviour
+ {
+ public void Update()
+ {
+ for (int i = prefabs.Count - 1; i >= 0; i--)
+ {
+ if (Time.time < prefabs[i].Item1 + cleanDelay || Builder.prefab == prefabs[i].Item2)
+ continue;
+
+ Logger.Debug($"ModPrefabCache: removing prefab {prefabs[i].Item2}");
+
+ Destroy(prefabs[i].Item2);
+ prefabs.RemoveAt(i);
+ }
+ }
+ }
+
+ private static void Init()
+ {
+ if (root)
+ return;
+
+ root = new GameObject("SMLHelper.PrefabCache", typeof(SceneCleanerPreserve), typeof(CacheCleaner));
+ UnityEngine.Object.DontDestroyOnLoad(root);
+
+ prefabRoot = new GameObject("PrefabRoot");
+ prefabRoot.transform.parent = root.transform;
+ prefabRoot.SetActive(false);
+ }
+
+ /// Add prefab to cache
+ /// Prefab to add.
+ ///
+ /// Is prefab needed to be removed from cache after use.
+ /// Prefabs without autoremove flag can be safely deleted by
+ ///
+ public static void AddPrefab(GameObject prefab, bool autoremove = true)
+ {
+ Init();
+ prefab.transform.parent = prefabRoot.transform;
+
+ AddPrefabInternal(prefab, autoremove);
+ }
+
+ /// Add prefab copy to cache (instatiated copy will not run 'Awake')
+ /// Prefab to copy and add.
+ ///
+ /// Is prefab copy needed to be removed from cache after use.
+ /// Prefabs without autoremove flag can be safely deleted by
+ ///
+ /// Prefab copy
+ public static GameObject AddPrefabCopy(GameObject prefab, bool autoremove = true)
+ {
+ Init();
+ var prefabCopy = UnityEngine.Object.Instantiate(prefab, prefabRoot.transform);
+
+ AddPrefabInternal(prefabCopy, autoremove);
+ return prefabCopy;
+ }
+
+ private static void AddPrefabInternal(GameObject prefab, bool autoremove)
+ {
+ if (autoremove)
+ prefabs.Add(Tuple.Create(Time.time, prefab));
+
+ Logger.Debug($"ModPrefabCache: adding prefab {prefab}");
+ }
+ }
+}
diff --git a/SMLHelper/Assets/ModPrefabRequest.cs b/SMLHelper/Assets/ModPrefabRequest.cs
new file mode 100644
index 00000000..3cd696d0
--- /dev/null
+++ b/SMLHelper/Assets/ModPrefabRequest.cs
@@ -0,0 +1,59 @@
+namespace SMLHelper.V2.Assets
+{
+ using System.Collections;
+ using UnityEngine;
+ using UWE;
+
+ // request for getting ModPrefab asynchronously
+ internal class ModPrefabRequest: IPrefabRequest, IEnumerator
+ {
+ private readonly ModPrefab modPrefab;
+
+ private int state = 0;
+ private CoroutineTask task;
+ private TaskResult taskResult;
+
+ public ModPrefabRequest(ModPrefab modPrefab)
+ {
+ this.modPrefab = modPrefab;
+ }
+
+ private void Init()
+ {
+ if (task != null)
+ return;
+
+ taskResult = new TaskResult();
+ task = new CoroutineTask(modPrefab.GetGameObjectInternalAsync(taskResult), taskResult);
+ }
+
+ public object Current
+ {
+ get
+ {
+ Init();
+ return task;
+ }
+ }
+
+ public bool TryGetPrefab(out GameObject result)
+ {
+ result = taskResult.Get();
+ return result != null;
+ }
+
+ public bool MoveNext()
+ {
+ Init();
+ return state++ == 0;
+ }
+
+ public void Reset() {}
+
+#if BELOWZERO || SUBNAUTICA_EXP
+ public void Release()
+ {
+ }
+#endif
+ }
+}
diff --git a/SMLHelper/Assets/PdaItem.cs b/SMLHelper/Assets/PdaItem.cs
new file mode 100644
index 00000000..fb908746
--- /dev/null
+++ b/SMLHelper/Assets/PdaItem.cs
@@ -0,0 +1,155 @@
+namespace SMLHelper.V2.Assets
+{
+ using Crafting;
+ using SMLHelper.V2.Interfaces;
+ using System.Collections.Generic;
+
+ ///
+ /// A item that appears in the PDA blueprints.
+ ///
+ ///
+ public abstract class PdaItem: Spawnable
+ {
+ internal IKnownTechHandler KnownTechHandler { get; set; } = Handlers.KnownTechHandler.Main;
+ internal IPDAHandler PDAHandler { get; set; } = Handlers.PDAHandler.Main;
+ internal IPDAEncyclopediaHandler PDAEncyclopediaHandler { get; set; } = Handlers.PDAEncyclopediaHandler.Main;
+
+ ///
+ /// Override to set the that must first be scanned or picked up to unlock the blueprint for this item.
+ /// If not overriden, it this item will be unlocked from the start of the game.
+ ///
+ public virtual TechType RequiredForUnlock => TechType.None;
+
+ ///
+ /// Override to add a scanner entry to the TechType if it does not have one.
+ /// WARNING. You can overwrite an existing entry with this. Use with Caution as this can break recipe unlocks of the original!
+ /// Default is .
+ ///
+ public virtual bool AddScannerEntry => false;
+
+ ///
+ /// Override to set the number of that must be scanned to unlock;
+ /// If not overriden, Default value is .
+ ///
+ public virtual int FragmentsToScan => 1;
+
+ ///
+ /// Override to set the speed that the fragments are scanned;
+ /// If not overriden, Default value is .
+ ///
+ public virtual float TimeToScanFragment => 2f;
+
+ ///
+ /// Override to allow fragments to be scanned for materials after the relavent TechType is already Unlocked.
+ /// Default is .
+ ///
+ public virtual bool DestroyFragmentOnScan => false;
+
+ ///
+ /// Override to add a into the PDA's Encyclopedia for this object.
+ /// WARNING. You can overwrite an existing entry with this. Use with Caution!
+ /// Default is .
+ ///
+ public virtual PDAEncyclopedia.EntryData EncyclopediaEntryData => null;
+
+ ///
+ /// Override with the main group in the PDA blueprints where this item appears.
+ ///
+ public virtual TechGroup GroupForPDA => TechGroup.Uncategorized;
+
+ ///
+ /// Override with the category within the group in the PDA blueprints where this item appears.
+ ///
+ public virtual TechCategory CategoryForPDA => TechCategory.Misc;
+
+ ///
+ /// Override this property to assign whether or not the should be unlocked at the start, defaulted to ==
+ ///
+ public virtual bool UnlockedAtStart => RequiredForUnlock == TechType.None;
+
+ ///
+ /// Message which should be shown when the item is unlocked.
+ /// If not overridden, the message will default to Subnautica's (language key "").
+ ///
+ public virtual string DiscoverMessage => null;
+
+ internal string DiscoverMessageResolved => DiscoverMessage == null ? "NotificationBlueprintUnlocked" : $"{TechType.AsString()}_DiscoverMessage";
+
+ ///
+ /// Initializes a new , the basic class for any item that appears among your PDA blueprints.
+ ///
+ /// The main internal identifier for this item. Your item's will be created using this name.
+ /// The name displayed in-game for this item whether in the open world or in the inventory.
+ /// The description for this item; Typically seen in the PDA, inventory, or crafting screens.
+ protected PdaItem(string classId, string friendlyName, string description)
+ : base(classId, friendlyName, description)
+ {
+ CorePatchEvents += PatchTechDataEntry;
+ }
+#if SUBNAUTICA
+ ///
+ /// This provides the instance used to designate how this item is crafted or constructed.
+ ///
+ protected abstract TechData GetBlueprintRecipe();
+#elif BELOWZERO
+ ///
+ /// This provides the instance used to designate how this item is crafted or constructed.
+ ///
+ protected abstract RecipeData GetBlueprintRecipe();
+#endif
+ private void PatchTechDataEntry()
+ {
+ CraftDataHandler.SetTechData(TechType, GetBlueprintRecipe());
+
+
+ if(GroupForPDA != TechGroup.Uncategorized)
+ {
+ List categories = new List();
+ CraftData.GetBuilderCategories(GroupForPDA, categories);
+ if(categories.Contains(CategoryForPDA))
+ {
+ CraftDataHandler.AddToGroup(GroupForPDA, CategoryForPDA, TechType);
+ }
+ else
+ {
+ Logger.Error($"Failed to add {TechType} to {GroupForPDA}/{CategoryForPDA} as it is not a registered combination.");
+ }
+ }
+
+ if(EncyclopediaEntryData != null)
+ {
+ PDAEncyclopediaHandler.AddCustomEntry(EncyclopediaEntryData);
+ }
+
+ if(!UnlockedAtStart)
+ {
+ KnownTechHandler.SetAnalysisTechEntry(RequiredForUnlock, new TechType[1] { TechType }, DiscoverMessageResolved);
+
+ if(AddScannerEntry)
+ {
+ PDAScanner.EntryData entryData = new PDAScanner.EntryData()
+ {
+ key = RequiredForUnlock,
+ blueprint = TechType,
+ destroyAfterScan = DestroyFragmentOnScan,
+ isFragment = true,
+ locked = true,
+ scanTime = TimeToScanFragment,
+ totalFragments = FragmentsToScan
+ };
+
+ if(EncyclopediaEntryData != null)
+ {
+ entryData.encyclopedia = EncyclopediaEntryData.key;
+ }
+ PDAHandler.AddCustomScannerEntry(entryData);
+ }
+ }
+ }
+
+ internal sealed override void PatchTechType()
+ {
+ TechType = TechTypeHandler.AddTechType(Mod, ClassID, FriendlyName, Description, UnlockedAtStart);
+ }
+ }
+}
diff --git a/SMLHelper/Assets/Spawnable.cs b/SMLHelper/Assets/Spawnable.cs
new file mode 100644
index 00000000..1ccc0b24
--- /dev/null
+++ b/SMLHelper/Assets/Spawnable.cs
@@ -0,0 +1,240 @@
+namespace SMLHelper.V2.Assets
+{
+ using Handlers;
+ using Interfaces;
+ using Utility;
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Reflection;
+ using UnityEngine;
+ using UWE;
+ using Logger = Logger;
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+#endif
+
+
+ ///
+ /// An item that can be spawned into the game.
+ ///
+ ///
+ public abstract class Spawnable: ModPrefab
+ {
+ internal IPrefabHandler PrefabHandler { get; set; } = Handlers.PrefabHandler.Main;
+ internal ISpriteHandler SpriteHandler { get; set; } = Handlers.SpriteHandler.Main;
+ internal ICraftDataHandler CraftDataHandler { get; set; } = Handlers.CraftDataHandler.Main;
+ internal ITechTypeHandlerInternal TechTypeHandler { get; set; } = Handlers.TechTypeHandler.Singleton;
+ internal IWorldEntityDatabaseHandler WorldEntityDatabaseHandler { get; set; } = Handlers.WorldEntityDatabaseHandler.Main;
+ internal ILootDistributionHandler LootDistributionHandler { get; set; } = Handlers.LootDistributionHandler.Main;
+
+ private static readonly Vector2int defaultSize = new Vector2int(1, 1);
+
+ ///
+ /// A simple delegate type that takes no parameters and returns void.
+ ///
+ public delegate void PatchEvent();
+
+ ///
+ /// Override with the folder where your mod's icons and other assets are stored.
+ /// By default, this will point to the same folder where your mod DLL is.
+ ///
+ /// "MyModAssembly/Assets"
+ public virtual string AssetsFolder => modFolderLocation;
+
+ ///
+ /// Override with the file name for this item's icon.
+ /// If not overriden, this defaults to "[this item's ClassID].png".
+ ///
+ /// "MyClassID.png"
+ public virtual string IconFileName => $"{ClassID}.png";
+
+ ///
+ /// The in-game name of this spawnable item.
+ ///
+ public string FriendlyName { get; protected set; }
+
+ ///
+ /// The description text when viewing this spawnable item from the inventory or crafting UI.
+ ///
+ public string Description { get; protected set; }
+
+ ///
+ /// Returns true if this spawnable item has already been patched; Otherwise false.
+ /// This will become true after the method has finished running.
+ ///
+ public bool IsPatched { get; private set; } = false;
+
+ ///
+ /// Returns the size that this entity will occupy inside the player inventory.
+ /// By default this will be 1x1. Override to change the size.
+ ///
+ public virtual Vector2int SizeInInventory { get; } = defaultSize;
+
+ ///
+ /// A lightweight class used to specify the position of a Coordinated Spawn and optionally set its rotation.
+ ///
+ ///
+ ///
+ public record SpawnLocation(Vector3 position, Vector3 eulerAngles = default);
+
+ ///
+ /// Returns the list of s that specify the prefab's Coordinated Spawns.
+ /// By default this will be null.
+ ///
+ public virtual List CoordinatedSpawns { get; } = null;
+
+ ///
+ /// Returns the List of BiomeData that handles what Biomes this prefab will spawn, how probable it is to spawn there and how many per spawn.
+ /// By default this will be null. Override to change this.
+ ///
+ public virtual List BiomesToSpawnIn { get; } = null;
+
+ ///
+ /// Returns the of this object if it has one.
+ /// By default this will be null. Override to change this.
+ ///
+ public virtual WorldEntityInfo EntityInfo { get; } = null;
+
+ ///
+ /// Gets a value indicating whether if we should be looking for a Sprite when NOT overriding .
+ ///
+ public virtual bool HasSprite => false;
+
+ ///
+ /// Initializes a new , the basic class needed for any item that can be spawned into the Subnautica game world.
+ ///
+ /// The main internal identifier for this item. Your item's will be created using this name.
+ /// The name displayed in-game for this item whether in the open world or in the inventory.
+ /// The description for this item; Typically seen in the PDA, inventory, or crafting screens.
+ protected Spawnable(string classId, string friendlyName, string description)
+ : base(classId, $"{classId}Prefab")
+ {
+ if(string.IsNullOrEmpty(classId))
+ {
+ Logger.Log($"ClassID for Spawnables must be a non-empty value.", LogLevel.Error);
+ throw new ArgumentException($"Error patching Spawnable");
+ }
+
+ FriendlyName = friendlyName;
+ Description = description;
+
+ CorePatchEvents += () =>
+ {
+ PrefabHandler.RegisterPrefab(this);
+
+#if SUBNAUTICA
+ SpriteHandler.RegisterSprite(TechType, GetItemSprite());
+#elif BELOWZERO
+ CoroutineHost.StartCoroutine(RegisterSpriteAsync());
+#endif
+ if (!SizeInInventory.Equals(defaultSize))
+ {
+ CraftDataHandler.SetItemSize(TechType, SizeInInventory);
+ }
+
+ if(EntityInfo != null)
+ {
+ if(BiomesToSpawnIn != null)
+ LootDistributionHandler.AddLootDistributionData(this, BiomesToSpawnIn, EntityInfo);
+ else
+ WorldEntityDatabaseHandler.AddCustomInfo(ClassID, EntityInfo);
+ }
+
+ if (CoordinatedSpawns != null)
+ {
+ foreach (var (position, eulerAngles) in CoordinatedSpawns)
+ {
+ CoordinatedSpawnsHandler.RegisterCoordinatedSpawn(new SpawnInfo(TechType, position, eulerAngles));
+ }
+ }
+ };
+ }
+
+#if BELOWZERO
+ private IEnumerator RegisterSpriteAsync()
+ {
+ while (!SpriteManager.hasInitialized)
+ yield return new WaitForSecondsRealtime(1);
+
+ SpriteHandler.RegisterSprite(TechType, GetItemSprite());
+ yield break;
+ }
+#endif
+
+ ///
+ /// This event triggers before the core patching methods begins.
+ /// You can attach simple methods to this event if you want to run code before the any of the core patching methods begin.
+ ///
+ protected PatchEvent OnStartedPatching;
+
+ ///
+ /// The main patching methods are executed here.
+ /// This event should only be used by the SMLHelper QuickStart classes.
+ ///
+ internal PatchEvent CorePatchEvents;
+
+ ///
+ /// This event triggers after the core patching methods begins.
+ /// You can attach simple methods to this event if you want to run code after the core patching methods have finished.
+ ///
+ protected PatchEvent OnFinishedPatching;
+
+ private readonly string modFolderLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+
+ ///
+ /// Starts all patching code in SMLHelper.
+ /// If is true then this method is skipped to avoid duplicate patching.
+ ///
+ ///
+ ///
+ public void Patch()
+ {
+ if(IsPatched)
+ {
+ return; // Already patched. Skip.
+ }
+
+ OnStartedPatching?.Invoke();
+
+ // Because invocation order isn't guaranteed by event handlers,
+ // we make sure the TechType is patched first before anything else that might require it.
+ PatchTechType();
+
+ CorePatchEvents.Invoke();
+
+ IsPatched = true;
+
+ OnFinishedPatching?.Invoke();
+ }
+
+ internal virtual void PatchTechType()
+ {
+ TechType = TechTypeHandler.AddTechType(Mod, ClassID, FriendlyName, Description, false);
+ }
+
+ ///
+ /// Determines thee to be used for this spawnable's icon.
+ /// Default behavior will look for a PNG file named inside .
+ ///
+ /// Returns the that will be used in the call.
+ protected virtual Sprite GetItemSprite()
+ {
+ // This is for backwards compatibility with mods that were using the "ModName/Assets" format
+ string path = this.AssetsFolder != modFolderLocation
+ ? IOUtilities.Combine(".", "QMods", this.AssetsFolder.Trim('/'), this.IconFileName)
+ : Path.Combine(this.AssetsFolder, this.IconFileName);
+
+ if(File.Exists(path))
+ {
+ return ImageUtils.LoadSpriteFromFile(path);
+ }
+
+ if(HasSprite)
+ Logger.Error($"Sprite for '{this.PrefabFileName}'{Environment.NewLine}Did not find an image file at '{path}'");
+
+ return SpriteManager.defaultSprite;
+ }
+ }
+}
diff --git a/SMLHelper/Assets/SubnauticaModSprite.cs b/SMLHelper/Assets/SubnauticaModSprite.cs
new file mode 100644
index 00000000..9287ed27
--- /dev/null
+++ b/SMLHelper/Assets/SubnauticaModSprite.cs
@@ -0,0 +1,120 @@
+#if SUBNAUTICA
+namespace SMLHelper.V2.Assets
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// A class that handles a custom sprite and what item it is associated to.
+ ///
+ internal class ModSprite
+ {
+ internal static void Add(SpriteManager.Group group, string name, Atlas.Sprite sprite)
+ {
+ if (group == SpriteManager.Group.None)
+ group = SpriteManager.Group.Item;
+ // There are no calls for sprites in the None Group.
+ // All sprite calls for almost anything we don't manually group is in the Item group.
+
+ if (!ModSprites.ContainsKey(group))
+ ModSprites.Add(group, new Dictionary(StringComparer.InvariantCultureIgnoreCase));
+
+ if(ModSprites[group].ContainsKey(name))
+ Logger.Debug($"ModSprite already registered for {group}/{name}. Old sprite will be overwritten.");
+ ModSprites[group][name] = sprite;
+ }
+
+ internal static void Add(ModSprite sprite) => Add(sprite.Group, sprite.Id, sprite.Sprite);
+
+ internal static Dictionary> ModSprites
+ = new Dictionary>();
+
+ ///
+ /// The tech type of a specific item associated with this sprite.
+ /// Can be if this sprite is for used on a group.
+ ///
+ public TechType TechType;
+
+ ///
+ /// The actual sprite used in-game when this sprite is references.
+ ///
+ public Atlas.Sprite Sprite;
+
+ ///
+ /// The group that this sprite belongs to.
+ /// Can be if this sprite is for used on an item.
+ ///
+ public SpriteManager.Group Group;
+
+ ///
+ /// The internal identifier of this sprite when it isn't associated to an item.
+ ///
+ public string Id;
+
+ ///
+ /// Creates a new ModSprite to be used with a specific TechType.
+ /// Created with an Atlas Sprite.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public ModSprite(TechType type, Atlas.Sprite sprite)
+ {
+ TechType = type;
+ Id = type.AsString();
+ Sprite = sprite;
+ Group = SpriteManager.Group.Item;
+ }
+
+ ///
+ /// Creates a new ModSprite to be used with a specific group and internal ID.
+ /// Created with an Atlas Sprite.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ public ModSprite(SpriteManager.Group group, string id, Atlas.Sprite sprite)
+ {
+ Group = group;
+ Id = id;
+ Sprite = sprite;
+ TechType = TechType.None;
+ }
+
+ ///
+ /// Creates a new ModSprite to be used with a specific group and internal ID.
+ /// Created with an Atlas Sprite.
+ ///
+ /// The sprite group.
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public ModSprite(SpriteManager.Group group, TechType type, Atlas.Sprite sprite)
+ {
+ Group = group;
+ Id = type.AsString();
+ Sprite = sprite;
+ TechType = type;
+ }
+
+ ///
+ /// Creates a new ModSprite to be used with a specific group and internal ID.
+ /// Created with a UnityEngine Sprite.
+ ///
+ /// The sprite group this sprite will be added to.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ public ModSprite(SpriteManager.Group group, string id, UnityEngine.Sprite sprite) : this(group, id, new Atlas.Sprite(sprite, false))
+ {
+ }
+
+ ///
+ /// Creates a new ModSprite to be used with a specific TechType.
+ /// Created with a UnityEngine Sprite.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public ModSprite(TechType type, UnityEngine.Sprite sprite) : this(type, new Atlas.Sprite(sprite, false))
+ {
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/SMLHelper/Commands/ConsoleCommand.cs b/SMLHelper/Commands/ConsoleCommand.cs
new file mode 100644
index 00000000..7125c873
--- /dev/null
+++ b/SMLHelper/Commands/ConsoleCommand.cs
@@ -0,0 +1,145 @@
+namespace SMLHelper.V2.Commands
+{
+ using HarmonyLib;
+ using QModManager.API;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+
+ ///
+ /// Represents a console command.
+ ///
+ internal class ConsoleCommand
+ {
+ ///
+ /// The string that triggers the command.
+ ///
+ public string Trigger { get; }
+
+ ///
+ /// The QMod that registered the command.
+ ///
+ public IQMod QMod { get; }
+
+ ///
+ /// The parameters for the command.
+ ///
+ public IEnumerable Parameters { get; }
+
+ ///
+ /// The types of the parameters.
+ ///
+ public Type[] ParameterTypes { get; }
+
+ private Type DeclaringType { get; }
+ private string MethodName { get; }
+ private bool IsMethodStatic { get; }
+ private bool IsDelegate { get; }
+ private object Instance { get; }
+
+ ///
+ /// Creates an instance of .
+ ///
+ /// The string that triggers the command.
+ /// The method targeted by the command.
+ /// Whether or not the method is a delegate.
+ /// The instance the method belongs to.
+ public ConsoleCommand(string trigger, MethodInfo targetMethod, bool isDelegate = false, object instance = null)
+ {
+ Trigger = trigger.ToLowerInvariant();
+ DeclaringType = targetMethod.DeclaringType;
+ MethodName = targetMethod.Name;
+ IsMethodStatic = targetMethod.IsStatic;
+ IsDelegate = isDelegate;
+ Instance = instance;
+ QMod = QModServices.Main.GetMod(DeclaringType.Assembly);
+ Parameters = targetMethod.GetParameters().Select(param => new Parameter(param));
+ ParameterTypes = Parameters.Select(param => param.ParameterType).ToArray();
+ }
+
+ ///
+ /// Determines whether the targeted method is valid in terms of whether it is static or delegate.
+ ///
+ ///
+ public bool HasValidInvoke() => IsDelegate || Instance != null || IsMethodStatic;
+
+ ///
+ /// Determines whether the target methods parameters are valid.
+ ///
+ ///
+ public bool HasValidParameterTypes()
+ {
+ foreach (Parameter parameter in Parameters)
+ {
+ if (!parameter.IsValidParameterType)
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Returns a list of all invalid parameters.
+ ///
+ ///
+ public IEnumerable GetInvalidParameters()
+ => Parameters.Where(param => !param.IsValidParameterType);
+
+ ///
+ /// Attempts to parse input parameters into appropriate types as defined in the target method.
+ ///
+ /// The parameters as input by the user.
+ /// The parameters that have been successfully parsed.
+ /// Whether or not all parameters were succesfully parsed.
+ public bool TryParseParameters(IEnumerable inputParameters, out object[] parsedParameters)
+ {
+ parsedParameters = null;
+
+ // Detect incorrect number of parameters (allow for optional)
+ if (Parameters.Count() < inputParameters.Count() ||
+ Parameters.Where(param => !param.IsOptional).Count() > inputParameters.Count())
+ {
+ return false;
+ }
+
+ parsedParameters = new object[Parameters.Count()];
+ for (int i = 0; i < Parameters.Count(); i++)
+ {
+ Parameter parameter = Parameters.ElementAt(i);
+
+ if (i >= inputParameters.Count()) // It's an optional parameter that wasn't passed by the user
+ {
+ parsedParameters[i] = Type.Missing;
+ continue;
+ }
+
+ string input = inputParameters.ElementAt(i);
+
+ try
+ {
+ parsedParameters[i] = parameter.Parse(input);
+ }
+ catch (Exception)
+ {
+ return false; // couldn't parse, wasn't a valid conversion
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Invokes the command with the given parameters.
+ ///
+ /// The command parameters.
+ /// The string returned from the command.
+ public string Invoke(object[] parameters)
+ {
+ if (Instance != null)
+ return Traverse.Create(Instance).Method(MethodName, ParameterTypes).GetValue(parameters)?.ToString();
+ else
+ return Traverse.Create(DeclaringType).Method(MethodName, ParameterTypes).GetValue(parameters)?.ToString();
+ }
+ }
+}
diff --git a/SMLHelper/Commands/ConsoleCommandAttribute.cs b/SMLHelper/Commands/ConsoleCommandAttribute.cs
new file mode 100644
index 00000000..a61847e5
--- /dev/null
+++ b/SMLHelper/Commands/ConsoleCommandAttribute.cs
@@ -0,0 +1,55 @@
+namespace SMLHelper.V2.Commands
+{
+ using Interfaces;
+ using Handlers;
+ using System;
+
+ ///
+ /// Attribute used to signify the decorated method should be called in response to a console command.
+ ///
+ /// Decorated method must be both and .
+ ///
+ ///
+ /// The command can take parameters and will respect optional parameters as outlined in the method's signature.
+ /// Supported parameter types: , , , ,
+ /// .
+ ///
+ /// If the method has a return type, it will be printed to both the screen and the log.
+ ///
+ ///
+ ///
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+ public sealed class ConsoleCommandAttribute : Attribute
+ {
+ ///
+ /// The unique, case-insensitive command that when entered, will call the decorated method.
+ ///
+ public string Command { get; }
+
+ ///
+ /// Signifies the decorated method should be called when the given is entered
+ /// in the dev console.
+ ///
+ /// Decorated method must be both and .
+ ///
+ ///
+ /// The command can take parameters and will respect optional parameters as outlined in the method's signature.
+ /// Supported parameter types: , , , ,
+ /// .
+ ///
+ /// If the method has a return type, it will be printed to both the screen and the log.
+ ///
+ /// The unique, case-insensitive command that when entered into the dev console will call the
+ /// decorated method.
+ ///
+ ///
+ ///
+ ///
+ public ConsoleCommandAttribute(string command)
+ {
+ Command = command;
+ }
+ }
+}
diff --git a/SMLHelper/Commands/Parameter.cs b/SMLHelper/Commands/Parameter.cs
new file mode 100644
index 00000000..afc9eab6
--- /dev/null
+++ b/SMLHelper/Commands/Parameter.cs
@@ -0,0 +1,39 @@
+
+namespace SMLHelper.V2.Commands
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+
+ internal struct Parameter
+ {
+ private static Dictionary> TypeConverters = new Dictionary>()
+ {
+ [typeof(string)] = (s) => s,
+ [typeof(bool)] = (s) => bool.Parse(s),
+ [typeof(int)] = (s) => int.Parse(s, CultureInfo.InvariantCulture.NumberFormat),
+ [typeof(float)] = (s) => float.Parse(s, CultureInfo.InvariantCulture.NumberFormat),
+ [typeof(double)] = (s) => double.Parse(s, CultureInfo.InvariantCulture.NumberFormat)
+ };
+
+ public static IEnumerable SupportedTypes => TypeConverters.Keys;
+
+ public Type ParameterType { get; }
+ public bool IsOptional { get; }
+ public string Name { get; }
+ public bool IsValidParameterType { get; }
+
+ public Parameter(ParameterInfo parameter)
+ {
+ ParameterType = parameter.ParameterType;
+ IsOptional = parameter.IsOptional;
+ Name = parameter.Name;
+ IsValidParameterType = SupportedTypes.Contains(ParameterType);
+ }
+
+ public object Parse(string input)
+ => TypeConverters[ParameterType](input);
+ }
+}
diff --git a/SMLHelper/Crafting/CraftingNode.cs b/SMLHelper/Crafting/CraftingNode.cs
new file mode 100644
index 00000000..add0c73c
--- /dev/null
+++ b/SMLHelper/Crafting/CraftingNode.cs
@@ -0,0 +1,12 @@
+namespace SMLHelper.V2.Crafting
+{
+ internal class CraftingNode : Node
+ {
+ internal TechType TechType { get; set; }
+
+ internal CraftingNode(string[] path, CraftTree.Type scheme, TechType techType) : base(path, scheme)
+ {
+ TechType = techType;
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/ModCraftTreeCraft.cs b/SMLHelper/Crafting/ModCraftTreeCraft.cs
new file mode 100644
index 00000000..8e33c728
--- /dev/null
+++ b/SMLHelper/Crafting/ModCraftTreeCraft.cs
@@ -0,0 +1,14 @@
+namespace SMLHelper.V2.Crafting
+{
+ ///
+ /// A crafting node of a CraftTree. This is the last node on a tree; The one that actually crafts something.
+ ///
+ ///
+ public class ModCraftTreeCraft : ModCraftTreeNode
+ {
+ internal ModCraftTreeCraft(TechType techType)
+ : base(techType.AsString(), TreeAction.Craft, techType)
+ {
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/ModCraftTreeLinkingNode.cs b/SMLHelper/Crafting/ModCraftTreeLinkingNode.cs
new file mode 100644
index 00000000..f4c55e9e
--- /dev/null
+++ b/SMLHelper/Crafting/ModCraftTreeLinkingNode.cs
@@ -0,0 +1,203 @@
+namespace SMLHelper.V2.Crafting
+{
+ using System.Collections.Generic;
+ using Patchers;
+ using SMLHelper.V2.Patchers.EnumPatching;
+ using UnityEngine;
+ using UnityEngine.Assertions;
+ using Utility;
+
+ ///
+ /// Abstract class that provides methods for adding new nodes into the crafting tree.
+ ///
+ ///
+ public abstract class ModCraftTreeLinkingNode : ModCraftTreeNode
+ {
+ ///
+ /// The child nodes linked bellow this node.
+ ///
+ public readonly List ChildNodes = new List();
+
+ internal ModCraftTreeLinkingNode(string name, TreeAction action, TechType techType)
+ : base(name, action, techType)
+ {
+ }
+#if SUBNAUTICA
+ ///
+ /// Creates a new tab node for the crafting tree and links it to the calling node.
+ ///
+ /// The name/ID of this node.
+ /// The hover text to display in-game.
+ /// The custom sprite to display on this tab node.
+ /// A new tab node linked to the root node and ready to use.
+ public ModCraftTreeTab AddTabNode(string nameID, string displayText, Atlas.Sprite sprite)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ var tabNode = new ModCraftTreeTab(modName, nameID, displayText, sprite);
+ tabNode.LinkToParent(this);
+
+ ChildNodes.Add(tabNode);
+
+ return tabNode;
+ }
+
+#endif
+ ///
+ /// Creates a new tab node for the crafting tree and links it to the calling node.
+ ///
+ /// The name/ID of this node.
+ /// The hover text to display in-game.
+ /// The custom sprite to display on this tab node.
+ /// A new tab node linked to the root node and ready to use.
+ public ModCraftTreeTab AddTabNode(string nameID, string displayText, Sprite sprite)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ var tabNode = new ModCraftTreeTab(modName, nameID, displayText, sprite);
+ tabNode.LinkToParent(this);
+
+ ChildNodes.Add(tabNode);
+
+ return tabNode;
+ }
+
+ ///
+ /// Creates a new tab node for the crafting tree and links it to the calling node.
+ ///
+ /// The name/ID of this node.
+ /// A new tab node linked to the root node and ready to use.
+ public ModCraftTreeTab AddTabNode(string nameID)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ var tabNode = new ModCraftTreeTab(modName, nameID);
+ tabNode.LinkToParent(this);
+
+ ChildNodes.Add(tabNode);
+
+ return tabNode;
+ }
+
+ ///
+ /// Gets the tab from the calling node.
+ ///
+ /// The name id of the tab to get.
+ ///
+ public ModCraftTreeTab GetTabNode(string nameID)
+ {
+ foreach (ModCraftTreeTab node in ChildNodes)
+ {
+ if (node == null)
+ continue;
+
+ if (node.Name == nameID && node.Action == TreeAction.Expand)
+ {
+ var tab = node;
+ return tab;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the crafting node from the calling node.
+ ///
+ /// The TechType whose node to get.
+ ///
+ public ModCraftTreeCraft GetCraftingNode(TechType techType)
+ {
+ foreach (ModCraftTreeNode node in ChildNodes)
+ {
+ if (node == null)
+ continue;
+
+ if (node.TechType == techType && node.Action == TreeAction.Craft)
+ {
+ var craftNode = (ModCraftTreeCraft)node;
+ return craftNode;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the node associated with the ID specified. Used if you don't know whether node is a tab or a craft node.
+ ///
+ ///
+ ///
+ public ModCraftTreeNode GetNode(string nameID)
+ {
+ foreach (ModCraftTreeNode node in ChildNodes)
+ {
+ if (node == null)
+ continue;
+
+ if (node.Name == nameID)
+ return node;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Creates a new crafting node for the crafting tree and links it to the calling node.
+ ///
+ /// The TechType to be crafted.
+ public void AddCraftingNode(TechType techType)
+ {
+ Assert.AreNotEqual(TechType.None, techType, "Attempt to add TechType.None as a crafting node.");
+
+ var craftNode = new ModCraftTreeCraft(techType);
+ craftNode.LinkToParent(this);
+
+ ChildNodes.Add(craftNode);
+ }
+
+ ///
+ /// Creates a collection of new crafting nodes for the crafting tree and links it to the calling node.
+ ///
+ /// The TechTypes to be crafted.
+ public void AddCraftingNode(params TechType[] techTypes)
+ {
+ AddCraftingNode((IEnumerable)techTypes);
+ }
+
+ ///
+ /// Creates a collection of new crafting nodes for the crafting tree and links it to the calling node.
+ ///
+ /// The TechTypes to be crafted.
+ public void AddCraftingNode(IEnumerable techTypes)
+ {
+ foreach (TechType tType in techTypes)
+ {
+ Assert.AreNotEqual(TechType.None, tType, "Attempt to add TechType.None as a crafting node.");
+ AddCraftingNode(tType);
+ }
+ }
+
+ ///
+ /// Creates a new crafting node for a modded item and links it to the calling node.
+ /// If the modded item isn't present for the player, this call is safely ignored.
+ ///
+ /// The internal name of the custom TechType to be crafted.
+ ///
+ /// If the player doesn't have the mod for this TechType installed, then nothing will happen.
+ ///
+ public void AddModdedCraftingNode(string moddedTechTypeName)
+ {
+ EnumTypeCache cache = TechTypePatcher.cacheManager.RequestCacheForTypeName(moddedTechTypeName);
+
+ if (cache != null)
+ {
+ var techType = (TechType)cache.Index;
+ var craftNode = new ModCraftTreeCraft(techType);
+ craftNode.LinkToParent(this);
+
+ ChildNodes.Add(craftNode);
+ }
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/ModCraftTreeNode.cs b/SMLHelper/Crafting/ModCraftTreeNode.cs
new file mode 100644
index 00000000..f616fa14
--- /dev/null
+++ b/SMLHelper/Crafting/ModCraftTreeNode.cs
@@ -0,0 +1,78 @@
+namespace SMLHelper.V2.Crafting
+{
+ using UnityEngine.Assertions;
+
+ ///
+ /// Basic data structure of a crafting tree node.
+ ///
+ public abstract class ModCraftTreeNode
+ {
+ internal static bool Initialized = false;
+ internal static bool HasCustomTrees { get; set; } = false;
+
+ ///
+ /// The action this node takes in the crafting tree.
+ ///
+ public readonly TreeAction Action;
+
+ ///
+ /// The tech type ID associated to this node.
+ /// For item nodes, it is the item ID to be crafted.
+ /// For root and tab nodes, this is always .
+ ///
+ public readonly TechType TechType;
+
+ ///
+ /// The name ID for this tab node.
+ ///
+ public readonly string Name;
+
+ ///
+ /// The the in-game representation of this node.
+ ///
+ public CraftNode CraftNode;
+
+ internal ModCraftTreeLinkingNode Parent = null;
+
+ internal virtual CraftTree.Type Scheme => this.Parent.Scheme;
+ internal virtual string SchemeAsString => this.Parent.SchemeAsString;
+
+ internal ModCraftTreeNode(string name, TreeAction action, TechType techType)
+ {
+ Name = name;
+ Action = action;
+ TechType = techType;
+
+ // This is the actual class used for the real CraftTree
+ CraftNode = new CraftNode(Name, Action, TechType);
+ }
+
+ ///
+ /// Removes the calling node from parent.
+ ///
+ public void RemoveNode()
+ {
+ Assert.IsNotNull(this.Parent, "No parent found to remove node from!");
+ Assert.IsNotNull(this.Parent.CraftNode, "No CraftNode found on parent!");
+
+ if (this is ModCraftTreeLinkingNode)
+ {
+ ModCraftTreeLinkingNode linkingNode = this as ModCraftTreeLinkingNode;
+ foreach (ModCraftTreeNode cNode in linkingNode.ChildNodes)
+ {
+ cNode.RemoveNode();
+ }
+ }
+
+ this.Parent.ChildNodes.Remove(this);
+ this.Parent.CraftNode.RemoveNode(this.CraftNode);
+ this.Parent = null;
+ }
+
+ internal virtual void LinkToParent(ModCraftTreeLinkingNode parent)
+ {
+ parent.CraftNode.AddNode(this.CraftNode);
+ this.Parent = parent;
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/ModCraftTreeRoot.cs b/SMLHelper/Crafting/ModCraftTreeRoot.cs
new file mode 100644
index 00000000..b00c70c4
--- /dev/null
+++ b/SMLHelper/Crafting/ModCraftTreeRoot.cs
@@ -0,0 +1,122 @@
+namespace SMLHelper.V2.Crafting
+{
+ using System;
+ using Patchers;
+ using SMLHelper.V2.Handlers;
+ using SMLHelper.V2.Patchers.EnumPatching;
+ using UnityEngine.Assertions;
+
+ ///
+ /// The root node of a CraftTree. The whole tree starts here.
+ /// Build up your custom crafting tree from this root node using the AddCraftingNode and AddTabNode methods.
+ /// This tree will be automatically patched into the game. No further calls into required.
+ /// For more advanced usage, you can replace the default value of with your own custom function.
+ ///
+ ///
+ public class ModCraftTreeRoot : ModCraftTreeLinkingNode
+ {
+ private readonly string _schemeAsString;
+ private readonly CraftTree.Type _scheme;
+
+ internal override string SchemeAsString => _schemeAsString;
+ internal override CraftTree.Type Scheme => _scheme;
+
+ internal ModCraftTreeRoot(CraftTree.Type scheme, string schemeAsString)
+ : base("Root", TreeAction.None, TechType.None)
+ {
+ Assert.IsTrue((int)scheme > CraftTreeTypePatcher.startingIndex, "Custom CraftTree types must have an index higher than the in-game types.");
+
+ _schemeAsString = schemeAsString;
+ _scheme = scheme;
+ HasCustomTrees = true;
+
+ CraftTreeCreation = () => new CraftTree(_schemeAsString, CraftNode);
+ }
+
+ ///
+ /// Dynamically creates the CraftTree object for this crafting tree.
+ /// The CraftNode objects were created and linked as the classes of the ModCraftTreeFamily were created and linked.
+ ///
+ internal CraftTree CustomCraftingTree => CraftTreeCreation.Invoke();
+
+ ///
+ /// Populates a new ModCraftTreeRoot from a CraftNode tree.
+ ///
+ /// The tree to create the ModCraftTreeRoot from.
+ ///
+ internal static void CreateFromExistingTree(CraftNode tree, ref ModCraftTreeLinkingNode root)
+ {
+ foreach (CraftNode node in tree)
+ {
+ if (node.action == TreeAction.Expand)
+ {
+ ModCraftTreeTab tab = root.AddTabNode(node.id);
+ var thing = (ModCraftTreeLinkingNode)tab;
+ CreateFromExistingTree(node, ref thing);
+ }
+
+ if (node.action == TreeAction.Craft)
+ {
+ TechTypeExtensions.FromString(node.id, out TechType techType, false);
+
+ root.AddCraftingNode(techType);
+ }
+ }
+ }
+
+ ///
+ /// The craft tree creation function.
+ /// Default implementaion returns a new instantiated with and the root .
+ /// You can replace this function with your own to have more control of the crafting tree when it is being created.
+ ///
+ public Func CraftTreeCreation;
+
+ ///
+ /// Gets the tab node at the specified path from the root.
+ ///
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ /// If the specified tab node is found, returns that ; Otherwise, returns null.
+ public ModCraftTreeTab GetTabNode(params string[] stepsToTab)
+ {
+ ModCraftTreeTab tab = base.GetTabNode(stepsToTab[0]);
+
+ for (int i = 1; i < stepsToTab.Length && tab != null; i++)
+ {
+ tab = tab.GetTabNode(stepsToTab[i]);
+ }
+
+ return tab;
+ }
+
+ ///
+ /// Gets the node at the specified path from the root.
+ ///
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ /// If the specified tab node is found, returns that ; Otherwise, returns null.
+ public ModCraftTreeNode GetNode(params string[] stepsToNode)
+ {
+ if (stepsToNode.Length == 1)
+ {
+ return base.GetNode(stepsToNode[0]);
+ }
+
+ int stepCountToTab = stepsToNode.Length - 1;
+
+ string nodeID = stepsToNode[stepCountToTab];
+ string[] stepsToTab = new string[stepCountToTab];
+ Array.Copy(stepsToNode, stepsToTab, stepCountToTab);
+
+ ModCraftTreeTab tab = GetTabNode(stepsToTab);
+
+ return tab?.GetNode(nodeID);
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/ModCraftTreeTab.cs b/SMLHelper/Crafting/ModCraftTreeTab.cs
new file mode 100644
index 00000000..366b9d2b
--- /dev/null
+++ b/SMLHelper/Crafting/ModCraftTreeTab.cs
@@ -0,0 +1,82 @@
+namespace SMLHelper.V2.Crafting
+{
+ using Assets;
+ using Patchers;
+ using UnityEngine;
+
+ ///
+ /// A tab node of a CraftTree. Tab nodes help organize crafting nodes by grouping them into categories.
+ ///
+ ///
+ public class ModCraftTreeTab : ModCraftTreeLinkingNode
+ {
+ private readonly string DisplayText;
+#if SUBNAUTICA
+ private readonly Atlas.Sprite Asprite;
+#endif
+ private readonly Sprite Usprite;
+ private readonly bool IsExistingTab;
+ private readonly string ModName;
+
+#if SUBNAUTICA
+ internal ModCraftTreeTab(string modName, string nameID, string displayText, Atlas.Sprite sprite)
+ : base(nameID, TreeAction.Expand, TechType.None)
+ {
+ DisplayText = displayText;
+ Asprite = sprite;
+ Usprite = null;
+ ModName = modName;
+ }
+#endif
+
+ internal ModCraftTreeTab(string modName, string nameID, string displayText, Sprite sprite)
+ : base(nameID, TreeAction.Expand, TechType.None)
+ {
+ DisplayText = displayText;
+#if SUBNAUTICA
+ Asprite = null;
+#endif
+ Usprite = sprite;
+ ModName = modName;
+ }
+
+ internal ModCraftTreeTab(string modName, string nameID)
+ : base(nameID, TreeAction.Expand, TechType.None)
+ {
+ IsExistingTab = true;
+ ModName = modName;
+ }
+
+ internal override void LinkToParent(ModCraftTreeLinkingNode parent)
+ {
+ base.LinkToParent(parent);
+
+ if (IsExistingTab) return;
+
+ LanguagePatcher.AddCustomLanguageLine(ModName, $"{base.SchemeAsString}Menu_{Name}", DisplayText);
+
+ string spriteID = $"{SchemeAsString}_{Name}";
+
+#if SUBNAUTICA
+ ModSprite modSprite;
+ if (Asprite != null)
+ {
+ modSprite = new ModSprite(SpriteManager.Group.Category, spriteID, Asprite);
+ }
+ else
+ {
+ modSprite = new ModSprite(SpriteManager.Group.Category, spriteID, Usprite);
+
+ }
+
+ ModSprite.Add(modSprite);
+#elif BELOWZERO
+
+ ModSprite modSprite;
+ modSprite = new ModSprite(SpriteManager.Group.Category, spriteID, Usprite);
+ ModSprite.Add(modSprite);
+
+#endif
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/Node.cs b/SMLHelper/Crafting/Node.cs
new file mode 100644
index 00000000..a323e7d9
--- /dev/null
+++ b/SMLHelper/Crafting/Node.cs
@@ -0,0 +1,14 @@
+namespace SMLHelper.V2.Crafting
+{
+ internal class Node
+ {
+ internal string[] Path { get; set; }
+ internal CraftTree.Type Scheme { get; set; }
+
+ internal Node(string[] path, CraftTree.Type scheme)
+ {
+ Path = path;
+ Scheme = scheme;
+ }
+ }
+}
diff --git a/SMLHelper/Crafting/RecipeData.cs b/SMLHelper/Crafting/RecipeData.cs
new file mode 100644
index 00000000..338ca04e
--- /dev/null
+++ b/SMLHelper/Crafting/RecipeData.cs
@@ -0,0 +1,104 @@
+#if BELOWZERO
+#pragma warning disable IDE1006 // Naming Styles - Ignored for backwards compatibility
+namespace SMLHelper.V2.Crafting
+{
+ using System.Collections.Generic;
+
+ ///
+ /// A class that fully describes a recipe for a identified item.
+ ///
+ ///
+ public class RecipeData
+ {
+ ///
+ /// Gets or sets the how many copies of the item are created when crafting this recipe.
+ ///
+ ///
+ /// The quantity of the item this recipe yields.
+ ///
+ public int craftAmount { get; set; }
+
+ ///
+ /// Gets the number of different ingredients for this recipe.
+ ///
+ ///
+ /// The number of ingredients for this recipe.
+ ///
+ public int ingredientCount => Ingredients.Count;
+
+ ///
+ /// Gets the number of items linked to this recipe.
+ ///
+ ///
+ /// The number of linked items.
+ ///
+ public int linkedItemCount => LinkedItems.Count;
+
+ ///
+ /// The list of ingredients required for this recipe.
+ ///
+ public List Ingredients = new List();
+
+ ///
+ /// The items that will also be created when this recipe is crafted.
+ ///
+ public List LinkedItems = new List();
+
+ ///
+ /// Initializes a new instance of the class a custom recipe.
+ ///
+ public RecipeData() { }
+
+ ///
+ /// Initializes a new instance of the class for a custom recipe with a list of ingridients.
+ ///
+ /// The ingredients.
+ public RecipeData(List ingredients)
+ {
+ Ingredients = ingredients;
+ }
+
+ ///
+ /// Initializes a new instance of the class for a custom recipe with a collection of ingridients.
+ ///
+ /// The ingredients.
+ public RecipeData(params Ingredient[] ingredients)
+ {
+ foreach (Ingredient ingredient in ingredients)
+ {
+ Ingredients.Add(ingredient);
+ }
+ }
+
+ ///
+ /// Gets the ingredient at the specified index.
+ ///
+ /// The index.
+ /// The at the requested the index if the index is value; Otherwise returns null.
+ public Ingredient GetIngredient(int index)
+ {
+ if (Ingredients != null && Ingredients.Count > index)
+ {
+ return Ingredients[index];
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the linked item at the specified index.
+ ///
+ /// The index.
+ /// The at the requested the index if the index is value; Otherwise returns null.
+ public TechType GetLinkedItem(int index)
+ {
+ if (LinkedItems != null && LinkedItems.Count > index)
+ {
+ return LinkedItems[index];
+ }
+ return TechType.None;
+ }
+ }
+}
+#pragma warning restore IDE1006 // Naming Styles
+#endif
\ No newline at end of file
diff --git a/SMLHelper/Crafting/TabNode.cs b/SMLHelper/Crafting/TabNode.cs
new file mode 100644
index 00000000..b6dbf268
--- /dev/null
+++ b/SMLHelper/Crafting/TabNode.cs
@@ -0,0 +1,43 @@
+namespace SMLHelper.V2.Crafting
+{
+ using Assets;
+ using Patchers;
+
+ internal class TabNode : Node
+ {
+#if SUBNAUTICA
+
+ internal Atlas.Sprite Sprite { get; set; }
+ internal string DisplayName { get; set; }
+ internal string Name { get; set; }
+
+ internal TabNode(string[] path, CraftTree.Type scheme, Atlas.Sprite sprite, string modName, string name, string displayName) : base(path, scheme)
+ {
+ Sprite = sprite;
+ DisplayName = displayName;
+ Name = name;
+
+ ModSprite.Add(new ModSprite(SpriteManager.Group.Category, $"{Scheme.ToString()}_{Name}", Sprite));
+ LanguagePatcher.AddCustomLanguageLine(modName, $"{Scheme.ToString()}Menu_{Name}", DisplayName);
+ }
+
+#elif BELOWZERO
+
+ internal UnityEngine.Sprite Sprite { get; set; }
+ internal string DisplayName { get; set; }
+ internal string Name { get; set; }
+
+ internal TabNode(string[] path, CraftTree.Type scheme, UnityEngine.Sprite sprite, string modName, string name, string displayName) : base(path, scheme)
+ {
+ Sprite = sprite;
+ DisplayName = displayName;
+ Name = name;
+
+ ModSprite.Add(new ModSprite(SpriteManager.Group.Category, $"{Scheme.ToString()}_{Name}", Sprite));
+ LanguagePatcher.AddCustomLanguageLine(modName, $"{Scheme.ToString()}Menu_{Name}", DisplayName);
+ }
+
+#endif
+
+ }
+}
diff --git a/SMLHelper/Crafting/TechData.cs b/SMLHelper/Crafting/TechData.cs
new file mode 100644
index 00000000..001b314c
--- /dev/null
+++ b/SMLHelper/Crafting/TechData.cs
@@ -0,0 +1,140 @@
+#if SUBNAUTICA
+#pragma warning disable IDE1006 // Naming Styles - Ignored for backwards compatibility
+namespace SMLHelper.V2.Crafting
+{
+ using System.Collections.Generic;
+
+ ///
+ /// A class that fully describes a recipe for a identified item.
+ ///
+ ///
+ public class TechData : ITechData
+ {
+ ///
+ /// Gets or sets the how many copies of the item are created when crafting this recipe.
+ ///
+ ///
+ /// The quantity of the item this recipe yields.
+ ///
+ public int craftAmount { get; set; }
+
+ ///
+ /// Gets the number of different ingredients for this recipe.
+ ///
+ ///
+ /// The number of ingredients for this recipe.
+ ///
+ public int ingredientCount => Ingredients.Count;
+
+ ///
+ /// Gets the number of items linked to this recipe.
+ ///
+ ///
+ /// The number of linked items.
+ ///
+ public int linkedItemCount => LinkedItems.Count;
+
+ ///
+ /// The list of ingredients required for this recipe.
+ ///
+ public List Ingredients = new List();
+
+ ///
+ /// The items that will also be created when this recipe is crafted.
+ ///
+ public List LinkedItems = new List();
+
+ ///
+ /// Initializes a new instance of the class a custom recipe.
+ ///
+ public TechData() { }
+
+ ///
+ /// Initializes a new instance of the class for a custom recipe with a list of ingridients.
+ ///
+ /// The ingredients.
+ public TechData(List ingredients)
+ {
+ Ingredients = ingredients;
+ }
+
+ ///
+ /// Initializes a new instance of the class for a custom recipe with a collection of ingridients.
+ ///
+ /// The ingredients.
+ public TechData(params Ingredient[] ingredients)
+ {
+ foreach (Ingredient ingredient in ingredients)
+ {
+ Ingredients.Add(ingredient);
+ }
+ }
+
+ ///
+ /// Gets the ingredient at the specified index.
+ ///
+ /// The index.
+ /// The at the requested the index if the index is value; Otherwise returns null.
+ public IIngredient GetIngredient(int index)
+ {
+ if (Ingredients != null && Ingredients.Count > index)
+ {
+ return Ingredients[index];
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the linked item at the specified index.
+ ///
+ /// The index.
+ /// The at the requested the index if the index is value; Otherwise returns null.
+ public TechType GetLinkedItem(int index)
+ {
+ if (LinkedItems != null && LinkedItems.Count > index)
+ {
+ return LinkedItems[index];
+ }
+
+ return TechType.None;
+ }
+ }
+
+ ///
+ /// A class for representing a required ingredient in a recipe.
+ ///
+ ///
+ ///
+ public class Ingredient : IIngredient
+ {
+ ///
+ /// Gets or sets the item ID.
+ ///
+ ///
+ /// The item ID.
+ ///
+ public TechType techType { get; set; }
+
+ ///
+ /// Gets or sets the number of this item required for the recipe.
+ ///
+ ///
+ /// The amount of this item needed for the recipe.
+ ///
+ public int amount { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The item ID.
+ /// The number of instances of this item required for the recipe.
+ public Ingredient(TechType techType, int amount)
+ {
+ this.techType = techType;
+ this.amount = amount;
+ }
+ }
+}
+#pragma warning restore IDE1006 // Naming Styles
+#endif
\ No newline at end of file
diff --git a/SMLHelper/Handler.cs b/SMLHelper/Handler.cs
new file mode 100644
index 00000000..b01a6815
--- /dev/null
+++ b/SMLHelper/Handler.cs
@@ -0,0 +1,135 @@
+namespace SMLHelper.V2
+{
+ using Interfaces;
+
+ ///
+ /// A simple location where every SMLHelper handler class can be accessed.
+ ///
+ public static class Handler
+ {
+ ///
+ /// A handler with common methods for updating BioReactor values.
+ ///
+ public static IBioReactorHandler BioReactorHandler => Handlers.BioReactorHandler.Main;
+
+ ///
+ /// A handler class for adding and editing crafted items.
+ ///
+ public static ICraftDataHandler CraftDataHandler => Handlers.CraftDataHandler.Main;
+
+ ///
+ /// A handler class for creating and editing of crafting trees.
+ ///
+ public static ICraftTreeHandler CraftTreeHandler => Handlers.CraftTreeHandler.Main;
+
+ ///
+ /// A handler class that offers simple ways to tap into functionality of the in game menu.
+ ///
+ public static IIngameMenuHandler IngameMenuHandler => Handlers.IngameMenuHandler.Main;
+
+ ///
+ /// Class to manage registering of fish into the game
+ ///
+ public static IFishHandler FishHandler => Handlers.FishHandler.Main;
+
+ ///
+ /// A handler class for registering your custom middle click actions for items
+ ///
+ public static IItemActionHandler ItemActionHandler => Handlers.ItemActionHandler.Main;
+
+ ///
+ /// A handler class for configuring custom unlocking conditions for item blueprints.
+ ///
+ public static IKnownTechHandler KnownTechHandler => Handlers.KnownTechHandler.Main;
+
+ ///
+ /// A handler for adding language lines.
+ ///
+ public static ILanguageHandler LanguageHandler => Handlers.LanguageHandler.Main;
+
+ ///
+ /// A handler class for registering your custom in-game mod options.
+ ///
+ public static IOptionsPanelHandler OptionsPanelHandler => Handlers.OptionsPanelHandler.Main;
+
+ ///
+ /// A handler class for various scanner related data.
+ ///
+ public static IPDAHandler PDAHandler => Handlers.PDAHandler.Main;
+
+ ///
+ /// A handler class for adding custom sprites into the game.
+ ///
+ public static ISpriteHandler SpriteHandler => Handlers.SpriteHandler.Main;
+
+ ///
+ /// A handler class for everything related to creating new TechTypes.
+ ///
+ public static ITechTypeHandler TechTypeHandler => Handlers.TechTypeHandler.Main;
+
+ ///
+ /// A handler class for adding and editing resource spawns.
+ ///
+ public static ILootDistributionHandler LootDistributionHandler => Handlers.LootDistributionHandler.Main;
+
+ ///
+ /// A handler for adding custom entries to the world entity database.
+ ///
+ public static IWorldEntityDatabaseHandler WorldEntityDatabaseHandler => Handlers.WorldEntityDatabaseHandler.Main;
+
+ ///
+ /// A handler for adding custom entries to the PDA Encyclopedia.
+ ///
+ public static IPDAEncyclopediaHandler PDAEncyclopediaHandler => Handlers.PDAEncyclopediaHandler.Main;
+
+ ///
+ /// A handler for registering Unity prefabs associated to a .
+ ///
+ public static IPrefabHandler PrefabHandler => Handlers.PrefabHandler.Main;
+
+ ///
+ /// a handler for common uses to the Survival component
+ ///
+ public static ISurvivalHandler SurvivalHandler => Handlers.SurvivalHandler.Main;
+
+ ///
+ /// A handler to making coordinated Vector3 spawns ingame.
+ ///
+ public static ICoordinatedSpawnHandler CoordinatedSpawnHandler => Handlers.CoordinatedSpawnsHandler.Main;
+
+ ///
+ /// A handler class for adding custom TechGroups into the game.
+ ///
+ public static ITechGroupHandler TechGroupHandler => Handlers.TechGroupHandler.Main;
+
+ ///
+ /// A handler class for adding custom TechCategories into the game.
+ ///
+ public static ITechCategoryHandler TechCategoryHandler => Handlers.TechCategoryHandler.Main;
+
+ ///
+ /// A handler class for registering your custom console commands.
+ ///
+ public static IConsoleCommandHandler ConsoleCommandsHandler => Handlers.ConsoleCommandsHandler.Main;
+
+ ///
+ /// A handler related to PingTypes
+ ///
+ public static IPingHandler PingHandler => Handlers.PingHandler.Main;
+
+ ///
+ /// A handler for everything related to creating new BackgroundTypes.
+ ///
+ public static IBackgroundTypeHandler BackgroundTypeHandler => Handlers.BackgroundTypeHandler.Main;
+
+ ///
+ /// A handler related to Custom Sounds
+ ///
+ public static ICustomSoundHandler SoundHandler => Handlers.CustomSoundHandler.Main;
+
+ ///
+ /// A handler for adding custom EquipmentTypes into the game.
+ ///
+ public static IEquipmentHandler EquipmentHandler => Handlers.EquipmentHandler.Main;
+ }
+}
diff --git a/SMLHelper/Handlers/BackgroundTypeHandler.cs b/SMLHelper/Handlers/BackgroundTypeHandler.cs
new file mode 100644
index 00000000..41771d37
--- /dev/null
+++ b/SMLHelper/Handlers/BackgroundTypeHandler.cs
@@ -0,0 +1,126 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System.Collections.Generic;
+ using Interfaces;
+ using Utility;
+ using Patchers.EnumPatching;
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+#elif BELOWZERO
+ using Sprite = UnityEngine.Sprite;
+#endif
+
+ ///
+ /// A handler for everything related to creating new BackgroundTypes.
+ ///
+ public class BackgroundTypeHandler : IBackgroundTypeHandler
+ {
+ internal static readonly Dictionary BackgroundSprites = new Dictionary();
+
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IBackgroundTypeHandler Main { get; } = new BackgroundTypeHandler();
+
+ private BackgroundTypeHandler()
+ {
+ // Hide constructor
+ }
+
+ #region Interface Implementations
+
+ ///
+ /// adds a new into the game.
+ ///
+ /// the name of the BackgroundType, should not contain special characters.
+ /// The sprite for this BackgroundType.
+ /// The new that's created.
+ CraftData.BackgroundType IBackgroundTypeHandler.AddBackgroundType(string backgroundTypeName, Sprite backgroundSprite)
+ {
+ var backgroundType = BackgroundTypePatcher.AddBackgroundType(backgroundTypeName);
+
+ BackgroundSprites[backgroundType] = backgroundSprite;
+
+ return backgroundType;
+ }
+
+ ///
+ /// Safely looks for a modded Background Type from another mod in the SMLHelper BackgroundTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the BackgroundType
+ /// The BackgroundType enum value of the modded. Defaults to when the item was not found.
+ /// if the item was found; otherwise .
+ bool IBackgroundTypeHandler.TryGetModdedBackgroundType(string backgroundTypeString, out CraftData.BackgroundType modBackgroundType)
+ {
+ EnumTypeCache cache = BackgroundTypePatcher.cacheManager.RequestCacheForTypeName(backgroundTypeString, false);
+
+ if (cache != null) // Item Found
+ {
+ modBackgroundType = (CraftData.BackgroundType)cache.Index;
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ modBackgroundType = CraftData.BackgroundType.Normal;
+ return false;
+ }
+ }
+
+ ///
+ /// Safely looks for a modded Background Type from another mod in the SMLHelper BackgroundTypeCache.
+ ///
+ /// The string used to define the BackgroundType.
+ /// if the item was found; otherwise .
+ bool IBackgroundTypeHandler.ModdedBackgroundTypeExists(string backgroundTypeString)
+ {
+ EnumTypeCache cache = BackgroundTypePatcher.cacheManager.RequestCacheForTypeName(backgroundTypeString, false);
+
+ if (cache != null) // Item Found
+ {
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ return false;
+ }
+ }
+ #endregion
+
+ #region Static Methods
+
+ ///
+ /// adds a new into the game.
+ ///
+ /// the name of the BackgroundType, should not contain special characters.
+ /// The sprite for this BackgroundType.
+ /// The new that's created.
+ ///
+ public static CraftData.BackgroundType AddBackgroundType(string backgroundTypeName, Sprite backgroundSprite)
+ {
+ return Main.AddBackgroundType(backgroundTypeName, backgroundSprite);
+ }
+
+ ///
+ /// Safely looks for a modded Background Type from another mod in the SMLHelper BackgroundTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the BackgroundType
+ /// The BackgroundType enum value of the modded. Defaults to when the item was not found.
+ /// if the item was found; otherwise .
+ public static bool TryGetModdedBackgroundType(string backgroundTypeString, out CraftData.BackgroundType modBackgroundType)
+ {
+ return Main.TryGetModdedBackgroundType(backgroundTypeString, out modBackgroundType);
+ }
+
+ ///
+ /// Safely looks for a modded Background Type from another mod in the SMLHelper BackgroundTypeCache.
+ ///
+ /// The string used to define the BackgroundType.
+ /// if the item was found; otherwise .
+ public static bool ModdedBackgroundTypeExists(string backgroundTypeString)
+ {
+ return Main.ModdedBackgroundTypeExists(backgroundTypeString);
+ }
+
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/BioReactorHandler.cs b/SMLHelper/Handlers/BioReactorHandler.cs
new file mode 100644
index 00000000..b711bd81
--- /dev/null
+++ b/SMLHelper/Handlers/BioReactorHandler.cs
@@ -0,0 +1,43 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Patchers;
+ using Interfaces;
+
+ ///
+ /// A handler with common methods for updating BioReactor values.
+ ///
+ public class BioReactorHandler : IBioReactorHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IBioReactorHandler Main { get; } = new BioReactorHandler();
+
+ private BioReactorHandler()
+ {
+ // Hides constructor
+ }
+
+ ///
+ /// Allows you to specify the quantity of energy that a TechType will produce with bio reactors.
+ ///
+ /// The TechType that you want to use with bioreactors.
+ /// The quantity of energy that will be produced by this TechType.
+ ///
+ void IBioReactorHandler.SetBioReactorCharge(TechType techType, float charge)
+ {
+ BioReactorPatcher.CustomBioreactorCharges.Add(techType, charge);
+ }
+
+ ///
+ /// Allows you to specify the quantity of energy that a TechType will produce with bio reactors.
+ ///
+ /// The TechType that you want to use with bioreactors.
+ /// The quantity of energy that will be produced by this TechType.
+ ///
+ public static void SetBioReactorCharge(TechType techType, float charge)
+ {
+ Main.SetBioReactorCharge(techType, charge);
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/ConsoleCommandsHandler.cs b/SMLHelper/Handlers/ConsoleCommandsHandler.cs
new file mode 100644
index 00000000..a9cae9a7
--- /dev/null
+++ b/SMLHelper/Handlers/ConsoleCommandsHandler.cs
@@ -0,0 +1,34 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Commands;
+ using HarmonyLib;
+ using Interfaces;
+ using SMLHelper.V2.Patchers;
+ using System;
+ using System.Reflection;
+
+ ///
+ /// A handler class for registering your custom console commands.
+ ///
+ public class ConsoleCommandsHandler : IConsoleCommandHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IConsoleCommandHandler Main { get; } = new ConsoleCommandsHandler();
+
+ void IConsoleCommandHandler.RegisterConsoleCommand(string command, Type declaringType, string methodName, Type[] parameters)
+ {
+ MethodInfo targetMethod = parameters == null
+ ? AccessTools.Method(declaringType, methodName)
+ : AccessTools.Method(declaringType, methodName, parameters);
+ ConsoleCommandsPatcher.AddCustomCommand(command, targetMethod);
+ }
+
+ void IConsoleCommandHandler.RegisterConsoleCommand(string command, T callback)
+ => ConsoleCommandsPatcher.AddCustomCommand(command, callback.Method, true, callback.Target);
+
+ void IConsoleCommandHandler.RegisterConsoleCommands(Type type)
+ => ConsoleCommandsPatcher.ParseCustomCommands(type);
+ }
+}
diff --git a/SMLHelper/Handlers/CoordinatedSpawnsHandler.cs b/SMLHelper/Handlers/CoordinatedSpawnsHandler.cs
new file mode 100644
index 00000000..6ac465b2
--- /dev/null
+++ b/SMLHelper/Handlers/CoordinatedSpawnsHandler.cs
@@ -0,0 +1,284 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Patchers;
+
+ ///
+ /// a Handler that handles and registers Coordinated ( spawns).
+ ///
+ public class CoordinatedSpawnsHandler : ICoordinatedSpawnHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ICoordinatedSpawnHandler Main { get; } = new CoordinatedSpawnsHandler();
+
+ private CoordinatedSpawnsHandler()
+ {
+ // Hide Constructor
+ }
+
+ #region Interface Implementations
+ ///
+ /// Registers Multiple Coordinated spawns for one single passed TechType
+ ///
+ /// The TechType to spawn
+ /// the coordinates the should spawn to
+ void ICoordinatedSpawnHandler.RegisterCoordinatedSpawnsForOneTechType(TechType techTypeToSpawn, List coordinatesToSpawnTo)
+ {
+ var spawnInfos = new List();
+ foreach (var coordinate in coordinatesToSpawnTo)
+ {
+ spawnInfos.Add(new SpawnInfo(techTypeToSpawn, coordinate));
+ }
+ LargeWorldStreamerPatcher.spawnInfos.AddRange(spawnInfos);
+ }
+
+ ///
+ /// Registers a Coordinated Spawn
+ ///
+ /// the SpawnInfo to spawn
+ void ICoordinatedSpawnHandler.RegisterCoordinatedSpawn(SpawnInfo spawnInfo)
+ {
+ LargeWorldStreamerPatcher.spawnInfos.Add(spawnInfo);
+ }
+
+ ///
+ /// registers Many Coordinated Spawns.
+ ///
+ /// The SpawnInfos to spawn.
+ void ICoordinatedSpawnHandler.RegisterCoordinatedSpawns(List spawnInfos)
+ {
+ LargeWorldStreamerPatcher.spawnInfos.AddRange(spawnInfos);
+ }
+
+ ///
+ /// Registers Multiple Coordinated spawns with rotations for one single passed TechType
+ ///
+ /// The TechType to spawn
+ /// the coordinates(Key) and the rotations(Value) the should spawn to
+ void ICoordinatedSpawnHandler.RegisterCoordinatedSpawnsForOneTechType(TechType techTypeToSpawn, Dictionary coordinatesAndRotationsToSpawnTo)
+ {
+ var spawnInfos = new List();
+ foreach (var kvp in coordinatesAndRotationsToSpawnTo)
+ {
+ spawnInfos.Add(new SpawnInfo(techTypeToSpawn, kvp.Key, kvp.Value));
+ }
+ LargeWorldStreamerPatcher.spawnInfos.AddRange(spawnInfos);
+ }
+
+ #endregion
+
+ #region Static Methods
+
+ ///
+ /// Registers a Coordinated Spawn
+ ///
+ /// the SpawnInfo to spawn
+ public static void RegisterCoordinatedSpawn(SpawnInfo spawnInfo)
+ {
+ Main.RegisterCoordinatedSpawn(spawnInfo);
+ }
+
+ ///
+ /// registers Many Coordinated Spawns.
+ ///
+ /// The SpawnInfo to spawn.
+ public static void RegisterCoordinatedSpawns(List spawnInfos)
+ {
+ Main.RegisterCoordinatedSpawns(spawnInfos);
+ }
+
+ ///
+ /// Registers Multiple Coordinated spawns for one single passed TechType
+ ///
+ /// The TechType to spawn
+ /// the coordinates the should spawn to
+ public static void RegisterCoordinatedSpawnsForOneTechType(TechType techTypeToSpawn, List coordinatesToSpawnTo)
+ {
+ Main.RegisterCoordinatedSpawnsForOneTechType(techTypeToSpawn, coordinatesToSpawnTo);
+ }
+
+ ///
+ /// Registers Multiple Coordinated spawns with rotations for one single passed TechType
+ ///
+ /// The TechType to spawn
+ /// the coordinates(Key) and the rotations(Value) the should spawn to
+ public static void RegisterCoordinatedSpawnsForOneTechType(TechType techTypeToSpawn, Dictionary coordinatesAndRotationsToSpawnTo)
+ {
+ Main.RegisterCoordinatedSpawnsForOneTechType(techTypeToSpawn, coordinatesAndRotationsToSpawnTo);
+ }
+
+ #endregion
+ }
+
+ #region SpawnInfo
+ ///
+ /// A basic struct that provides enough info for the System to function.
+ ///
+ public struct SpawnInfo : IEquatable
+ {
+ [JsonProperty]
+ internal TechType TechType { get; }
+ [JsonProperty]
+ internal string ClassId { get; }
+ [JsonProperty]
+ internal Vector3 SpawnPosition { get; }
+ [JsonProperty]
+ internal Quaternion Rotation { get; }
+ [JsonProperty]
+ internal SpawnType Type { get; }
+
+ ///
+ /// Initializes a new .
+ ///
+ /// TechType to spawn.
+ /// Position to spawn into.
+ public SpawnInfo(TechType techType, Vector3 spawnPosition)
+ : this(default, techType, spawnPosition, Quaternion.identity) { }
+
+ ///
+ /// Initializes a new .
+ ///
+ /// ClassID to spawn.
+ /// Position to spawn into.
+ public SpawnInfo(string classId, Vector3 spawnPosition)
+ : this(classId, default, spawnPosition, Quaternion.identity) { }
+
+ ///
+ /// Initializes a new .
+ ///
+ /// TechType to spawn.
+ /// Position to spawn into.
+ /// Rotation to spawn at.
+ public SpawnInfo(TechType techType, Vector3 spawnPosition, Quaternion rotation)
+ : this(default, techType, spawnPosition, rotation) { }
+
+ ///
+ /// Initializes a new .
+ ///
+ /// ClassID to spawn.
+ /// Position to spawn into.
+ /// Rotation to spawn at.
+ public SpawnInfo(string classId, Vector3 spawnPosition, Quaternion rotation)
+ : this(classId, default, spawnPosition, rotation) { }
+
+ ///
+ /// Initializes a new .
+ ///
+ /// TechType to spawn.
+ /// Position to spawn into.
+ /// Rotation to spawn at.
+ public SpawnInfo(TechType techType, Vector3 spawnPosition, Vector3 rotation)
+ : this(default, techType, spawnPosition, Quaternion.Euler(rotation)) { }
+
+ ///
+ /// Initializes a new .
+ ///
+ /// ClassID to spawn.
+ /// Position to spawn into.
+ /// Rotation to spawn at.
+ public SpawnInfo(string classId, Vector3 spawnPosition, Vector3 rotation)
+ : this(classId, default, spawnPosition, Quaternion.Euler(rotation)) { }
+
+ [JsonConstructor]
+ internal SpawnInfo(string classId, TechType techType, Vector3 spawnPosition, Quaternion rotation)
+ {
+ ClassId = classId;
+ TechType = techType;
+ SpawnPosition = spawnPosition;
+ Rotation = rotation;
+ Type = TechType switch
+ {
+ default(TechType) => SpawnType.ClassId,
+ _ => SpawnType.TechType
+ };
+ }
+
+ ///
+ /// Indicates whether this instance and a specified object are equal.
+ ///
+ ///
+ /// It is worth noting that we use Unity's and
+ /// operator comparisons for comparing the and properties of each instance,
+ /// to allow for an approximate comparison of these values.
+ ///
+ /// The object to compare with the current instance.
+ /// if is a and represents the same
+ /// value as this instance; otherwise, .
+ ///
+ public override bool Equals(object obj) => obj is SpawnInfo spawnInfo && Equals(spawnInfo);
+
+ ///
+ /// A custom hash code algorithm that takes into account the values of each property of the instance,
+ /// and attempts to reduce diagonal collisions.
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ public override int GetHashCode()
+ {
+ unchecked // overflow is fine, just wrap around
+ {
+ int hash = 13;
+ hash = (hash * 7) + TechType.GetHashCode();
+ hash = (hash * 7) + ClassId.GetHashCode();
+ hash = (hash * 7) + SpawnPosition.GetHashCode();
+ hash = (hash * 7) + Rotation.GetHashCode();
+ hash = (hash * 7) + Type.GetHashCode();
+ return hash;
+ }
+ }
+
+ ///
+ /// Indicates whether the current is equal to another.
+ ///
+ ///
+ /// It is worth noting that we use Unity's and
+ /// operator comparisons for comparing the and properties of each instance,
+ /// to allow for an approximate comparison of these values.
+ ///
+ /// The other .
+ /// if the current is equal to the parameter;
+ /// otherwise .
+ public bool Equals(SpawnInfo other) => other.TechType == TechType
+ && other.ClassId == ClassId
+ && other.SpawnPosition == SpawnPosition
+ && other.Rotation == Rotation
+ && other.Type == Type;
+
+ internal enum SpawnType
+ {
+ ClassId,
+ TechType
+ }
+
+ ///
+ /// Indicates whether two instances are equal.
+ ///
+ /// The first instance to compare.
+ /// The second instance to compare.
+ /// if the instances are equal; otherwise, .
+ ///
+ ///
+ public static bool operator ==(SpawnInfo a, SpawnInfo b) => a.Equals(b);
+
+ ///
+ /// Indicates whether two instances are not equal.
+ ///
+ /// The first instance to compare.
+ /// The second instance to compare.
+ /// if the instances are not equal; otherwise, .
+ ///
+ ///
+ public static bool operator !=(SpawnInfo a, SpawnInfo b) => !(a == b);
+ }
+ #endregion
+}
diff --git a/SMLHelper/Handlers/CraftDataHandler.cs b/SMLHelper/Handlers/CraftDataHandler.cs
new file mode 100644
index 00000000..25ec9cf3
--- /dev/null
+++ b/SMLHelper/Handlers/CraftDataHandler.cs
@@ -0,0 +1,230 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Patchers;
+
+ ///
+ /// A handler class for adding and editing crafted items.
+ ///
+ public partial class CraftDataHandler: ICraftDataHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ICraftDataHandler Main { get; } = new CraftDataHandler();
+
+ private CraftDataHandler()
+ {
+ // Hides constructor
+ }
+
+ #region Common Static Methods
+
+ ///
+ /// Allows you to edit EquipmentTypes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose EqiupmentType you want to edit.
+ /// The EquipmentType for that TechType.
+ public static void SetEquipmentType(TechType techType, EquipmentType equipmentType)
+ {
+ Main.SetEquipmentType(techType, equipmentType);
+ }
+
+#if SUBNAUTICA
+ ///
+ /// Allows you to edit QuickSlotType for TechTypes. Can be used for existing TechTypes too.
+ /// Careful: This has to be called after and .
+ ///
+ /// The TechType whose QuickSlotType you want to edit.
+ /// The QuickSlotType for that TechType.
+#elif BELOWZERO
+ ///
+ /// Allows you to edit QuickSlotType for TechTypes. Can be used for existing TechTypes too.
+ /// Careful: This has to be called after .
+ ///
+ /// The TechType whose QuickSlotType you want to edit.
+ /// The QuickSlotType for that TechType.
+#endif
+ public static void SetQuickSlotType(TechType techType, QuickSlotType slotType)
+ {
+ Main.SetQuickSlotType(techType, slotType);
+ }
+
+ ///
+ /// Allows you to edit harvest output, i.e. what TechType you get when you "harvest" a TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose harvest output you want to edit.
+ /// The harvest output for that TechType.
+ public static void SetHarvestOutput(TechType techType, TechType harvestOutput)
+ {
+ Main.SetHarvestOutput(techType, harvestOutput);
+ }
+
+ ///
+ /// Allows you to edit how TechTypes are harvested.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose HarvestType you want to edit.
+ /// The HarvestType for that TechType.
+ public static void SetHarvestType(TechType techType, HarvestType harvestType)
+ {
+ Main.SetHarvestType(techType, harvestType);
+ }
+
+ ///
+ /// Allows you to edit how much additional slices/seeds are given upon last knife hit.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose final cut bonus you want to edit.
+ /// The number of additional slices/seeds you'll receive on last cut.
+ public static void SetHarvestFinalCutBonus(TechType techType, int bonus)
+ {
+ Main.SetHarvestFinalCutBonus(techType, bonus);
+ }
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The item size for that TechType.
+ public static void SetItemSize(TechType techType, Vector2int size)
+ {
+ Main.SetItemSize(techType, size);
+ }
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The width of the item
+ /// The height of the item
+ public static void SetItemSize(TechType techType, int x, int y)
+ {
+ Main.SetItemSize(techType, x, y);
+ }
+
+ ///
+ /// Allows you to edit crafting times for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose crafting time you want to edit.
+ /// The crafting time, in seconds, for that TechType.
+ public static void SetCraftingTime(TechType techType, float time)
+ {
+ Main.SetCraftingTime(techType, time);
+ }
+
+ ///
+ /// Allows you to edit the cooked creature list, i.e. associate the unedible TechType to the cooked TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose cooked creature counterpart to edit.
+ /// The cooked creature counterpart for that TechType.
+ public static void SetCookedVariant(TechType uncooked, TechType cooked)
+ {
+ Main.SetCookedVariant(uncooked, cooked);
+ }
+
+ ///
+ /// Allows you to edit inventory background colors for TechTypes.
+ ///
+ /// The TechType whose BackgroundType you want to edit.
+ /// The background color for that TechType.
+ ///
+ public static void SetBackgroundType(TechType techType, CraftData.BackgroundType backgroundColor)
+ {
+ Main.SetBackgroundType(techType, backgroundColor);
+ }
+
+ ///
+ /// Allows you to add items to the buildable list.
+ ///
+ /// The TechType which you want to add to the buildable list.
+ public static void AddBuildable(TechType techType)
+ {
+ Main.AddBuildable(techType);
+ }
+
+ ///
+ /// Allows you to add items to the game's internal grouping system.
+ /// Required if you want to make buildable items show up in the Habitat Builder.
+ ///
+ /// The TechGroup you want to add your TechType to.
+ /// The TechCategory (in the TechGroup) you want to add your TechType to.
+ /// The TechType you want to add.
+ public static void AddToGroup(TechGroup group, TechCategory category, TechType techType)
+ {
+ Main.AddToGroup(group, category, techType, TechType.None);
+ }
+
+ ///
+ /// Allows you to add items to the game's internal grouping system.
+ /// Required if you want to make buildable items show up in the Habitat Builder.
+ ///
+ /// The TechGroup you want to add your TechType to.
+ /// The TechCategory (in the TechGroup) you want to add your TechType to.
+ /// The TechType you want to add.
+ /// Added TechType will be added after this TechType, for sorting purposes.
+ public static void AddToGroup(TechGroup group, TechCategory category, TechType techType, TechType after)
+ {
+ Main.AddToGroup(group, category, techType, after);
+ }
+
+ ///
+ /// Allows you to remove an existing TechType from the game's internal group system.
+ ///
+ /// The TechGroup in which the TechType is located.
+ /// The TechCategory in which the TechType is located.
+ /// The TechType which you want to remove.
+ public static void RemoveFromGroup(TechGroup group, TechCategory category, TechType techType)
+ {
+ Main.RemoveFromGroup(group, category, techType);
+ }
+
+ #endregion
+
+ #region Common Interface Methods
+
+ ///
+ /// Allows you to add items to the game's internal grouping system.
+ /// Required if you want to make buildable items show up in the Habitat Builder or show in the Blueprints Tab of the PDA.
+ ///
+ /// The TechGroup you want to add your TechType to.
+ /// The TechCategory (in the TechGroup) you want to add your TechType to.
+ /// The TechType you want to add.
+ void ICraftDataHandler.AddToGroup(TechGroup group, TechCategory category, TechType techType)
+ {
+ CraftDataPatcher.AddToCustomGroup(group, category, techType, TechType.None);
+ }
+
+ ///
+ /// Allows you to add items to the game's internal grouping system.
+ /// Required if you want to make buildable items show up in the Habitat Builder or show in the Blueprints Tab of the PDA.
+ ///
+ /// The TechGroup you want to add your TechType to.
+ /// The TechCategory (in the TechGroup) you want to add your TechType to.
+ /// The TechType you want to add.
+ /// Added TechType will be added after this TechType, for sorting purposes.
+ void ICraftDataHandler.AddToGroup(TechGroup group, TechCategory category, TechType techType, TechType after)
+ {
+ CraftDataPatcher.AddToCustomGroup(group, category, techType, after);
+ }
+
+ ///
+ /// Allows you to remove an existing TechType from the game's internal group system.
+ ///
+ /// The TechGroup in which the TechType is located.
+ /// The TechCategory in which the TechType is located.
+ /// The TechType which you want to remove.
+ void ICraftDataHandler.RemoveFromGroup(TechGroup group, TechCategory category, TechType techType)
+ {
+ CraftDataPatcher.RemoveFromCustomGroup(group, category, techType);
+ }
+
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/CraftDataHandler_BelowZero.cs b/SMLHelper/Handlers/CraftDataHandler_BelowZero.cs
new file mode 100644
index 00000000..5d3f3ef4
--- /dev/null
+++ b/SMLHelper/Handlers/CraftDataHandler_BelowZero.cs
@@ -0,0 +1,539 @@
+#if BELOWZERO
+namespace SMLHelper.V2.Handlers
+{
+ using Crafting;
+ using Interfaces;
+ using Patchers;
+ using System.Collections.Generic;
+
+ ///
+ /// A handler class for adding and editing crafted items.
+ ///
+ public partial class CraftDataHandler : ICraftDataHandler
+ {
+
+ #region BelowZero specific Static Methods
+
+ ///
+ /// Allows you to edit recipes, i.e. RecipeData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ public static void SetTechData(TechType techType, RecipeData recipeData)
+ {
+ Main.SetTechData(techType, recipeData);
+ }
+
+ ///
+ /// Allows you to add ingredients for a TechType crafting recipe.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose ingredient list you want to edit.
+ /// The collection of Ingredients for that TechType.
+ ///
+ public static void SetIngredients(TechType techType, ICollection ingredients)
+ {
+ Main.SetIngredients(techType, ingredients);
+ }
+
+
+ ///
+ /// Allows you to edit the Cold Resistance for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose Cold Resistance you want to edit.
+ /// The int value for the Cold Resistance.
+ public static void SetColdResistance(TechType techType, int resistance)
+ {
+ Main.SetColdResistance(techType, resistance);
+ }
+
+ ///
+ /// Allows you to add linked items for a TechType crafting recipe.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose ingredient list you want to edit.
+ /// The collection of linked items for that TechType
+ public static void SetLinkedItems(TechType techType, ICollection linkedItems)
+ {
+ Main.SetLinkedItems(techType, linkedItems);
+ }
+
+ ///
+ /// Safely accesses the crafting data from a modded or vanilla item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The JsonValue from the modded item if it exists; Otherwise, returns null.
+ public static RecipeData GetRecipeData(TechType techType)
+ {
+ return Main.GetRecipeData(techType);
+ }
+
+
+ ///
+ /// Safely accesses the crafting data from a modded or vanilla item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The JsonValue from the modded item if it exists; Otherwise, returns null.
+ public static RecipeData GetTechData(TechType techType)
+ {
+ return Main.GetRecipeData(techType);
+ }
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The JsonValue from the modded item if it exists; Otherwise, returns null.
+ public static RecipeData GetModdedRecipeData(TechType techType)
+ {
+ return Main.GetModdedRecipeData(techType);
+ }
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The JsonValue from the modded item if it exists; Otherwise, returns null.
+ public static RecipeData GetModdedTechData(TechType techType)
+ {
+ return Main.GetModdedRecipeData(techType);
+ }
+
+ ///
+ /// Sets the maximum charge.
+ ///
+ /// The TechType whose MaxCharge you want to edit.
+ /// The maximum charge.
+ public static void SetMaxCharge(TechType techType, float maxCharge)
+ {
+ Main.SetMaxCharge(techType, maxCharge);
+ }
+
+ ///
+ /// Sets the energy cost.
+ ///
+ /// The TechType whose EnergyCost you want to edit.
+ /// The energy cost.
+ public static void SetEnergyCost(TechType techType, float energyCost)
+ {
+ Main.SetEnergyCost(techType, energyCost);
+ }
+
+
+ ///
+ /// Sets the type of the sound.
+ ///
+ /// Type of the tech.
+ /// Type of the sound.
+ public static void SetSoundType(TechType techType, TechData.SoundType soundType)
+ {
+ Main.SetSoundType(techType, soundType);
+ }
+
+ #endregion
+
+ #region BelowZero specific implementations
+
+ ///
+ /// Allows you to add or edit RecipeData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ void ICraftDataHandler.SetTechData(TechType techType, RecipeData recipeData)
+ {
+ if (CraftDataPatcher.CustomTechData.TryGetValue(techType, out JsonValue jsonValue))
+ {
+ jsonValue[TechData.PropertyToID("techType")] = new JsonValue((int)techType);
+ jsonValue[TechData.PropertyToID("craftAmount")] = new JsonValue(recipeData.craftAmount);
+ }
+ else
+ {
+ jsonValue = new JsonValue
+ {
+ { TechData.PropertyToID("techType"), new JsonValue((int)techType) },
+ { TechData.PropertyToID("craftAmount"), new JsonValue(recipeData.craftAmount) }
+ };
+
+ CraftDataPatcher.CustomTechData.Add(techType, jsonValue);
+ }
+
+ if (recipeData.ingredientCount > 0)
+ {
+ Main.SetIngredients(techType, recipeData.Ingredients);
+ }
+ if (recipeData.linkedItemCount > 0)
+ {
+ Main.SetLinkedItems(techType, recipeData.LinkedItems);
+ }
+ }
+
+ ///
+ /// Allows you to edit recipes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The collection of Ingredients for that TechType.
+ ///
+ void ICraftDataHandler.SetIngredients(TechType techType, ICollection ingredients)
+ {
+ if (!CraftDataPatcher.CustomTechData.TryGetValue(techType, out JsonValue smlJsonValue))
+ {
+ smlJsonValue = new JsonValue();
+ CraftDataPatcher.CustomTechData.Add(techType, smlJsonValue);
+ }
+
+ if (!smlJsonValue.Contains(TechData.PropertyToID("ingredients")))
+ {
+ smlJsonValue.Add(TechData.PropertyToID("ingredients"), new JsonValue(JsonValue.Type.Array));
+ }
+ else
+ {
+ smlJsonValue[TechData.PropertyToID("ingredients")] = new JsonValue(JsonValue.Type.Array);
+ }
+
+ JsonValue ingredientslist = smlJsonValue[TechData.PropertyToID("ingredients")];
+
+ int amount = TechData.PropertyToID("amount");
+ int tech = TechData.PropertyToID("techType");
+ int current = 0;
+
+ foreach (Ingredient i in ingredients)
+ {
+ ingredientslist.Add(new JsonValue(current));
+ ingredientslist[current] = new JsonValue(JsonValue.Type.Object)
+ {
+ { amount, new JsonValue(i.amount) },
+ { tech, new JsonValue((int)i.techType) }
+ };
+ current++;
+ }
+ }
+
+ ///
+ /// Allows you to edit Linked Items for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The collection of Ingredients for that TechType.
+ ///
+ void ICraftDataHandler.SetLinkedItems(TechType techType, ICollection linkedItems)
+ {
+ if (!CraftDataPatcher.CustomTechData.TryGetValue(techType, out JsonValue smlJsonValue))
+ {
+ CraftDataPatcher.CustomTechData.Add(techType, new JsonValue());
+ smlJsonValue = CraftDataPatcher.CustomTechData[techType];
+ }
+
+ if (!smlJsonValue.Contains(TechData.PropertyToID("linkedItems")))
+ {
+ smlJsonValue.Add(TechData.PropertyToID("linkedItems"), new JsonValue(JsonValue.Type.Array));
+ }
+ else
+ {
+ smlJsonValue[TechData.PropertyToID("linkedItems")] = new JsonValue(JsonValue.Type.Array);
+ }
+
+ JsonValue linkedItemslist = smlJsonValue[TechData.PropertyToID("linkedItems")];
+
+ int current = 0;
+
+ foreach (TechType i in linkedItems)
+ {
+ linkedItemslist.Add(new JsonValue(current));
+ linkedItemslist[current] = new JsonValue((int)i);
+ current++;
+ }
+ }
+
+ ///
+ /// Safely accesses the crafting data from a Modded or Vanilla item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The RecipeData from the item if it exists; Otherwise, returns null.
+ RecipeData ICraftDataHandler.GetRecipeData(TechType techType)
+ {
+ RecipeData moddedRecipeData = GetModdedRecipeData(techType);
+
+ if (moddedRecipeData != null)
+ {
+ return moddedRecipeData;
+ }
+
+ if (!TechData.Contains(TechType.Knife))
+ {
+ TechData.Initialize();
+ }
+
+ if (TechData.TryGetValue(techType, out JsonValue techData))
+ {
+ return ConvertToRecipeData(techData);
+ }
+
+ return null;
+ }
+
+ private static RecipeData ConvertToRecipeData(JsonValue techData)
+ {
+ RecipeData currentRecipeData = new RecipeData
+ {
+ craftAmount = techData.GetInt(TechData.propertyCraftAmount, out int craftAmount, 0) ? craftAmount : TechData.defaultCraftAmount
+ };
+
+ if (techData.GetArray(TechData.propertyIngredients, out JsonValue jsonValue, null))
+ {
+ for (int i = 0; i < jsonValue.Count; i++)
+ {
+ JsonValue jsonValue2 = jsonValue[i];
+ TechType techType = (TechType)jsonValue2.GetInt(TechData.propertyTechType, 0);
+ int int2 = jsonValue2.GetInt(TechData.propertyAmount, 0);
+ if (techType != TechType.None && int2 > 0)
+ {
+ if (currentRecipeData.Ingredients == null)
+ {
+ currentRecipeData.Ingredients = new List();
+ }
+ currentRecipeData.Ingredients.Add(new Ingredient(techType, int2));
+ }
+ }
+ }
+
+ if (techData.GetArray(TechData.propertyLinkedItems, out JsonValue jsonValue3, null))
+ {
+ for (int j = 0; j < jsonValue3.Count; j++)
+ {
+ TechType techType1 = (TechType)jsonValue3[j].GetInt(0);
+ if (currentRecipeData.LinkedItems == null)
+ {
+ currentRecipeData.LinkedItems = new List();
+ }
+ currentRecipeData.LinkedItems.Add(techType1);
+ }
+ }
+ return currentRecipeData;
+ }
+
+ ///
+ /// Safely accesses the crafting data from a Modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The RecipeData from the modded item if it exists; Otherwise, returns null.
+ RecipeData ICraftDataHandler.GetModdedRecipeData(TechType techType)
+ {
+ return CraftDataPatcher.CustomTechData.TryGetValue(techType, out JsonValue techData) ? ConvertToRecipeData(techData) : null;
+ }
+
+ ///
+ /// Allows you to edit EquipmentTypes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose EqiupmentType you want to edit.
+ /// The EquipmentType for that TechType.
+ void ICraftDataHandler.SetEquipmentType(TechType techType, EquipmentType equipmentType)
+ {
+ AddJsonProperty(techType, "equipmentType", new JsonValue((int)equipmentType));
+ }
+
+ ///
+ /// Allows you to edit QuickSlotType for TechTypes. Can be used for existing TechTypes too.
+ /// Careful: This has to be called after .
+ ///
+ /// The TechType whose QuickSlotType you want to edit.
+ /// The QuickSlotType for that TechType.
+ void ICraftDataHandler.SetQuickSlotType(TechType techType, QuickSlotType slotType)
+ {
+ AddJsonProperty(techType, "slotType", new JsonValue((int)slotType));
+ }
+
+ ///
+ /// Allows you to edit harvest output, i.e. what TechType you get when you "harvest" a TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose harvest output you want to edit.
+ /// The harvest output for that TechType.
+ void ICraftDataHandler.SetHarvestOutput(TechType techType, TechType harvestOutput)
+ {
+ AddJsonProperty(techType, "harvestOutput", new JsonValue((int)harvestOutput));
+ }
+
+ ///
+ /// Allows you to edit how TechTypes are harvested.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose HarvestType you want to edit.
+ /// The HarvestType for that TechType.
+ void ICraftDataHandler.SetHarvestType(TechType techType, HarvestType harvestType)
+ {
+ AddJsonProperty(techType, "harvestType", new JsonValue((int)harvestType));
+ }
+
+ ///
+ /// Allows you to edit how much additional slices/seeds are given upon last knife hit.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose final cut bonus you want to edit.
+ /// The number of additional slices/seeds you'll receive on last cut.
+ void ICraftDataHandler.SetHarvestFinalCutBonus(TechType techType, int bonus)
+ {
+ AddJsonProperty(techType, "harvestFinalCutBonus", new JsonValue(bonus));
+ }
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The item size for that TechType.
+ void ICraftDataHandler.SetItemSize(TechType techType, Vector2int size)
+ {
+ SetItemSize(techType, size.x, size.y);
+ }
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The width of the item
+ /// The height of the item
+ void ICraftDataHandler.SetItemSize(TechType techType, int x, int y)
+ {
+ JsonValue jsonValue = new JsonValue()
+ {
+ {
+ TechData.propertyX,
+ new JsonValue(x)
+ },
+ {
+ TechData.propertyY,
+ new JsonValue(y)
+ }
+ };
+ AddJsonProperty(techType, "itemSize", jsonValue);
+ }
+
+ ///
+ /// Allows you to edit crafting times for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose crafting time you want to edit.
+ /// The crafting time, in seconds, for that TechType.
+ void ICraftDataHandler.SetCraftingTime(TechType techType, float time)
+ {
+ AddJsonProperty(techType, "craftTime", new JsonValue(time));
+ }
+
+ ///
+ /// Allows you to edit the cooked creature list, i.e. associate the unedible TechType to the cooked TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose cooked creature counterpart to edit.
+ /// The cooked creature counterpart for that TechType.
+ void ICraftDataHandler.SetCookedVariant(TechType uncooked, TechType cooked)
+ {
+ AddJsonProperty(uncooked, "processed", new JsonValue((int)cooked));
+ }
+
+ ///
+ /// Allows you to edit the Cold Resistance of a TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose Cold Resistance to edit.
+ /// The Cold Resistance for that TechType.
+ void ICraftDataHandler.SetColdResistance(TechType uncooked, int resistance)
+ {
+ AddJsonProperty(uncooked, "coldResistance", new JsonValue((int)resistance));
+ }
+
+ ///
+ /// Allows you to edit inventory background colors for TechTypes.
+ ///
+ /// The TechType whose BackgroundType you want to edit.
+ /// The background color for that TechType.
+ ///
+ void ICraftDataHandler.SetBackgroundType(TechType techType, CraftData.BackgroundType backgroundColor)
+ {
+ AddJsonProperty(techType, "backgroundType", new JsonValue((int)backgroundColor));
+ }
+
+ ///
+ /// Allows you to add items to the buildable list.
+ ///
+ /// The TechType which you want to add to the buildable list.
+ void ICraftDataHandler.AddBuildable(TechType techType)
+ {
+ AddJsonProperty(techType, "buildable", new JsonValue(true));
+ }
+
+ ///
+ /// Sets the maximum charge.
+ ///
+ /// The TechType whose MaxCharge you want to edit.
+ /// The maximum charge.
+ void ICraftDataHandler.SetMaxCharge(TechType techType, float maxCharge)
+ {
+ AddJsonProperty(techType, "maxCharge", new JsonValue((double)maxCharge));
+ }
+
+ ///
+ /// Sets the energy cost.
+ ///
+ /// The TechType whose EnergyCost you want to edit.
+ /// The energy cost.
+ void ICraftDataHandler.SetEnergyCost(TechType techType, float energyCost)
+ {
+ AddJsonProperty(techType, "energyCost", new JsonValue((double)energyCost));
+ }
+
+ ///
+ /// Sets the type of the sound.
+ ///
+ /// Type of the tech.
+ /// Type of the sound.
+ void ICraftDataHandler.SetSoundType(TechType techType, TechData.SoundType soundType)
+ {
+ AddJsonProperty(techType, "soundType", new JsonValue((int)soundType));
+ }
+
+ private static void AddJsonProperty(TechType techType, string key, JsonValue newValue)
+ {
+ if (CraftDataPatcher.CustomTechData.TryGetValue(techType, out JsonValue techData))
+ {
+ techData[TechData.PropertyToID(key)] = newValue;
+ }
+ else
+ {
+ CraftDataPatcher.CustomTechData[techType] = new JsonValue
+ {
+ {
+ TechData.PropertyToID("techType"),
+ new JsonValue((int)techType)
+ },
+ {
+ TechData.PropertyToID(key),
+ newValue
+ }
+ };
+ }
+ }
+
+ #endregion
+ }
+}
+#endif
diff --git a/SMLHelper/Handlers/CraftDataHandler_Subnautica.cs b/SMLHelper/Handlers/CraftDataHandler_Subnautica.cs
new file mode 100644
index 00000000..c820ffc9
--- /dev/null
+++ b/SMLHelper/Handlers/CraftDataHandler_Subnautica.cs
@@ -0,0 +1,311 @@
+#if SUBNAUTICA
+namespace SMLHelper.V2.Handlers
+{
+ using Crafting;
+ using Interfaces;
+ using Patchers;
+
+ ///
+ /// A handler class for adding and editing crafted items.
+ ///
+ public partial class CraftDataHandler : ICraftDataHandler
+ {
+#region Subnautica Specific Static Methods
+
+ ///
+ /// Allows you to edit recipes, i.e. TechData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ public static void SetTechData(TechType techType, ITechData techData)
+ {
+ Main.SetTechData(techType, techData);
+ }
+
+ ///
+ /// Allows you to edit recipes, i.e. TechData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ public static void SetTechData(TechType techType, TechData techData)
+ {
+ Main.SetTechData(techType, techData);
+ }
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The ITechData from the modded item if it exists; Otherwise, returns null.
+ public static ITechData GetModdedTechData(TechType techType)
+ {
+ return Main.GetModdedTechData(techType);
+ }
+
+ ///
+ /// Safely accesses the crafting data from any item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// Returns TechData if it exists; Otherwise, returns null.
+ public static TechData GetTechData(TechType techType)
+ {
+ return Main.GetTechData(techType);
+ }
+
+ ///
+ /// Sets the eating sound for the provided TechType.
+ ///
+ /// The item being consumed during .
+ ///
+ /// The sound path.
+ ///
+ /// Value values are
+ /// - "event:/player/drink"
+ /// - "event:/player/drink_stillsuit"
+ /// - "event:/player/use_first_aid"
+ /// - "event:/player/eat" (default)
+ ///
+ ///
+ public static void SetEatingSound(TechType consumable, string soundPath)
+ {
+ Main.SetEatingSound(consumable, soundPath);
+ }
+
+#endregion
+
+#region Subnautica specific implementations
+
+ ///
+ /// Allows you to edit recipes, i.e. TechData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ void ICraftDataHandler.SetTechData(TechType techType, ITechData techData)
+ {
+ CraftDataPatcher.CustomTechData[techType] = techData;
+ }
+
+ ///
+ /// Allows you to edit recipes, i.e. TechData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ void ICraftDataHandler.SetTechData(TechType techType, TechData techData)
+ {
+ CraftDataPatcher.CustomTechData[techType] = techData;
+ }
+
+ ///
+ /// Allows you to edit EquipmentTypes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose EqiupmentType you want to edit.
+ /// The EquipmentType for that TechType.
+ void ICraftDataHandler.SetEquipmentType(TechType techType, EquipmentType equipmentType)
+ {
+ CraftDataPatcher.CustomEquipmentTypes[techType] = equipmentType;
+ }
+
+ ///
+ /// Allows you to edit QuickSlotType for TechTypes. Can be used for existing TechTypes too.
+ /// Careful: This has to be called after and .
+ ///
+ /// The TechType whose QuickSlotType you want to edit.
+ /// The QuickSlotType for that TechType.
+ void ICraftDataHandler.SetQuickSlotType(TechType techType, QuickSlotType slotType)
+ {
+ CraftDataPatcher.CustomSlotTypes[techType] = slotType;
+ }
+
+ ///
+ /// Allows you to edit harvest output, i.e. what TechType you get when you "harvest" a TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose harvest output you want to edit.
+ /// The harvest output for that TechType.
+ void ICraftDataHandler.SetHarvestOutput(TechType techType, TechType harvestOutput)
+ {
+ CraftDataPatcher.CustomHarvestOutputList[techType] = harvestOutput;
+ }
+
+ ///
+ /// Allows you to edit how TechTypes are harvested.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose HarvestType you want to edit.
+ /// The HarvestType for that TechType.
+ void ICraftDataHandler.SetHarvestType(TechType techType, HarvestType harvestType)
+ {
+ CraftDataPatcher.CustomHarvestTypeList[techType] = harvestType;
+ }
+
+ ///
+ /// Allows you to edit how much additional slices/seeds are given upon last knife hit.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose final cut bonus you want to edit.
+ /// The number of additional slices/seeds you'll receive on last cut.
+ void ICraftDataHandler.SetHarvestFinalCutBonus(TechType techType, int bonus)
+ {
+ CraftDataPatcher.CustomFinalCutBonusList[techType] = bonus;
+ }
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The item size for that TechType.
+ void ICraftDataHandler.SetItemSize(TechType techType, Vector2int size)
+ {
+ CraftDataPatcher.CustomItemSizes[techType] = size;
+ }
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The width of the item
+ /// The height of the item
+ void ICraftDataHandler.SetItemSize(TechType techType, int x, int y)
+ {
+ CraftDataPatcher.CustomItemSizes[techType] = new Vector2int(x, y);
+ }
+
+ ///
+ /// Allows you to edit crafting times for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose crafting time you want to edit.
+ /// The crafting time, in seconds, for that TechType.
+ void ICraftDataHandler.SetCraftingTime(TechType techType, float time)
+ {
+ CraftDataPatcher.CustomCraftingTimes[techType] = time;
+ }
+
+ ///
+ /// Allows you to edit the cooked creature list, i.e. associate the unedible TechType to the cooked TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose cooked creature counterpart to edit.
+ /// The cooked creature counterpart for that TechType.
+ void ICraftDataHandler.SetCookedVariant(TechType uncooked, TechType cooked)
+ {
+ CraftDataPatcher.CustomCookedCreatureList[uncooked] = cooked;
+ }
+
+ ///
+ /// Allows you to edit inventory background colors for TechTypes.
+ ///
+ /// The TechType whose BackgroundType you want to edit.
+ /// The background color for that TechType.
+ ///
+ void ICraftDataHandler.SetBackgroundType(TechType techType, CraftData.BackgroundType backgroundColor)
+ {
+ CraftDataPatcher.CustomBackgroundTypes[techType] = backgroundColor;
+ }
+
+ ///
+ /// Allows you to add items to the buildable list.
+ ///
+ /// The TechType which you want to add to the buildable list.
+ void ICraftDataHandler.AddBuildable(TechType techType)
+ {
+ CraftDataPatcher.CustomBuildables.Add(techType);
+ }
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The ITechData from the modded item if it exists; Otherwise, returns null.
+ ITechData ICraftDataHandler.GetModdedTechData(TechType techType)
+ {
+ if (!CraftDataPatcher.CustomTechData.TryGetValue(techType, out ITechData moddedTechData))
+ {
+ return null;
+ }
+ return moddedTechData;
+ }
+
+ ///
+ /// Safely accesses the crafting data from any item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// Returns TechData if it exists; Otherwise, returns null.
+ TechData ICraftDataHandler.GetTechData(TechType techType)
+ {
+ if (CraftDataPatcher.CustomTechData.TryGetValue(techType, out ITechData iTechData))
+ {
+ return ConvertToTechData(iTechData);
+ }
+
+ iTechData = CraftData.Get(techType, true);
+
+ if (iTechData != null)
+ {
+ return ConvertToTechData(iTechData);
+ }
+
+ return null;
+ }
+
+ private static TechData ConvertToTechData(ITechData iTechData)
+ {
+ var techData = new TechData() { craftAmount = iTechData.craftAmount };
+
+ for (int i = 0; i < iTechData.ingredientCount; i++)
+ {
+ IIngredient ingredient = iTechData.GetIngredient(i);
+ var smlingredient = new Ingredient(ingredient.techType, ingredient.amount);
+ techData.Ingredients.Add(smlingredient);
+ }
+
+ for (int i = 0; i < iTechData.linkedItemCount; i++)
+ {
+ techData.LinkedItems.Add(iTechData.GetLinkedItem(i));
+ }
+
+ return techData;
+ }
+
+ ///
+ /// Sets the eating sound for the provided TechType.
+ ///
+ /// The item being consumed during .
+ ///
+ /// The sound path.
+ ///
+ /// Value values are
+ /// - "event:/player/drink"
+ /// - "event:/player/drink_stillsuit"
+ /// - "event:/player/use_first_aid"
+ /// - "event:/player/eat" (default)
+ ///
+ ///
+ void ICraftDataHandler.SetEatingSound(TechType consumable, string soundPath)
+ {
+ CraftDataPatcher.CustomEatingSounds.Add(consumable, soundPath);
+ }
+
+#endregion
+ }
+}
+#endif
\ No newline at end of file
diff --git a/SMLHelper/Handlers/CraftTreeHandler.cs b/SMLHelper/Handlers/CraftTreeHandler.cs
new file mode 100644
index 00000000..11744401
--- /dev/null
+++ b/SMLHelper/Handlers/CraftTreeHandler.cs
@@ -0,0 +1,393 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System;
+ using Crafting;
+ using Interfaces;
+ using Patchers;
+ using Utility;
+ using UnityEngine;
+ using SMLHelper.V2.Patchers.EnumPatching;
+
+ ///
+ /// A handler class for creating and editing of crafting trees.
+ ///
+ public class CraftTreeHandler : ICraftTreeHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ICraftTreeHandler Main { get; } = new CraftTreeHandler();
+
+ private CraftTreeHandler()
+ {
+ // Hides constructor
+ }
+
+ #region Static Methods
+
+ ///
+ /// Your first method call to start a new custom crafting tree.
+ /// Creating a new CraftTree only makes sense if you're going to use it in a new type of .
+ ///
+ /// The name for the new enum.
+ /// The new enum instance for your custom craft tree type.
+ ///
+ /// Returns the root node for your custom craft tree, as a new instance.
+ /// Build up your custom crafting tree from this root node.
+ /// This tree will be automatically patched into the game. No further calls into required.
+ /// For more advanced usage, you can replace the default value of with your own custom function.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static ModCraftTreeRoot CreateCustomCraftTreeAndType(string name, out CraftTree.Type craftTreeType)
+ {
+ return Main.CreateCustomCraftTreeAndType(name, out craftTreeType);
+ }
+
+ ///
+ /// Safely looks for a modded CraftTree Type from another mod in the SMLHelper CraftTreeTypeCache.
+ ///
+ /// The string used to define the modded item's new techtype.
+ ///
+ /// True if the craft tree was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ public static bool ModdedCraftTreeTypeExists(string craftTreeString)
+ {
+ return Main.ModdedCraftTreeTypeExists(craftTreeString);
+ }
+
+ ///
+ /// Adds a new crafting node to the root of the specified crafting tree, at the provided tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The item to craft.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem, params string[] stepsToTab)
+ {
+ Main.AddCraftingNode(craftTree, craftingItem, stepsToTab);
+ }
+
+ ///
+ /// Adds a new crafting node to the root of the specified crafting tree
+ ///
+ /// The target craft tree to edit.
+ /// The item to craft.
+ public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem)
+ {
+ Main.AddCraftingNode(craftTree, craftingItem);
+ }
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Sprite sprite)
+ {
+ Main.AddTabNode(craftTree, name, displayName, sprite);
+ }
+
+#if SUBNAUTICA
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite)
+ {
+ Main.AddTabNode(craftTree, name, displayName, sprite);
+ }
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite, params string[] stepsToTab)
+ {
+ Main.AddTabNode(craftTree, name, displayName, sprite, stepsToTab);
+ }
+
+#endif
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Sprite sprite, params string[] stepsToTab)
+ {
+ Main.AddTabNode(craftTree, name, displayName, sprite, stepsToTab);
+ }
+
+ ///
+ /// Removes a node at the specified node location. Can be used to remove either tabs or craft nodes.
+ /// If a tab node is selected, all child nodes to it will also be removed.
+ ///
+ /// The target craft tree to edit.
+ ///
+ /// The steps to the target node.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// This means matching the id of the crafted item or the id of the tab name.
+ /// Do not include "root" in this path.
+ ///
+ public static void RemoveNode(CraftTree.Type craftTree, params string[] stepsToNode)
+ {
+ Main.RemoveNode(craftTree, stepsToNode);
+ }
+
+ #endregion
+
+ #region Interface Methods
+
+ ///
+ /// Your first method call to start a new custom crafting tree.
+ /// Creating a new CraftTree only makes sense if you're going to use it in a new type of .
+ ///
+ /// The name for the new enum.
+ /// The new enum instance for your custom craft tree type.
+ ///
+ /// Returns the root node for your custom craft tree, as a new instance.
+ /// Build up your custom crafting tree from this root node.
+ /// This tree will be automatically patched into the game. No further calls into required.
+ /// For more advanced usage, you can replace the default value of with your own custom function.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ModCraftTreeRoot ICraftTreeHandler.CreateCustomCraftTreeAndType(string name, out CraftTree.Type craftTreeType)
+ {
+ return CraftTreeTypePatcher.CreateCustomCraftTreeAndType(name, out craftTreeType);
+ }
+
+ ///
+ /// Safely looks for a modded CraftTree Type from another mod in the SMLHelper CraftTreeTypeCache.
+ ///
+ /// The string used to define the modded item's new techtype.
+ ///
+ /// True if the craft tree was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ bool ICraftTreeHandler.ModdedCraftTreeTypeExists(string craftTreeString)
+ {
+ EnumTypeCache cache = CraftTreeTypePatcher.cacheManager.RequestCacheForTypeName(craftTreeString);
+ return cache != null;
+ }
+
+ ///
+ /// Adds a new crafting node to the root of the specified crafting tree, at the provided tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The item to craft.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void ICraftTreeHandler.AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem, params string[] stepsToTab)
+ {
+ ValidateStandardCraftTree(craftTree);
+ CraftTreePatcher.CraftingNodes.Add(new CraftingNode(stepsToTab, craftTree, craftingItem));
+ }
+
+ ///
+ /// Adds a new crafting node to the root of the specified crafting tree
+ ///
+ /// The target craft tree to edit.
+ /// The item to craft.
+
+ void ICraftTreeHandler.AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem)
+ {
+ ValidateStandardCraftTree(craftTree);
+ CraftTreePatcher.CraftingNodes.Add(new CraftingNode(new string[0], craftTree, craftingItem));
+ }
+
+#if SUBNAUTICA
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ void ICraftTreeHandler.AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite)
+ {
+ ValidateStandardCraftTree(craftTree);
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ CraftTreePatcher.TabNodes.Add(new TabNode(new string[0], craftTree, sprite, modName, name, displayName));
+ }
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+
+ void ICraftTreeHandler.AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite)
+ {
+ ValidateStandardCraftTree(craftTree);
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ CraftTreePatcher.TabNodes.Add(new TabNode(new string[0], craftTree, new Atlas.Sprite(sprite), modName, name, displayName));
+ }
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void ICraftTreeHandler.AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite, params string[] stepsToTab)
+ {
+ ValidateStandardCraftTree(craftTree);
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ CraftTreePatcher.TabNodes.Add(new TabNode(stepsToTab, craftTree, sprite, modName, name, displayName));
+ }
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void ICraftTreeHandler.AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite, params string[] stepsToTab)
+ {
+ ValidateStandardCraftTree(craftTree);
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ CraftTreePatcher.TabNodes.Add(new TabNode(stepsToTab, craftTree, new Atlas.Sprite(sprite), modName, name, displayName));
+ }
+
+#elif BELOWZERO
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ void ICraftTreeHandler.AddTabNode(CraftTree.Type craftTree, string name, string displayName, Sprite sprite)
+ {
+ ValidateStandardCraftTree(craftTree);
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ CraftTreePatcher.TabNodes.Add(new TabNode(new string[0], craftTree, sprite, modName, name, displayName));
+ }
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void ICraftTreeHandler.AddTabNode(CraftTree.Type craftTree, string name, string displayName, Sprite sprite, params string[] stepsToTab)
+ {
+ ValidateStandardCraftTree(craftTree);
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ CraftTreePatcher.TabNodes.Add(new TabNode(stepsToTab, craftTree, sprite, modName, name, displayName));
+ }
+
+#endif
+
+ ///
+ /// Removes a node at the specified node location. Can be used to remove either tabs or craft nodes.
+ /// If a tab node is selected, all child nodes to it will also be removed.
+ ///
+ /// The target craft tree to edit.
+ ///
+ /// The steps to the target node.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// This means matching the id of the crafted item or the id of the tab name.
+ /// Do not include "root" in this path.
+ ///
+
+ void ICraftTreeHandler.RemoveNode(CraftTree.Type craftTree, params string[] stepsToNode)
+ {
+ ValidateStandardCraftTree(craftTree);
+ CraftTreePatcher.NodesToRemove.Add(new Node(stepsToNode, craftTree));
+ }
+
+ #endregion
+
+ private static void ValidateStandardCraftTree(CraftTree.Type craftTree)
+ {
+ switch (craftTree)
+ {
+ case CraftTree.Type.Fabricator:
+ case CraftTree.Type.Constructor:
+ case CraftTree.Type.Workbench:
+ case CraftTree.Type.SeamothUpgrades:
+ case CraftTree.Type.MapRoom:
+ case CraftTree.Type.Centrifuge:
+ case CraftTree.Type.CyclopsFabricator:
+ case CraftTree.Type.Rocket:
+#if BELOWZERO
+ case CraftTree.Type.SeaTruckFabricator:
+#endif
+ break; // Okay
+ case CraftTree.Type.Unused1:
+ case CraftTree.Type.Unused2:
+ case CraftTree.Type.None:
+ default:
+ throw new ArgumentException($"{nameof(craftTree)} value of '{craftTree}' does not correspond to a standard crafting tree.{Environment.NewLine}" +
+ $"This method is intended for use only with standard crafting trees, not custom ones or unused ones.");
+ }
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/CustomSoundHandler.cs b/SMLHelper/Handlers/CustomSoundHandler.cs
new file mode 100644
index 00000000..7c359247
--- /dev/null
+++ b/SMLHelper/Handlers/CustomSoundHandler.cs
@@ -0,0 +1,157 @@
+namespace SMLHelper.V2.Handlers
+{
+ using FMOD;
+ using Interfaces;
+ using Patchers;
+ using UnityEngine;
+ using Utility;
+
+ ///
+ /// A handler class for adding and overriding Sounds.
+ ///
+ public class CustomSoundHandler: ICustomSoundHandler
+ {
+
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ICustomSoundHandler Main { get; } = new CustomSoundHandler();
+
+ private CustomSoundHandler()
+ {
+ // Hides constructor
+ }
+
+ #region Interface Methods
+
+ ///
+ /// Register a Custom sound by file path. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The file path on disk of the sound file to load
+ /// The sound channel to get the volume to play the sound at. defaults to
+ /// the loaded
+
+ Sound ICustomSoundHandler.RegisterCustomSound(string id, string filePath, SoundChannel soundChannel)
+ {
+ Sound sound = AudioUtils.CreateSound(filePath);
+ CustomSoundPatcher.CustomSounds[id] = sound;
+ CustomSoundPatcher.CustomSoundChannels[id] = soundChannel;
+ return sound;
+ }
+
+ ///
+ /// Register a custom sound by an instance. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The AudioClip to register.
+ /// The sound channel to get the volume to play the sound at. defaults to
+ /// the registered.
+ Sound ICustomSoundHandler.RegisterCustomSound(string id, AudioClip audio, SoundChannel soundChannel)
+ {
+ var sound = AudioUtils.CreateSound(audio);
+ CustomSoundPatcher.CustomSounds[id] = sound;
+ CustomSoundPatcher.CustomSoundChannels[id] = soundChannel;
+ return sound;
+ }
+
+ ///
+ /// Register a Custom sound that has been loaded using AudioUtils. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The pre loaded sound
+ /// The sound channel to get the volume to play the sound at.
+
+ void ICustomSoundHandler.RegisterCustomSound(string id, Sound sound, SoundChannel soundChannel)
+ {
+ CustomSoundPatcher.CustomSounds[id] = sound;
+ CustomSoundPatcher.CustomSoundChannels[id] = soundChannel;
+ }
+
+ ///
+ /// Try to find and play a custom that has been registered.
+ ///
+ /// The Id of the custom sound
+ void ICustomSoundHandler.TryPlayCustomSound(string id)
+ {
+ if(!CustomSoundPatcher.CustomSounds.TryGetValue(id, out Sound sound)) return;
+ if (!CustomSoundPatcher.CustomSoundChannels.TryGetValue(id, out var soundChannel))
+ soundChannel = SoundChannel.Master;
+ AudioUtils.PlaySound(sound, soundChannel);
+ }
+
+ ///
+ /// Try to get a registered custom .
+ ///
+ /// The Id of the custom sound
+ /// Outputs the if found and null if not found.
+ /// true or false depending on if the id was found
+ bool ICustomSoundHandler.TryGetCustomSound(string id, out Sound sound)
+ {
+ return CustomSoundPatcher.CustomSounds.TryGetValue(id, out sound);
+ }
+
+
+ #endregion
+ #region Static Methods
+
+ ///
+ /// Register a Custom sound by file path. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The file path on disk of the sound file to load
+ /// The sound channel to get the volume to play the sound at. defaults to
+ /// the loaded
+
+ public static Sound RegisterCustomSound(string id, string filePath, SoundChannel soundChannel = SoundChannel.Master)
+ {
+ return Main.RegisterCustomSound(id, filePath, soundChannel);
+ }
+
+ ///
+ /// Register a custom sound by an instance. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The AudioClip to register.
+ /// The sound channel to get the volume to play the sound at. defaults to
+ /// the registered.
+ public static Sound RegisterCustomSound(string id, AudioClip audio, SoundChannel soundChannel = SoundChannel.Master)
+ {
+ return Main.RegisterCustomSound(id, audio, soundChannel);
+ }
+
+ ///
+ /// Register a Custom sound that has been loaded using AudioUtils. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The pre loaded sound
+ /// The sound channel to get the volume to play the sound at.
+
+ public static void RegisterCustomSound(string id, Sound sound, SoundChannel soundChannel = SoundChannel.Master)
+ {
+ Main.RegisterCustomSound(id, sound, soundChannel);
+ }
+
+ ///
+ /// Try to find and play a custom that has been registered.
+ ///
+ /// The Id of the custom sound
+ public static void TryPlayCustomSound(string id)
+ {
+ Main.TryPlayCustomSound(id);
+ }
+
+ ///
+ /// Try to get a registered custom .
+ ///
+ /// The Id of the custom sound
+ /// Outputs the if found and null if not found.
+ /// true or false depending on if the id was found
+ public static bool TryGetCustomSound(string id, out Sound sound)
+ {
+ return Main.TryGetCustomSound(id, out sound);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Handlers/EquipmentHandler.cs b/SMLHelper/Handlers/EquipmentHandler.cs
new file mode 100644
index 00000000..fb120f78
--- /dev/null
+++ b/SMLHelper/Handlers/EquipmentHandler.cs
@@ -0,0 +1,74 @@
+namespace SMLHelper.V2.Handlers
+{
+ using SMLHelper.V2.Handlers;
+ using SMLHelper.V2.Interfaces;
+ using SMLHelper.V2.Patchers.EnumPatching;
+ using SMLHelper.V2.Utility;
+
+ ///
+ /// A handler class for everything related to creating new Equipments.
+ ///
+ public class EquipmentHandler : IEquipmentHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IEquipmentHandler Main { get; } = new EquipmentHandler();
+
+ private EquipmentHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The name of the Equipment. Should not contain special characters.
+ ///
+ /// The new that is created.
+ ///
+ public EquipmentType AddEquipmentType(string equipmentName)
+ {
+ EquipmentType equipment = EquipmentTypePatcher.AddEquipmentType(equipmentName);
+ return equipment;
+ }
+
+ ///
+ /// Safely looks for a modded group from another mod in the SMLHelper EquipmentCache.
+ ///
+ /// The string used to define the techgroup.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ public bool ModdedEquipmentTypeExists(string equipmentString)
+ {
+ EnumTypeCache cache = EquipmentTypePatcher.cacheManager.RequestCacheForTypeName(equipmentString, false);
+ // if we don't have it cached, the mod is not present or not yet loaded
+ return cache != null;
+ }
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper EquipmentCache and outputs its value when found.
+ ///
+ /// The string used to define the techgroup.
+ /// The Equipment enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ public bool TryGetModdedEquipmentType(string equipmentString, out EquipmentType modEquipment)
+ {
+ EnumTypeCache cache = EquipmentTypePatcher.cacheManager.RequestCacheForTypeName(equipmentString, false);
+ if (cache != null) // Item Found
+ {
+ modEquipment = (EquipmentType)cache.Index;
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ modEquipment = EquipmentType.None;
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/SMLHelper/Handlers/FishHandler.cs b/SMLHelper/Handlers/FishHandler.cs
new file mode 100644
index 00000000..c58eef31
--- /dev/null
+++ b/SMLHelper/Handlers/FishHandler.cs
@@ -0,0 +1,70 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using SMLHelper.V2.Assets;
+ using SMLHelper.V2.Interfaces;
+
+ ///
+ /// Class to manage registering of fish into the game
+ ///
+ public class FishHandler : IFishHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IFishHandler Main { get; } = new FishHandler();
+
+ private FishHandler() { }
+
+ ///
+ /// A list of all the custom fish that have so far been registered into the game. This includes ones from mods that may have been loaded earlier.
+ /// It is mainly used by CustomFishPatcher to spawn fish in
+ ///
+ internal static List fishTechTypes = new List();
+
+ ///
+ /// Registers a CustomFish object into the game
+ ///
+ /// The CustomFish that you are registering
+ /// The TechType created using the info from your CustomFish object
+ TechType IFishHandler.RegisterFish(Fish fish)
+ {
+ TechType type = TechTypeHandler.AddTechType(fish.id, fish.displayName, fish.tooltip);
+
+ fishTechTypes.Add(type);
+
+ FishPrefab fishPrefab = new FishPrefab(fish.id, $"WorldEntities/Tools/{fish.id}", type)
+ {
+ modelPrefab = fish.modelPrefab,
+ swimSpeed = fish.swimSpeed,
+ swimRadius = fish.swimRadius,
+ swimInterval = fish.swimInterval,
+ pickupable = fish.isPickupable,
+ isWaterCreature = fish.isWaterCreature
+ };
+
+ if (!string.IsNullOrEmpty(fish.spriteFileName))
+ {
+ SpriteHandler.RegisterSprite(type, Path.Combine(Path.Combine(Environment.CurrentDirectory, "QMods"), fish.spriteFileName));
+ }
+
+ PrefabHandler.RegisterPrefab(fishPrefab);
+
+ Logger.Debug($"Successfully registered fish: '{fish.displayName}' with Tech Type: '{fish.id}'");
+
+ return type;
+ }
+
+ ///
+ /// Registers a CustomFish object into the game
+ ///
+ /// The CustomFish that you are registering
+ /// The TechType created using the info from your CustomFish object
+ public static TechType RegisterFish(Fish fish)
+ {
+ return Main.RegisterFish(fish);
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/IngameMenuHandler.cs b/SMLHelper/Handlers/IngameMenuHandler.cs
new file mode 100644
index 00000000..e7adf0f6
--- /dev/null
+++ b/SMLHelper/Handlers/IngameMenuHandler.cs
@@ -0,0 +1,174 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System;
+ using SMLHelper.V2.Interfaces;
+ using SMLHelper.V2.Patchers;
+
+ ///
+ /// A handler class that offers simple ways to tap into functionality of the in game menu.
+ ///
+ public class IngameMenuHandler : IIngameMenuHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IIngameMenuHandler Main { get; } = new IngameMenuHandler();
+
+ private IngameMenuHandler() { } // Hide constructor
+
+ ///
+ /// Registers a simple method to invoke whenever the player saves the game via the in game menu.
+ ///
+ /// The method to invoke.
+ public static void RegisterOnSaveEvent(Action onSaveAction)
+ {
+ Main.RegisterOnSaveEvent(onSaveAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke whenever the player loads a saved game via the in game menu.
+ ///
+ /// The method to invoke.
+ public static void RegisterOnLoadEvent(Action onLoadAction)
+ {
+ Main.RegisterOnLoadEvent(onLoadAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke whenever the player quits the game via the in game menu.
+ ///
+ /// The method to invoke.
+ public static void RegisterOnQuitEvent(Action onQuitAction)
+ {
+ Main.RegisterOnQuitEvent(onQuitAction);
+ }
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when saving the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ public static void UnregisterOnSaveEvent(Action onSaveAction)
+ {
+ Main.UnregisterOnSaveEvent(onSaveAction);
+ }
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when loading the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ public static void UnregisterOnLoadEvent(Action onLoadAction)
+ {
+ Main.UnregisterOnLoadEvent(onLoadAction);
+ }
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when quitting the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ public static void UnregisterOnQuitEvent(Action onQuitAction)
+ {
+ Main.UnregisterOnQuitEvent(onQuitAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke the first time the player saves the game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ public static void RegisterOneTimeUseOnSaveEvent(Action onSaveAction)
+ {
+ Main.RegisterOneTimeUseOnSaveEvent(onSaveAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke the first time the player loads a saved game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ public static void RegisterOneTimeUseOnLoadEvent(Action onLoadAction)
+ {
+ Main.RegisterOneTimeUseOnLoadEvent(onLoadAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke the first time the player quits the game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ public static void RegisterOneTimeUseOnQuitEvent(Action onQuitAction)
+ {
+ Main.RegisterOneTimeUseOnQuitEvent(onQuitAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke whenever the player saves the game via the in game menu.
+ ///
+ /// The method to invoke.
+ void IIngameMenuHandler.RegisterOnSaveEvent(Action onSaveAction)
+ {
+ IngameMenuPatcher.OnSaveEvents += onSaveAction;
+ }
+
+ void IIngameMenuHandler.RegisterOnLoadEvent(Action onLoadAction)
+ {
+ IngameMenuPatcher.OnLoadEvents += onLoadAction;
+ }
+
+ ///
+ /// Registers a simple method to invoke whenever the player quits the game via the in game menu.
+ ///
+ /// The method to invoke.
+ void IIngameMenuHandler.RegisterOnQuitEvent(Action onQuitAction)
+ {
+ IngameMenuPatcher.OnQuitEvents += onQuitAction;
+ }
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when saving the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ void IIngameMenuHandler.UnregisterOnSaveEvent(Action onSaveAction)
+ {
+ IngameMenuPatcher.OnSaveEvents -= onSaveAction;
+ }
+
+ void IIngameMenuHandler.UnregisterOnLoadEvent(Action onLoadAction)
+ {
+ IngameMenuPatcher.OnLoadEvents -= onLoadAction;
+ }
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when quiting the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ void IIngameMenuHandler.UnregisterOnQuitEvent(Action onQuitAction)
+ {
+ IngameMenuPatcher.OnQuitEvents -= onQuitAction;
+ }
+
+ ///
+ /// Registers a simple method to invoke the first time the player saves the game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ void IIngameMenuHandler.RegisterOneTimeUseOnSaveEvent(Action onSaveAction)
+ {
+ IngameMenuPatcher.AddOneTimeUseSaveEvent(onSaveAction);
+ }
+
+ void IIngameMenuHandler.RegisterOneTimeUseOnLoadEvent(Action onLoadAction)
+ {
+ IngameMenuPatcher.AddOneTimeUseLoadEvent(onLoadAction);
+ }
+
+ ///
+ /// Registers a simple method to invoke the first time the player quits the game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ void IIngameMenuHandler.RegisterOneTimeUseOnQuitEvent(Action onQuitAction)
+ {
+ IngameMenuPatcher.AddOneTimeUseQuitEvent(onQuitAction);
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/ItemActionHandler.cs b/SMLHelper/Handlers/ItemActionHandler.cs
new file mode 100644
index 00000000..8ded7e07
--- /dev/null
+++ b/SMLHelper/Handlers/ItemActionHandler.cs
@@ -0,0 +1,82 @@
+namespace SMLHelper.V2.Handlers
+{
+ using SMLHelper.V2.Interfaces;
+ using SMLHelper.V2.Patchers;
+ using System;
+
+ ///
+ /// A handler class for registering your custom middle click actions for items
+ ///
+ public class ItemActionHandler : IItemActionHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IItemActionHandler Main { get; } = new ItemActionHandler();
+
+ private ItemActionHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Registers a custom left click action for a
+ ///
+ /// The to which the left click action will be assigned
+ /// The method which will be called when a matching with the specified was left-clicked
+ /// The secondary tooltip which will appear in the description of the item
+ /// The condition which must return for the action to be called when the item is clickedIf ommited, the action will always be called
+ void IItemActionHandler.RegisterLeftClickAction(TechType targetTechType, Action callback, string tooltip, Predicate condition)
+ {
+ string languageLine = $"LeftClickAction_{targetTechType.AsString()}";
+ LanguageHandler.SetLanguageLine(languageLine, tooltip);
+
+ condition = condition ?? ((item) => true);
+ ItemActionPatcher.LeftClickActions.Add(targetTechType, new ItemActionPatcher.CustomItemAction(callback, languageLine, condition));
+ }
+
+ ///
+ /// Registers a custom middle click action for a
+ ///
+ /// The which the middle click action will be assigned
+ /// The method which will be called when a matching with the specified was middle-clicked
+ /// The secondary tooltip which will appear in the description of the item
+ /// The condition which must return for the action to be called when the item is clickedIf ommited, the action will always be called
+ void IItemActionHandler.RegisterMiddleClickAction(TechType targetTechType, Action callback, string tooltip, Predicate condition)
+ {
+ string languageLine = $"MiddleClickAction_{targetTechType.AsString()}";
+ LanguageHandler.SetLanguageLine(languageLine, tooltip);
+
+ condition = condition ?? ((item) => true);
+ ItemActionPatcher.MiddleClickActions.Add(targetTechType, new ItemActionPatcher.CustomItemAction(callback, languageLine, condition));
+ }
+
+ #region Static Methods
+
+ ///
+ /// Registers a custom left click action for a
+ ///
+ /// The to which the left click action will be assigned
+ /// The method which will be called when a matching with the specified was left-clicked
+ /// The secondary tooltip which will appear in the description of the item
+ /// The condition which must return for the action to be called when the item is clickedIf ommited, the action will always be called
+ public static void RegisterLeftClickAction(TechType targetTechType, Action callback, string tooltip, Predicate condition = null)
+ {
+ Main.RegisterLeftClickAction(targetTechType, callback, tooltip, condition);
+ }
+
+ ///
+ /// Registers a custom middle click action for a
+ ///
+ /// The which the middle click action will be assigned
+ /// The method which will be called when a matching with the specified was middle-clicked
+ /// The secondary tooltip which will appear in the description of the item
+ /// The condition which must return for the action to be called when the item is clickedIf ommited, the action will always be called
+ public static void RegisterMiddleClickAction(TechType targetTechType, Action callback, string tooltip, Predicate condition = null)
+ {
+ Main.RegisterMiddleClickAction(targetTechType, callback, tooltip, condition);
+ }
+
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/KnownTechHandler.cs b/SMLHelper/Handlers/KnownTechHandler.cs
new file mode 100644
index 00000000..060c11be
--- /dev/null
+++ b/SMLHelper/Handlers/KnownTechHandler.cs
@@ -0,0 +1,173 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Patchers;
+ using System.Collections.Generic;
+ using UnityEngine;
+
+ ///
+ /// A handler class for configuring custom unlocking conditions for item blueprints.
+ ///
+ public class KnownTechHandler : IKnownTechHandler
+ {
+ private static readonly KnownTechHandler singleton = new KnownTechHandler();
+
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IKnownTechHandler Main => singleton;
+
+ private KnownTechHandler()
+ {
+ // Hides constructor
+ }
+
+ ///
+ /// Allows you to unlock a TechType on game start.
+ ///
+ ///
+ public static void UnlockOnStart(TechType techType)
+ {
+ Main.UnlockOnStart(techType);
+ }
+
+ ///
+ /// Allows you to unlock a TechType on game start.
+ ///
+ ///
+ void IKnownTechHandler.UnlockOnStart(TechType techType)
+ {
+ KnownTechPatcher.UnlockedAtStart.Add(techType);
+ }
+
+ internal void AddAnalysisTech(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage = "NotificationBlueprintUnlocked", FMODAsset UnlockSound = null, UnityEngine.Sprite UnlockSprite = null)
+ {
+ if (KnownTechPatcher.AnalysisTech.TryGetValue(techTypeToBeAnalysed, out KnownTech.AnalysisTech existingEntry))
+ {
+ existingEntry.unlockMessage = existingEntry.unlockMessage ?? UnlockMessage;
+ existingEntry.unlockSound = existingEntry.unlockSound ?? UnlockSound;
+ existingEntry.unlockPopup = existingEntry.unlockPopup ?? UnlockSprite;
+ existingEntry.unlockTechTypes.AddRange(techTypesToUnlock);
+ }
+ else
+ {
+ KnownTechPatcher.AnalysisTech.Add(techTypeToBeAnalysed, new KnownTech.AnalysisTech()
+ {
+ techType = techTypeToBeAnalysed,
+ unlockMessage = UnlockMessage,
+ unlockSound = UnlockSound,
+ unlockPopup = UnlockSprite,
+ unlockTechTypes = new List(techTypesToUnlock)
+ });
+ }
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ /// The sprite that shows up when you unlock the blueprint.
+ public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage = "NotificationBlueprintUnlocked", FMODAsset UnlockSound = null, UnityEngine.Sprite UnlockSprite = null)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, UnlockMessage, UnlockSound, UnlockSprite);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, UnlockMessage);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, FMODAsset UnlockSound)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, "NotificationBlueprintUnlocked", UnlockSound);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The sprite that shows up when you unlock the blueprint.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, Sprite UnlockSprite)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, "NotificationBlueprintUnlocked", null, UnlockSprite);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage, FMODAsset UnlockSound)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, UnlockMessage, UnlockSound, null);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ /// The sprite that shows up when you unlock the blueprint.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage, Sprite UnlockSprite)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, UnlockMessage, null, UnlockSprite);
+ }
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ /// The sprite that shows up when you unlock the blueprint.
+ void IKnownTechHandler.SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, FMODAsset UnlockSound, Sprite UnlockSprite)
+ {
+ singleton.AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, "NotificationBlueprintUnlocked", UnlockSound, UnlockSprite);
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/LanguageHandler.cs b/SMLHelper/Handlers/LanguageHandler.cs
new file mode 100644
index 00000000..f160fb5c
--- /dev/null
+++ b/SMLHelper/Handlers/LanguageHandler.cs
@@ -0,0 +1,96 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Patchers;
+ using Utility;
+
+ ///
+ /// A handler for adding custom language lines.
+ ///
+ public class LanguageHandler : ILanguageHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ILanguageHandler Main { get; } = new LanguageHandler();
+
+ private LanguageHandler()
+ {
+ // Hides contructor
+ }
+
+ #region Static Methods
+
+ ///
+ /// Allows you to define a language entry into the game.
+ ///
+ /// The ID of the entry, this is what is used to get the actual text.
+ /// The actual text related to the entry.
+ public static void SetLanguageLine(string lineId, string text)
+ {
+ Main.SetLanguageLine(lineId, text);
+ }
+
+ ///
+ /// Allows you to set the display name of a specific .
+ ///
+ /// The whose display name that is to be changed.
+ /// The new display name for the chosen .
+ public static void SetTechTypeName(TechType techType, string text)
+ {
+ Main.SetTechTypeName(techType, text);
+ }
+
+ ///
+ /// Allows you to set the tooltip of a specific .
+ ///
+ /// The whose tooltip that is to be changed.
+ /// The new tooltip for the chosen .
+ public static void SetTechTypeTooltip(TechType techType, string text)
+ {
+ Main.SetTechTypeTooltip(techType, text);
+ }
+
+ #endregion
+
+ #region Interface Methods
+
+ ///
+ /// Allows you to define a language entry into the game.
+ ///
+ /// The ID of the entry, this is what is used to get the actual text.
+ /// The actual text related to the entry.
+ void ILanguageHandler.SetLanguageLine(string lineId, string text)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ LanguagePatcher.AddCustomLanguageLine(modName, lineId, text);
+ }
+
+ ///
+ /// Allows you to set the display name of a specific .
+ ///
+ /// The whose display name that is to be changed.
+ /// The new display name for the chosen .
+ void ILanguageHandler.SetTechTypeName(TechType techType, string text)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ LanguagePatcher.AddCustomLanguageLine(modName, techType.AsString(), text);
+ }
+
+ ///
+ /// Allows you to set the tooltip of a specific .
+ ///
+ /// The whose tooltip that is to be changed.
+ /// The new tooltip for the chosen .
+ void ILanguageHandler.SetTechTypeTooltip(TechType techType, string text)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ LanguagePatcher.AddCustomLanguageLine(modName, $"Tooltip_{techType.AsString()}", text);
+ }
+
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/LootDistributionHandler.cs b/SMLHelper/Handlers/LootDistributionHandler.cs
new file mode 100644
index 00000000..362c3ca0
--- /dev/null
+++ b/SMLHelper/Handlers/LootDistributionHandler.cs
@@ -0,0 +1,232 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System.Collections.Generic;
+ using Interfaces;
+ using Patchers;
+ using SMLHelper.V2.Assets;
+ using UWE;
+
+ ///
+ /// A handler that manages Loot Distribution.
+ ///
+ public class LootDistributionHandler : ILootDistributionHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ILootDistributionHandler Main { get; } = new LootDistributionHandler();
+
+ private LootDistributionHandler() { } // Hides constructor
+
+ #region Static Methods
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ ///
+ ///
+ ///
+ public static void AddLootDistributionData(string classId, LootDistributionData.SrcData data)
+ {
+ Main.AddLootDistributionData(classId, data);
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The that contains data related to the spawning of a prefab, also contains the path to the prefab.
+ /// The classId of the prefab.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ public static void AddLootDistributionData(string classId, LootDistributionData.SrcData data, WorldEntityInfo info)
+ {
+ Main.AddLootDistributionData(classId, data, info);
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The classId of the prefab.
+ /// The prefab path of the prefab.
+ /// The dictating how the prefab should spawn in the world.
+ public static void AddLootDistributionData(string classId, string prefabPath, IEnumerable biomeDistribution)
+ {
+ Main.AddLootDistributionData(classId, prefabPath, biomeDistribution);
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The classId of the prefab.
+ /// The prefab path of the prefab.
+ /// The dictating how the prefab should spawn in the world.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ public static void AddLootDistributionData(string classId, string prefabPath, IEnumerable biomeDistribution, WorldEntityInfo info)
+ {
+ Main.AddLootDistributionData(classId, prefabPath, biomeDistribution, info);
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The custom prefab which you want to spawn naturally in the game.
+ /// The dictating how the prefab should spawn in the world.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ public static void AddLootDistributionData(ModPrefab prefab, IEnumerable biomeDistribution, WorldEntityInfo info)
+ {
+ Main.AddLootDistributionData(prefab, biomeDistribution, info);
+ }
+
+ ///
+ /// Edits Loot Distribution Data for existing/original class IDs.
+ ///
+ public static void EditLootDistributionData(string classId, BiomeType biome, float probability, int count)
+ {
+ Main.EditLootDistributionData(classId, biome, probability, count);
+ }
+
+ ///
+ /// Edits Loot Distribution data for existing prefabs, for e.g. original game prefabs.
+ ///
+ /// The ClassID of the prefab. If unsure, use CraftData.GetClassIdForTechType.
+ /// The list of that contains information about how/when it should spawn in biomes.
+ public static void EditLootDistributionData(string classId, IEnumerable biomeDistribution)
+ {
+ Main.EditLootDistributionData(classId, biomeDistribution);
+ }
+
+ #endregion
+
+ #region Interface methods
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The that contains data related to the spawning of a prefab, also contains the path to the prefab.
+ /// The classId of the prefab.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ void ILootDistributionHandler.AddLootDistributionData(string classId, LootDistributionData.SrcData data, WorldEntityInfo info)
+ {
+ Main.AddLootDistributionData(classId, data);
+
+ WorldEntityDatabaseHandler.AddCustomInfo(classId, info);
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The classId of the prefab.
+ /// The prefab path of the prefab.
+ /// The dictating how the prefab should spawn in the world.
+ void ILootDistributionHandler.AddLootDistributionData(string classId, string prefabPath, IEnumerable biomeDistribution)
+ {
+ Main.AddLootDistributionData(classId, new LootDistributionData.SrcData()
+ {
+ distribution = new List(biomeDistribution),
+ prefabPath = prefabPath
+ });
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ ///
+ /// The classId of the prefab.
+ /// The prefab path of the prefab.
+ /// The dictating how the prefab should spawn in the world.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ void ILootDistributionHandler.AddLootDistributionData(string classId, string prefabPath, IEnumerable biomeDistribution, WorldEntityInfo info)
+ {
+ Main.AddLootDistributionData(classId, new LootDistributionData.SrcData()
+ {
+ distribution = new List(biomeDistribution),
+ prefabPath = prefabPath
+ });
+
+ WorldEntityDatabaseHandler.AddCustomInfo(classId, info);
+ }
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ ///
+ /// The custom prefab which you want to spawn naturally in the game.
+ /// The dictating how the prefab should spawn in the world.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ void ILootDistributionHandler.AddLootDistributionData(ModPrefab prefab, IEnumerable biomeDistribution, WorldEntityInfo info)
+ {
+ Main.AddLootDistributionData(prefab.ClassID, prefab.PrefabFileName, biomeDistribution);
+
+ WorldEntityDatabaseHandler.AddCustomInfo(prefab.ClassID, info);
+ }
+
+ void ILootDistributionHandler.AddLootDistributionData(string classId, LootDistributionData.SrcData data)
+ {
+ if (LootDistributionPatcher.CustomSrcData.ContainsKey(classId))
+ Logger.Log($"{classId}-{data.prefabPath} already has custom distribution data. Replacing with latest.", LogLevel.Debug);
+
+ LootDistributionPatcher.CustomSrcData[classId] = data;
+ }
+
+ void ILootDistributionHandler.EditLootDistributionData(string classId, BiomeType biome, float probability, int count)
+ {
+ if (!LootDistributionPatcher.CustomSrcData.TryGetValue(classId, out LootDistributionData.SrcData srcData))
+ {
+ LootDistributionPatcher.CustomSrcData[classId] = (srcData = new LootDistributionData.SrcData());
+
+ var biomeDistribution = new List
+ {
+ new LootDistributionData.BiomeData()
+ {
+ biome = biome,
+ probability = probability,
+ count = count
+ }
+ };
+
+ srcData.distribution = biomeDistribution;
+
+ return;
+ }
+
+ for (int i = 0; i < srcData.distribution.Count; i++)
+ {
+ LootDistributionData.BiomeData distribution = srcData.distribution[i];
+
+ if (distribution.biome == biome)
+ {
+ distribution.count = count;
+ distribution.probability = probability;
+
+ return;
+ }
+ }
+
+ // If we reached this point, that means the srcData is present, but the biome in the distribution is not.
+ // Lets add it manually.
+ srcData.distribution.Add(new LootDistributionData.BiomeData()
+ {
+ biome = biome,
+ probability = probability,
+ count = count
+ });
+ }
+
+ ///
+ /// Edits Loot Distribution data for existing prefabs, for e.g. original game prefabs.
+ ///
+ /// The ClassID of the prefab. If unsure, use CraftData.GetClassIdForTechType.
+ /// The list of that contains information about how/when it should spawn in biomes.
+ void ILootDistributionHandler.EditLootDistributionData(string classId, IEnumerable biomeDistribution)
+ {
+ foreach (LootDistributionData.BiomeData distribution in biomeDistribution)
+ {
+ Main.EditLootDistributionData(classId, distribution.biome, distribution.probability, distribution.count);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/OptionsPanelHandler.cs b/SMLHelper/Handlers/OptionsPanelHandler.cs
new file mode 100644
index 00000000..80331d3e
--- /dev/null
+++ b/SMLHelper/Handlers/OptionsPanelHandler.cs
@@ -0,0 +1,82 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Options;
+ using Options.Attributes;
+ using Patchers;
+ using Interfaces;
+ using Json;
+ using System.Reflection;
+
+ ///
+ /// A handler class for registering your custom in-game mod options.
+ ///
+ public class OptionsPanelHandler : IOptionsPanelHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IOptionsPanelHandler Main { get; } = new OptionsPanelHandler();
+
+ private OptionsPanelHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Registers your mod options to the in-game menu.
+ ///
+ /// The mod options. Create a new child class inheriting from this one
+ /// and add your options to it.
+ ///
+ public static void RegisterModOptions(ModOptions options)
+ {
+ Main.RegisterModOptions(options);
+ }
+
+ ///
+ /// Registers your mod options to the in-game menu.
+ ///
+ /// The mod options. Create a new child class inheriting from this one
+ /// and add your options to it.
+ ///
+ void IOptionsPanelHandler.RegisterModOptions(ModOptions options)
+ {
+ OptionsPanelPatcher.modOptions.Add(options.Name, options);
+ }
+
+ ///
+ /// Generates an options menu based on the attributes and members declared in the
+ /// and registers it to the in-game menu.
+ ///
+ /// A class derived from to generate the options menu from.
+ /// An instance of the : with values loaded
+ /// from the config file on disk.
+ public static T RegisterModOptions() where T : ConfigFile, new()
+ => Main.RegisterModOptions();
+
+ ///
+ /// Generates an options menu based on the attributes and members declared in the
+ /// and registers it to the in-game menu.
+ ///
+ /// A class derived from to generate the options menu from.
+ /// An instance of the : with values loaded
+ /// from the config file on disk.
+ T IOptionsPanelHandler.RegisterModOptions()
+ {
+ var optionsMenuBuilder = new OptionsMenuBuilder();
+ RegisterModOptions(optionsMenuBuilder);
+ optionsMenuBuilder.ConfigFileMetadata.Registered = true;
+
+ var menuAttribute = typeof(T).GetCustomAttribute(true)
+ ?? new MenuAttribute(optionsMenuBuilder.Name);
+
+ if (menuAttribute.SaveOn.HasFlag(MenuAttribute.SaveEvents.SaveGame))
+ IngameMenuHandler.RegisterOnSaveEvent(() => optionsMenuBuilder.ConfigFileMetadata.Config.Save());
+
+ if (menuAttribute.SaveOn.HasFlag(MenuAttribute.SaveEvents.QuitGame))
+ IngameMenuHandler.RegisterOnQuitEvent(() => optionsMenuBuilder.ConfigFileMetadata.Config.Save());
+
+ return optionsMenuBuilder.ConfigFileMetadata.Config;
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/PDAEncyclopediaHandler.cs b/SMLHelper/Handlers/PDAEncyclopediaHandler.cs
new file mode 100644
index 00000000..5be808b6
--- /dev/null
+++ b/SMLHelper/Handlers/PDAEncyclopediaHandler.cs
@@ -0,0 +1,38 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Patchers;
+
+ ///
+ /// Handles Encyclopedia.
+ ///
+ public class PDAEncyclopediaHandler : IPDAEncyclopediaHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IPDAEncyclopediaHandler Main { get; } = new PDAEncyclopediaHandler();
+
+ private PDAEncyclopediaHandler()
+ {
+ // Hide constructor
+ }
+
+ void IPDAEncyclopediaHandler.AddCustomEntry(PDAEncyclopedia.EntryData entry)
+ {
+ if (PDAEncyclopediaPatcher.CustomEntryData.ContainsKey(entry.key))
+ Logger.Log($"{entry.key} already has custom PDAEncyclopedia.EntryData. Replacing with latest.", LogLevel.Debug);
+
+ PDAEncyclopediaPatcher.CustomEntryData[entry.key] = entry;
+ }
+
+ ///
+ /// Adds custom entry.
+ ///
+ /// The entry.
+ public static void AddCustomEntry(PDAEncyclopedia.EntryData entry)
+ {
+ Main.AddCustomEntry(entry);
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/PDAHandler.cs b/SMLHelper/Handlers/PDAHandler.cs
new file mode 100644
index 00000000..78ed4efe
--- /dev/null
+++ b/SMLHelper/Handlers/PDAHandler.cs
@@ -0,0 +1,115 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Patchers;
+
+ ///
+ /// A handler class for various scanner related data.
+ ///
+ public class PDAHandler : IPDAHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IPDAHandler Main { get; } = new PDAHandler();
+
+ private PDAHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Edits how many fragments must be scanned before unlocking the techtype's blueprint.
+ ///
+ /// Can be either techtype of the fragment or the crafted item.
+ /// The number of fragments to scan.
+ void IPDAHandler.EditFragmentsToScan(TechType techType, int fragmentCount)
+ {
+ if (fragmentCount <= 0)
+ {
+ fragmentCount = 1;
+ }
+
+ PDAPatcher.FragmentCount[techType] = fragmentCount;
+ }
+
+ ///
+ /// Edits the time it takes to finish scanning a fragment.
+ ///
+ /// Can be either techtype of the fragment or the crafted item.
+ /// The relative time spent on scanning. Default value is 1.
+ void IPDAHandler.EditFragmentScanTime(TechType techType, float scanTime)
+ {
+ if (scanTime <= 0f)
+ {
+ scanTime = 1f;
+ }
+
+ PDAPatcher.FragmentScanTime[techType] = scanTime;
+ }
+
+ ///
+ /// Adds in a custom . ***Cannot be used to Change the values of a techtype that has data already!***
+ ///
+ /// The of the entry. Must be populated when passed in.
+ void IPDAHandler.AddCustomScannerEntry(PDAScanner.EntryData entryData)
+ {
+ if (PDAPatcher.CustomEntryData.ContainsKey(entryData.key))
+ Logger.Log($"{entryData.key} already has custom PDAScanner.EntryData. Replacing with latest.", LogLevel.Debug);
+
+ PDAPatcher.CustomEntryData[entryData.key] = entryData;
+ }
+
+ ///
+ /// Edits how many fragments must be scanned before unlocking the techtype's blueprint.
+ ///
+ /// Can be either techtype of the fragment or the crafted item.
+ /// The number of fragments to scan.
+ public static void EditFragmentsToScan(TechType techType, int fragmentCount)
+ {
+ Main.EditFragmentsToScan(techType, fragmentCount);
+ }
+
+ ///
+ /// Edits the time it takes to finish scanning a fragment.
+ ///
+ /// Can be either techtype of the fragment or the crafted item.
+ /// The relative time spent on scanning. Default value is 1.
+ public static void EditFragmentScanTime(TechType techType, float scanTime)
+ {
+ Main.EditFragmentScanTime(techType, scanTime);
+ }
+
+ ///
+ /// Adds in a custom .
+ ///
+ /// The of the entry. Must be populated when passed in.
+ public static void AddCustomScannerEntry(PDAScanner.EntryData entryData)
+ {
+ Main.AddCustomScannerEntry(entryData);
+ }
+
+ ///
+ /// Adds in a custom .
+ ///
+ /// The scanned object's . In case of fragments, the fragment is the key.
+ /// The when unlocked when scanned. In case of fragments, this is the actual that unlocks when all fragments are scanned.
+ /// Whether the is a fragment or not.
+ /// The total amount of objects of that need to be scanned to unlock the and .
+ /// The amount of time it takes to finish one scan. In seconds.
+ /// Whether the object should be destroyed after the scan is finished.
+ /// The key to the encyclopedia entry.
+ public static void AddCustomScannerEntry(TechType key, TechType blueprint, bool isFragment, string encyclopediaKey, int totalFragmentsRequired = 2, float scanTime = 2f, bool destroyAfterScan = true)
+ {
+ Main.AddCustomScannerEntry(new PDAScanner.EntryData()
+ {
+ key = key,
+ blueprint = blueprint,
+ isFragment = isFragment,
+ totalFragments = totalFragmentsRequired,
+ scanTime = scanTime,
+ destroyAfterScan = destroyAfterScan
+ });
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/PingHandler.cs b/SMLHelper/Handlers/PingHandler.cs
new file mode 100644
index 00000000..2801ca2d
--- /dev/null
+++ b/SMLHelper/Handlers/PingHandler.cs
@@ -0,0 +1,79 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Patchers;
+ using Interfaces;
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+ using SMLHelper.V2.Patchers.EnumPatching;
+#elif BELOWZERO
+ using Sprite = UnityEngine.Sprite;
+ using SMLHelper.V2.Patchers.EnumPatching;
+#endif
+
+ ///
+ /// A handler related to PingTypes
+ ///
+ public class PingHandler : IPingHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IPingHandler Main { get; } = new PingHandler();
+
+ private PingHandler()
+ {
+ }
+
+ ///
+ /// Registers a ping type for use when creating a beacon
+ ///
+ /// The name of the new ping type
+ /// The sprite that is associated with the ping
+ /// The newly registered PingType
+ public static PingType RegisterNewPingType(string pingName, Sprite sprite)
+ {
+ return Main.RegisterNewPingType(pingName, sprite);
+ }
+
+ ///
+ /// Safely looks for a modded ping type in the SMLHelper PingTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded PingType
+ /// The PingType enum value. Defaults to when the PingType was not found.
+ /// True if the PingType was found; Otherwise false
+ public static bool TryGetModdedPingType(string pingTypeString, out PingType moddedPingType)
+ {
+ return Main.TryGetModdedPingType(pingTypeString, out moddedPingType);
+ }
+
+ ///
+ /// Registers a ping type for use when creating a beacon
+ ///
+ /// The name of the new ping type
+ /// The sprite that is associated with the ping
+ /// The newly registered PingType
+ PingType IPingHandler.RegisterNewPingType(string pingName, Sprite sprite)
+ {
+ return PingTypePatcher.AddPingType(pingName, sprite);
+ }
+
+ ///
+ /// Safely looks for a modded ping type in the SMLHelper PingTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded PingType
+ /// The PingType enum value. Defaults to when the PingType was not found.
+ /// True if the PingType was found; Otherwise false
+ bool IPingHandler.TryGetModdedPingType(string pingTypeString, out PingType moddedPingType)
+ {
+ var cache = PingTypePatcher.cacheManager.RequestCacheForTypeName(pingTypeString, false);
+ if (cache != null)
+ {
+ moddedPingType = (PingType) cache.Index;
+ return true;
+ }
+
+ moddedPingType = PingType.None;
+ return false;
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/PrefabHandler.cs b/SMLHelper/Handlers/PrefabHandler.cs
new file mode 100644
index 00000000..8829125e
--- /dev/null
+++ b/SMLHelper/Handlers/PrefabHandler.cs
@@ -0,0 +1,47 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Assets;
+ using Interfaces;
+
+ ///
+ /// A handler for registering Unity prefabs associated to a .
+ ///
+ public class PrefabHandler : IPrefabHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IPrefabHandler Main { get; } = new PrefabHandler();
+
+ private PrefabHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Registers a ModPrefab into the game.
+ ///
+ /// The mod prefab to register. Create a child class inheriting off this one and configure as needed.
+ ///
+ void IPrefabHandler.RegisterPrefab(ModPrefab prefab)
+ {
+ foreach (ModPrefab modPrefab in ModPrefab.Prefabs)
+ {
+ if (modPrefab.TechType == prefab.TechType || modPrefab.ClassID == prefab.ClassID || modPrefab.PrefabFileName == prefab.PrefabFileName)
+ return;
+ }
+
+ ModPrefab.Add(prefab);
+ }
+
+ ///
+ /// Registers a ModPrefab into the game.
+ ///
+ /// The mod prefab to register. Create a child class inheriting off this one and configure as needed.
+ ///
+ public static void RegisterPrefab(ModPrefab prefab)
+ {
+ Main.RegisterPrefab(prefab);
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/SaveDataHandler.cs b/SMLHelper/Handlers/SaveDataHandler.cs
new file mode 100644
index 00000000..d8d5c557
--- /dev/null
+++ b/SMLHelper/Handlers/SaveDataHandler.cs
@@ -0,0 +1,31 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using Json;
+
+ ///
+ /// A handler class for registering your .
+ ///
+ public class SaveDataHandler : ISaveDataHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ISaveDataHandler Main { get; } = new SaveDataHandler();
+
+ private SaveDataHandler()
+ {
+ // Hide Constructor
+ }
+
+ T ISaveDataHandler.RegisterSaveDataCache()
+ {
+ T cache = new();
+
+ IngameMenuHandler.Main.RegisterOnLoadEvent(() => cache.Load());
+ IngameMenuHandler.Main.RegisterOnSaveEvent(() => cache.Save());
+
+ return cache;
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/SpriteHandler.cs b/SMLHelper/Handlers/SpriteHandler.cs
new file mode 100644
index 00000000..436ad132
--- /dev/null
+++ b/SMLHelper/Handlers/SpriteHandler.cs
@@ -0,0 +1,316 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Assets;
+ using Interfaces;
+ using UnityEngine;
+ using Utility;
+
+ ///
+ /// A handler class for adding custom sprites into the game.
+ ///
+ public class SpriteHandler : ISpriteHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ISpriteHandler Main { get; } = new SpriteHandler();
+
+ private SpriteHandler()
+ {
+ // Hide constructor
+ }
+
+ #region Static Methods
+
+#if SUBNAUTICA
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public static void RegisterSprite(TechType type, Atlas.Sprite sprite)
+ {
+ Main.RegisterSprite(type, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ public static void RegisterSprite(SpriteManager.Group group, string id, Atlas.Sprite sprite)
+ {
+ Main.RegisterSprite(group, id, sprite);
+ }
+#endif
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group this sprite will be added to.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ public static void RegisterSprite(SpriteManager.Group group, string id, Sprite sprite)
+ {
+ Main.RegisterSprite(group, id, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ public static void RegisterSprite(TechType type, Sprite sprite)
+ {
+ Main.RegisterSprite(type, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ ///
+ public static void RegisterSprite(TechType type, string filePathToImage)
+ {
+ Main.RegisterSprite(type, filePathToImage);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ public static void RegisterSprite(TechType type, string filePathToImage, TextureFormat format)
+ {
+ Main.RegisterSprite(type, filePathToImage, format);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ ///
+ public static void RegisterSprite(SpriteManager.Group group, string id, string filePathToImage)
+ {
+ Main.RegisterSprite(group, id, filePathToImage);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ public static void RegisterSprite(SpriteManager.Group group, string id, string filePathToImage, TextureFormat format)
+ {
+ Main.RegisterSprite(group, id, filePathToImage, format);
+ }
+
+ #endregion
+
+ #region Interface Methods
+
+#if SUBNAUTICA
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ void ISpriteHandler.RegisterSprite(TechType type, Atlas.Sprite sprite)
+ {
+ ModSprite.Add(SpriteManager.Group.None, type.AsString(), sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, Atlas.Sprite sprite)
+ {
+ ModSprite.Add(group, id, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group this sprite will be added to.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, Sprite sprite)
+ {
+ ModSprite.Add(group, id, new Atlas.Sprite(sprite));
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ void ISpriteHandler.RegisterSprite(TechType type, Sprite sprite)
+ {
+ ModSprite.Add(SpriteManager.Group.None, type.AsString(), new Atlas.Sprite(sprite));
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ ///
+ void ISpriteHandler.RegisterSprite(TechType type, string filePathToImage)
+ {
+ Atlas.Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, TextureFormat.BC7);
+
+ Main.RegisterSprite(type, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ void ISpriteHandler.RegisterSprite(TechType type, string filePathToImage, TextureFormat format)
+ {
+ Atlas.Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, format);
+
+ Main.RegisterSprite(type, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ ///
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, string filePathToImage)
+ {
+ Atlas.Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, TextureFormat.BC7);
+
+ Main.RegisterSprite(group, id, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, string filePathToImage, TextureFormat format)
+ {
+ Atlas.Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, format);
+
+ Main.RegisterSprite(group, id, sprite);
+ }
+
+#elif BELOWZERO
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group this sprite will be added to.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, Sprite sprite)
+ {
+ ModSprite.Add(group, id, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ void ISpriteHandler.RegisterSprite(TechType type, Sprite sprite)
+ {
+ ModSprite.Add(SpriteManager.Group.None, type.AsString(), sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ ///
+ void ISpriteHandler.RegisterSprite(TechType type, string filePathToImage)
+ {
+ Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, TextureFormat.BC7);
+
+ Main.RegisterSprite(type, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ void ISpriteHandler.RegisterSprite(TechType type, string filePathToImage, TextureFormat format)
+ {
+ Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, format);
+
+ Main.RegisterSprite(type, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ ///
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, string filePathToImage)
+ {
+ Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, TextureFormat.BC7);
+
+ Main.RegisterSprite(group, id, sprite);
+ }
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ void ISpriteHandler.RegisterSprite(SpriteManager.Group group, string id, string filePathToImage, TextureFormat format)
+ {
+ Sprite sprite = ImageUtils.LoadSpriteFromFile(filePathToImage, format);
+
+ Main.RegisterSprite(group, id, sprite);
+ }
+
+
+#endif
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/SurvivalHandler.cs b/SMLHelper/Handlers/SurvivalHandler.cs
new file mode 100644
index 00000000..c6959886
--- /dev/null
+++ b/SMLHelper/Handlers/SurvivalHandler.cs
@@ -0,0 +1,107 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System;
+ using System.Collections.Generic;
+ using Patchers;
+ using Interfaces;
+
+ ///
+ /// a common handler for uses specified to the Survival component
+ ///
+ public class SurvivalHandler : ISurvivalHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ISurvivalHandler Main { get; } = new SurvivalHandler();
+
+ private SurvivalHandler()
+ {
+ // Hides constructor
+ }
+
+ #region Implementation Methods
+ ///
+ /// makes the item gives oxygen on use.
+ ///
+ /// the TechType that you want to make it give oxygen on use
+ /// the oxygen amount the item gives
+ /// set it to if the item is edible and has the component attached to it.
+ /// defaults to
+ ///
+ void ISurvivalHandler.GiveOxygenOnConsume(TechType techType, float oxygenGiven, bool isEdible)
+ {
+ if (SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List action))
+ {
+ action.Add(() => { Player.main.GetComponent().AddOxygen(oxygenGiven); }); // add an action to the list
+ return;
+ }
+
+ // if we reach to this point then the techtype doesn't exist in the dictionary so we add it
+ SurvivalPatcher.CustomSurvivalInventoryAction[techType] = new List()
+ {
+ () =>
+ {
+ Player.main.GetComponent().AddOxygen(oxygenGiven);
+ }
+ };
+ if (!isEdible)
+ SurvivalPatcher.InventoryUseables.Add(techType);
+ }
+ ///
+ /// makes the item Heal the player on consume.
+ ///
+ /// the TechType that you want it to heal back
+ /// amount to heal the player
+ /// set it to if the item is edible and has the component attached to it.
+ /// defaults to
+ ///
+ void ISurvivalHandler.GiveHealthOnConsume(TechType techType, float healthBack, bool isEdible)
+ {
+ if (SurvivalPatcher.CustomSurvivalInventoryAction.TryGetValue(techType, out List action))
+ {
+ action.Add(() => { Player.main.GetComponent().AddHealth(healthBack); }); // add an action to the list
+ return;
+ }
+
+ // if we reach to this point then the techtype doesn't exist in the dictionary so we add it
+ SurvivalPatcher.CustomSurvivalInventoryAction[techType] = new List()
+ {
+ () =>
+ {
+ Player.main.GetComponent().AddHealth(healthBack);
+ }
+ };
+ if (!isEdible)
+ SurvivalPatcher.InventoryUseables.Add(techType);
+ }
+ #endregion
+
+ #region Static Methods
+ ///
+ /// makes the item gives oxygen on use.
+ ///
+ /// the TechType that you want to make it give oxygen on use
+ /// the oxygen amount the item gives
+ /// set it to if the item is edible and has the component attached to it.
+ /// defaults to
+ ///
+ public static void GiveOxygenOnConsume(TechType techType, float oxygenGiven, bool isEdible = false)
+ {
+ Main.GiveOxygenOnConsume(techType, oxygenGiven, isEdible);
+ }
+ ///
+ /// makes the item Heal the player on consume.
+ ///
+ /// the TechType that you want it to heal back
+ /// amount to heal the player
+ /// set it to if the item is edible and has the component attached to it.
+ /// defaults to
+ ///
+ public static void GiveHealthOnConsume(TechType techType, float healthBack, bool isEdible = false)
+ {
+ Main.GiveHealthOnConsume(techType, healthBack, isEdible);
+ }
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/TechCategoryHandler.cs b/SMLHelper/Handlers/TechCategoryHandler.cs
new file mode 100644
index 00000000..840c3b5f
--- /dev/null
+++ b/SMLHelper/Handlers/TechCategoryHandler.cs
@@ -0,0 +1,111 @@
+namespace SMLHelper.V2.Handlers
+{
+ using SMLHelper.V2.Interfaces;
+ using SMLHelper.V2.Patchers.EnumPatching;
+ using SMLHelper.V2.Utility;
+ using System.Collections.Generic;
+
+ ///
+ /// A handler class for everything related to creating new TechCategories.
+ ///
+ ///
+ public class TechCategoryHandler: ITechCategoryHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ITechCategoryHandler Main { get; } = new TechCategoryHandler();
+
+ private TechCategoryHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The name of the TechCategory. Should not contain special characters.
+ /// The display name of the TechCategory. Can be anything.
+ ///
+ /// The new that is created.
+ ///
+ public TechCategory AddTechCategory(string techCatagoryName, string displayName)
+ {
+ TechCategory techCategory = TechCategoryPatcher.AddTechCategory(techCatagoryName);
+
+ Dictionary valueToString = uGUI_BlueprintsTab.techCategoryStrings.valueToString;
+ valueToString[techCategory] = "TechCategory" + techCatagoryName;
+
+
+ LanguageHandler.SetLanguageLine("TechCategory" + techCatagoryName, displayName);
+
+ return techCategory;
+ }
+
+ ///
+ /// Safely looks for a modded category from another mod in the SMLHelper TechCategoryCache.
+ ///
+ /// The string used to define the techcategory.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ public bool ModdedTechCategoryExists(string techCategoryString)
+ {
+ EnumTypeCache cache = TechCategoryPatcher.cacheManager.RequestCacheForTypeName(techCategoryString, false);
+
+ if(cache != null) // Item Found
+ {
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Safely looks for a modded category from another mod in the SMLHelper TechCategoryCache and outputs its value when found.
+ ///
+ /// The string used to define the techcategory.
+ /// The TechCategory enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ public bool TryGetModdedTechCategory(string techCategoryString, out TechCategory modTechCategory)
+ {
+ EnumTypeCache cache = TechCategoryPatcher.cacheManager.RequestCacheForTypeName(techCategoryString, false);
+
+ if(cache != null) // Item Found
+ {
+ modTechCategory = (TechCategory)cache.Index;
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ modTechCategory = TechCategory.Misc;
+ return false;
+ }
+ }
+
+ ///
+ /// Registers the TechCategory to a TechGroup in CraftData.groups.
+ ///
+ /// The tech group.
+ /// The tech category.
+ ///
+ public bool TryRegisterTechCategoryToTechGroup(TechGroup techGroup, TechCategory techCategory)
+ {
+ if(!CraftData.groups.TryGetValue(techGroup, out var techCategories))
+ {
+ //Should not even really be possible but just incase.
+ return false;
+ }
+
+ if(techCategories.ContainsKey(techCategory))
+ return true;
+
+ techCategories[techCategory] = new List();
+ return true;
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/TechGroupHandler.cs b/SMLHelper/Handlers/TechGroupHandler.cs
new file mode 100644
index 00000000..023be54c
--- /dev/null
+++ b/SMLHelper/Handlers/TechGroupHandler.cs
@@ -0,0 +1,85 @@
+namespace SMLHelper.V2.Handlers
+{
+ using SMLHelper.V2.Handlers;
+ using SMLHelper.V2.Interfaces;
+ using SMLHelper.V2.Patchers.EnumPatching;
+ using SMLHelper.V2.Utility;
+
+ ///
+ /// A handler class for everything related to creating new TechGroups.
+ ///
+ public class TechGroupHandler : ITechGroupHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ITechGroupHandler Main { get; } = new TechGroupHandler();
+
+ private TechGroupHandler()
+ {
+ // Hide constructor
+ }
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The name of the TechGroup. Should not contain special characters.
+ /// The display name of the TechGroup. Can be anything.
+ ///
+ /// The new that is created.
+ ///
+ public TechGroup AddTechGroup(string techGroupName, string displayName)
+ {
+ TechGroup techGroup = TechGroupPatcher.AddTechGroup(techGroupName);
+
+ LanguageHandler.SetLanguageLine("Group" + techGroupName, displayName);
+
+ return techGroup;
+ }
+
+ ///
+ /// Safely looks for a modded group from another mod in the SMLHelper TechGroupCache.
+ ///
+ /// The string used to define the techgroup.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ public bool ModdedTechGroupExists(string techGroupString)
+ {
+ EnumTypeCache cache = TechGroupPatcher.cacheManager.RequestCacheForTypeName(techGroupString, false);
+
+ if (cache != null) // Item Found
+ {
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechGroupCache and outputs its value when found.
+ ///
+ /// The string used to define the techgroup.
+ /// The TechGroup enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ public bool TryGetModdedTechGroup(string techGroupString, out TechGroup modTechGroup)
+ {
+ EnumTypeCache cache = TechGroupPatcher.cacheManager.RequestCacheForTypeName(techGroupString, false);
+
+ if (cache != null) // Item Found
+ {
+ modTechGroup = (TechGroup)cache.Index;
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ modTechGroup = TechGroup.Uncategorized;
+ return false;
+ }
+ }
+ }
+}
diff --git a/SMLHelper/Handlers/TechTypeHandler.cs b/SMLHelper/Handlers/TechTypeHandler.cs
new file mode 100644
index 00000000..145be756
--- /dev/null
+++ b/SMLHelper/Handlers/TechTypeHandler.cs
@@ -0,0 +1,374 @@
+namespace SMLHelper.V2.Handlers
+{
+ using System.Collections.Generic;
+ using System.Reflection;
+ using Assets;
+ using Patchers;
+ using SMLHelper.V2.Interfaces;
+ using SMLHelper.V2.Patchers.EnumPatching;
+ using UnityEngine;
+ using Utility;
+
+ ///
+ /// A handler class for everything related to creating new TechTypes.
+ ///
+ public class TechTypeHandler : ITechTypeHandlerInternal, ITechTypeHandler
+ {
+ internal static readonly Dictionary TechTypesAddedBy = new Dictionary();
+
+ internal static readonly ITechTypeHandlerInternal Singleton = new TechTypeHandler();
+
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static ITechTypeHandler Main => Singleton;
+
+ private TechTypeHandler()
+ {
+ // Hide constructor
+ }
+
+ TechType ITechTypeHandlerInternal.AddTechType(Assembly modAssembly, string internalName, string displayName, string tooltip)
+ {
+ return Singleton.AddTechType(modAssembly, internalName, displayName, tooltip, true);
+ }
+
+ TechType ITechTypeHandlerInternal.AddTechType(Assembly modAssembly, string internalName, string displayName, string tooltip, bool unlockAtStart)
+ {
+ string modName = modAssembly.GetName().Name;
+
+ // Register the TechType.
+ var techType = TechTypePatcher.AddTechType(internalName);
+
+ // Remember which Assembly added it
+ TechTypesAddedBy[techType] = modAssembly;
+
+ // register Language Lines.
+ LanguagePatcher.AddCustomLanguageLine(modName, internalName, displayName);
+ LanguagePatcher.AddCustomLanguageLine(modName, "Tooltip_" + internalName, tooltip);
+
+ Dictionary valueToString = TooltipFactory.techTypeTooltipStrings.valueToString;
+ valueToString[techType] = "Tooltip_" + internalName;
+
+ // Unlock the TechType on start
+ if (unlockAtStart)
+ KnownTechPatcher.UnlockedAtStart.Add(techType);
+
+ // Return the new TechType.
+ return techType;
+ }
+
+ #region Static Methods
+
+ ///
+ /// Adds a new into the game. This new will be unlocked at the start of a game.
+ ///
+ /// The assembly this TechType is getting added by.
+ /// The internal name of the TechType. Should not contain special characters
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The new that is created.
+ public static TechType AddTechType(Assembly addedByAssembly, string internalName, string displayName, string tooltip) =>
+ Singleton.AddTechType(addedByAssembly, internalName, displayName, tooltip);
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The assembly this TechType is getting added by.
+ /// The internal name of the TechType. Should not contain special characters
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// Whether this TechType should be unlocked on game start, or not. By default, .
+ /// The new that is created.
+ public static TechType AddTechType(Assembly addedByAssembly, string internalName, string displayName, string tooltip, bool unlockAtStart) =>
+ Singleton.AddTechType(addedByAssembly, internalName, displayName, tooltip, unlockAtStart);
+
+ ///
+ /// Adds a new into the game. This new will be unlocked at the start of a game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The new that is created.
+ public static TechType AddTechType(string internalName, string displayName, string tooltip)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip);
+ }
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// Whether this TechType should be unlocked on game start, or not. By default, .
+ /// The new that is created.
+ public static TechType AddTechType(string internalName, string displayName, string tooltip, bool unlockAtStart)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, unlockAtStart);
+ }
+#if SUBNAUTICA
+ ///
+ /// Adds a new into the game, with a sprite. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// The new that is created.
+ public static TechType AddTechType(string internalName, string displayName, string tooltip, Atlas.Sprite sprite)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, sprite);
+ }
+
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, .
+ /// The new that is created.
+ public static TechType AddTechType(string internalName, string displayName, string tooltip, Atlas.Sprite sprite, bool unlockAtStart)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, sprite, unlockAtStart);
+ }
+#endif
+ ///
+ /// Adds a new into the game, with a sprite. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// The new that is created.
+ public static TechType AddTechType(string internalName, string displayName, string tooltip, Sprite sprite)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, sprite);
+ }
+
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, .
+ /// The new that is created.
+ public static TechType AddTechType(string internalName, string displayName, string tooltip, Sprite sprite, bool unlockAtStart)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, sprite, unlockAtStart);
+ }
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded item's new techtype.
+ /// The TechType enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// if the item was found; Otherwise .
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ public static bool TryGetModdedTechType(string techtypeString, out TechType modTechType)
+ {
+ return Main.TryGetModdedTechType(techtypeString, out modTechType);
+ }
+
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechTypeCache.
+ ///
+ /// The string used to define the modded item's new .
+ ///
+ /// True if the item was found; Otherwise .
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ public static bool ModdedTechTypeExists(string techtypeString)
+ {
+ return Main.ModdedTechTypeExists(techtypeString);
+ }
+
+ #endregion
+
+ #region Interface methods
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// Whether this TechType should be unlocked on game start, or not. By default, .
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip, bool unlockAtStart)
+ {
+ var mod = ReflectionHelper.CallingAssemblyByStackTrace();
+ return Singleton.AddTechType(mod, internalName, displayName, tooltip, unlockAtStart);
+ }
+
+ ///
+ /// Adds a new into the game. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, true);
+ }
+#if SUBNAUTICA
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, true.
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip, Atlas.Sprite sprite, bool unlockAtStart)
+ {
+ // Register the TechType using overload.
+ TechType techType = Main.AddTechType(internalName, displayName, tooltip, unlockAtStart);
+
+ // Register the Sprite
+ if (sprite != null)
+ ModSprite.Add(SpriteManager.Group.None, internalName, sprite);
+
+ // Return the new TechType
+ return techType;
+ }
+
+ ///
+ /// Adds a new into the game, with a sprite. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip, Atlas.Sprite sprite)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, sprite, true);
+ }
+
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, true.
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip, Sprite sprite, bool unlockAtStart)
+ {
+ // Register the TechType using overload.
+ TechType techType = Main.AddTechType(internalName, displayName, tooltip, unlockAtStart);
+
+ // Register the Sprite
+ if (sprite != null)
+ ModSprite.Add(SpriteManager.Group.None, internalName, new Atlas.Sprite(sprite));
+
+ // Return the new TechType
+ return techType;
+ }
+
+#elif BELOWZERO
+
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, true.
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip, Sprite sprite, bool unlockAtStart)
+ {
+ string modName = ReflectionHelper.CallingAssemblyNameByStackTrace();
+
+ // Register the TechType using overload.
+ TechType techType = Main.AddTechType(internalName, displayName, tooltip, unlockAtStart);
+
+ // Register the Sprite
+ if (sprite != null)
+ ModSprite.Add(SpriteManager.Group.None, internalName, sprite);
+
+ // Return the new TechType
+ return techType;
+ }
+
+#endif
+
+ ///
+ /// Adds a new into the game, with a sprite. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// The new that is created.
+ TechType ITechTypeHandler.AddTechType(string internalName, string displayName, string tooltip, Sprite sprite)
+ {
+ return Main.AddTechType(internalName, displayName, tooltip, sprite, true);
+ }
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded item's new techtype.
+ /// The TechType enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ bool ITechTypeHandler.TryGetModdedTechType(string techtypeString, out TechType modTechType)
+ {
+ EnumTypeCache cache = TechTypePatcher.cacheManager.RequestCacheForTypeName(techtypeString, false);
+
+ if (cache != null) // Item Found
+ {
+ modTechType = (TechType)cache.Index;
+ return true;
+ }
+ else // Mod not present or not yet loaded
+ {
+ modTechType = TechType.None;
+ return false;
+ }
+ }
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechTypeCache.
+ ///
+ /// The string used to define the modded item's new techtype.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ bool ITechTypeHandler.ModdedTechTypeExists(string techtypeString)
+ {
+ EnumTypeCache cache = TechTypePatcher.cacheManager.RequestCacheForTypeName(techtypeString, false);
+ return cache != null;
+ }
+
+ #endregion
+ }
+}
diff --git a/SMLHelper/Handlers/WorldEntityDatabaseHandler.cs b/SMLHelper/Handlers/WorldEntityDatabaseHandler.cs
new file mode 100644
index 00000000..9cba0dca
--- /dev/null
+++ b/SMLHelper/Handlers/WorldEntityDatabaseHandler.cs
@@ -0,0 +1,63 @@
+namespace SMLHelper.V2.Handlers
+{
+ using Interfaces;
+ using SMLHelper.V2.Patchers;
+ using UnityEngine;
+ using UWE;
+
+ ///
+ /// A handler for the WorldEntityDatabase of the game.
+ ///
+ public class WorldEntityDatabaseHandler : IWorldEntityDatabaseHandler
+ {
+ ///
+ /// Main entry point for all calls to this handler.
+ ///
+ public static IWorldEntityDatabaseHandler Main { get; } = new WorldEntityDatabaseHandler();
+
+ private WorldEntityDatabaseHandler() { } // Hides constructor
+
+ ///
+ /// Adds in a custom to the of the game.
+ /// It contains information about the entity, like its , its , etc.
+ ///
+ /// The classID of the entity whose data you are adding in.
+ /// The data. Data is stored in the fields of the class, so they must be populated when passed in.
+ public static void AddCustomInfo(string classId, WorldEntityInfo data)
+ {
+
+ Main.AddCustomInfo(classId, data);
+ }
+
+ ///
+ /// Adds in a custom to the of the game.
+ /// It contains information about the entity, like its , its , etc.
+ ///
+ /// The classId of the entity.
+ /// The of the entity.
+ /// Whether the prefab's Z-axis should be facing up, when spawned.
+ /// The of the entity.
+ /// The of the entity. Dictates which "slots" are suitable for this entity to spawn in. For e.g., most in-crate fragments have a slot type.
+ /// The scale that the entity's local scale is set to when spawned.
+ public static void AddCustomInfo(string classId, TechType techType, Vector3 localScale, bool prefabZUp = false, LargeWorldEntity.CellLevel cellLevel = LargeWorldEntity.CellLevel.Global, EntitySlot.Type slotType = EntitySlot.Type.Small)
+ {
+ Main.AddCustomInfo(classId, new WorldEntityInfo()
+ {
+ classId = classId,
+ techType = techType,
+ cellLevel = cellLevel,
+ slotType = slotType,
+ localScale = localScale,
+ prefabZUp = prefabZUp
+ });
+ }
+
+ void IWorldEntityDatabaseHandler.AddCustomInfo(string classId, WorldEntityInfo data)
+ {
+ if(WorldEntityDatabasePatcher.CustomWorldEntityInfos.ContainsKey(classId))
+ V2.Logger.Log($"{classId}-{data.techType} already has custom WorldEntityInfo. Replacing with latest.", LogLevel.Debug);
+
+ WorldEntityDatabasePatcher.CustomWorldEntityInfos[classId] = data;
+ }
+ }
+}
diff --git a/SMLHelper/Initializer.cs b/SMLHelper/Initializer.cs
new file mode 100644
index 00000000..bf443925
--- /dev/null
+++ b/SMLHelper/Initializer.cs
@@ -0,0 +1,93 @@
+namespace SMLHelper.V2
+{
+ using System;
+ using System.Reflection;
+ using HarmonyLib;
+ using Patchers;
+ using Patchers.EnumPatching;
+ using QModManager.API.ModLoading;
+
+
+ ///
+ /// WARNING: This class is for use only by QModManager.
+ ///
+ [QModCore]
+ public class Initializer
+ {
+ internal static readonly Harmony harmony = new Harmony("com.ahk1221.smlhelper");
+
+ ///
+ /// WARNING: This method is for use only by QModManager.
+ ///
+ [QModPrePatch]
+ [Obsolete("This method is for use only by QModManager.", true)]
+ public static void PrePatch()
+ {
+ Logger.Initialize();
+#if SUBNAUTICA
+ Logger.Log($"Loading v{Assembly.GetExecutingAssembly().GetName().Version} for Subnautica", LogLevel.Info);
+#elif BELOWZERO
+ Logger.Log($"Loading v{Assembly.GetExecutingAssembly().GetName().Version} for BelowZero", LogLevel.Info);
+#endif
+
+ Logger.Debug("Loading TechType Cache");
+ TechTypePatcher.cacheManager.LoadCache();
+ Logger.Debug("Loading CraftTreeType Cache");
+ CraftTreeTypePatcher.cacheManager.LoadCache();
+ Logger.Debug("Loading PingType Cache");
+ PingTypePatcher.cacheManager.LoadCache();
+
+ PrefabDatabasePatcher.PrePatch(harmony);
+ }
+
+ ///
+ /// WARNING: This method is for use only by QModManager.
+ ///
+ [QModPostPatch("E3DC72597463233E62D01BD222AD0C96")]
+ [Obsolete("This method is for use only by QModManager.", true)]
+ public static void PostPatch()
+ {
+ FishPatcher.Patch(harmony);
+
+ TechTypePatcher.Patch();
+ CraftTreeTypePatcher.Patch();
+ PingTypePatcher.Patch();
+ TechCategoryPatcher.Patch();
+ TechGroupPatcher.Patch();
+ BackgroundTypePatcher.Patch();
+ EquipmentTypePatcher.Patch();
+ EnumPatcher.Patch(harmony);
+
+ CraftDataPatcher.Patch(harmony);
+ CraftTreePatcher.Patch(harmony);
+ ConsoleCommandsPatcher.Patch(harmony);
+ LanguagePatcher.Patch(harmony);
+ PrefabDatabasePatcher.PostPatch(harmony);
+ SpritePatcher.Patch(harmony);
+ KnownTechPatcher.Patch(harmony);
+ BioReactorPatcher.Patch();
+ OptionsPanelPatcher.Patch(harmony);
+ ItemsContainerPatcher.Patch(harmony);
+ PDAPatcher.Patch(harmony);
+ PDAEncyclopediaPatcher.Patch(harmony);
+ ItemActionPatcher.Patch(harmony);
+ LootDistributionPatcher.Patch(harmony);
+ WorldEntityDatabasePatcher.Patch(harmony);
+ LargeWorldStreamerPatcher.Patch(harmony);
+ IngameMenuPatcher.Patch(harmony);
+ TooltipPatcher.Patch(harmony);
+ SurvivalPatcher.Patch(harmony);
+ CustomSoundPatcher.Patch(harmony);
+
+
+
+ Logger.Debug("Saving TechType Cache");
+ TechTypePatcher.cacheManager.SaveCache();
+ Logger.Debug("Saving CraftTreeType Cache");
+ CraftTreeTypePatcher.cacheManager.SaveCache();
+ Logger.Debug("Saving PingType Cache");
+ PingTypePatcher.cacheManager.SaveCache();
+
+ }
+ }
+}
diff --git a/SMLHelper/Interfaces/IBackgroundTypeHandler.cs b/SMLHelper/Interfaces/IBackgroundTypeHandler.cs
new file mode 100644
index 00000000..01960d4e
--- /dev/null
+++ b/SMLHelper/Interfaces/IBackgroundTypeHandler.cs
@@ -0,0 +1,37 @@
+namespace SMLHelper.V2.Interfaces
+{
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+#elif BELOWZERO
+ using Sprite = UnityEngine.Sprite;
+#endif
+
+ ///
+ /// A handler interface for everything related to creating new BackgroundTypes.
+ ///
+ public interface IBackgroundTypeHandler
+ {
+ ///
+ /// adds a new into the game.
+ ///
+ /// the name of the BackgroundType, should not contain special characters.
+ /// The sprite for this BackgroundType.
+ /// The new that's created.
+ CraftData.BackgroundType AddBackgroundType(string backgroundTypeName, Sprite backgroundSprite);
+
+ ///
+ /// Safely looks for a modded Background Type from another mod in the SMLHelper BackgroundTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the BackgroundType
+ /// The BackgroundType enum value of the modded. Defaults to when the item was not found.
+ /// if the item was found; otherwise .
+ bool TryGetModdedBackgroundType(string backgroundTypeString, out CraftData.BackgroundType modBackgroundType);
+
+ ///
+ /// Safely looks for a modded Background Type from another mod in the SMLHelper BackgroundTypeCache.
+ ///
+ /// The string used to define the BackgroundType.
+ /// if the item was found; otherwise .
+ bool ModdedBackgroundTypeExists(string backgroundTypeString);
+ }
+}
diff --git a/SMLHelper/Interfaces/IBasicText.cs b/SMLHelper/Interfaces/IBasicText.cs
new file mode 100644
index 00000000..8bb5a93f
--- /dev/null
+++ b/SMLHelper/Interfaces/IBasicText.cs
@@ -0,0 +1,126 @@
+#if BELOWZERO
+using Text = TMPro.TextMeshPro;
+using Font = TMPro.TMP_FontAsset;
+using FontStyle = TMPro.FontStyles;
+#endif
+
+using UnityEngine;
+
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ ///
+ /// Places a simple text object on the screen and keeps it there until either hidden (or a designated fade-to-black timer has passed).
+ /// By default uses the same font/size/color as the "Press Any Button To Begin" message at the beginning of the game, and appears
+ /// centered about 1/3 down the screen, but all parameters can be reconfigured.
+ ///
+ ///
+ /// The idea of the defaults is that new modders don't have to bootstrap a bunch of irritating Unity stuff -- don't have to understand
+ /// what a "Material" is or how to make one, don't have to know to initialize a font, or even a color. Can just start adding text and
+ /// then can always custom and configure on further revision.
+ ///
+ ///
+ ///
+ /// SIMPLE USAGE EXAMPLE:
+ /// BasicText message = new BasicText();
+ /// message.ShowMessage("This Message Will Fade In 10 Seconds", 10);
+ ///
+ /// COMPLEX USAGE EXAMPLE:
+ /// BasicText message = new BasicText(TextAnchor.UpperLeft); // Note many other properties could also be set as constructor parameters
+ /// message.setColor(Color.red); // Set Color
+ /// message.setSize(20); // Set Font Size
+ /// message.setLoc(200, 400); // Set x/y position (0,0 is center of screen)
+ /// message.setFontStyle(FontStyle.Bold); // Bold
+ /// message.ShowMessage("This message stays on screen until hidden"); // Display message; if fadeout seconds not specified, it just keeps showing
+ /// ... // other things happen, time goes by
+ /// message.Hide(); // Hides the message
+ ///
+ public interface IBasicText
+ {
+ ///
+ /// Resets to using "cloned" font style of Subnautica default
+ ///
+ void ClearAlign();
+
+ ///
+ /// Resets to using "cloned" color of Subnautica default.
+ ///
+ void ClearColor();
+
+ ///
+ /// Resets to using "cloned" font of Subnautica default.
+ ///
+ void ClearFont();
+
+ ///
+ /// Resets to using "cloned" font style of Subnautica default.
+ ///
+ void ClearFontStyle();
+
+ ///
+ /// Resets to using "cloned" size of Subnautica default.
+ ///
+ void ClearSize();
+
+ ///
+ /// Returns our current text.
+ ///
+ ///
+ string GetText();
+
+ ///
+ /// Hides our text item if it is displaying.
+ ///
+ void Hide();
+
+ ///
+ /// Sets the text anchor.
+ ///
+ /// The text anchor to align to
+ void SetAlign(TextAnchor useAlign);
+
+ ///
+ /// Sets the text color
+ ///
+ /// The text color to use
+ void SetColor(Color useColor);
+
+ ///
+ /// Sets the font
+ ///
+ /// The font to render the text as.
+ void SetFont(Font useFont);
+
+ ///
+ /// Sets the font style.
+ ///
+ /// The text font style to use
+ void SetFontStyle(FontStyle useStyle);
+
+ ///
+ /// Sets screen display location (position relative to the actual text is determined by the alignment)
+ ///
+ /// The x coordinate to set
+ /// The y coordinate to set
+ void SetLocation(float set_x, float set_y);
+
+ ///
+ /// Sets the font size.
+ ///
+ /// The text size to use
+ void SetSize(int useSize);
+
+ ///
+ /// Shows our text item, with no schedule fade (i.e. indefinitely)
+ ///
+ /// The text to display
+ void ShowMessage(string s);
+
+ ///
+ /// Shows our text item, fading after a specified number of seconds (or stays on indefinitely if 0 seconds)
+ ///
+ /// The text to display
+ /// The duration to hold before fading
+ void ShowMessage(string s, float seconds);
+ }
+}
diff --git a/SMLHelper/Interfaces/IBioReactorHandler.cs b/SMLHelper/Interfaces/IBioReactorHandler.cs
new file mode 100644
index 00000000..0f9bb6e6
--- /dev/null
+++ b/SMLHelper/Interfaces/IBioReactorHandler.cs
@@ -0,0 +1,16 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler with common methods for updating BioReactor values.
+ ///
+ public interface IBioReactorHandler
+ {
+ ///
+ /// Allows you to specify the quantity of energy that a TechType will produce with bio reactors.
+ ///
+ /// The TechType that you want to use with bioreactors.
+ /// The quantity of energy that will be produced by this TechType.
+ ///
+ void SetBioReactorCharge(TechType techType, float charge);
+ }
+}
diff --git a/SMLHelper/Interfaces/IConsoleCommandHandler.cs b/SMLHelper/Interfaces/IConsoleCommandHandler.cs
new file mode 100644
index 00000000..8b086fcb
--- /dev/null
+++ b/SMLHelper/Interfaces/IConsoleCommandHandler.cs
@@ -0,0 +1,68 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Commands;
+ using System;
+
+ ///
+ /// A handler class for registering your custom console commands.
+ ///
+ public interface IConsoleCommandHandler
+ {
+ ///
+ /// Registers your custom console command by targeting a method.
+ ///
+ ///
+ /// Target method must be .
+ ///
+ /// The command can take parameters and will respect optional parameters as outlined in the method's signature.
+ /// Supported parameter types: , , , ,
+ /// .
+ ///
+ /// If the method has a return type, it will be printed to both the screen and the log.
+ ///
+ /// The case-insensitive command to register.
+ /// The declaring type that holds the method to call when the command is entered.
+ /// The name of the method to call within the declaring type when the command is entered.
+ /// Method must be .
+ /// The parameter types the method receives, for targeting overloads.
+ ///
+ ///
+ ///
+ void RegisterConsoleCommand(string command, Type declaringType, string methodName, Type[] parameters = null);
+
+ ///
+ /// Registers your custom console command by passing a .
+ ///
+ ///
+ /// Supported parameter types: , , , ,
+ /// .
+ ///
+ /// If the delegate has a return type, it will be printed to both the screen and the log.
+ ///
+ /// The delegate type.
+ /// The case-insensitive command to register.
+ /// The callback to handle the command.
+ ///
+ ///
+ ///
+ void RegisterConsoleCommand(string command, T callback) where T : Delegate;
+
+ ///
+ /// Registers methods decorated with the
+ /// within the as console commands.
+ ///
+ ///
+ /// Target methods must be .
+ ///
+ /// Commands can take parameters and will respect optional parameters as outlined in the method's signature.
+ /// Supported parameter types: , , , ,
+ /// .
+ ///
+ /// If a decorated method has a return type, it will be printed to both the screen and the log.
+ ///
+ ///
+ ///
+ ///
+ void RegisterConsoleCommands(Type type);
+ }
+}
diff --git a/SMLHelper/Interfaces/ICoordinatedSpawnHandler.cs b/SMLHelper/Interfaces/ICoordinatedSpawnHandler.cs
new file mode 100644
index 00000000..56caf7c0
--- /dev/null
+++ b/SMLHelper/Interfaces/ICoordinatedSpawnHandler.cs
@@ -0,0 +1,38 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using System.Collections.Generic;
+ using Handlers;
+ using UnityEngine;
+
+ ///
+ /// a Handler interface that handles and registers Coordinated ( spawns).
+ ///
+ public interface ICoordinatedSpawnHandler
+ {
+ ///
+ /// Registers a Coordinated Spawn
+ ///
+ /// the SpawnInfo to spawn
+ void RegisterCoordinatedSpawn(SpawnInfo spawnInfo);
+
+ ///
+ /// registers Many Coordinated Spawns.
+ ///
+ /// The SpawnInfos to spawn.
+ void RegisterCoordinatedSpawns(List spawnInfos);
+
+ ///
+ /// Registers Multiple Coordinated spawns for one single passed TechType
+ ///
+ /// The TechType to spawn
+ /// the coordinates the should spawn to
+ void RegisterCoordinatedSpawnsForOneTechType(TechType techTypeToSpawn, List coordinatesToSpawnTo);
+
+ ///
+ /// Registers Multiple Coordinated spawns with rotations for one single passed TechType
+ ///
+ /// The TechType to spawn
+ /// the coordinates(Key) and the rotations(Value) the should spawn to
+ void RegisterCoordinatedSpawnsForOneTechType(TechType techTypeToSpawn, Dictionary coordinatesAndRotationsToSpawnTo);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Interfaces/ICraftDataHandler.cs b/SMLHelper/Interfaces/ICraftDataHandler.cs
new file mode 100644
index 00000000..593047fd
--- /dev/null
+++ b/SMLHelper/Interfaces/ICraftDataHandler.cs
@@ -0,0 +1,131 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler class for adding and editing crafted and some non-crafted items.
+ ///
+ public partial interface ICraftDataHandler
+ {
+ ///
+ /// Allows you to edit EquipmentTypes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose EqiupmentType you want to edit.
+ /// The EquipmentType for that TechType.
+ void SetEquipmentType(TechType techType, EquipmentType equipmentType);
+
+#if SUBNAUTICA
+ ///
+ /// Allows you to edit QuickSlotType for TechTypes. Can be used for existing TechTypes too.
+ /// Careful: This has to be called after and .
+ ///
+ /// The TechType whose QuickSlotType you want to edit.
+ /// The QuickSlotType for that TechType.
+#elif BELOWZERO
+ ///
+ /// Allows you to edit QuickSlotType for TechTypes. Can be used for existing TechTypes too.
+ /// Careful: This has to be called after .
+ ///
+ /// The TechType whose QuickSlotType you want to edit.
+ /// The QuickSlotType for that TechType.
+#endif
+ void SetQuickSlotType(TechType techType, QuickSlotType slotType);
+
+ ///
+ /// Allows you to edit harvest output, i.e. what TechType you get when you "harvest" a TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose harvest output you want to edit.
+ /// The harvest output for that TechType.
+ void SetHarvestOutput(TechType techType, TechType harvestOutput);
+
+ ///
+ /// Allows you to edit how TechTypes are harvested.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose HarvestType you want to edit.
+ /// The HarvestType for that TechType.
+ void SetHarvestType(TechType techType, HarvestType harvestType);
+
+ ///
+ /// Allows you to edit how much additional slices/seeds are given upon last knife hit.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose final cut bonus you want to edit.
+ /// The number of additional slices/seeds you'll receive on last cut.
+ void SetHarvestFinalCutBonus(TechType techType, int bonus);
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The item size for that TechType.
+ void SetItemSize(TechType techType, Vector2int size);
+
+ ///
+ /// Allows you to edit item sizes for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose item size you want to edit.
+ /// The width of the item
+ /// The height of the item
+ void SetItemSize(TechType techType, int x, int y);
+
+ ///
+ /// Allows you to edit crafting times for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose crafting time you want to edit.
+ /// The crafting time, in seconds, for that TechType.
+ void SetCraftingTime(TechType techType, float time);
+
+ ///
+ /// Allows you to edit the cooked creature list, i.e. associate the unedible TechType to the cooked TechType.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose cooked creature counterpart to edit.
+ /// The cooked creature counterpart for that TechType.
+ void SetCookedVariant(TechType uncooked, TechType cooked);
+
+ ///
+ /// Allows you to edit inventory background colors for TechTypes.
+ ///
+ /// The TechType whose BackgroundType you want to edit.
+ /// The background color for that TechType.
+ ///
+ void SetBackgroundType(TechType techType, CraftData.BackgroundType backgroundColor);
+
+ ///
+ /// Allows you to add items to the buildable list.
+ ///
+ /// The TechType which you want to add to the buildable list.
+ void AddBuildable(TechType techType);
+
+ ///
+ /// Allows you to add items to the game's internal grouping system.
+ /// Required if you want to make buildable items show up in the Habitat Builder.
+ ///
+ /// The TechGroup you want to add your TechType to.
+ /// The TechCategory (in the TechGroup) you want to add your TechType to.
+ /// The TechType you want to add.
+ void AddToGroup(TechGroup group, TechCategory category, TechType techType);
+
+ ///
+ /// Allows you to add items to the game's internal grouping system.
+ /// Required if you want to make buildable items show up in the Habitat Builder.
+ ///
+ /// The TechGroup you want to add your TechType to.
+ /// The TechCategory (in the TechGroup) you want to add your TechType to.
+ /// The TechType you want to add.
+ /// Added TechType will be added after this TechType, for sorting purposes.
+ void AddToGroup(TechGroup group, TechCategory category, TechType techType, TechType after);
+
+ ///
+ /// Allows you to remove an existing TechType from the game's internal group system.
+ ///
+ /// The TechGroup in which the TechType is located.
+ /// The TechCategory in which the TechType is located.
+ /// The TechType which you want to remove.
+ void RemoveFromGroup(TechGroup group, TechCategory category, TechType techType);
+ }
+}
diff --git a/SMLHelper/Interfaces/ICraftDataHandler_BelowZero.cs b/SMLHelper/Interfaces/ICraftDataHandler_BelowZero.cs
new file mode 100644
index 00000000..a3e60b35
--- /dev/null
+++ b/SMLHelper/Interfaces/ICraftDataHandler_BelowZero.cs
@@ -0,0 +1,84 @@
+#if BELOWZERO
+namespace SMLHelper.V2.Interfaces
+{
+ using System.Collections.Generic;
+ using Crafting;
+
+ public partial interface ICraftDataHandler
+ {
+ ///
+ /// Allows you to edit RecipeData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ void SetTechData(TechType techType, RecipeData techData);
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The RecipeData from the modded item if it exists; Otherwise, returns null.
+ RecipeData GetRecipeData(TechType techType);
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The RecipeData from the modded item if it exists; Otherwise, returns null.
+ RecipeData GetModdedRecipeData(TechType techType);
+
+ ///
+ /// Allows you to set ingredients for a TechType crafting recipe.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose ingredient list you want to edit.
+ /// The collection of Ingredients for that TechType.
+ ///
+ void SetIngredients(TechType techType, ICollection ingredients);
+
+ ///
+ /// Allows you to set linked items for a TechType crafting recipe.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose ingredient list you want to edit.
+ /// The collection of linked items for that TechType
+ void SetLinkedItems(TechType techType, ICollection linkedItems);
+
+
+ ///
+ /// Allows you to edit the Cold Resistance for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose Cold Resistance you want to edit.
+ /// The int value for the Cold Resistance.
+ void SetColdResistance(TechType techType, int resistance);
+
+ ///
+ /// Sets the maximum charge.
+ ///
+ /// The TechType whose MaxCharge you want to edit.
+ /// The maximum charge.
+ void SetMaxCharge(TechType techType, float maxCharge);
+
+ ///
+ /// Sets the energy cost.
+ ///
+ /// The TechType whose EnergyCost you want to edit.
+ /// The energy cost.
+ void SetEnergyCost(TechType techType, float energyCost);
+
+ ///
+ /// Sets the type of the sound.
+ ///
+ /// Type of the tech.
+ /// Type of the sound.
+ void SetSoundType(TechType techType, TechData.SoundType soundType);
+ }
+}
+#endif
diff --git a/SMLHelper/Interfaces/ICraftDataHandler_Subnautica.cs b/SMLHelper/Interfaces/ICraftDataHandler_Subnautica.cs
new file mode 100644
index 00000000..17fb8efd
--- /dev/null
+++ b/SMLHelper/Interfaces/ICraftDataHandler_Subnautica.cs
@@ -0,0 +1,61 @@
+#if SUBNAUTICA
+namespace SMLHelper.V2.Interfaces
+{
+ using Crafting;
+
+ public partial interface ICraftDataHandler
+ {
+ ///
+ /// Allows you to edit recipes, i.e. TechData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ void SetTechData(TechType techType, ITechData techData);
+
+ ///
+ /// Allows you to edit recipes, i.e. TechData for TechTypes.
+ /// Can be used for existing TechTypes too.
+ ///
+ /// The TechType whose TechData you want to edit.
+ /// The TechData for that TechType.
+ ///
+ void SetTechData(TechType techType, TechData techData);
+
+ ///
+ /// Safely accesses the crafting data from a modded item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// The ITechData from the modded item if it exists; Otherwise, returns null.
+ ITechData GetModdedTechData(TechType techType);
+
+ ///
+ /// Safely accesses the crafting data from any item.
+ /// WARNING: This method is highly dependent on mod load order.
+ /// Make sure your mod is loading after the mod whose TechData you are trying to access.
+ ///
+ /// The TechType whose TechData you want to access.
+ /// Returns TechData if it exists; Otherwise, returns null.
+ TechData GetTechData(TechType techType);
+
+ ///
+ /// Sets the eating sound for the provided TechType.
+ ///
+ /// The item being consumed during .
+ ///
+ /// The sound path.
+ ///
+ /// Value values are
+ /// - "event:/player/drink"
+ /// - "event:/player/drink_stillsuit"
+ /// - "event:/player/use_first_aid"
+ /// - "event:/player/eat" (default)
+ ///
+ ///
+ void SetEatingSound(TechType consumable, string soundPath);
+ }
+}
+#endif
diff --git a/SMLHelper/Interfaces/ICraftTreeHandler.cs b/SMLHelper/Interfaces/ICraftTreeHandler.cs
new file mode 100644
index 00000000..e834c833
--- /dev/null
+++ b/SMLHelper/Interfaces/ICraftTreeHandler.cs
@@ -0,0 +1,124 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Crafting;
+ using SMLHelper.V2.Handlers;
+ using UnityEngine;
+
+ ///
+ /// A handler class for creating and editing of crafting trees.
+ ///
+ public interface ICraftTreeHandler
+ {
+ ///
+ /// Your first method call to start a new custom crafting tree.
+ /// Creating a new CraftTree only makes sense if you're going to use it in a new type of .
+ ///
+ /// The name for the new enum.
+ /// The new enum instance for your custom craft tree type.
+ ///
+ /// Returns the root node for your custom craft tree, as a new instance.
+ /// Build up your custom crafting tree from this root node.
+ /// This tree will be automatically patched into the game. No further calls into required.
+ /// For more advanced usage, you can replace the default value of with your own custom function.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ModCraftTreeRoot CreateCustomCraftTreeAndType(string name, out CraftTree.Type craftTreeType);
+
+ ///
+ /// Safely looks for a modded CraftTree Type from another mod in the SMLHelper CraftTreeTypeCache.
+ ///
+ /// The string used to define the modded item's new techtype.
+ ///
+ /// True if the craft tree was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ bool ModdedCraftTreeTypeExists(string craftTreeString);
+
+ ///
+ /// Adds a new crafting node to the root of the specified crafting tree, at the provided tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The item to craft.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem, params string[] stepsToTab);
+
+ ///
+ /// Adds a new crafting node to the root of the specified crafting tree
+ ///
+ /// The target craft tree to edit.
+ /// The item to craft.
+ void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem);
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Sprite sprite);
+
+#if SUBNAUTICA
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite);
+
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite, params string[] stepsToTab);
+
+#endif
+ ///
+ /// Adds a new tab node to the root of the specified crafting tree, at the specified tab location.
+ ///
+ /// The target craft tree to edit.
+ /// The ID of the tab node. Must be unique!
+ /// The display name of the tab, which will show up when you hover your mouse on the tab.
+ /// The sprite of the tab.
+ ///
+ /// The steps to the target tab.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// Do not include "root" in this path.
+ ///
+ void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Sprite sprite, params string[] stepsToTab);
+
+ ///
+ /// Removes a node at the specified node location. Can be used to remove either tabs or craft nodes.
+ /// If a tab node is selected, all child nodes to it will also be removed.
+ ///
+ /// The target craft tree to edit.
+ ///
+ /// The steps to the target node.
+ /// These must match the id value of the CraftNode in the crafting tree you're targeting.
+ /// This means matching the id of the crafted item or the id of the tab name.
+ /// Do not include "root" in this path.
+ ///
+ void RemoveNode(CraftTree.Type craftTree, params string[] stepsToNode);
+ }
+}
diff --git a/SMLHelper/Interfaces/ICustomSoundHandler.cs b/SMLHelper/Interfaces/ICustomSoundHandler.cs
new file mode 100644
index 00000000..a0d4ce98
--- /dev/null
+++ b/SMLHelper/Interfaces/ICustomSoundHandler.cs
@@ -0,0 +1,52 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using FMOD;
+ using UnityEngine;
+ using Utility;
+
+ ///
+ /// A handler class for adding and overriding Sounds.
+ ///
+ public interface ICustomSoundHandler
+ {
+ ///
+ /// Register a Custom sound by file path. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The file path on disk of the sound file to load
+ /// The sound channel to get the volume to play the sound at. defaults to
+ /// the loaded
+ Sound RegisterCustomSound(string id, string filePath, SoundChannel soundChannel = SoundChannel.Master);
+
+ ///
+ /// Register a custom sound by an instance. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The AudioClip to register.
+ /// The sound channel to get the volume to play the sound at. defaults to
+ /// the registered.
+ Sound RegisterCustomSound(string id, AudioClip audio, SoundChannel soundChannel = SoundChannel.Master);
+
+ ///
+ /// Register a Custom sound that has been loaded using AudioUtils. Some vanilla game sounds can be overridden by matching the id to the .
+ ///
+ /// The Id of your custom sound which is used when checking which sounds to play.
+ /// The pre loaded sound
+ /// The sound channel to get the volume to play the sound at.
+ void RegisterCustomSound(string id, Sound sound, SoundChannel soundChannel = SoundChannel.Master);
+
+ ///
+ /// Try to find and play a custom that has been registered.
+ ///
+ /// The Id of the custom sound
+ void TryPlayCustomSound(string id);
+
+ ///
+ /// Try to get a registered custom .
+ ///
+ /// The Id of the custom sound
+ /// Outputs the if found and null if not found.
+ /// true or false depending on if the id was found
+ bool TryGetCustomSound(string id, out Sound sound);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Interfaces/IEquipmentHandler.cs b/SMLHelper/Interfaces/IEquipmentHandler.cs
new file mode 100644
index 00000000..aba369d7
--- /dev/null
+++ b/SMLHelper/Interfaces/IEquipmentHandler.cs
@@ -0,0 +1,30 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler related to EquipmentTypes
+ ///
+ public interface IEquipmentHandler
+ {
+ ///
+ /// Registers an equipment type for use when creating a equipment
+ ///
+ /// The name of the new equipment type
+ /// The newly registered EquipmentType
+ EquipmentType AddEquipmentType(string equipmentName);
+
+ ///
+ /// Safely looks for a modded equipment type in the SMLHelper EquipmentTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded EquipmentType
+ /// The EquipmentType enum value. Defaults to when the EquipmentType was not found.
+ /// True if the EquipmentType was found; Otherwise false
+ bool TryGetModdedEquipmentType(string equipmentTypeString, out EquipmentType moddedEquipmentType);
+
+ ///
+ /// Checks for the existence of an added EquipmentType in the SMLHelper EquipmentTypeCache and outputs the result of the check
+ ///
+ /// The string used to define the modded EquipmentType
+ /// True if the EquipmentType was found; Otherwise false
+ public bool ModdedEquipmentTypeExists(string equipmentString);
+ }
+}
diff --git a/SMLHelper/Interfaces/IFishHandler.cs b/SMLHelper/Interfaces/IFishHandler.cs
new file mode 100644
index 00000000..bb2dbb8a
--- /dev/null
+++ b/SMLHelper/Interfaces/IFishHandler.cs
@@ -0,0 +1,19 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Assets;
+ using Handlers;
+
+ ///
+ /// Interface for
+ /// Can be used for dependency injection
+ ///
+ public interface IFishHandler
+ {
+ ///
+ /// Registers a CustomFish object into the game
+ ///
+ /// The CustomFish that you are registering
+ /// The TechType created using the info from your CustomFish object
+ TechType RegisterFish(Fish fish);
+ }
+}
diff --git a/SMLHelper/Interfaces/IIngameMenuHandler.cs b/SMLHelper/Interfaces/IIngameMenuHandler.cs
new file mode 100644
index 00000000..e10aeb62
--- /dev/null
+++ b/SMLHelper/Interfaces/IIngameMenuHandler.cs
@@ -0,0 +1,67 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using System;
+
+ ///
+ /// A handler class that offers simple ways to tap into functionality of the in game menu.
+ ///
+ public interface IIngameMenuHandler
+ {
+ ///
+ /// Registers a simple method to invoke whenever the player saves the game via the in game menu.
+ ///
+ /// The method to invoke.
+ void RegisterOnSaveEvent(Action onSaveAction);
+
+ ///
+ /// Registers a simple method to invoke whenever the player loads a saved game via the in game menu.
+ ///
+ /// The method to invoke.
+ void RegisterOnLoadEvent(Action onLoadAction);
+
+ ///
+ /// Registers a simple method to invoke whenever the player quits the game via the in game menu.
+ ///
+ /// The method to invoke.
+ void RegisterOnQuitEvent(Action onQuitAction);
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when saving the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ void UnregisterOnSaveEvent(Action onSaveAction);
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when loading the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ void UnregisterOnLoadEvent(Action onLoadAction);
+
+ ///
+ /// Removes a method previously added through so it is no longer invoked when quiting the game.
+ /// If you plan on using this, do not register an anonymous method.
+ ///
+ /// The method invoked.
+ void UnregisterOnQuitEvent(Action onQuitAction);
+
+ ///
+ /// Registers a simple method to invoke the first time the player saves the game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ void RegisterOneTimeUseOnSaveEvent(Action onSaveAction);
+
+ ///
+ /// Registers a simple method to invoke the first time the player loads a saved game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ void RegisterOneTimeUseOnLoadEvent(Action onLoadAction);
+
+ ///
+ /// Registers a simple method to invoke the first time the player quits the game via the in game menu.
+ ///
+ /// The method to invoke. This action will not be invoked a second time.
+ void RegisterOneTimeUseOnQuitEvent(Action onQuitAction);
+ }
+}
diff --git a/SMLHelper/Interfaces/IItemActionHandler.cs b/SMLHelper/Interfaces/IItemActionHandler.cs
new file mode 100644
index 00000000..69990d08
--- /dev/null
+++ b/SMLHelper/Interfaces/IItemActionHandler.cs
@@ -0,0 +1,28 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using System;
+
+ ///
+ /// A handler class for registering your custom middle click actions for items
+ ///
+ public interface IItemActionHandler
+ {
+ ///
+ /// Registers a custom left click action for a
+ ///
+ /// The to which the left click action will be assigned
+ /// The method which will be called when a matching with the specified was left-clicked
+ /// The secondary tooltip which will appear in the description of the item
+ /// The condition which must return for the action to be called when the item is clickedIf ommited, the action will always be called
+ void RegisterLeftClickAction(TechType targetTechType, Action callback, string tooltip, Predicate condition = null);
+
+ ///
+ /// Registers a custom middle click action for a
+ ///
+ /// The which the middle click action will be assigned
+ /// The method which will be called when a matching with the specified was middle-clicked
+ /// The secondary tooltip which will appear in the description of the item
+ /// The condition which must return for the action to be called when the item is clickedIf ommited, the action will always be called
+ void RegisterMiddleClickAction(TechType targetTechType, Action callback, string tooltip, Predicate condition = null);
+ }
+}
diff --git a/SMLHelper/Interfaces/IKnownTechHandler.cs b/SMLHelper/Interfaces/IKnownTechHandler.cs
new file mode 100644
index 00000000..8e8f88a9
--- /dev/null
+++ b/SMLHelper/Interfaces/IKnownTechHandler.cs
@@ -0,0 +1,88 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using System.Collections.Generic;
+
+ ///
+ /// A handler class for configuring custom unlocking conditions for item blueprints.
+ ///
+ public interface IKnownTechHandler
+ {
+ ///
+ /// Allows you to unlock a TechType on game start.
+ ///
+ ///
+ void UnlockOnStart(TechType techType);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, FMODAsset UnlockSound);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The sprite that shows up when you unlock the blueprint.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, UnityEngine.Sprite UnlockSprite);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage, FMODAsset UnlockSound);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The message that shows up on the right when the blueprint is unlocked.
+ /// The sprite that shows up when you unlock the blueprint.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, string UnlockMessage, UnityEngine.Sprite UnlockSprite);
+
+ ///
+ /// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
+ /// If there is already an exisitng AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
+ /// added to the existing AnalysisTech entry unlocks.
+ ///
+ /// This TechType is the criteria for all of the "unlock TechTypes"; when this TechType is unlocked, so are all the ones in that list
+ /// The TechTypes that will be unlocked when "techTypeToSet" is unlocked.
+ /// The sound that plays when you unlock the blueprint.
+ /// The sprite that shows up when you unlock the blueprint.
+ void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable techTypesToUnlock, FMODAsset UnlockSound, UnityEngine.Sprite UnlockSprite);
+ }
+}
diff --git a/SMLHelper/Interfaces/ILanguageHandler.cs b/SMLHelper/Interfaces/ILanguageHandler.cs
new file mode 100644
index 00000000..4274a53e
--- /dev/null
+++ b/SMLHelper/Interfaces/ILanguageHandler.cs
@@ -0,0 +1,29 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler for adding custom language lines.
+ ///
+ public interface ILanguageHandler
+ {
+ ///
+ /// Allows you to define a language entry into the game.
+ ///
+ /// The ID of the entry, this is what is used to get the actual text.
+ /// The actual text related to the entry.
+ void SetLanguageLine(string lineId, string text);
+
+ ///
+ /// Allows you to set the display name of a specific .
+ ///
+ /// The whose display name that is to be changed.
+ /// The new display name for the chosen .
+ void SetTechTypeName(TechType techType, string text);
+
+ ///
+ /// Allows you to set the tooltip of a specific .
+ ///
+ /// The whose tooltip that is to be changed.
+ /// The new tooltip for the chosen .
+ void SetTechTypeTooltip(TechType techType, string text);
+ }
+}
diff --git a/SMLHelper/Interfaces/ILootDistributionHandler.cs b/SMLHelper/Interfaces/ILootDistributionHandler.cs
new file mode 100644
index 00000000..d5b2de89
--- /dev/null
+++ b/SMLHelper/Interfaces/ILootDistributionHandler.cs
@@ -0,0 +1,69 @@
+using System.Collections.Generic;
+using SMLHelper.V2.Assets;
+using SMLHelper.V2.Handlers;
+using UWE;
+
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler that manages Loot Distribution (spawning of fragments, fish, etc).
+ ///
+ public interface ILootDistributionHandler
+ {
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ ///
+ ///
+ ///
+ void AddLootDistributionData(string classId, LootDistributionData.SrcData data);
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The that contains data related to the spawning of a prefab, also contains the path to the prefab.
+ /// The classId of the prefab.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ void AddLootDistributionData(string classId, LootDistributionData.SrcData data, WorldEntityInfo info);
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The classId of the prefab.
+ /// The prefab path of the prefab.
+ /// The dictating how the prefab should spawn in the world.
+ void AddLootDistributionData(string classId, string prefabPath, IEnumerable biomeDistribution);
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The classId of the prefab.
+ /// The prefab path of the prefab.
+ /// The dictating how the prefab should spawn in the world.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ void AddLootDistributionData(string classId, string prefabPath, IEnumerable biomeDistribution, WorldEntityInfo info);
+
+ ///
+ /// Adds in a custom entry into the Loot Distribution of the game.
+ /// You must also add the into the using .
+ ///
+ /// The custom prefab which you want to spawn naturally in the game.
+ /// The dictating how the prefab should spawn in the world.
+ /// The WorldEntityInfo of the prefab. For more information on how to set this up, see .
+ void AddLootDistributionData(ModPrefab prefab, IEnumerable biomeDistribution, WorldEntityInfo info);
+
+ ///
+ /// Edits Loot Distribution Data for existing/original class IDs.
+ ///
+ void EditLootDistributionData(string classId, BiomeType biome, float probability, int count);
+
+ ///
+ /// Edits Loot Distribution data for existing prefabs, for e.g. original game prefabs.
+ ///
+ /// The ClassID of the prefab. If unsure, use CraftData.GetClassIdForTechType.
+ /// The list of that contains information about how/when it should spawn in biomes.
+ void EditLootDistributionData(string classId, IEnumerable biomeDistribution);
+ }
+}
diff --git a/SMLHelper/Interfaces/IModOptionAttribute.cs b/SMLHelper/Interfaces/IModOptionAttribute.cs
new file mode 100644
index 00000000..d6d7edcd
--- /dev/null
+++ b/SMLHelper/Interfaces/IModOptionAttribute.cs
@@ -0,0 +1,46 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Handlers;
+ using Json;
+ using Options;
+ using Options.Attributes;
+
+ ///
+ /// Defines properties for derivatives to implement for the purpose of holding
+ /// metadata about fields and their generation.
+ ///
+ public interface IModOptionAttribute
+ {
+ ///
+ /// The label to use when displaying the field in the mod's options menu.
+ ///
+ string Label { get; set; }
+
+ ///
+ /// The Id to be used for the field in the mod's option menu. If none is specified, one will be automatically generated when
+ /// your is registered to the . This means it will
+ /// change every time the game is launched, but is guaranteed to be unique. If you would like to specify an Id to use for
+ /// internal comparisons, you can do so here.
+ ///
+ string Id { get; set; }
+
+ ///
+ /// The order in which to display fields in the mod's option menu, in ascending order. If none is specified, the order will be
+ /// automatically set.
+ ///
+ int Order { get; set; }
+
+ ///
+ /// An optional tooltip to display for the field.
+ ///
+ string Tooltip { get; }
+
+ ///
+ /// An optional id to be parsed with for the label, allowing for custom language-based strings
+ /// via the API.
+ ///
+ ///
+ ///
+ string LabelLanguageId { get; }
+ }
+}
diff --git a/SMLHelper/Interfaces/IModOptionEventArgs.cs b/SMLHelper/Interfaces/IModOptionEventArgs.cs
new file mode 100644
index 00000000..cfe73d4b
--- /dev/null
+++ b/SMLHelper/Interfaces/IModOptionEventArgs.cs
@@ -0,0 +1,13 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// Interface for event arguments for a .
+ ///
+ public interface IModOptionEventArgs
+ {
+ ///
+ /// The ID of the this event corresponds to.
+ ///
+ string Id { get; }
+ }
+}
diff --git a/SMLHelper/Interfaces/IModOptionEventAttribute.cs b/SMLHelper/Interfaces/IModOptionEventAttribute.cs
new file mode 100644
index 00000000..692f03e1
--- /dev/null
+++ b/SMLHelper/Interfaces/IModOptionEventAttribute.cs
@@ -0,0 +1,16 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Options.Attributes;
+
+ ///
+ /// Defines properties for derivatives to implement for the purpose
+ /// of holding metadata about events.
+ ///
+ public interface IModOptionEventAttribute
+ {
+ ///
+ /// The name of the method to invoke.
+ ///
+ string MethodName { get; }
+ }
+}
diff --git a/SMLHelper/Interfaces/IOptionsPanelHandler.cs b/SMLHelper/Interfaces/IOptionsPanelHandler.cs
new file mode 100644
index 00000000..283c26c0
--- /dev/null
+++ b/SMLHelper/Interfaces/IOptionsPanelHandler.cs
@@ -0,0 +1,28 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Options;
+ using Json;
+
+ ///
+ /// A handler class for registering your custom in-game mod options.
+ ///
+ public interface IOptionsPanelHandler
+ {
+ ///
+ /// Registers your mod options to the in-game menu.
+ ///
+ /// The mod options. Create a new child class inheriting from this one
+ /// and add your options to it.
+ ///
+ void RegisterModOptions(ModOptions options);
+
+ ///
+ /// Generates an options menu based on the attributes and members declared in the
+ /// and registers it to the in-game menu.
+ ///
+ /// A class derived from to generate the options menu from.
+ /// An instance of the : with values loaded
+ /// from the config file on disk.
+ T RegisterModOptions() where T : ConfigFile, new();
+ }
+}
diff --git a/SMLHelper/Interfaces/IPDAEncyclopediaHandler.cs b/SMLHelper/Interfaces/IPDAEncyclopediaHandler.cs
new file mode 100644
index 00000000..026a822d
--- /dev/null
+++ b/SMLHelper/Interfaces/IPDAEncyclopediaHandler.cs
@@ -0,0 +1,14 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// Handles Encyclopedia.
+ ///
+ public interface IPDAEncyclopediaHandler
+ {
+ ///
+ /// Adds custom entry.
+ ///
+ ///
+ void AddCustomEntry(PDAEncyclopedia.EntryData entry);
+ }
+}
diff --git a/SMLHelper/Interfaces/IPDAHandler.cs b/SMLHelper/Interfaces/IPDAHandler.cs
new file mode 100644
index 00000000..a3d094f6
--- /dev/null
+++ b/SMLHelper/Interfaces/IPDAHandler.cs
@@ -0,0 +1,28 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler class for various scanner related data.
+ ///
+ public interface IPDAHandler
+ {
+ ///
+ /// Edits how many fragments must be scanned before unlocking the techtype's blueprint.
+ ///
+ /// Can be either techtype of the fragment or the crafted item.
+ /// The number of fragments to scan.
+ void EditFragmentsToScan(TechType techType, int fragmentCount);
+
+ ///
+ /// Edits the time it takes to finish scanning a fragment.
+ ///
+ /// Can be either techtype of the fragment or the crafted item.
+ /// The relative time spent on scanning. Default value is 1.
+ void EditFragmentScanTime(TechType techType, float scanTime);
+
+ ///
+ /// Adds custom scanner entry.
+ ///
+ ///
+ void AddCustomScannerEntry(PDAScanner.EntryData entryData);
+ }
+}
diff --git a/SMLHelper/Interfaces/IPingHandler.cs b/SMLHelper/Interfaces/IPingHandler.cs
new file mode 100644
index 00000000..32c46b68
--- /dev/null
+++ b/SMLHelper/Interfaces/IPingHandler.cs
@@ -0,0 +1,30 @@
+namespace SMLHelper.V2.Interfaces
+{
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+#elif BELOWZERO
+ using Sprite = UnityEngine.Sprite;
+#endif
+
+ ///
+ /// A handler related to PingTypes
+ ///
+ public interface IPingHandler
+ {
+ ///
+ /// Registers a ping type for use when creating a beacon
+ ///
+ /// The name of the new ping type
+ /// The sprite that is associated with the ping
+ /// The newly registered PingType
+ PingType RegisterNewPingType(string pingName, Sprite sprite);
+
+ ///
+ /// Safely looks for a modded ping type in the SMLHelper PingTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded PingType
+ /// The PingType enum value. Defaults to when the PingType was not found.
+ /// True if the PingType was found; Otherwise false
+ bool TryGetModdedPingType(string pingTypeString, out PingType moddedPingType);
+ }
+}
diff --git a/SMLHelper/Interfaces/IPrefabHandler.cs b/SMLHelper/Interfaces/IPrefabHandler.cs
new file mode 100644
index 00000000..e6b3e0bc
--- /dev/null
+++ b/SMLHelper/Interfaces/IPrefabHandler.cs
@@ -0,0 +1,17 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Assets;
+
+ ///
+ /// A handler for registering Unity prefabs associated to a .
+ ///
+ public interface IPrefabHandler
+ {
+ ///
+ /// Registers a ModPrefab into the game.
+ ///
+ /// The mod prefab to register. Create a child class inheriting off this one and configure as needed.
+ ///
+ void RegisterPrefab(ModPrefab prefab);
+ }
+}
diff --git a/SMLHelper/Interfaces/ISaveDataHandler.cs b/SMLHelper/Interfaces/ISaveDataHandler.cs
new file mode 100644
index 00000000..fdbad4fd
--- /dev/null
+++ b/SMLHelper/Interfaces/ISaveDataHandler.cs
@@ -0,0 +1,18 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Json;
+
+ ///
+ /// A handler class for registering your .
+ ///
+ public interface ISaveDataHandler
+ {
+ ///
+ /// Registers your to be automatically loaded and saved whenever the game is.
+ ///
+ /// A class derived from to hold your save data.
+ /// An instance of the : with values loaded
+ /// from the json file on disk whenever a save slot is loaded.
+ T RegisterSaveDataCache() where T : SaveDataCache, new();
+ }
+}
diff --git a/SMLHelper/Interfaces/ISpriteHandler.cs b/SMLHelper/Interfaces/ISpriteHandler.cs
new file mode 100644
index 00000000..5f163ed8
--- /dev/null
+++ b/SMLHelper/Interfaces/ISpriteHandler.cs
@@ -0,0 +1,85 @@
+namespace SMLHelper.V2.Interfaces
+{
+ using Utility;
+ using UnityEngine;
+
+ ///
+ /// A handler class for adding custom sprites into the game.
+ ///
+ public interface ISpriteHandler
+ {
+
+#if SUBNAUTICA
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ void RegisterSprite(TechType type, Atlas.Sprite sprite);
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ void RegisterSprite(SpriteManager.Group group, string id, Atlas.Sprite sprite);
+
+#endif
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group this sprite will be added to.
+ /// The sprite internal identifier.
+ /// The sprite to be added.
+ void RegisterSprite(SpriteManager.Group group, string id, Sprite sprite);
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The sprite to be added.
+ void RegisterSprite(TechType type, Sprite sprite);
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ void RegisterSprite(TechType type, string filePathToImage, TextureFormat format);
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The techtype paired to this sprite.
+ /// The file path to image to be converted into a sprite.
+ ///
+ void RegisterSprite(TechType type, string filePathToImage);
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ /// The texture format. By default, this uses .
+ /// https://docs.unity3d.com/ScriptReference/TextureFormat.BC7.html
+ /// Don't change this unless you really know what you're doing.
+ ///
+ void RegisterSprite(SpriteManager.Group group, string id, string filePathToImage, TextureFormat format);
+
+ ///
+ /// Registers a new sprite to the game.
+ ///
+ /// The sprite group.
+ /// The sprite internal identifier.
+ /// The file path to image.
+ ///
+ void RegisterSprite(SpriteManager.Group group, string id, string filePathToImage);
+ }
+}
diff --git a/SMLHelper/Interfaces/IStorageHelper.cs b/SMLHelper/Interfaces/IStorageHelper.cs
new file mode 100644
index 00000000..319f72a3
--- /dev/null
+++ b/SMLHelper/Interfaces/IStorageHelper.cs
@@ -0,0 +1,72 @@
+namespace SMLHelper.V2.Utility
+{
+ using System.Collections.Generic;
+
+ ///
+ /// A utility class that offers additional info about instances.
+ ///
+ ///
+ public interface IStorageHelper
+ {
+ ///
+ /// Using the cached container info, determines whether the specified container has room for an item of the specified size.
+ ///
+ /// The container to check.
+ /// The item width.
+ /// The item height.
+ ///
+ /// true if there is room for the item in the container,; otherwise, false.
+ ///
+ bool HasRoomForCached(ItemsContainer container, int width, int height);
+
+ ///
+ /// Using the cached container info, determines whether the specified container has room for an item of the specified size.
+ ///
+ /// The container to check.
+ /// Size of the item.
+ ///
+ /// true if there is room for the item in the container,; otherwise, false.
+ ///
+ bool HasRoomForCached(ItemsContainer container, Vector2int itemSize);
+
+ ///
+ /// Determines whether the specified container is empty.
+ ///
+ /// The items container to check.
+ ///
+ /// true if the specified container is empty; otherwise, false.
+ ///
+ bool IsEmpty(ItemsContainer container);
+
+ ///
+ /// Determines whether the specified container is full.
+ ///
+ /// The items container to check.
+ ///
+ /// true if the specified container is full; otherwise, false.
+ ///
+ bool IsFull(ItemsContainer container);
+
+ ///
+ /// Get the totals number of 1x1 slots in the container, as calculated by the container's width and height.
+ ///
+ /// The container.
+ /// The total number of slots in the container.
+ int GetTotalSlots(ItemsContainer container);
+
+ ///
+ /// Get the inernal label for the storage container.
+ ///
+ /// The container.
+ /// The label used and displayed in-game for the container.
+ string GetStorageLabel(ItemsContainer container);
+
+ ///
+ /// Gets the set of techtypes allowed in this container. This set can be altered.
+ /// If the set is null or empty, then all items can be added.
+ ///
+ /// The container to check.
+ /// The collection of techtypes allowed in the container.
+ HashSet GetAllowedTechTypes(ItemsContainer container);
+ }
+}
diff --git a/SMLHelper/Interfaces/ISurvivalHandler.cs b/SMLHelper/Interfaces/ISurvivalHandler.cs
new file mode 100644
index 00000000..7b5fe795
--- /dev/null
+++ b/SMLHelper/Interfaces/ISurvivalHandler.cs
@@ -0,0 +1,27 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// a handler for common uses to the Survival component
+ ///
+ public interface ISurvivalHandler
+ {
+ ///
+ /// makes the item gives oxygen on use.
+ ///
+ /// the TechType that you want to make it give oxygen on use
+ /// the oxygen amount the item gives
+ /// set it to if the item is edible and has the component attached to it.
+ /// defaults to
+ ///
+ void GiveOxygenOnConsume(TechType techType, float oxygenGiven, bool isEdible = false);
+ ///
+ /// makes the item Heal the player on consume.
+ ///
+ /// the TechType that you want it to heal back
+ /// amount to heal the player
+ /// set it to if the item is edible and has the component attached to it.
+ /// defaults to
+ ///
+ void GiveHealthOnConsume(TechType techType, float healthBack, bool isEdible = false);
+ }
+}
diff --git a/SMLHelper/Interfaces/ITechCategoryHandler.cs b/SMLHelper/Interfaces/ITechCategoryHandler.cs
new file mode 100644
index 00000000..ce800fd6
--- /dev/null
+++ b/SMLHelper/Interfaces/ITechCategoryHandler.cs
@@ -0,0 +1,43 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler class for everything related to creating new TechCategorys.
+ ///
+ public interface ITechCategoryHandler
+ {
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The name of the TechCategory. Should not contain special characters.
+ /// The display name of the TechCategory. Can be anything.
+ /// The new that is created.
+ TechCategory AddTechCategory(string techCatagoryName, string displayName);
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechCategoryCache and outputs its value when found.
+ ///
+ /// The string used to define the techcategory.
+ /// The TechCategory enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ bool TryGetModdedTechCategory(string techCategoryString, out TechCategory modTechCategory);
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechCategoryCache.
+ ///
+ /// The string used to define the techcategory.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ bool ModdedTechCategoryExists(string techCategoryString);
+
+ ///
+ /// Registers the TechCategory to a TechGroup.
+ ///
+ /// The tech group.
+ /// The tech category.
+ ///
+ bool TryRegisterTechCategoryToTechGroup(TechGroup techGroup, TechCategory techCategory);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Interfaces/ITechGroupHandler.cs b/SMLHelper/Interfaces/ITechGroupHandler.cs
new file mode 100644
index 00000000..9ed63843
--- /dev/null
+++ b/SMLHelper/Interfaces/ITechGroupHandler.cs
@@ -0,0 +1,42 @@
+namespace SMLHelper.V2.Interfaces
+{
+#if SUBNAUTICA
+ using Sprite = Atlas.Sprite;
+#else
+ using UnityEngine;
+#endif
+
+ ///
+ /// A handler class for everything related to creating new TechGroups.
+ ///
+ public interface ITechGroupHandler
+ {
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The name of the TechGroup. Should not contain special characters.
+ /// The display name of the TechGroup. Can be anything.
+ /// The new that is created.
+ TechGroup AddTechGroup(string techGroupName, string displayName);
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechGroupCache and outputs its value when found.
+ ///
+ /// The string used to define the techgroup.
+ /// The TechGroup enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ bool TryGetModdedTechGroup(string techGroupString, out TechGroup modTechGroup);
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechGroupCache.
+ ///
+ /// The string used to define the techgroup.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ bool ModdedTechGroupExists(string techGroupString);
+
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Interfaces/ITechTypeHandler.cs b/SMLHelper/Interfaces/ITechTypeHandler.cs
new file mode 100644
index 00000000..0f6e44a1
--- /dev/null
+++ b/SMLHelper/Interfaces/ITechTypeHandler.cs
@@ -0,0 +1,98 @@
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// A handler class for everything related to creating new TechTypes.
+ ///
+ public interface ITechTypeHandler
+ {
+ ///
+ /// Adds a new into the game. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The new that is created.
+ TechType AddTechType(string internalName, string displayName, string tooltip);
+
+ ///
+ /// Adds a new into the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// Whether this TechType should be unlocked on game start, or not. By default, true.
+ /// The new that is created.
+ TechType AddTechType(string internalName, string displayName, string tooltip, bool unlockAtStart);
+
+#if SUBNAUTICA
+ ///
+ /// Adds a new into the game, with a sprite. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// The new that is created.
+ TechType AddTechType(string internalName, string displayName, string tooltip, Atlas.Sprite sprite);
+
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, true.
+ /// The new that is created.
+ TechType AddTechType(string internalName, string displayName, string tooltip, Atlas.Sprite sprite, bool unlockAtStart);
+
+#endif
+ ///
+ /// Adds a new into the game, with a sprite. This new techtype will be unlocked at the start of a the game.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// The new that is created.
+ TechType AddTechType(string internalName, string displayName, string tooltip, UnityEngine.Sprite sprite);
+
+ ///
+ /// Adds a new into the game, with a sprite.
+ ///
+ /// The internal name of the TechType. Should not contain special characters.
+ /// The display name of the TechType. Can be anything.
+ /// The tooltip, displayed when hovered in an inventory. Can be anything.
+ /// The sprite that will related to this TechType.
+ /// Whether this TechType should be unlocked on game start, or not. By default, true.
+ /// The new that is created.
+ TechType AddTechType(string internalName, string displayName, string tooltip, UnityEngine.Sprite sprite, bool unlockAtStart);
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechTypeCache and outputs its value when found.
+ ///
+ /// The string used to define the modded item's new techtype.
+ /// The TechType enum value of the modded. Defaults to when the item was not found.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ bool TryGetModdedTechType(string techtypeString, out TechType modTechType);
+
+ ///
+ /// Safely looks for a modded item from another mod in the SMLHelper TechTypeCache.
+ ///
+ /// The string used to define the modded item's new techtype.
+ ///
+ /// True if the item was found; Otherwise false.
+ ///
+ ///
+ /// There's no guarantee in which order SMLHelper dependent mods are loaded,
+ /// so if two mods are added at the same time, it may take a second game load for both to be visible to each other.
+ ///
+ bool ModdedTechTypeExists(string techtypeString);
+ }
+}
diff --git a/SMLHelper/Interfaces/ITechTypeHandlerInternal.cs b/SMLHelper/Interfaces/ITechTypeHandlerInternal.cs
new file mode 100644
index 00000000..e4fda442
--- /dev/null
+++ b/SMLHelper/Interfaces/ITechTypeHandlerInternal.cs
@@ -0,0 +1,10 @@
+using System.Reflection;
+
+namespace SMLHelper.V2.Interfaces
+{
+ internal interface ITechTypeHandlerInternal : ITechTypeHandler
+ {
+ TechType AddTechType(Assembly modAssembly, string internalName, string displayName, string tooltip);
+ TechType AddTechType(Assembly modAssembly, string internalName, string displayName, string tooltip, bool unlockAtStart);
+ }
+}
diff --git a/SMLHelper/Interfaces/IWorldEntityDatabaseHandler.cs b/SMLHelper/Interfaces/IWorldEntityDatabaseHandler.cs
new file mode 100644
index 00000000..a615af8d
--- /dev/null
+++ b/SMLHelper/Interfaces/IWorldEntityDatabaseHandler.cs
@@ -0,0 +1,17 @@
+using UWE;
+
+namespace SMLHelper.V2.Interfaces
+{
+ ///
+ /// Handles WorldEntityDatabase.
+ ///
+ public interface IWorldEntityDatabaseHandler
+ {
+ ///
+ /// Adds custom World Entity data to WorldEntityDatabase.
+ ///
+ ///
+ ///
+ void AddCustomInfo(string classId, WorldEntityInfo data);
+ }
+}
diff --git a/SMLHelper/Json/Attributes/FileNameAttribute.cs b/SMLHelper/Json/Attributes/FileNameAttribute.cs
new file mode 100644
index 00000000..d36abe48
--- /dev/null
+++ b/SMLHelper/Json/Attributes/FileNameAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace SMLHelper.V2.Json.Attributes
+{
+ ///
+ /// Attribute used to specify a file name for use with a .
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public class FileNameAttribute : Attribute
+ {
+ ///
+ /// The filename.
+ ///
+ public string FileName { get; }
+
+ ///
+ /// Used to specify the file name for a .
+ ///
+ ///
+ public FileNameAttribute(string fileName)
+ {
+ FileName = fileName;
+ }
+ }
+}
diff --git a/SMLHelper/Json/ConfigFile.cs b/SMLHelper/Json/ConfigFile.cs
new file mode 100644
index 00000000..73785c3d
--- /dev/null
+++ b/SMLHelper/Json/ConfigFile.cs
@@ -0,0 +1,174 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Linq;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+using Oculus.Newtonsoft.Json.Converters;
+#else
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+#endif
+
+namespace SMLHelper.V2.Json
+{
+ using Converters;
+ using ExtensionMethods;
+ using Interfaces;
+
+ ///
+ /// A simple implementation of for use with config files.
+ ///
+ public abstract class ConfigFile : IJsonFile
+ {
+ [JsonIgnore]
+ private readonly string JsonFilename;
+ [JsonIgnore]
+ private readonly string JsonPath;
+
+ ///
+ /// The file path at which the JSON file is accessible for reading and writing.
+ ///
+ public string JsonFilePath => Path.Combine(JsonPath, $"{JsonFilename}.json");
+
+ [JsonIgnore]
+ private static readonly JsonConverter[] alwaysIncludedJsonConverters = new JsonConverter[] {
+ new KeyCodeConverter(),
+ new FloatConverter(),
+ new StringEnumConverter(),
+ new VersionConverter(),
+ new Vector2Converter(),
+ new Vector3Converter(),
+ new Vector4Converter(),
+ new Vector2IntConverter(),
+ new Vector3IntConverter(),
+ new QuaternionConverter()
+ };
+
+ ///
+ /// The s that should always be used when reading/writing JSON data.
+ ///
+ ///
+ public JsonConverter[] AlwaysIncludedJsonConverters => alwaysIncludedJsonConverters;
+
+ ///
+ /// Creates a new instance of , parsing the filename and subfolder from a
+ /// if declared, or with default values otherwise.
+ ///
+ public ConfigFile()
+ {
+ if (GetType().GetCustomAttribute(true) is ConfigFileAttribute configFile)
+ {
+ JsonFilename = configFile.Filename;
+ JsonPath = Path.Combine(
+ Path.GetDirectoryName(Assembly.GetCallingAssembly().Location),
+ configFile.Subfolder);
+ }
+ else
+ {
+ JsonFilename = "config";
+ JsonPath = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location);
+ }
+ }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The name of the , "config" by default.
+ /// Optional subfolder for the .
+ ///
+ ///
+ /// using SMLHelper.V2.Options;
+ /// using UnityEngine;
+ ///
+ /// public class MyConfig : ConfigFile
+ /// {
+ /// public KeyCode ActivationKey { get; set; } = KeyCode.Escape;
+ /// public MyConfig() : base("options", "Config Files") { }
+ /// // The config file will be stored at the path "QMods\YourModName\Config Files\options.json"
+ /// }
+ ///
+ ///
+ protected ConfigFile(string fileName = "config", string subfolder = null)
+ {
+ JsonFilename = fileName;
+ JsonPath = Path.Combine(
+ Path.GetDirectoryName(Assembly.GetCallingAssembly().Location),
+ string.IsNullOrEmpty(subfolder) ? string.Empty : subfolder);
+ }
+
+ ///
+ /// An event that is invoked whenever the is about to load data from disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnStartedLoading;
+ ///
+ /// An event that is invoked whenever the has finished loading data from disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnFinishedLoading;
+
+ ///
+ /// An event that is invoked whenever the is about to save data to disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnStartedSaving;
+ ///
+ /// An event that is invoked whenever the has finished saving data to disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnFinishedSaving;
+
+ ///
+ /// Loads the JSON properties from the file on disk into the .
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ ///
+ ///
+ public void Load(bool createFileIfNotExist = true)
+ {
+ var e = new ConfigFileEventArgs(this);
+ OnStartedLoading?.Invoke(this, e);
+ this.LoadJson(JsonFilePath, createFileIfNotExist, AlwaysIncludedJsonConverters.Distinct().ToArray());
+ OnFinishedLoading?.Invoke(this, e);
+ }
+
+ ///
+ /// Saves the current fields and properties of the as JSON properties to the file on disk.
+ ///
+ ///
+ ///
+ public void Save()
+ {
+ var e = new ConfigFileEventArgs(this);
+ OnStartedSaving?.Invoke(this, e);
+ this.SaveJson(JsonFilePath, AlwaysIncludedJsonConverters.Distinct().ToArray());
+ OnFinishedSaving?.Invoke(this, e);
+ }
+
+ ///
+ /// Loads the JSON properties from the file on disk into the .
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ /// Optional s to be used for serialization.
+ /// The will always be used, regardless of whether you pass them.
+ ///
+ ///
+ public void LoadWithConverters(bool createFileIfNotExist = true, params JsonConverter[] jsonConverters)
+ => this.LoadJson(JsonFilePath, true,
+ AlwaysIncludedJsonConverters.Concat(jsonConverters).Distinct().ToArray());
+
+ ///
+ /// Saves the current fields and properties of the as JSON properties to the file on disk.
+ ///
+ /// Optional s to be used for deserialization.
+ /// The will always be used, regardless of whether you pass them.
+ ///
+ ///
+ public void SaveWithConverters(params JsonConverter[] jsonConverters)
+ => this.SaveJson(JsonFilePath,
+ AlwaysIncludedJsonConverters.Concat(jsonConverters).Distinct().ToArray());
+ }
+}
diff --git a/SMLHelper/Json/ConfigFileAttribute.cs b/SMLHelper/Json/ConfigFileAttribute.cs
new file mode 100644
index 00000000..4959c3b1
--- /dev/null
+++ b/SMLHelper/Json/ConfigFileAttribute.cs
@@ -0,0 +1,50 @@
+namespace SMLHelper.V2.Json
+{
+ using System;
+
+ ///
+ /// Attribute used to specify the filename and subfolder for a .
+ ///
+ ///
+ /// When used alongside the constructor, this attribute is ignored.
+ ///
+ ///
+ ///
+ /// using SMLHelper.V2.Json;
+ ///
+ /// [ConfigFile("options", "configs")]
+ /// public class Config : ConfigFile
+ /// {
+ /// public float MyFloatValue;
+ /// }
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public sealed class ConfigFileAttribute : Attribute
+ {
+ ///
+ /// The filename to use for the .
+ ///
+ public string Filename { get; }
+
+ ///
+ /// The subfolder within the mod's folder for the .
+ ///
+ public string Subfolder { get; set; } = string.Empty;
+
+ ///
+ /// Used to specify the filename for the .
+ ///
+ ///
+ /// When used alongside the constructor, this attribute is ignored.
+ ///
+ /// The filename to use for the .
+ public ConfigFileAttribute(string filename = "config")
+ {
+ if (string.IsNullOrEmpty(filename))
+ filename = "config";
+
+ Filename = filename;
+ }
+ }
+}
diff --git a/SMLHelper/Json/ConfigFileEventArgs.cs b/SMLHelper/Json/ConfigFileEventArgs.cs
new file mode 100644
index 00000000..2dd6dfec
--- /dev/null
+++ b/SMLHelper/Json/ConfigFileEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace SMLHelper.V2.Json
+{
+ ///
+ /// Contains basic information for a event.
+ ///
+ public class ConfigFileEventArgs : EventArgs
+ {
+ ///
+ /// The instance of the this event pertains to.
+ ///
+ public ConfigFile Instance { get; }
+
+ ///
+ /// Instantiates a new .
+ ///
+ /// The instance the event pertains to.
+ public ConfigFileEventArgs(ConfigFile instance)
+ {
+ Instance = instance;
+ }
+ }
+}
diff --git a/SMLHelper/Json/Converters/FloatConverter.cs b/SMLHelper/Json/Converters/FloatConverter.cs
new file mode 100644
index 00000000..a7524170
--- /dev/null
+++ b/SMLHelper/Json/Converters/FloatConverter.cs
@@ -0,0 +1,93 @@
+namespace SMLHelper.V2.Json.Converters
+{
+ using System;
+ using System.Globalization;
+#if SUBNAUTICA_STABLE
+ using Oculus.Newtonsoft.Json;
+#else
+ using Newtonsoft.Json;
+#endif
+
+ ///
+ /// A for rounding floats or doubles to a given number of decimal places,
+ /// trimming trailing 0s.
+ ///
+ public class FloatConverter : JsonConverter
+ {
+ private readonly int DecimalPlaces = 4;
+ private readonly MidpointRounding Mode = MidpointRounding.AwayFromZero;
+ ///
+ /// Creates a new .
+ ///
+ /// The number of decimal places to round to, where -1 disables rounding.
+ /// The rounding mode to use when rounding.
+ ///
+ public FloatConverter(int decimalPlaces, MidpointRounding mode)
+ {
+ DecimalPlaces = decimalPlaces;
+ Mode = mode;
+ }
+ ///
+ /// Creates a new .
+ ///
+ /// The number of decimal places to round to, where -1 disables rounding.
+ public FloatConverter(int decimalPlaces) => DecimalPlaces = decimalPlaces;
+ ///
+ /// Creates a new .
+ ///
+ /// The rounding mode to use when rounding.
+ public FloatConverter(MidpointRounding mode) => Mode = mode;
+ ///
+ /// Creates a new .
+ ///
+ public FloatConverter() { }
+
+ ///
+ /// The method for writing the data to the .
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ double d = Convert.ToDouble(value);
+ if (DecimalPlaces > -1)
+ {
+ writer.WriteValue(Math.Round(d, DecimalPlaces, Mode).ToString(CultureInfo.InvariantCulture.NumberFormat));
+ }
+ else
+ {
+ writer.WriteValue(d.ToString(CultureInfo.InvariantCulture.NumberFormat));
+ }
+ }
+
+ ///
+ /// The method for reading the or data from the .
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var s = (string)reader.Value;
+ if (objectType == typeof(float))
+ {
+ return float.Parse(s, CultureInfo.InvariantCulture.NumberFormat);
+ }
+ else
+ {
+ return double.Parse(s, CultureInfo.InvariantCulture.NumberFormat);
+ }
+ }
+
+ ///
+ /// The method for determining whether the current can be processed byt this
+ ///
+ ///
+ ///
+ ///
+ public override bool CanConvert(Type objectType) => objectType == typeof(float) || objectType == typeof(double);
+ }
+}
diff --git a/SMLHelper/Json/Converters/KeyCodeConverter.cs b/SMLHelper/Json/Converters/KeyCodeConverter.cs
new file mode 100644
index 00000000..32de9174
--- /dev/null
+++ b/SMLHelper/Json/Converters/KeyCodeConverter.cs
@@ -0,0 +1,52 @@
+namespace SMLHelper.V2.Json.Converters
+{
+ using System;
+ using SMLHelper.V2.Utility;
+ using UnityEngine;
+#if SUBNAUTICA_STABLE
+ using Oculus.Newtonsoft.Json;
+#else
+ using Newtonsoft.Json;
+#endif
+
+ ///
+ /// A for handling s.
+ ///
+ public class KeyCodeConverter : JsonConverter
+ {
+ ///
+ /// The method for writing the data to the .
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var keyCode = (KeyCode)value;
+ writer.WriteValue(KeyCodeUtils.KeyCodeToString(keyCode));
+ }
+
+ ///
+ /// The method for reading the data from the .
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType,
+ object existingValue, JsonSerializer serializer)
+ {
+ var s = (string)reader.Value;
+ return KeyCodeUtils.StringToKeyCode(s);
+ }
+
+ ///
+ /// The method for determining whether the current can be processed by this
+ /// .
+ ///
+ ///
+ ///
+ public override bool CanConvert(Type objectType) => objectType == typeof(KeyCode);
+ }
+}
diff --git a/SMLHelper/Json/Converters/QuaternionConverter.cs b/SMLHelper/Json/Converters/QuaternionConverter.cs
new file mode 100644
index 00000000..81e4c3e6
--- /dev/null
+++ b/SMLHelper/Json/Converters/QuaternionConverter.cs
@@ -0,0 +1,56 @@
+using System;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+ using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+namespace SMLHelper.V2.Json.Converters
+{
+ ///
+ /// A Quaternion json converter that simplifies the Vector3 to only x,y,z serialization.
+ ///
+ public class QuaternionConverter : JsonConverter
+ {
+ ///
+ /// A method that determines when this converter should process.
+ ///
+ /// the current object type
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Quaternion);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Serialize the current object.
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var quaternion = (Quaternion)value;
+ serializer.Serialize(writer, (QuaternionJson)quaternion);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Deserialize and read the current object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return (Quaternion)serializer.Deserialize(reader);
+ }
+ }
+
+ internal record QuaternionJson(float X, float Y, float Z, float W)
+ {
+ public static explicit operator Quaternion(QuaternionJson q) => new(q.X, q.Y, q.Z, q.W);
+ public static explicit operator QuaternionJson(Quaternion q) => new(q.x, q.y, q.z, q.w);
+ }
+}
diff --git a/SMLHelper/Json/Converters/Vector2Converter.cs b/SMLHelper/Json/Converters/Vector2Converter.cs
new file mode 100644
index 00000000..adf6a09e
--- /dev/null
+++ b/SMLHelper/Json/Converters/Vector2Converter.cs
@@ -0,0 +1,56 @@
+using System;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+namespace SMLHelper.V2.Json.Converters
+{
+ ///
+ /// A Vector2 json converter that simplifies the Vector2 to only x,y serialization.
+ ///
+ public class Vector2Converter : JsonConverter
+ {
+ ///
+ /// A method that determines when this converter should process.
+ ///
+ /// the current object type
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Vector2);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Serialize the current object.
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var vector2 = (Vector2)value;
+ serializer.Serialize(writer, (Vector2Json)vector2);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Deserialize and read the current object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return (Vector2)serializer.Deserialize(reader);
+ }
+ }
+
+ internal record Vector2Json(float X, float Y)
+ {
+ public static explicit operator Vector2(Vector2Json v) => new(v.X, v.Y);
+ public static explicit operator Vector2Json(Vector2 v) => new(v.x, v.y);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Json/Converters/Vector2IntConverter.cs b/SMLHelper/Json/Converters/Vector2IntConverter.cs
new file mode 100644
index 00000000..849813e9
--- /dev/null
+++ b/SMLHelper/Json/Converters/Vector2IntConverter.cs
@@ -0,0 +1,56 @@
+using System;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+namespace SMLHelper.V2.Json.Converters
+{
+ ///
+ /// A Vector2Int json converter that simplifies the Vector2Int to only x,y serialization.
+ ///
+ public class Vector2IntConverter : JsonConverter
+ {
+ ///
+ /// A method that determines when this converter should process.
+ ///
+ /// the current object type
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Vector2Int);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Serialize the current object.
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var vector2Int = (Vector2Int)value;
+ serializer.Serialize(writer, (Vector2IntJson)vector2Int);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Deserialize and read the current object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return (Vector2Int)serializer.Deserialize(reader);
+ }
+ }
+
+ internal record Vector2IntJson(int X, int Y)
+ {
+ public static explicit operator Vector2Int(Vector2IntJson v) => new(v.X, v.Y);
+ public static explicit operator Vector2IntJson(Vector2Int v) => new(v.x, v.y);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Json/Converters/Vector3Converter.cs b/SMLHelper/Json/Converters/Vector3Converter.cs
new file mode 100644
index 00000000..c68989c8
--- /dev/null
+++ b/SMLHelper/Json/Converters/Vector3Converter.cs
@@ -0,0 +1,56 @@
+using System;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+namespace SMLHelper.V2.Json.Converters
+{
+ ///
+ /// A Vector3 json converter that simplifies the Vector3 to only x,y,z serialization.
+ ///
+ public class Vector3Converter : JsonConverter
+ {
+ ///
+ /// A method that determines when this converter should process.
+ ///
+ /// the current object type
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Vector3);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Serialize the current object.
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var vector3 = (Vector3)value;
+ serializer.Serialize(writer, (Vector3Json)vector3);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Deserialize and read the current object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return (Vector3)serializer.Deserialize(reader);
+ }
+ }
+
+ internal record Vector3Json(float X, float Y, float Z)
+ {
+ public static explicit operator Vector3(Vector3Json v) => new(v.X, v.Y, v.Z);
+ public static explicit operator Vector3Json(Vector3 v) => new(v.x, v.y, v.z);
+ }
+}
diff --git a/SMLHelper/Json/Converters/Vector3IntConverter.cs b/SMLHelper/Json/Converters/Vector3IntConverter.cs
new file mode 100644
index 00000000..b052e560
--- /dev/null
+++ b/SMLHelper/Json/Converters/Vector3IntConverter.cs
@@ -0,0 +1,56 @@
+using System;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+namespace SMLHelper.V2.Json.Converters
+{
+ ///
+ /// A Vector3Int json converter that simplifies the Vector3Int to only x,y,z serialization.
+ ///
+ public class Vector3IntConverter : JsonConverter
+ {
+ ///
+ /// A method that determines when this converter should process.
+ ///
+ /// the current object type
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Vector3Int);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Serialize the current object.
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var vector3Int = (Vector3Int)value;
+ serializer.Serialize(writer, (Vector3IntJson)vector3Int);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Deserialize and read the current object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return (Vector3Int)serializer.Deserialize(reader);
+ }
+ }
+
+ internal record Vector3IntJson(int X, int Y, int Z)
+ {
+ public static explicit operator Vector3Int(Vector3IntJson v) => new(v.X, v.Y, v.Z);
+ public static explicit operator Vector3IntJson(Vector3Int v) => new(v.x, v.y, v.z);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Json/Converters/Vector4Converter.cs b/SMLHelper/Json/Converters/Vector4Converter.cs
new file mode 100644
index 00000000..6b747cff
--- /dev/null
+++ b/SMLHelper/Json/Converters/Vector4Converter.cs
@@ -0,0 +1,56 @@
+using System;
+using UnityEngine;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+namespace SMLHelper.V2.Json.Converters
+{
+ ///
+ /// A Vector4 json converter that simplifies the Vector4 to only x,y,z,w serialization.
+ ///
+ public class Vector4Converter : JsonConverter
+ {
+ ///
+ /// A method that determines when this converter should process.
+ ///
+ /// the current object type
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Vector4);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Serialize the current object.
+ ///
+ ///
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var vector4 = (Vector4)value;
+ serializer.Serialize(writer, (Vector4Json)vector4);
+ }
+
+ ///
+ /// A method that tells Newtonsoft how to Deserialize and read the current object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return (Vector4)serializer.Deserialize(reader);
+ }
+ }
+
+ internal record Vector4Json(float X, float Y, float Z, float W)
+ {
+ public static explicit operator Vector4(Vector4Json v) => new(v.X, v.Y, v.Z, v.W);
+ public static explicit operator Vector4Json(Vector4 v) => new(v.x, v.y, v.z, v.w);
+ }
+}
\ No newline at end of file
diff --git a/SMLHelper/Json/ExtensionMethods/JsonExtensions.cs b/SMLHelper/Json/ExtensionMethods/JsonExtensions.cs
new file mode 100644
index 00000000..b1512724
--- /dev/null
+++ b/SMLHelper/Json/ExtensionMethods/JsonExtensions.cs
@@ -0,0 +1,40 @@
+namespace SMLHelper.V2.Json.ExtensionMethods
+{
+ using SMLHelper.V2.Utility;
+#if SUBNAUTICA_STABLE
+ using Oculus.Newtonsoft.Json;
+#else
+ using Newtonsoft.Json;
+#endif
+
+ ///
+ /// Extension methods for parsing objects as JSON data.
+ ///
+ public static class JsonExtensions
+ {
+ ///
+ /// Loads the JSON properties from a file on disk into the .
+ ///
+ /// The type of the .
+ /// The object instance to load the properties into.
+ /// The file path to the JSON file to parse.
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ /// The s to be used for deserialization.
+ ///
+ public static void LoadJson(this T jsonObject, string path = null,
+ bool createIfNotExist = true, params JsonConverter[] jsonConverters) where T : class
+ => JsonUtils.Load(jsonObject, path, createIfNotExist, jsonConverters);
+
+ ///
+ /// Saves the fields and properties of the as JSON properties to the file on disk.
+ ///
+ /// The type of the .
+ /// The object instance to save the fields and properties from.
+ /// The file path at which to save the JSON file.
+ /// The s to be used for serialization.
+ public static void SaveJson(this T jsonObject, string path = null,
+ params JsonConverter[] jsonConverters) where T : class
+ => JsonUtils.Save(jsonObject, path, jsonConverters);
+ }
+}
diff --git a/SMLHelper/Json/Interfaces/IJsonFile.cs b/SMLHelper/Json/Interfaces/IJsonFile.cs
new file mode 100644
index 00000000..b3d4fade
--- /dev/null
+++ b/SMLHelper/Json/Interfaces/IJsonFile.cs
@@ -0,0 +1,61 @@
+namespace SMLHelper.V2.Json.Interfaces
+{
+#if SUBNAUTICA_STABLE
+ using Oculus.Newtonsoft.Json;
+#else
+ using Newtonsoft.Json;
+#endif
+
+ ///
+ /// A simple interface for a JSON file framework.
+ ///
+ public interface IJsonFile
+ {
+ ///
+ /// The file path at which the JSON file is accessible for reading and writing.
+ ///
+ [JsonIgnore]
+ string JsonFilePath { get; }
+
+ ///
+ /// The s that should always be used when reading/writing JSON data.
+ ///
+ [JsonIgnore]
+ JsonConverter[] AlwaysIncludedJsonConverters { get; }
+
+ ///
+ /// A method for loading the JSON properties from disk.
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ ///
+ ///
+ void Load(bool createFileIfNotExist = true);
+
+ ///
+ /// A method for saving the JSON properties to disk.
+ ///
+ ///
+ ///
+ void Save();
+
+ ///
+ /// A method for loading the JSON properties from disk.
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ /// Optional s to be used for
+ /// deserialization.
+ ///
+ ///
+ void LoadWithConverters(bool createFileIfNotExist = true, params JsonConverter[] jsonConverters);
+
+ ///
+ /// A method for saving the JSON properties to disk.
+ ///
+ /// Optional s to be used for serialization.
+ ///
+ ///
+ void SaveWithConverters(params JsonConverter[] jsonConverters);
+ }
+}
diff --git a/SMLHelper/Json/JsonFile.cs b/SMLHelper/Json/JsonFile.cs
new file mode 100644
index 00000000..59da1b13
--- /dev/null
+++ b/SMLHelper/Json/JsonFile.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Linq;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+using Oculus.Newtonsoft.Json.Converters;
+#else
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+#endif
+
+namespace SMLHelper.V2.Json
+{
+ using Converters;
+ using ExtensionMethods;
+ using Interfaces;
+
+ ///
+ /// A simple abstract implementation of .
+ ///
+ public abstract class JsonFile : IJsonFile
+ {
+ ///
+ /// The file path at which the JSON file is accessible for reading and writing.
+ ///
+ [JsonIgnore]
+ public abstract string JsonFilePath { get; }
+
+ [JsonIgnore]
+ private static readonly JsonConverter[] alwaysIncludedJsonConverters = new JsonConverter[] {
+ new FloatConverter(),
+ new KeyCodeConverter(),
+ new StringEnumConverter(),
+ new VersionConverter(),
+ new Vector2Converter(),
+ new Vector3Converter(),
+ new Vector4Converter(),
+ new Vector2IntConverter(),
+ new Vector3IntConverter(),
+ new QuaternionConverter()
+ };
+
+ ///
+ /// The s that should always be used when reading/writing JSON data.
+ ///
+ ///
+ public virtual JsonConverter[] AlwaysIncludedJsonConverters => alwaysIncludedJsonConverters;
+
+ ///
+ /// An event that is invoked whenever the is about to load data from disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnStartedLoading;
+ ///
+ /// An event that is invoked whenever the has finished loading data from disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnFinishedLoading;
+
+ ///
+ /// An event that is invoked whenever the is about to save data to disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnStartedSaving;
+ ///
+ /// An event that is invoked whenever the has finished saving data to disk.
+ ///
+ [JsonIgnore]
+ public EventHandler OnFinishedSaving;
+
+ ///
+ /// Loads the JSON properties from the file on disk into the .
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ ///
+ ///
+ public virtual void Load(bool createFileIfNotExist = true)
+ {
+ var e = new JsonFileEventArgs(this);
+ OnStartedLoading?.Invoke(this, e);
+ this.LoadJson(JsonFilePath, createFileIfNotExist, AlwaysIncludedJsonConverters.Distinct().ToArray());
+ OnFinishedLoading?.Invoke(this, e);
+ }
+
+ ///
+ /// Saves the current fields and properties of the as JSON properties to the file on disk.
+ ///
+ ///
+ ///
+ public virtual void Save()
+ {
+ var e = new JsonFileEventArgs(this);
+ OnStartedSaving?.Invoke(this, e);
+ this.SaveJson(JsonFilePath, AlwaysIncludedJsonConverters.Distinct().ToArray());
+ OnFinishedSaving?.Invoke(this, e);
+ }
+
+ ///
+ /// Loads the JSON properties from the file on disk into the .
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ /// Optional s to be used for serialization.
+ /// The will always be used, regardless of whether you pass them.
+ ///
+ ///
+ public virtual void LoadWithConverters(bool createFileIfNotExist = true, params JsonConverter[] jsonConverters)
+ => this.LoadJson(JsonFilePath, true,
+ AlwaysIncludedJsonConverters.Concat(jsonConverters).Distinct().ToArray());
+
+ ///
+ /// Saves the current fields and properties of the as JSON properties to the file on disk.
+ ///
+ /// Optional s to be used for deserialization.
+ /// The will always be used, regardless of whether you pass them.
+ ///
+ ///
+ public virtual void SaveWithConverters(params JsonConverter[] jsonConverters)
+ => this.SaveJson(JsonFilePath,
+ AlwaysIncludedJsonConverters.Concat(jsonConverters).Distinct().ToArray());
+ }
+}
diff --git a/SMLHelper/Json/JsonFileEventArgs.cs b/SMLHelper/Json/JsonFileEventArgs.cs
new file mode 100644
index 00000000..586fb74c
--- /dev/null
+++ b/SMLHelper/Json/JsonFileEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace SMLHelper.V2.Json
+{
+ ///
+ /// Contains basic information for a event.
+ ///
+ public class JsonFileEventArgs : EventArgs
+ {
+ ///
+ /// The instance of the this event pertains to.
+ ///
+ public JsonFile Instance { get; }
+
+ ///
+ /// Instantiates a new .
+ ///
+ /// The instance the event pertains to.
+ public JsonFileEventArgs(JsonFile instance)
+ {
+ Instance = instance;
+ }
+ }
+}
diff --git a/SMLHelper/Json/SaveDataCache.cs b/SMLHelper/Json/SaveDataCache.cs
new file mode 100644
index 00000000..4b2702bf
--- /dev/null
+++ b/SMLHelper/Json/SaveDataCache.cs
@@ -0,0 +1,130 @@
+using QModManager.API;
+using System;
+using System.IO;
+using System.Reflection;
+#if SUBNAUTICA_STABLE
+using Oculus.Newtonsoft.Json;
+#else
+using Newtonsoft.Json;
+#endif
+
+namespace SMLHelper.V2.Json
+{
+ using Attributes;
+ using Interfaces;
+
+ ///
+ /// An abstract implementation of intended for use with caching per-save data.
+ ///
+ public abstract class SaveDataCache : JsonFile
+ {
+ private string QModId { get; init; }
+
+ private bool InGame => !string.IsNullOrWhiteSpace(SaveLoadManager.GetTemporarySavePath());
+
+ private string jsonFileName = null;
+ private string JsonFileName => jsonFileName ??= GetType().GetCustomAttribute() switch
+ {
+ FileNameAttribute fileNameAttribute => fileNameAttribute.FileName,
+ _ => QModId
+ };
+
+ ///
+ /// The file path at which the JSON file is accessible for reading and writing.
+ ///
+ public override string JsonFilePath => Path.Combine(SaveLoadManager.GetTemporarySavePath(), QModId, $"{JsonFileName}.json");
+
+ ///
+ /// Creates a new instance of , parsing the file name from
+ /// if declared, or with default values otherwise.
+ ///
+ public SaveDataCache()
+ {
+ QModId = QModServices.Main.FindModByAssembly(GetType().Assembly).Id;
+ }
+
+ ///
+ /// Loads the JSON properties from the file on disk into the .
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ ///
+ ///
+ /// Thrown when the player is not in-game.
+ public override void Load(bool createFileIfNotExist = true)
+ {
+ if (InGame)
+ {
+ base.Load(createFileIfNotExist);
+ Logger.Log($"[{QModId}] Loaded save data from {JsonFileName}.json");
+ }
+ else
+ {
+ throw new InvalidOperationException($"[{QModId}] Cannot load save data when not in game!");
+ }
+ }
+
+ ///
+ /// Saves the current fields and properties of the as JSON properties to the file on disk.
+ ///
+ ///
+ ///
+ /// Thrown when the player is not in-game.
+ public override void Save()
+ {
+ if (InGame)
+ {
+ base.Save();
+ Logger.Log($"[{QModId}] Saved save data to {JsonFileName}.json");
+ }
+ else
+ {
+ throw new InvalidOperationException($"[{QModId}] Cannot save save data when not in game!");
+ }
+ }
+
+ ///
+ /// Loads the JSON properties from the file on disk into the .
+ ///
+ /// Whether a new JSON file should be created with default values if it does not
+ /// already exist.
+ /// Optional s to be used for serialization.
+ /// The will always be used, regardless of whether you pass them.
+ ///
+ ///
+ /// Thrown when the player is not in-game.
+ public override void LoadWithConverters(bool createFileIfNotExist = true, params JsonConverter[] jsonConverters)
+ {
+ if (InGame)
+ {
+ base.LoadWithConverters(createFileIfNotExist, jsonConverters);
+ Logger.Log($"[{QModId}] Loaded save data from {JsonFileName}.json");
+ }
+ else
+ {
+ throw new InvalidOperationException($"[{QModId}] Cannot load save data when not in game!");
+ }
+ }
+
+ ///
+ /// Saves the current fields and properties of the as JSON properties to the file on disk.
+ ///
+ /// Optional s to be used for deserialization.
+ /// The will always be used, regardless of whether you pass them.
+ ///
+ ///
+ /// Thrown when the player is not in-game.
+ public override void SaveWithConverters(params JsonConverter[] jsonConverters)
+ {
+ if (InGame)
+ {
+ base.SaveWithConverters(jsonConverters);
+ Logger.Log($"[{QModId}] Saved save data to {JsonFileName}.json");
+ }
+ else
+ {
+ throw new InvalidOperationException($"[{QModId}] Cannot save save data when not in game!");
+ }
+ }
+ }
+}
diff --git a/SMLHelper/Logger.cs b/SMLHelper/Logger.cs
new file mode 100644
index 00000000..b81819c0
--- /dev/null
+++ b/SMLHelper/Logger.cs
@@ -0,0 +1,109 @@
+namespace SMLHelper.V2
+{
+ using System;
+ using System.IO;
+ using System.Reflection;
+
+ internal enum LogLevel
+ {
+ Debug = 0,
+ Info = 1,
+ Warn = 2,
+ Error = 3,
+ }
+
+ internal static class Logger
+ {
+ internal static bool Initialized = false;
+
+ internal static bool EnableDebugging { get; private set; }
+ internal static void SetDebugging(bool value)
+ {
+ string configPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "EnableDebugLogs.txt");
+
+ File.WriteAllText(configPath, value.ToString());
+ EnableDebugging = value;
+ }
+
+ internal static void Initialize()
+ {
+ if (Initialized) return;
+ Initialized = true;
+
+ string configPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "EnableDebugLogs.txt");
+
+ if (!File.Exists(configPath))
+ {
+ File.WriteAllText(configPath, "False");
+ EnableDebugging = false;
+
+ return;
+ }
+
+ string fileContents = File.ReadAllText(configPath);
+
+ try
+ {
+ EnableDebugging = bool.Parse(fileContents);
+
+ Log($"Enable debug logs set to: {EnableDebugging}", LogLevel.Info);
+ }
+ catch (Exception)
+ {
+ File.WriteAllText(configPath, "False");
+ EnableDebugging = false;
+
+ Log("Error reading EnableDebugLogs.txt configuration file. Defaulted to false", LogLevel.Warn);
+ }
+ }
+
+ internal static void Debug(string text) => Log(text, LogLevel.Debug);
+ internal static void Info(string text) => Log(text, LogLevel.Info);
+ internal static void Warn(string text) => Log(text, LogLevel.Warn);
+ internal static void Error(string text) => Log(text, LogLevel.Error);
+
+ internal static void Debug(string text, params object[] args) => Log(text, LogLevel.Debug, args);
+ internal static void Info(string text, params object[] args) => Log(text, LogLevel.Info, args);
+ internal static void Warn(string text, params object[] args) => Log(text, LogLevel.Warn, args);
+ internal static void Error(string text, params object[] args) => Log(text, LogLevel.Error, args);
+
+ internal static void Log(string text, LogLevel level = LogLevel.Info)
+ {
+ Initialize();
+
+ if (level >= LogLevel.Info || EnableDebugging)
+ Console.WriteLine($"[SMLHelper/{level}] {text}");
+ }
+
+ internal static void Log(string text, LogLevel level = LogLevel.Info, params object[] args)
+ {
+ Initialize();
+
+ if (args != null && args.Length > 0)
+ text = string.Format(text, args);
+
+ if (level >= LogLevel.Info || EnableDebugging)
+ Console.WriteLine($"[SMLHelper/{level}] {text}");
+ }
+
+ internal static void Announce(string text, LogLevel level = LogLevel.Info, bool logToFile = false)
+ {
+ Initialize();
+
+ ErrorMessage.AddMessage(text);
+
+ if (logToFile)
+ Log(text, level);
+ }
+
+ internal static void Announce(string text, LogLevel level = LogLevel.Info, bool logToFile = false, params object[] args)
+ {
+ Initialize();
+
+ ErrorMessage.AddMessage(string.Format(text, args));
+
+ if (logToFile)
+ Log(text, level, args);
+ }
+ }
+}
diff --git a/SMLHelper/MonoBehaviours/EntitySpawner.cs b/SMLHelper/MonoBehaviours/EntitySpawner.cs
new file mode 100644
index 00000000..c7494957
--- /dev/null
+++ b/SMLHelper/MonoBehaviours/EntitySpawner.cs
@@ -0,0 +1,88 @@
+namespace SMLHelper.V2.MonoBehaviours
+{
+ using System.Collections;
+ using Handlers;
+ using Patchers;
+ using UnityEngine;
+ using UWE;
+ using Logger = Logger;
+
+ internal class EntitySpawner : MonoBehaviour
+ {
+ internal SpawnInfo spawnInfo;
+
+ void Start()
+ {
+ StartCoroutine(SpawnAsync());
+ }
+
+ IEnumerator SpawnAsync()
+ {
+ var stringToLog = spawnInfo.Type switch
+ {
+ SpawnInfo.SpawnType.ClassId => spawnInfo.ClassId,
+ _ => spawnInfo.TechType.AsString()
+ };
+
+ var task = new TaskResult();
+ yield return GetPrefabAsync(task);
+
+ var prefab = task.Get();
+
+ if (prefab == null)
+ {
+ Logger.Error($"no prefab found for {stringToLog}; process for Coordinated Spawn canceled.");
+ Destroy(gameObject);
+ }
+
+
+ var obj = Utils.InstantiateDeactivated(prefab, spawnInfo.SpawnPosition, spawnInfo.Rotation);
+
+ var lwe = obj.GetComponent();
+
+ var lws = LargeWorldStreamer.main;
+ yield return new WaitUntil(() => lws != null && lws.IsReady()); // first we make sure the world streamer is initialized
+
+ // non-global objects cannot be spawned in unloaded terrain so we need to wait
+ if (lwe is {cellLevel: not (LargeWorldEntity.CellLevel.Batch or LargeWorldEntity.CellLevel.Global)})
+ {
+ var batch = lws.GetContainingBatch(spawnInfo.SpawnPosition);
+ yield return new WaitUntil(() => lws.IsBatchReadyToCompile(batch)); // then we wait until the terrain is fully loaded (must be checked on each frame for faster spawns)
+ }
+
+ var lw = LargeWorld.main;
+
+ yield return new WaitUntil(() => lw != null && lw.streamer.globalRoot != null); // need to make sure global root is ready too for global spawns.
+
+ lw.streamer.cellManager.RegisterEntity(obj);
+
+ obj.SetActive(true);
+
+ LargeWorldStreamerPatcher.savedSpawnInfos.Add(spawnInfo);
+
+ Destroy(gameObject);
+ }
+
+ IEnumerator GetPrefabAsync(IOut gameObject)
+ {
+ GameObject obj;
+
+ if (spawnInfo.Type == SpawnInfo.SpawnType.ClassId) // Spawn is via ClassID.
+ {
+ var request = PrefabDatabase.GetPrefabAsync(spawnInfo.ClassId);
+ yield return request;
+
+ request.TryGetPrefab(out obj);
+ }
+ else // spawn is via TechType.
+ {
+ var task = CraftData.GetPrefabForTechTypeAsync(spawnInfo.TechType);
+ yield return task;
+
+ obj = task.GetResult();
+ }
+
+ gameObject.Set(obj);
+ }
+ }
+}
diff --git a/SMLHelper/MonoBehaviours/Fixer.cs b/SMLHelper/MonoBehaviours/Fixer.cs
new file mode 100644
index 00000000..c2837082
--- /dev/null
+++ b/SMLHelper/MonoBehaviours/Fixer.cs
@@ -0,0 +1,12 @@
+namespace SMLHelper.V2.MonoBehaviours
+{
+ using System;
+ using UnityEngine;
+
+ /// This component is obsolete
+ [Obsolete("Use SMLHelper.V2.Assets.ModPrefabCache instead", true)]
+ public class Fixer : MonoBehaviour
+ {
+ private void Awake() => V2.Logger.Warn("Fixer component is obsolete.");
+ }
+}
diff --git a/SMLHelper/Options/Attributes/ButtonAttribute.cs b/SMLHelper/Options/Attributes/ButtonAttribute.cs
new file mode 100644
index 00000000..53e247b6
--- /dev/null
+++ b/SMLHelper/Options/Attributes/ButtonAttribute.cs
@@ -0,0 +1,49 @@
+namespace SMLHelper.V2.Options.Attributes
+{
+ using Json;
+ using System;
+ using QModManager.Utility;
+
+ ///
+ /// Attribute used to signify the decorated method should be represented in the mod's options menu
+ /// as a .
+ /// When the button is clicked, the given method will run.
+ ///
+ ///
+ ///
+ /// using SMLHelper.V2.Json;
+ /// using SMLHelper.V2.Options;
+ /// using QModManager.Utility;
+ ///
+ /// [Menu("My Options Menu")]
+ /// public class Config : ConfigFile
+ /// {
+ /// [Button("My Cool Button")]
+ /// public static void MyCoolButton(object sender, ButtonClickedEventArgs e)
+ /// {
+ /// Logger.Log(Logger.Level.Info, "Button was clicked!");
+ /// Logger.Log(Logger.Level.Info, e.Id.ToString());
+ /// }
+ /// }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ public sealed class ButtonAttribute : ModOptionAttribute
+ {
+ ///
+ /// Signifies the decorated method should be represented in the mod's options menu as a
+ /// and sets its label.
+ ///
+ /// The label for the button. If none is set, the name of the method will be used.
+ public ButtonAttribute(string label = null) : base(label) { }
+
+ ///
+ /// Signifies the decorated method should be represented in the mod's options menu as a .
+ ///
+ public ButtonAttribute() { }
+ }
+}
diff --git a/SMLHelper/Options/Attributes/ChoiceAttribute.cs b/SMLHelper/Options/Attributes/ChoiceAttribute.cs
new file mode 100644
index 00000000..13264ce6
--- /dev/null
+++ b/SMLHelper/Options/Attributes/ChoiceAttribute.cs
@@ -0,0 +1,76 @@
+namespace SMLHelper.V2.Options.Attributes
+{
+ using Json;
+ using System;
+
+ ///
+ /// Attribute used to signify the decorated member should be represented in the mod's options menu as a
+ /// . Works for either index-based, -based, or
+ /// -based members.
+ ///
+ ///
+ /// choices can also be parsed from their values by merely omitting the .
+ ///
+ ///
+ ///
+ /// using SMLHelper.V2.Json;
+ /// using SMLHelper.V2.Options;
+ ///
+ /// public enum CustomChoice { One, Two, Three }
+ ///
+ /// [Menu("My Options Menu")]
+ /// public class Config : ConfigFile
+ /// {
+ /// [Choice("My index-based choice", "One", "Two", "Three")]
+ /// public int MyIndexBasedChoice;
+ ///
+ /// [Choice]
+ /// public CustomChoice MyEnumBasedChoice;
+ /// }
+ ///
+ ///
+ ///
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public sealed class ChoiceAttribute : ModOptionAttribute
+ {
+ ///
+ /// The list of options that will be displayed.
+ ///
+ public string[] Options { get; set; }
+
+ ///
+ /// Attribute used to signify the decorated member should be represented in the mod's options menu as a
+ /// . Works for either index-based, -based, or
+ /// -based members.
+ ///
+ ///
+ /// choices can also be parsed from their values by merely omitting the .
+ ///
+ /// The label for the choice. If none is set, the name of the member will be used.
+ /// The list of options for the user to choose from.
+ public ChoiceAttribute(string label = null, params string[] options) : base(label)
+ {
+ Options = options;
+ }
+
+ ///
+ /// Attribute used to signify the decorated member should be represented in the mod's options menu as a
+ /// . Works for either index-based, -based, or
+ /// -based members.
+ ///
+ ///
+ /// choices can also be parsed from their values by merely omitting the .
+ ///
+ /// The list of options for the user to choose from.
+ public ChoiceAttribute(string[] options) : this(null, options) { }
+
+ ///
+ /// Attribute used to signify the decorated member should be represented in the mod's options menu as a
+ /// . Works for either index-based, -based, or
+ /// -based members.
+ ///
+ public ChoiceAttribute() { }
+ }
+}
diff --git a/SMLHelper/Options/Attributes/ConfigFileMetadata.cs b/SMLHelper/Options/Attributes/ConfigFileMetadata.cs
new file mode 100644
index 00000000..c7d10a9a
--- /dev/null
+++ b/SMLHelper/Options/Attributes/ConfigFileMetadata.cs
@@ -0,0 +1,648 @@
+namespace SMLHelper.V2.Options.Attributes
+{
+ using Interfaces;
+ using Json;
+ using QModManager.API;
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Reflection;
+ using UnityEngine;
+ using Logger = Logger;
+#if SUBNAUTICA
+ using Text = UnityEngine.UI.Text;
+#elif BELOWZERO
+ using Text = TMPro.TextMeshProUGUI;
+#endif
+#if SUBNAUTICA_STABLE
+ using Oculus.Newtonsoft.Json;
+#else
+ using Newtonsoft.Json;
+#endif
+
+ internal class ConfigFileMetadata where T : ConfigFile, new()
+ {
+ public T Config { get; } = new T();
+
+ public bool Registered { get; set; } = false;
+
+ public IQMod QMod { get; } = QModServices.Main.GetMod(Assembly.GetAssembly(typeof(T)));
+
+ ///
+ /// The relating to this menu.
+ ///
+ public MenuAttribute MenuAttribute { get; private set; }
+
+ ///
+ /// A dictionary of , indexed by .
+ ///
+ public Dictionary> ModOptionAttributesMetadata { get; private set; }
+
+ ///
+ /// Process metadata for members of .
+ ///
+ public void ProcessMetadata()
+ {
+ Stopwatch stopwatch = new Stopwatch();
+
+ if (Logger.EnableDebugging)
+ stopwatch.Start();
+
+ MenuAttribute = typeof(T).GetCustomAttribute(true) ?? new MenuAttribute(QMod.DisplayName);
+ ModOptionAttributesMetadata = new Dictionary>();
+
+ processMetadata();
+
+ if (Logger.EnableDebugging)
+ {
+ stopwatch.Stop();
+ Logger.Debug($"[{QMod.DisplayName}] [{typeof(T).Name}] OptionsMenuBuilder metadata parsed in {stopwatch.ElapsedMilliseconds}ms.");
+ }
+ }
+
+ private void processMetadata()
+ {
+ var bindingFlags = BindingFlags.Instance | BindingFlags.Public;
+ foreach (PropertyInfo property in typeof(T).GetProperties(bindingFlags)
+ .Where(memberIsDeclaredInConfigFileSubclass) // Only care about members declared in a subclass of ConfigFile
+ .Where(memberIsNotIgnored)) // Filter out explicitly ignored members
+ {
+ processFieldOrProperty(property, MemberType.Property, property.PropertyType);
+ }
+
+ foreach (FieldInfo field in typeof(T).GetFields(bindingFlags)
+ .Where(memberIsDeclaredInConfigFileSubclass) // Only care about members declared in a subclass of ConfigFile
+ .Where(memberIsNotIgnored)) // Filter out explicitly ignored members
+ {
+ processFieldOrProperty(field, MemberType.Field, field.FieldType);
+ }
+
+ foreach (MethodInfo method in typeof(T).GetMethods(bindingFlags | BindingFlags.Static)
+ .Where(memberIsDeclaredInConfigFileSubclass) // Only care about members declared in a subclass of ConfigFile
+ .Where(memberIsNotIgnored)) // Filter out explicitly ignored members
+ {
+ processMethod(method);
+ }
+
+ Logger.Debug($"[{QMod.DisplayName}] [{typeof(T).Name}] Found {ModOptionAttributesMetadata.Count()} options to add to the menu.");
+ }
+
+ ///
+ /// Checks whether a given is declared in any subclass of .
+ ///
+ /// The to check.
+ /// Whether the given is declared in any subclass of .
+ private static bool memberIsDeclaredInConfigFileSubclass(MemberInfo memberInfo)
+ => memberInfo.DeclaringType.IsSubclassOf(typeof(ConfigFile));
+
+ ///
+ /// Checks whether a given should be ignored when generating the options menu, based on whether
+ /// the member has a declared , or the 's
+ /// property.
+ ///
+ /// The to check.
+ /// Whether the given member should be ignored when generating the options menu.
+ private bool memberIsNotIgnored(MemberInfo memberInfo)
+ {
+ if (Attribute.IsDefined(memberInfo, typeof(IgnoreMemberAttribute)))
+ return false;
+
+ switch (MenuAttribute.MemberProcessing)
+ {
+ case MenuAttribute.Members.Implicit:
+ if (memberInfo is MethodInfo)
+ {
+ if (Attribute.IsDefined(memberInfo, typeof(ButtonAttribute), true))
+ return true;
+
+ IEnumerable> eventMetadatas
+ = ModOptionAttributesMetadata.Values.SelectMany(modOptionsMetadata =>
+ {
+ IEnumerable> result = new List>();
+
+ if (modOptionsMetadata.OnChangeMetadata != null)
+ result.Concat(modOptionsMetadata.OnChangeMetadata);
+
+ if (modOptionsMetadata.OnGameObjectCreatedMetadata != null)
+ result.Concat(modOptionsMetadata.OnGameObjectCreatedMetadata);
+
+ return result;
+ });
+ return eventMetadatas.Any(memberInfoMetadata => memberInfoMetadata.Name == memberInfo.Name);
+ }
+ return true;
+
+ case MenuAttribute.Members.Explicit:
+ return Attribute.IsDefined(memberInfo, typeof(ModOptionAttribute), true) ||
+ Attribute.IsDefined(memberInfo, typeof(ModOptionEventAttribute), true);
+
+ default: throw new NotImplementedException();
+ }
+ }
+
+ ///
+ /// Processes the given field or property and hands off to
+ /// to generate a
+ /// and add it to the dictionary.
+ ///
+ /// The of the member.
+ /// The of the member.
+ /// The underlying of the member.
+ private void processFieldOrProperty(MemberInfo memberInfo, MemberType memberType, Type underlyingType)
+ {
+ if (underlyingType == typeof(bool))
+ {
+ addModOptionMetadata(memberInfo, memberType, underlyingType);
+ }
+ else if (underlyingType == typeof(KeyCode))
+ {
+ addModOptionMetadata(memberInfo, memberType, underlyingType);
+ }
+ else if (underlyingType.IsEnum || Attribute.IsDefined(memberInfo, typeof(ChoiceAttribute), true))
+ {
+ addModOptionMetadata(memberInfo, memberType, underlyingType);
+ }
+ else if (underlyingType == typeof(float) ||
+ underlyingType == typeof(double) ||
+ underlyingType == typeof(int) ||
+ Attribute.IsDefined(memberInfo, typeof(SliderAttribute), true))
+ {
+ addModOptionMetadata(memberInfo, memberType, underlyingType);
+ }
+ }
+
+ ///
+ /// Processes the given method and hands off to
+ /// to generate a and add it to the dictionary.
+ ///
+ /// The of the method.
+ private void processMethod(MethodInfo methodInfo)
+ {
+ addModOptionMetadata(methodInfo, MemberType.Method);
+ }
+
+ ///
+ /// Generates a based on the member and its attributes, then adds it to the
+ /// dictionary.
+ ///
+ /// The type of the to generate for this member.
+ /// The of the member.
+ /// The of the member.
+ /// The underlying of the member.
+ private void addModOptionMetadata(MemberInfo memberInfo, MemberType memberType,
+ Type underlyingType = null) where TAttribute : ModOptionAttribute, new()
+ {
+ try
+ {
+ // Get the ModOptionAttribute
+ ModOptionAttribute modOptionAttribute = memberInfo.GetCustomAttribute(true)
+ ?? new TAttribute();
+
+ // If there is no label specified, just use the member's name.
+ if (string.IsNullOrEmpty(modOptionAttribute.Label))
+ modOptionAttribute.Label = memberInfo.Name;
+
+ // ModOptionMetadata needed for all ModOptions
+ var modOptionMetadata = new ModOptionAttributeMetadata
+ {
+ ModOptionAttribute = modOptionAttribute,
+ MemberInfoMetadata = new MemberInfoMetadata
+ {
+ MemberType = memberType,
+ Name = memberInfo.Name,
+ ValueType = underlyingType
+ },
+ OnGameObjectCreatedMetadata = GetEventMetadata(memberInfo)
+ };
+
+ if (memberType == MemberType.Method)
+ modOptionMetadata.MemberInfoMetadata.ParseMethodParameterTypes(memberInfo as MethodInfo);
+
+ if (typeof(TAttribute) != typeof(ButtonAttribute))
+ modOptionMetadata.OnChangeMetadata = GetEventMetadata(memberInfo);
+
+ ModOptionAttributesMetadata.Add(modOptionAttribute.Id, modOptionMetadata);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"[OptionsMenuBuilder] {ex.Message}");
+ }
+ }
+
+ ///
+ /// Gets the metadata of every defined for a member.
+ ///
+ ///
+ /// The type of attribute defined on the member to gather metadata for.
+ ///
+ /// The member to gather attribute metadata for.
+ ///
+ private IEnumerable> GetEventMetadata(MemberInfo memberInfo)
+ where TAttribute : ModOptionEventAttribute
+ {
+ var metadatas = new List>();
+ foreach (TAttribute attribute in memberInfo.GetCustomAttributes(true))
+ {
+ var methodMetadata = new MemberInfoMetadata
+ {
+ MemberType = MemberType.Method,
+ Name = attribute.MethodName
+ };
+ methodMetadata.ParseMethodParameterTypes();
+ metadatas.Add(methodMetadata);
+ }
+ return metadatas;
+ }
+
+ public bool TryGetMetadata(string id, out ModOptionAttributeMetadata modOptionAttributeMetadata)
+ {
+ return ModOptionAttributesMetadata.TryGetValue(id, out modOptionAttributeMetadata);
+ }
+
+ #region Events
+ public void BindEvents()
+ {
+ Config.OnStartedLoading += OptionsMenuBuilder_Config_OnStartedLoading;
+ Config.OnFinishedLoading += OptionsMenuBuilder_Config_OnFinishedLoading;
+ }
+
+ private string jsonConfig;
+ public void OptionsMenuBuilder_Config_OnStartedLoading(object sender, ConfigFileEventArgs e)
+ {
+ jsonConfig = JsonConvert.SerializeObject(e.Instance as T);
+ }
+
+ private void OptionsMenuBuilder_Config_OnFinishedLoading(object sender, ConfigFileEventArgs e)
+ {
+ T oldConfig = JsonConvert.DeserializeObject(jsonConfig);
+ T currentConfig = e.Instance as T;
+
+ foreach (ModOptionAttributeMetadata modOptionMetadata in ModOptionAttributesMetadata.Values)
+ {
+ if (modOptionMetadata.MemberInfoMetadata.MemberType != MemberType.Field &&
+ modOptionMetadata.MemberInfoMetadata.MemberType != MemberType.Property)
+ {
+ continue;
+ }
+
+ if (!modOptionMetadata.MemberInfoMetadata.GetValue(oldConfig)
+ .Equals(modOptionMetadata.MemberInfoMetadata.GetValue(currentConfig)))
+ {
+ if (!Registered)
+ {
+ // if we haven't marked the options menu as being registered yet, its too soon to fire the events,
+ // so run a coroutine that waits until the first frame where Registered == true
+ // before routing the events
+ UWE.CoroutineHost.StartCoroutine(DeferredInvokeOnChangeEventsRoutine(modOptionMetadata, sender));
+ }
+ else
+ {
+ // otherwise, route the events immediately
+ InvokeOnChangeEvents(modOptionMetadata, sender);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Invokes the method for a given