From 4076574556e4fc12fad0ce6747956e8a3414d0c1 Mon Sep 17 00:00:00 2001 From: drojf Date: Sun, 4 Sep 2022 16:16:53 +1000 Subject: [PATCH] Migrate developer wiki to main wiki - See #40 for other docs which are not yet migrated --- docs/developer/legacy/adv-mode-and-lipsync.md | 5 + docs/developer/legacy/code-overview.md | 335 ++++++++++++++++++ docs/developer/legacy/technical-details.md | 56 +++ docs/developer/misc/git-line-endings.md | 5 + docs/developer/overview/features.md | 17 + docs/developer/overview/introduction.md | 6 + docs/developer/overview/overview.md | 83 +++++ .../overview/patch-folder-structure.md | 34 ++ .../how-to-use-higurashi-patch-compiler.md | 114 ++++++ docs/developer/scripting/on-scripting.md | 192 ++++++++++ docs/developer/scripting/syntax.md | 50 +++ .../sharedassets/images/LoadPackageFile.png | Bin 0 -> 14524 bytes .../sharedassets/images/LoadPackageStep2.png | Bin 0 -> 33744 bytes .../sharedassets/images/SaveOutputBundle.png | Bin 0 -> 33040 bytes .../developer/sharedassets/other-languages.md | 22 ++ .../sharedassets/ui-editing-scripts.md | 213 +++++++++++ .../voice/automated-voice-inserter.md | 87 +++++ docs/developer/voice/voice-details.md | 159 +++++++++ mkdocs.yml | 24 +- 19 files changed, 1401 insertions(+), 1 deletion(-) create mode 100644 docs/developer/legacy/adv-mode-and-lipsync.md create mode 100644 docs/developer/legacy/code-overview.md create mode 100644 docs/developer/legacy/technical-details.md create mode 100644 docs/developer/misc/git-line-endings.md create mode 100644 docs/developer/overview/features.md create mode 100644 docs/developer/overview/introduction.md create mode 100644 docs/developer/overview/overview.md create mode 100644 docs/developer/overview/patch-folder-structure.md create mode 100644 docs/developer/patch-compiler/how-to-use-higurashi-patch-compiler.md create mode 100644 docs/developer/scripting/on-scripting.md create mode 100644 docs/developer/scripting/syntax.md create mode 100644 docs/developer/sharedassets/images/LoadPackageFile.png create mode 100644 docs/developer/sharedassets/images/LoadPackageStep2.png create mode 100644 docs/developer/sharedassets/images/SaveOutputBundle.png create mode 100644 docs/developer/sharedassets/other-languages.md create mode 100644 docs/developer/sharedassets/ui-editing-scripts.md create mode 100644 docs/developer/voice/automated-voice-inserter.md create mode 100644 docs/developer/voice/voice-details.md diff --git a/docs/developer/legacy/adv-mode-and-lipsync.md b/docs/developer/legacy/adv-mode-and-lipsync.md new file mode 100644 index 0000000..bbf57db --- /dev/null +++ b/docs/developer/legacy/adv-mode-and-lipsync.md @@ -0,0 +1,5 @@ +Written by [@enumag](https://github.com/enumag/). + +The main thing to do here is to run the compiler script `higurashi:full-arc-upgrade`. See the [compiler documentation](https://github.com/07th-mod/higurashi-dev-guides/wiki/How-to-use-higurashi-patch-compiler#higurashifull-arc-upgrade-arc) for details. + +TODO: things to do before running the script, things to do after running the script, possibly a video covering the work with fixing bugs etc. \ No newline at end of file diff --git a/docs/developer/legacy/code-overview.md b/docs/developer/legacy/code-overview.md new file mode 100644 index 0000000..1405d49 --- /dev/null +++ b/docs/developer/legacy/code-overview.md @@ -0,0 +1,335 @@ +> Written by @ItaloKnox + +In this article, we are going to make a breakdown of all the installer code, written in Windows batch (``*.bat``). See the previous page for more information on how the patch is installed, the dependencies and file it uses to complete the installation. + +Below is a snapshot of the full code from a stable Higurashi installer release. The actual breakdown should be **below** the code. + +Use the index below to navigate around the page: + +- [Installer code](#installer-code) + - [Colored text](#colored-text) + - [timeout](#timeout) + - [nul](#nul) + - [aria2c](#aria2c) + - [7za](#7za) + - [powershell](#powershell) + - [set](#set) + - [xcopy](#xcopy) + - [rmdir, mkdir and del](#rmdir-mkdir-and-del) +- [Updater code](#updater-code) +- [Flowchart](#flowchart) + - [if exist](#if-exist) + - [fc](#fc) + +# Installer code + +```batch +@echo off +SETLOCAL EnableDelayedExpansion +for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( + set "DEL=%%a" +) + +set version=v4.0.1 + +call :colorEcho a0 "Downloading graphics patch... (1 of 3)" +echo. +timeout /t 1 > nul +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CG.zip +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CGAlt.zip +timeout /t 1 > nul + +call :colorEcho a0 "Downloading voice patch... (2 of 3)" +echo. +timeout /t 1 > nul +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-Voices.zip +timeout /t 1 > nul + +call :colorEcho a0 "Downloading patch... (3 of 3)" +echo. +timeout /t 1 > nul +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/onikakushi/releases/download/%version%/Onikakushi.Voice.and.Graphics.Patch.%version%.zip +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Higurashi-Textboxes.zip +.\aria2c.exe https://github.com/07th-mod/resources/raw/master/onikakushi/updater.bat +timeout /t 1 > nul + +call :colorEcho a0 "Checking for incomplete downloads..." +echo. +timeout /t 1 > nul +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/onikakushi/releases/download/%version%/Onikakushi.Voice.and.Graphics.Patch.%version%.zip +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Higurashi-Textboxes.zip +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-Voices.zip +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CG.zip +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CGAlt.zip +timeout /t 1 > nul + +call :colorEcho a0 "Extracting files..." +echo. +timeout /t 1 > nul +.\7za.exe x Onikakushi-CGAlt.zip +.\7za.exe x Onikakushi-CG.zip +.\7za.exe x Onikakushi-Voices.zip +.\7za.exe x Onikakushi.Voice.and.Graphics.Patch.*.zip +.\7za.exe x Higurashi-Textboxes.zip -aoa +rmdir /S /Q ..\StreamingAssets\CG > nul +rmdir /S /Q ..\StreamingAssets\CGAlt > nul +timeout /t 1 > nul + +call :colorEcho a0 "Moving folders..." +echo. +echo D | xcopy /E /Y .\Managed ..\Managed > nul +echo D | xcopy /E /Y .\CGAlt ..\StreamingAssets\CGAlt > nul +echo D | xcopy /E /Y .\CG ..\StreamingAssets\CG > nul +echo D | xcopy /E /Y .\voice ..\StreamingAssets\voice > nul +echo D | xcopy /E /Y .\StreamingAssets ..\StreamingAssets > nul +mkdir ..\StreamingAssets\BGMAlt +mkdir ..\StreamingAssets\voiceAlt +mkdir ..\StreamingAssets\SEAlt + +call :colorEcho a0 "Deleting useless files..." +echo. +timeout /t 1 > nul +rmdir /S /Q .\CG > nul +rmdir /S /Q .\CGAlt > nul +rmdir /S /Q .\StreamingAssets > nul +rmdir /S /Q .\voice > nul +rmdir /S /Q .\Managed > nul +del .\*.zip > nul +del ..\StreamingAssets\CompiledUpdateScripts\*.mg > nul +timeout /t 1 > nul + +call :colorEcho a0 "All done, finishing in three seconds..." +timeout /t 3 > nul + +exit +:colorEcho +echo off + "%~2" +findstr /v /a:%1 /R "^$" "%~2" nul +del "%~2" > nul 2>&1i +``` + +## Colored text + +```batch +@echo off +SETLOCAL EnableDelayedExpansion +for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( + set "DEL=%%a" +) +``` + +``...`` + +```batch +exit +:colorEcho +echo off + "%~2" +findstr /v /a:%1 /R "^$" "%~2" nul +del "%~2" > nul 2>&1i +``` + +These two pieces of code (first one at the start, last one at the end of the script file) will handle the colored text in the installer. You can find more information about it in this [Stack Overflow question](https://stackoverflow.com/questions/4339649/how-to-have-multiple-colors-in-a-windows-batch-file/5344911#5344911). + +The colors are called by using ``call :colorEcho a0 "string"``, where ``a0`` is the sample color used in this example. Usually, it's better to use ``echo.`` in the following line. + +## timeout + +``timeout /t 1 > nul`` + +``timeout`` is used to create a natural pause in the installation process. It's used in this installer to let the user read the text easier and artificially make the installation look less faster when multiple commands are being used. We use ``timeout`` together with the command ``> nul`` to hide the information from the user and make the screen cleaner. The sample above pauses the script for one second before running the next command. + +## nul + +```batch +timeout /t 1 > nul +rmdir /S /Q .\CG > nul +echo D | xcopy /E /Y .\Managed ..\Managed > nul +``` + +The ``nul`` command is widely used in the script to hide information from the user. It is often used to hide irrelevant information that is verbose and takes too much screen space. + +## aria2c + +``.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CG.zip`` +* ``--file-allocation=none`` forces ``aria2c`` to not allocate any space for the files. In case of failure, it will only take the actual downloaded space instead of the full space the downloaded file would normally take. +* ``--continue=true`` continues the download if the file already exists. Incomplete downloads will resume and complete downloads will be skipped. +* ``-x 8`` is the number of connections ``aria2c`` will make. Allows faster downloading, maximum number of connections is **16**. + +``.\aria2c.exe --file-allocation=none --continue=true -x 8 -i local.txt`` +* ``-i local.txt`` uses the file ``local.txt`` as the input for the download. The file can contain one or more links in a vertical list. + +We use ``aria2c`` because it offers us the best download speeds and flexibility. It can be easily changed for another program such as ``wget`` considering the changes to keep the functionality have been made in the script. + +## 7za + +``.\7za.exe x Higurashi-Textboxes.zip -aoa`` +* ``x`` extracts the file with its full path. +* ``-aoa`` overwrite files without the user input. Useful for applying updates. + +The reason we use 7-zip is that of ``lzma``. It's free, open-source and offers a higher compression compared to ``.rar`` files. The software can be easily changed to any other alternatives that support the commands above. + +## powershell + +> **This method is broken since March 2018.** We reverted to [set](#set). Last commit where this command was used: [08b15f45b1](https://github.com/07th-mod/resources/blob/08b15f45b1ca2b1bcd77454dc9ec319006f33b52/onikakushi/install.bat). + +``powershell -command "(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url | set-content local.txt"`` +* ``powershell -command ""`` calls Windows Powershell through the command line window and runs a command. +* ``(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url`` - a bit tricky: ``invoke-webrequest`` calls the link from the API to get the latest release; ``convertfrom-json`` converts the information to a readable state, looking especially for ``assets.browser_download_url``. +* ``|`` - runs a follow up command. +* ``set-content local.txt`` - instead of outputting the result in the screen, the command saves it in the file ``local.txt`` for use later. + +This command was introduced later in development. This command basically uses the Github API to get the latest release for the installed chapter. Previously, we used to add the link manually. This command eliminates the need of doing this work every time a new patch is released. + +This command has been pretty reliable but there are cases where it will miss. It is hard to reproduce and is probably related to security settings in the end user computer. Fortunately, it seems to have a 99% uptime despite commonly showing some information that might look like an error. It can be changed by ``grep`` with ``curl`` on Linux. + +## set + +``set version=v4.0.1`` +* ``set`` creates a variable +* ``version`` is the variable name +* ``v4.0.1`` is the value stored in this variable + + +Since March 2018, we had to rollback to use variables to download the patch. Unfortunately, there seems to be an issue with Github and ``Invoke-WebRequest`` that results in a broken SSL/TLS communication. Using a variable allows us to just change a little bit of text for every update released, instead of manually fishing for every instance where the patch version is called. The downside of this method compared to powershell is that we have to manually update the installer code every time a new version is released. Powershell, though, didn't require any extra work since it would always get the latest version from the API. + +The variable is called by using ``%version%``. Like in this example: + +``` +.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/onikakushi/releases/download/%version%/Onikakushi.Voice.and.Graphics.Patch.%version%.zip +``` + +## xcopy + +``echo D | xcopy /E /Y .\Managed ..\Managed > nul`` +* ``echo D`` will input automatically the ``D`` key, that in this command relates to copying a directory instead of a file. This allows the user to walk away from the computer and not stall the installation for a simple input. +* ``xcopy /E /Y .\Managed ..\Managed`` + * ``/E`` will copy every subfolder, even if empty; + * ``/Y`` will overwrite files with the same name without your consent (currently not very useful but still kept there); +* ``> nul`` - like already said above, hides the information from the user. After testing, it was decided to not display the copying process since it floods the screen with information that is hard to follow (the command copies a few hundred files). + +We employed ``xcopy`` because the patch use big filenames and paths. ``move`` turned out to be very unreliable for the amount of errors it would cause because of the long paths. Since Windows XP hasn't been used in a long while, we can safely use ``xcopy`` to move the folders. The downside is that we have to use extra space since it only **copies** the folders instead of moving them. It can be changed to ``rsync`` on Linux. + +## rmdir, mkdir and del + +* ``rmdir /S /Q .\CG > nul`` + * ``/S`` removes the tree (directory and subdirectories). + * ``/Q`` means quiet. Will remove without your consent. +* ``mkdir ..\StreamingAssets\BGMAlt`` - creates folders that can be used for alternative functions implemented by the modded [Assembly-CSharp.dll](https://github.com/07th-mod/higurashi-dev-guides/wiki/assembly-csharp.dll). +* ``del .\*.zip > nul`` deletes all the files with the ``.zip`` extension in the current folder. + +These are mostly used to clear up space after a successful installation. ``mkdir`` will create a few folders that can be used in the future, but are currently empty. This should make the process easier for modders that want to fork our work. + +*** + +# Updater code + +> **The updater is currently broken. See [powershell](#powershell) and [set](#set).** + +**Since most of the basic syntax used was also seen in the previous code, we will skip explaining those.** + +```batch +@echo off +SETLOCAL EnableDelayedExpansion +for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( + set "DEL=%%a" +) + +:start +call :colorEcho a0 "Comparing local version to remote version..." && echo: +timeout /t 3 > nul +if exist local.txt ( + goto :compare +) else ( + echo No information about the local version found. Downloading the newest remote version available... + goto :update +) + +:compare +call :colorEcho a0 "Downloading remote information for comparison" +timeout /t 3 > nul +powershell -command "(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url | set-content remote.txt" +fc remote.txt local.txt > nul +if errorlevel 1 goto :newver + +:samever +echo Looks like you have the latest version already installed, nothing to do here... +del remote.txt +exit + +:newver +echo New version found, redirecting to download and installation... +goto :download + +:update +call :colorEcho a0 "Downloading latest patch..." && echo: +powershell -command "(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url | set-content remote.txt" +goto :download + +:download +.\aria2c.exe --file-allocation=none --continue=true -x 8 -i remote.txt +del local.txt +ren remote.txt local.txt +goto :install + +:install +call :colorEcho a0 "Extracting files..." +echo. +timeout /t 1 > nul +.\7za.exe x *.Voice.and.Graphics.Patch.*.zip + +call :colorEcho a0 "Moving folders..." +echo. +echo D | xcopy /E /Y .\Managed ..\Managed > nul +echo D | xcopy /E /Y .\StreamingAssets ..\StreamingAssets > nul + +call :colorEcho a0 "Deleting leftovers..." +echo. +rmdir /S /Q .\StreamingAssets > nul +rmdir /S /Q .\Managed > nul +del ..\StreamingAssets\CompiledUpdateScripts\*.mg > nul +del *.zip + +exit +:colorEcho +echo off + "%~2" +findstr /v /a:%1 /R "^$" "%~2" nul +del "%~2" > nul 2>&1i +``` + +# Flowchart + +This flowchart shows how the updater behaves and the choices it will make: + +``` +check if local.txt exists >> if it exists >> download remote.txt >> compare both files >> if files are the same >> end updating process, nothing to update + ^ ^ if files are different >> download patch >> install and clean up files >> change remote.txt to local.txt + ^ if it doesn't exist >> download remote.txt >> download patch >> install and clean up files >> change remote.txt to local.txt +``` + +## if exist +```batch +if exist local.txt ( + goto :compare +) else ( + echo No information about the local version found. Downloading the newest remote version available... + goto :update +) +``` +* ``if exist local.txt`` looks for the file ``local.txt`` (that holds the information about the current version, as seen on the previous page) + * if it exists, it will compare the versions (see the next command) + * if it doesn't exist (``else``), it will download the newest remote version available + +This simple code checks if the file ``local.txt`` exists and proceeds to compare it to the file ``remote.txt`` (can be seen in the next command). If the file doesn't exist, it will proceed to download the file ``remote.txt`` and rename it to ``local.txt`` after a successful installation. + +## fc +```batch +fc remote.txt local.txt > nul +if errorlevel 1 goto :newver +``` + +In case you have a file called ``local.txt`` in the ``temp`` directory, ``fc`` will compare the file to another file called ``remote.txt`` that was downloaded prior to the comparison. The result is hidden from the user since it is irrelevant to the process. ``if errorlevel 1`` means that if the two files are different, it will use ``goto :newver`` to go to the section where it will download the newest patch. If both files are the same, the updater will return a message saying that there is nothing to update. + +The downside of this method is that both files can be tempered by the user to trigger the wrong result. It is a known flaw, but there is nothing to be afraid of as the user will most likely never touch it. \ No newline at end of file diff --git a/docs/developer/legacy/technical-details.md b/docs/developer/legacy/technical-details.md new file mode 100644 index 0000000..3f27640 --- /dev/null +++ b/docs/developer/legacy/technical-details.md @@ -0,0 +1,56 @@ +> Written by @ItaloKnox + +![code_2018-02-16_08-25-23](https://user-images.githubusercontent.com/4702556/36303352-0b09818e-12f3-11e8-9acc-49284797c69a.jpg) + +The patch installer (pictured above) was built entirely in [Advanced Installer](http://advancedinstaller.com/). It consists of two parts, which are going to be covered by this article: + +1. The main framework (built in Advanced Installer) +2. The installation code (built in Windows batch) + +The installer uses the default installation method, with no complex changes. The executable bundles all the dependencies ([aria2c](http://aria2.sourceforge.net/), [7z](http://7-zip.org/)) and a script (sample below) that pulls the latest installer code from our [Resources repository](https://github.com/07th-mod/resources/). It can be easily replaced with a zip containing all the dependencies, a better script that handles the download using available base command line tools or a different installer creator. + +```batch +@echo off +SETLOCAL EnableDelayedExpansion +for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( + set "DEL=%%a" +) + +call :colorEcho a0 "Removing the current installation script..." +echo. +call :colorEcho 0a "If you get an error, this is completely normal!" +echo. +del .\install.bat > nul +timeout /t 2 > nul + +call :colorEcho a0 "Downloading the installation script..." +echo. +timeout /t 3 > nul +.\aria2c.exe -o install.bat https://raw.githubusercontent.com/07th-mod/resources/master/onikakushi/install.bat + +call :colorEcho a0 "Running the installation script..." +echo. +timeout /t 3 > nul +call install.bat + +exit +:colorEcho +echo off + "%~2" +findstr /v /a:%1 /R "^$" "%~2" nul +del "%~2" > nul 2>&1i +``` + +After the user selects the install folder and start the installation, the installer will unpack the dependencies into a folder called ``temp``, that is located at ``HigurashiEp**_Data`` (where ``**`` equals the chapter number). This ``temp`` folders holds all the base files to install the patch, as pictured below: + +![firefox_2018-02-16_08-38-32](https://user-images.githubusercontent.com/4702556/36303874-cdd24132-12f4-11e8-90e9-ed04e3cef1b0.png) + +* ``7za.exe``, ``7za.dll`` and ``7zxa.dll`` are 7-zip dependencies required to extract the patch. +* ``aria2c.exe`` is a dependency required to download the patch. +* ``Onikakushi Patch Installer.exe`` is a converted Windows batch file (``*.bat``) that uses the code sampled above. We have a flaw in Advanced Installer that makes us unable to call Powershell using a regular ``.bat`` instead of the ``.exe`` file. See the next page for more details. +* ``installer.bat`` is the file pulled from our [Resources repository](https://github.com/07th-mod/resources/) to download and install the patch. See the next page for more details. +* ``updater.bat`` is the file pulled from our [Resources repository](https://github.com/07th-mod/resources/) to update the patch without requiring a new installation. It uses the file ``local.txt`` (that holds the link to the latest patch) to keep the version tracking. See the next page for more details. + +The current installer settings ends prematurely leaving only the command line window (that runs ``installer.bat``) open. This is a known issue, but it doesn't affect the usage or installation. The ideal scenario would be to have the installation window close after the command line window finished working, closing the two at the same time and showing the success message. + +For the breakdown on how the installation code works, see the next page: [Code overview](https://github.com/07th-mod/higurashi-dev-guides/wiki/code-overview). diff --git a/docs/developer/misc/git-line-endings.md b/docs/developer/misc/git-line-endings.md new file mode 100644 index 0000000..583c319 --- /dev/null +++ b/docs/developer/misc/git-line-endings.md @@ -0,0 +1,5 @@ +We have had some issues in the past with windows/unix line endings. This issue should be fixed now when using the new installer, as it automatically converts .bat files to windows line endings. However, if installing manually could still be an issue. + +This stackoverflow answer describes how to show the actual stored line endings of a file: [https://stackoverflow.com/a/35204436/848627](https://stackoverflow.com/a/35204436/848627). It uses the Git `ls-files` command. + +If you're committing to the `resources` directory and have many files listed as 'changed' even though you didn't change anything, consider deleting the .gitconfig file in your local repository (but don't commit it). diff --git a/docs/developer/overview/features.md b/docs/developer/overview/features.md new file mode 100644 index 0000000..868014d --- /dev/null +++ b/docs/developer/overview/features.md @@ -0,0 +1,17 @@ +Here is the list of the main features in out patches. Each feature should later have it's own page with documentation how the feature was implemented and how to replicate it for future chapters along with all the resources necessary to do so. + +---------- +|Feature|Credits| +|--|--| +|Voices from PS2 and PS3 versions|@enumag (voice insertion script), @ItaloKnox (main work)| +|Backgrounds, sprites and CGs from PS3|@Grelo, @IriPM| +|Weather sprites|@Grelo, @IriPM| +|Updated MG sprites|@jwgrlrrajn| +|ADV-mode|@enumag (automatic script), @Grelo (text background image)| +|DLL updates|@P-Chang (original contributor), @DoctorDiablo (current maintainer), @enumag (update script)| +|LipSync|@P-Chang (original contributor), @DoctorDiablo (current maintainer), @enumag (update script)| +|PS3 Poems|@DoctorDiablo, @ItaloKnox| +|Moving backgrounds|@P-Chang| +|New UI|@Inochi-PM, @Nozoki17| +|New scenes and translations improvements|@Norgus, @DoctorDiablo, @ItaloKnox| +|Patch installers|@ItaloKnox| \ No newline at end of file diff --git a/docs/developer/overview/introduction.md b/docs/developer/overview/introduction.md new file mode 100644 index 0000000..7f657b7 --- /dev/null +++ b/docs/developer/overview/introduction.md @@ -0,0 +1,6 @@ +## Welcome to the Higurashi developer wiki! + +This wiki is currently being moved and takes a long time to be updated. Because of that, some information might be old and not used anymore, but it is still up for reference. +The team is working very hard to make sure all documentation is properly written and updated as soon as possible, but this might take some time. + +If you are familiar with our workflow, please contribute by writing missing pages or updating information that doesn't reflect the current state of the patch. diff --git a/docs/developer/overview/overview.md b/docs/developer/overview/overview.md new file mode 100644 index 0000000..887a24f --- /dev/null +++ b/docs/developer/overview/overview.md @@ -0,0 +1,83 @@ +Written by [@enumag](https://github.com/enumag/). + +# Main process + +This section covers the main process of creating a patch for each new chapter. + +Step 1: Voices +---- + +1. [Run voice insertion script.](https://github.com/07th-mod/higurashi-dev-guides/wiki/Automated-voice-inserter) +2. Fix the rest manually (link). +3. Release a voices-only patch. + +Step 2: Graphics patch +---- + +It is recommended to have at least the automatic voice insertion completed first to avoid conflicts. + +TODO: We need to make a new workflow for this based on our recent rework of the graphics patches. + +Step 3: ADV-mode +---- + +Steps 1 and 2 are required for this. The game should still run with vanilla DLL after this step. + +Run ADV-mode update script (link). + +TODO: First release should probably happen either after this step or after DLL updates. + +Step 4: DLL update +---- + +1. DLL hacking (link). Note: This can be started right away when a new chapter comes out. No need to wait for the previous steps to be completed. +2. Run update script. This requires all previous steps to be completed first. + +Step 5: LipSync +---- + +1. DLL hacking (link). This can be done together with Step 4 DLL hacking. +2. Run update script. This requires all previous steps to be completed first. + +TODO: LipSync update script needs to be separated from new graphics patches update. + +# Other tasks + +These tasks can generally be done at any time during the process. + +MG patch +---- + +The first 2 steps need to be completed first, then this can be done at any time and doesn't block anything. + +(link) + +PS3 poems +---- + +TODO (Applies for question arcs only.) + +Moving backgrounds +---- + +TODO + +New UI +---- + +TODO + +New scenes and translations improvements +---- + +TODO + +Sui Soundtrack +---- + +TODO + +Patch installers +---- + +TODO diff --git a/docs/developer/overview/patch-folder-structure.md b/docs/developer/overview/patch-folder-structure.md new file mode 100644 index 0000000..8aebfb5 --- /dev/null +++ b/docs/developer/overview/patch-folder-structure.md @@ -0,0 +1,34 @@ +# Patch folder structure + +> Written by @ItaloKnox + +To make the installation as simple as possible, we can take advantage of nested folders to cut down the installation steps and greatly reduce the number of actions the installer requires to take. In the section below, you can see all the nested folders currently used in our patch. **It is greatly recommended to follow the example to the letter.** + +## Nested folders + +> HigurashiEp0``*``_data, where ``*`` is the chapter number. For example, Onikakushi = 1, Meakashi = 5. + +- *-CGAlt.7z + - ``HigurashiEp0*_data/StreamingAssets/CGAlt`` + +- *-CG.7z + - ``HigurashiEp0*_data/StreamingAssets/CG`` +- *-Voices.7z + - ``HigurashiEp0*_data/StreamingAssets/voice`` + - ``HigurashiEp0*_data/StreamingAssets/spectrum`` +- ``CHAPTER TITLE``.Voice.and.Graphics.Patch.*\.zip + - ``HigurashiEp0*_data/Managed`` + - ``HigurashiEp0*_data/Plugins`` + - ``HigurashiEp0*_data/StreamingAssets`` +- *-UI.7z + - ``HigurashiEp0*_data/sharedassets0.assets`` + - ``HigurashiEp0*_data/sharedassets0.assets.resS`` (from Chapter 3 - Tatarigoroshi onward) +- *-Movie.7z + - ``HigurashiEp0*_data/StreamingAssets/movies`` + +In **ConsoleArcs** only: + +- *-BGM.7z + - ``HigurashiEp04_data/StreamingAssets/BGM`` +- *-SE.7z + - ``HigurashiEp04_data/StreamingAssets/SE`` diff --git a/docs/developer/patch-compiler/how-to-use-higurashi-patch-compiler.md b/docs/developer/patch-compiler/how-to-use-higurashi-patch-compiler.md new file mode 100644 index 0000000..91619ac --- /dev/null +++ b/docs/developer/patch-compiler/how-to-use-higurashi-patch-compiler.md @@ -0,0 +1,114 @@ +Written by [@enumag](https://github.com/enumag/). + +Generally it's a collection of console scripts to easily do some changes in the higurashi game scripts and/or to analyze them to discover likely mistakes and make the work on other parts of the patch easier. + +The voice inserter is not part of this repository only because it's older and there is a little benefit for integrating it now. + +In this guide I'll cover how to make it work, how to use the scripts and which command is for what. Note that many of the commands are now obsolete and I'll intentionally skip them. + + +Installation +---- + +The voice inserter script is written in PHP and you can find it in this [repository](https://github.com/07th-mod/higurashi-patch-compiler). + +To run it you'll need PHP 7.1+. Some modules that are not enabled by default might be necessary too but I can't remember which. It should be relatively easy to find it out from the errors. + +You will also need [composer](https://getcomposer.org/) to install some PHP libraries. + +Some of the commands (mainly those related to ADV mode) require a MySQL database (MariaDB should work too). I also recommend to get a tool like [adminer](https://www.adminer.org/) to browse the database and run commands. + +When everything is installed check your installation using `php -v` and `composer --version`. + +Finally download or `git clone` the [compiler repository](https://github.com/07th-mod/higurashi-patch-compiler). + + +Preparation +---- + +Run composer install in the compiler root directory (where composer.json file is located). + +Create a MySQL database and run the database.sql file in it. I usually do it using adminer. + +Copy the `config/local.example.yml` to `config/local.yml` and adjust the database connection parameters to match your database. + + +Commands +---- + +You can run the commands using `php console.php []`. For full list of commands run `php console.php list`. Any command not listed on this page is either new, internal (used by other commands) or obsolete. Avoid using them unless you really know what you're doing. + +Most of the commands will put the updated scripts into `temp/` where directory should match the command you're running. + +## `higurashi:full-arc-upgrade ` + +Fully upgrades a newly released MG arc or a newly translated console arc with ADV mode, DLL updates and LipSync. + +`` = name of the arc you want to upgrade, for example `higurashi:full-arc-upgrade someutsushi`. Shortcuts are supported so `higu:full some` is equivalent. + +This command internally uses `higurashi:adv`, `higurashi:dll-update`, `higurashi:lip-sync`, `higurashi:voice-pack`, `higurashi:sprite-pack` and other commands. It's basically the all-in-one upgrade. + +The output files will be available in `temp/full-upgrade` directory. + +Note: for regular MG arc, the PS3 voices and graphics patch should be done before using this. + +Aside from the updated scripts there will be some sh files to generate sprite packs (including lipsync sprites) and voice packs (including spectrum files). To use these shell scripts you will need to prepare a directory structure with the PS3 sprites and voices. See this issue for details: https://github.com/07th-mod/onikakushi/issues/103 (Note: download links in that issue are outdated and won't work anymore. The devs have access to the backups.) + +## `higurashi:voice-pack` + +Simple script to generate an sh script to prepare `voice` + `spectrum` directories for a chapter. For now it's mainly useful for the console arcs since they don't use PS2 voices. + +The output files will be available in `temp/voicepack` directory. + +Unlike `higurashi:voices` it doesn't make any changes to the scripts. This means that the scripts have to use the correct convention for voice paths for this script to be used. Therefore this command is the one to use long-term while `higurashi:voices` is getting obsolete. + +Note: PS2 files are not yet supported. This will have to be implemented later. The problem is that in same cases it might not be possible to copy PS2 voices automatically since they have wrong directory structure and duplicate filenames. + +## `higurashi:adv` + +TODO: Link to ADV mode update guide + +## `higurashi:colors` + +This command can be used if to change character name colors for ADV mode when you can no longer rerun the normal compilation. + +The colors are set manually in the database. There are two columns for colors - one should be picked form PS3 sprite and the other from MG sprite of the character. The command will calculate the final color from these to get a good compromise. + +## `higurashi:combine` + +In the MG release of Meakashi there were a lot of cases like this: + +``` + OutputLineAll(NULL, "\n", Line_ContinueAfterTyping); + OutputLineAll(NULL, "\n", Line_ContinueAfterTyping); +``` + +This script combines such lines to this which is better for ADV mode compilation. + +``` + OutputLineAll(NULL, "\n\n", Line_ContinueAfterTyping); +``` + +## `higurashi:dll-update` + +TODO: Link to DLL update guide + +## `higurashi:lip-sync` + +TODO: Link to LipSync update guide + +## `higurashi:insert-names` + +I think this was written when we found out that Tatarigoroshi was missing LOT of voices which were PS2-specific. After manually inserting the voices I wrote this to add the character names needed for ADV mode since the normal ADV mode compiler command could not be used again. You'll not need it normally but could be useful sometimes. + +## `higurashi:missing` + +This command is basically the last thing to use when the patch is complete. It detects missing files (images, voices, sounds). Beware though that it can report some false-positives like missing MG sprites - that's ok for characters who don't have MG sprites. + +## `higurashi:normalize` + +A `ClearMessage();` command in the script means that a new screen follows. So the last line before it should have `Line_Normal`. Some lines have `Line_WaitForInput` which is fixed by this script. + +## `higurashi:parse` + +TODO: Link to ADV mode update guide diff --git a/docs/developer/scripting/on-scripting.md b/docs/developer/scripting/on-scripting.md new file mode 100644 index 0000000..cb205e7 --- /dev/null +++ b/docs/developer/scripting/on-scripting.md @@ -0,0 +1,192 @@ +# Introduction + +The goal of this page is to describe the proceedings by which the scripts can be edited to make it flow seamlessly. +After reading everything and experiencing a bit, you should be able to write scripts to play entire scenes properly from scratch. + +## Indentation / Whitespace + +Please pay attention to tabs/spaces in the script. **Every line with a function call must start with at least one tab character**. If you use spaces accidentally at the very start of a line, the engine will just display the raw code on the screen and skip the function call. + +Configuring your text editor to show whitespace and/or enabling tabs for spaces may help you diagnose this kind of issue. + +## Syntax Highlighting + +Since the script language is similar to C/C++, you can enable C/C++ syntax highlighting in your text editor to get some basic highlighting. + +For Visual Studio Code users, we have a highlighter specifically for Higurashi scripts that can be downloaded from our [script-syntax-highlighters](https://github.com/07th-mod/script-syntax-highlighters/releases) repository (see the README.md on the repo for install instructions). It will highlight some keywords specific to the Higurashi scripting language. However, please note that it may not always be correct, so don't rely on it 100% for detecting syntax errors. + +## Understanding The Script + +To start off, glancing at a random part of a script will probably look like occult scrawlings with some bits here and there that make some sense, but will come a time where you will be able to discern and understand every single part. + +Because every single line of the script has a meaning, you'll be able to see the superfluous lines that can be removed without affecting(or doing so in a good way) the script. Each additional line takes memory or sequencing time, so trimming down the process usage is important. + +A lot of it relies on learning what each part does, by reading documentation, but also by actually seeing the effects directly, so that you become able to associate the methods and the results, to a point where reading the script is enough to visualize it playing out. + +Experiencing the effects is an important part to then foresee what the line you're removing or adding will change in the outcome. Especially because there might be some engine-specific changes that might need an adjustment in the scripts from one chapter to the other. + +I recommend reading through the script while playing the corresponding scene on the game at the same time, and maybe even try to guess how it should pay out then see how it actually takes place to understand the little things you had missed. + +Here is a scene from Onikakushi to better understand: + +```c++ + if (GetGlobalFlag(GADVMode)) { OutputLineAll("", NULL, Line_ContinueAfterTyping); } + + OutputLine(NULL, " 今日はレナと魅音に雛見沢をいろいろと案内してもらう日だ。", + NULL, "Today was the day Rena and Mion were going to show me around Hinamizawa.", Line_Normal); + + ClearMessage(); +``` + +This here is going to be the most common lines you'll see in the script, I call that a 'text bulk' because it always come as one part. + +The first line is the 'header' and will fill the nametag in ADV mode, here it's empty because it's not a spoken line and thus requires an empty name. The last parameter means that it will instantly continue to the next method after being written. + +Next is the actual line of text that will be written in the textbox(with the 2 languages), followed at the end by the parameter that determines what happens while writing it. Here it will wait for an input after it has finished writing, then will erase all text from the textbox. + +And finally, the last small method line is for the backlog, it will clear everything that's been written since the last cleaning(and the current written text if any), and add it to the log so that you are able to read back if you went too fast or else. + +This is the most basic version of 'text bulks' but you'll get to see many variations. + +```c++ + DisableWindow(); + + PlayBGM( 1, "msys01", 128, 0 ); + + ModDrawCharacter(1, 3, "sprite/normal/me2_def_a1_", "0", -160, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 0, 0, FALSE ); + + ModDrawCharacter(3, 2, "sprite/normal/re2a_def_a1_", "0", 160, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 20, 0, FALSE ); + + DrawSceneWithMask("background/ie1", "left", 0, 0, 1300 ); +``` + +Now, the script is going to do a transition scene. First off, as for most of the effects outside of sounds and musics, to avoid dividing the attention, you will want to remove the now 'useless' textbox with DisableWindow();(which is being drawn back whenever you write text, so it's only temporary). + +Then, a music will start being played, and you'll see some magic at work while the new background is drawn from left to right with both character sprites. + +Normally, you'd think the sprites to be drawn first, but because of the last parameter being put to FALSE, the sprites 'pass priority' and have the last method keeping priority having everything happen at the same time, and that's how the character sprites are not being erased by the DrawScene happening afterwards. + +There is a number of other parameters that should be researched and learnt about by looking around the wiki and seeing the effects. + +```c++ + if (GetGlobalFlag(GADVMode)) { OutputLineAll("", NULL, Line_ContinueAfterTyping); } + + OutputLine(NULL, " 待ち合わせ場所ではすでにレナと魅音が待っていた。", + NULL, "Rena and Mion were already waiting at the meeting place.", Line_Normal); + + ClearMessage(); + + DisableWindow(); + + ModDrawCharacter(1, 3, "sprite/normal/me2_tokui_a1_", "1", -160, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 0, 200, TRUE ); +``` + +Another simple line followed by a facial expression change, done by drawing the character sprite on top of itself; by keeping most of the parameters the same, it will looks as if only the face changes. + +Note that this time, the priority is kept so that the character is fully drawn before continuing. + +```c++ + if (GetGlobalFlag(GADVMode)) { OutputLine("魅音", NULL, "Mion", NULL, Line_ContinueAfterTyping); } + + ModPlayVoiceLS(3, 3, "ps3/s19/03/990300077", 256, TRUE); + + OutputLine(NULL, "「圭ちゃん、遅いぞー!」", + NULL, "\"Kei-chan, you're late!\"", GetGlobalFlag(GLinemodeSp)); + + if (GetGlobalFlag(GADVMode)) { ClearMessage(); } else { OutputLineAll(NULL, "\n", Line_ContinueAfterTyping); } +``` + +Ending with a spoken sentence, this time, the 'header' line will write the name(with a color tag proper to Mion), then start playing the voice file before writing the sentence. + +That way, you will have the text and voice playing at the same time(because ModPlayVoiceLS doesn't hold priority). + +And for the last bit, it will act the same if you are in ADV mode, but in NVL mode, it won't clean the text and only skip a line, creating a wall of text because there is more space than with the little textbox of ADV mode. + +Now that I explained most of it, this is what it looks like in action: + +https://www.youtube.com/watch?v=gDOm4B5PYoY + +As you will have perhaps noticed, there was already a bgm playing and a scene drawn. That is because you need to always remember there is a continuity in the scripts and to be aware of it. + +## Sequencing Properly + +And this is where the next point will lead us. It is important to understand that to make a scene, you have a good number of factors to take into consideration, and how each of the components are handled. + +The timing is also important, as you won't be able to make everything jump out; you need a proper entrance to it, but also a proper exit. Let's say for example, I want to have a music start playing and replace the current one, I'd have to make it fade out while playing the other on another channel, to have an incremental change as the old music fades out and the new starts, without a brutal cut. + +And unless it is for a specific usage, this applies to everything else. It is important to make it fluid so that you won't have a coldwater splash of "Wait, this happened weirdly!" that, well, isn't needed when you're trying to enjoy the story. + +Now we'll be looking at trying to build a simple waking up scene. We'll be considering that nothing happens beforehand but we'll still clean the text and musics in case, and then we'll start with slowly drawing a white scene while a bgm plays during the transition to 'introduce' the actual scene in case of remaining sprites or scenes, once that's done, we can draw the real scene and finish up by playing another bgm in addition to the first to make the sound ambience as the text starts. + +```c++ + FadeOutBGM( 0, 0, FALSE ); + FadeOutBGM( 1, 0, FALSE ); + FadeOutBGM( 2, 0, FALSE ); + ClearMessage(); + + PlayBGM( 2, "lsys22", 128, 0 ); + DrawScene("white", 3000 ); + DrawSceneWithMask("background/ma_j3_01", "m1", 0, 0, 3000 ); + PlayBGM( 1, "msys06", 128, 0 ); + + if (GetGlobalFlag(GADVMode)) { OutputLineAll("", NULL, Line_ContinueAfterTyping); } + OutputLine(NULL, " 倦怠感と頭痛。", + NULL, "Weariness and a headache.", GetGlobalFlag(GLinemodeSp)); + if (GetGlobalFlag(GADVMode)) { ClearMessage(); } else { OutputLineAll(NULL, "\n", Line_ContinueAfterTyping); } +``` + +The high time to draw the scenes is important to give the feeling of waking up while hearing bird chirps, and you'll notice that it's these small things that are important to give a feeling of immersion as you go. + +There is many more things you can do in that regard, like shaking the screen while playing a loud sound to simulate a hit or as simple as drawing a facial expression to show the change in tone of a character. + +By using the various tools at disposition and a little touch of creativity, you can make an impressive variety of effects to create quite immersive scenes. + +## What To Watch Out For + +But there are still points to keep in mind to avoid falling into traps and have unexpected results. In the many examples I could give, there is to never forget when you start a bgm, to fade it out. Despite the safeguards, it's still better to not let a track when it's supposed to be over. + +Another point is the priority, where some methods can be used simultaneously, but others can't, and some only with specific ones. Most of the draw with filter hog priority and forbid to have higher priority events happening at the same time(you can't draw with filter and a new scene, for example, or several filter draws). Which is why it's important to keep in mind what is prioritized over what when sequencing. + +Do not forget to disable the text box when doing effects, to focus the attention. Don't use the same layer priority for different characters or they'll trample over the sprites. Some methods can pass priority, others hog it. Try to avoid having any timing at 0 because it creates too harsh a cut, prefer using 100 or 50 for very fast changes, or the more global 300. + +Also, different chapters have different engines(the biggest gap being question arcs/answer arcs) and there can be several changes in how they're handled. In answer arcs, drawing a scene will now automatically disable the textbox, so adding it is not needed for it. + +The way priority is handled is also slightly different in that several related methods can trample over and create annoying issues if they're not separated by a 'high priority' method. For example having a fadeout and a play bgm methods in the same 'priority line'(not cut by a hogging priority method) will prevent the play bgm to act, no matter how much methods you add in between(creating the well known music bug). + +It also acts weirdly with sprite drawing, where several drawing can become ported over to the next scene drawing or after a new outputline, even if you try to keep priority(I've had to try many workarounds as it's very annoying, and one of the most efficient if possible is to redraw the scene with the new expression, and/or change the layer of the new sprite). + +In the end it'll rely mostly on the feel, the flow and testing it out, but hopefully all this'll help making it easier to start out. + +## Commonly Used Tools + +Finally I'll describe some of the most commonly used tools I use to make editing easier: + +```c++ + DrawBustshot(5, "black", 0, 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 25, 500, TRUE ); +``` + +This is a convenient tool if you need to draw effects, or to not have to change a scene. It's the main way to make multi layered effects. Though note that it's not used to draw character sprites. It has weak priority and can be added in a priority line. Note that there is a DrawSprite variant that is more prioritizing, and is able to change some things like the orientation, to be used in more technical scenes. But don't forget to avoid drawing it using a layer that is already used. + +```c++ + FadeOutBGM( 0, 300, FALSE ); +``` + +It's pretty straightforward, but still requires some attention. Whenever you start a bgm, you'll have to add a fade-out for it, and depending on where it has to stop, you will change the timing to make it blend with other transitions. Weak priority and rarely used alone. + +```c++ + OutputLineAll(NULL, "", Line_WaitForInput); +``` + +It's mostlly used to write text, but it can be used to create pauses between effects, or if you want an effect to take place after a sentence but not continue afterwards. + +```c++ + Wait( 2000 ); +``` + +The backbone of every effect, despite being able to increase timing on a majority of methods, you'll be using this one very frequently to create delays and pauses, it structures a lot of sequencing and doesn't hold priority. + +```c++ + DrawScene("black", 500 ); +``` + +Often you'll have to change the background for scene, and this one is quite useful, but there's a few things to note. First, it's very priority hogging, it's usually what supports a priority line and you can add sprites to be drawn with it that way. It also disables the textbox(only answer arcs, don't forget to do it in question arcs) and most importantly, it cleans up the 'scene' by removing sprites and bustshots. diff --git a/docs/developer/scripting/syntax.md b/docs/developer/scripting/syntax.md new file mode 100644 index 0000000..f40dc0c --- /dev/null +++ b/docs/developer/scripting/syntax.md @@ -0,0 +1,50 @@ +This section includes examples of functions used in the script and their usage. + +Note: [this page](https://github.com/07th-mod/higurashi-patch-compiler/wiki/Command-headers-for-game-scripts) might also be useful. + +### Code breakdown + +**ModDrawCharacterWithFiltering(3, 10, "sprite/normal/iri1_def2_", "1", "right", 1, 160, 0, FALSE, 0, 0, 0, 0, 0, 20, 300, TRUE );** + +Draws a character sprite onscreen. Supports lipsync. + +* 3 is the sprite layer +* 10 is the character's voice folder +* "sprite/normal/iri1_def2_" is the sprite image to use minus the last character +* "1" is which sprite to use if lipsync is disabled +* "right" is the mask that controls the fade-in animation +* 160 is the horizontal coordinates to draw the character at +* 20 is the level that characters are drawn at (if they overlap, the one with a lower level is drawn on top of the other) +* 300 is the speed in milliseconds the sprite fades in +* the last FALSE determines if the system should continue before the sprite finishes drawing (setting it to TRUE allows you to have multiple sprites fading in at once) + +**DrawFilm( 2, 176, 155, 104, 255, 0, 1000, TRUE );** + +Draws a colored filter on top of all sprites and backgrounds onscreen. Used to tint the screen brown/red/gray/etc. Need to call FadeFilm() afterwards to remove this. + +* 2 is the layer of this film (used in FadeFilm to remove this film later) +* 176, 155, and 104 are the RGB color components of the film (this particular combination brown-tints the screen) +* 255 is the intensity/opacity of the film +* 1000 is the time in milliseconds to draw the film +* TRUE indicates if the process can continue while the film is drawing. + +**Negative( 1000, TRUE );** + +Inverts the colors onscreen. Need to call FadeFilm() afterwards to remove this. + +* 1000 is the time in milliseconds to draw the negative +* TRUE indicates if the process can continue while the negative is drawing. + +**FadeFilm( 500, TRUE );** + +Removes the effect of a previous DrawFilm() or Negative() call. + +* 1000 is the time in milliseconds to remove the film +* TRUE indicates if the process can continue while the film is fading out. + +**ShakeScreen( 1, 15, 15, 2, 0, );** + +Shakes the image on screen + +* 15, 15 controls how far the image should shake (in pixels?) I think the first one is in the x direction and second is in y +* 2 indicates how many times the screen should shake diff --git a/docs/developer/sharedassets/images/LoadPackageFile.png b/docs/developer/sharedassets/images/LoadPackageFile.png new file mode 100644 index 0000000000000000000000000000000000000000..446f6b0620f94c7905dd48937e76a25c3068862a GIT binary patch literal 14524 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 CcUq_b literal 0 HcmV?d00001 diff --git a/docs/developer/sharedassets/images/LoadPackageStep2.png b/docs/developer/sharedassets/images/LoadPackageStep2.png new file mode 100644 index 0000000000000000000000000000000000000000..aad41252110cba2a406e3b55ffba3a901bbbb99a GIT binary patch literal 33744 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 YYNn)fqv6Ot;FK^5vZ^xGQf5K_2eta-CIA2c literal 0 HcmV?d00001 diff --git a/docs/developer/sharedassets/images/SaveOutputBundle.png b/docs/developer/sharedassets/images/SaveOutputBundle.png new file mode 100644 index 0000000000000000000000000000000000000000..0214f4fffc3e11184cf1a84a95530a610d9c0dca GIT binary patch literal 33040 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 for , skipping`, which indicates that the script failed to find an object to replace. If this happens, manually look up the ID and prepend it to the file. +* Files that end in `.png` will be converted to `Texture2D` assets before they are inserted into the EMIP. +* Any file that does not start with a number or have an underscore in its name will be completely ignored +* Any file that does have an underscore in its name but doesn't match a file in the asset bundle will still be ignored but may cause the program to run slower (due to searching the whole bundle for a matching file) + +Run the program with `python EMIPGenerator.py sharedassets0.assets inputFolder output.emip`. Then, launch UABE and select `File → Load Package File` and select your emip file. If you get the error `Unable to read the bundle file! (Invalid file or unknown version?)`, this means that you accidentally selected `File → Open` instead of `File → Load Package File`. Check the box next to the asset file on the following screen and if it doesn't show the full path to the file (starting with `C:` or whatever drive letter it's in), click the button near the bottom to select a base folder that will get the correct path. Then click `OK`, which will load the main bundle editing interface with all the edits already in. From here you can immediately select `File → Save` and save the output asset bundle. + +### UABE Screenshots + +Select `File → Load Package File` and select your `.emip` file +![Load Package File](images/LoadPackageFile.png) +Check the box and add a base folder if needed, then press OK +![Select a base folder](images/LoadPackageStep2.png) +Save the new bundle +![Save the new bundle](images/SaveOutputBundle.png) + +# TMPAssetConverter + +This script converts assets outputted by the current version of TextMeshPro into ones that can be read by the version used by the games. Note that if TextMeshPro updates it may stop working. + +To use this, you will first need to generate a text atlas using TextMeshPro. Newer versions of Unity will output assets in a different format, so you will need an older one. `5.5.5` is known to work, while `2018.2` is known to not work. Due the python library having issues reading the assets outputted by TextMeshPro, you will need to then use UABE to extract the Atlas (Texture2D) and MonoBehaviour files from the asset, using the `Export Raw` button. If your version of Unity was too new, you'll find out here as UABE will fail to open the asset file. + +You'll also need the MonoBehaviour file that you're trying to replace extracted from the game's asset bundle. The MonoBehaviour files contain pointers to other files in the assets so you can't reuse a file extracted from one chapter. For example if you want to replace the `msgothic_0` font, use `Export Raw` on the `msgothic_0 SDF` file and save it as `oldMonoBehaviour.dat`: + +![mono](https://user-images.githubusercontent.com/539462/45201903-59aa8580-b277-11e8-969b-414e1376f570.png) + +Then, run `python TMPAssetConverter.py newAtlas.dat newMonoBehaviour.dat oldMonoBehaviour.dat outputFolder` and the converter will create two files in the output folder with the same name as the two input files. These can be substituted back into the asset bundle. The atlas file can be left out if you don't need to convert it (if you already have a converted copy) + +# Getting character lists for font files + +To make sure that the character sets you use have all the characters used by the games, use scripts in the [CharacterInfoExtraction](https://github.com/07th-mod/ui-editing-scripts/tree/master/scripts/CharacterInfoExtraction) subfolder. + +More detailed info on each of the scripts will be below, but assuming you compiled all the swift programs there and put them in the working directory, you can get a list of all the characters used in the Japanese script like this: `cat pathToHigurashiDataFolder/StreamingAssets/Update/*.txt | ./HigurashiTextExtractor - j | ./UniqueCharacters - > japaneseScriptCharsUsed.txt`. Note that you can get all the games at once by using multiple wildcards, for example if you have all the Mac versions in a folder you can do all of them at once with `folder/Higurashi*.app/Contents/Resources/Data/StreamingAssets/Update/*.txt`. Replace the `j` with `e` to get all the characters used in the English script. +That's all you really need for the Japanese script (I recommend taking the kanji from that file and pasting them over the `msgothic_0_charset_Japanese.txt` in the scripts repo to make your final charset) + +For English, you'll need to collect a few more files, since the font behind `msgothic_2` will be used to display the game's menus, including the Japanese ones. Collect all the text used in strings in asset files using `./AssetBundleStringExtractor pathToHigurashiDataFolder/*.assets | ./UniqueCharacters - > assetBundleCharsUsed.txt`. Then, clone [the DLL code repository](https://github.com/07th-mod/higurashi-assembly) and cat all the `Assets.Scripts.UI.Tips/TipsData.cs` files along with [the console arcs tips JSON](https://github.com/07th-mod/higurashi-console-arcs/blob/master/tips.json) together and run that through `./UniqueCharacters`. Finally, you can `cat englishScriptCharsUsed.txt assetBundleCharsUsed.txt tipsCharsUsed.txt | ./UniqueCharacters - > englishCharset.txt`. Like with the Japanese, copy the kanji out of that and paste it over the kanji in the scripts repo's `msgothic_2_charset_OtherLang.txt`. This is to preserve the extra characters which were added to make translation into other languages easier. + +# Adding Font Support for a New Language (Chapters 1-8 ONLY) + +The first thing you'll need is a [copy of Unity 5](https://unity3d.com/get-unity/download/archive) (I used 5.5.5 but I would expect newer versions of Unity 5 to work as well). Once you have that, create a new project, go to the Asset Store, and search for and download TextMeshPro. + +### Preparing the character list + +To make the font file, you will need a list of all the characters you want in it. Annoyingly, some of the Japanese parts of the games still rely on the English font, so you'll need to have those characters in addition to the ones you use for your language's character support. Download the current list from msgothic_2_charset_OtherLang.txt in [here](https://github.com/07th-mod/ui-editing-scripts/tree/master/scripts/CharacterInfoExtraction), which is the list of characters in the current English font. If your language's characters are already in that file, you shouldn't need to be doing font file editing at all. Otherwise, modify the text file to replace all the accented roman characters before the `←↑→↓` with all the characters you want in your font. Don't remove the characters after that point, because they're required to properly display some things in Japanese mode. Once you've done that, drag the new txt file into the Unity project you made. + +### Preparing the font file + +Since you'll need support for the Japanese characters in addition to your own language, you'll need to add them to your font file. If your font file already has Japanese support (which may be the case for some Chinese or Korean fonts), you can skip this step. Otherwise, download [FontForge](https://fontforge.github.io) and one of the TTF fonts [here](https://github.com/07th-mod/ui-editing-scripts/tree/master/assets/fonts). Open both the target font and the one you just downloaded using FontForge. Navigate to `Element → Font Info` with both fonts, go to the `General` tab, and adjust the Em Size of one of the fonts to match the other (if you don't do this, one of the fonts will be way too small or big). Then, select `Element → Merge Fonts...` in the window for the target font to copy all missing glyphs from the Japanese font over. +In addition, some fonts (like our current English font) use really thin lines for the ☆ character, which Higurashi uses a lot. Thin lines don't do very well with the font rendering system the game uses, which makes the character look really bad. Navigate to the entry for ☆, which you can do easily by selecting `View → Goto` and typing `uni2606`, and check if your font uses really thin lines. If it does, go to the Japanese font, go to the same spot, and copy and paste its ★ and ☆ over yours. +Finally, export the new font to a ttf with `File → Generate Fonts...` and drag the font file into Unity. + +### Generating the SDF font + +In your Unity project, choose `Window → TextMeshPro → Font Asset Creator`. Select your font as the font source, which should be there if you dragged it into your Unity project. For the font size, choose Custom Size. The current fonts use 26pt, but you can choose whatever you like. Bigger font sizes use more space but look nicer. Change Font Padding to 6 pt. If your characters almost fit in the atlas but not quite, you can change the packing method from Fast to Optimal to pack a few more characters in. + +For the Atlas Resolution, larger numbers take up more space but allow more or larger characters. The current Japanese font contains about 3000 26pt Japanese characters and fits in 2048x2048, taking up 4MB. For Character Set, choose Characters from file and choose the txt file you added earlier. Leave Font Style at Normal/2 and change Font Render Mode to Distance Field 32 (Note: This is the slowest and highest quality. If you're still trying to figure out what font/atlas sizes you need, I recommend first choosing Hinted Smooth, which will slightly underestimate the size of the characters but be super fast, then use Distance Field 16 which will get all the character sizes right while still being faster than Distance Field 32, before finally choosing Distance Field 32 for the one you save). + +Next, click Generate Font Atlas. This will take a while for large atlases. When it's done, it will say in the box if it was unable to fit any characters. If the resulting atlas is completely full, this is probably because those characters didn't fit, in which case you should adjust your atlas size or font size. Otherwise, this is because TextMeshPro failed to find those characters in your font. If it's just some newline characters or spaces this is fine, just look through them to make sure it didn't miss any characters that you think you need. + +Once you've saved the font, use the Unity inspector to change its Line Height to be equal to its Point Size. This is what MS Gothic uses, and a lot of the game's character spacing expects this to be the case. + +From here, follow the directions above for TMPAssetConverter to add your font to the game. + +# Adding Font Support for a New Language (Higurashi Rei onwards ONLY) + +For Rei, the method used for Chapters 1-8 doesn't work. For now, we have the following process. + +Please keep in mind the instructions are rough around the edges - please let us know if they don't make sense or need updating! + +Also, these instructions roughly [follow this guide in Korean](https://snowyegret.tistory.com/m/21) (thanks to 이칠공), which you [can translate](https://discord.com/channels/384426173821616128/750313515482480699/1013705389205897236). + +## Creating the font + +1. Install the version of Unity matching the game (roughly). For example, Rei is currently either verison `2019.4.36f1` or version `2019.4.40f1`, so we install version 2019.4.36 (generally the font generated on 2019.4.36 should work for all 2019.4.* versions) +2. Create a new project +3. Add a text object +4. Click Window->TextMeshPro->Font Asset Creator +5. Follow the existing instructions above to make a new font asset (starting from "Preparing the character list" up to and including "Generating the SDF font"), but **DO NOT run TMPAssetConverter** + * If you're not sure, also refer to the [translated Korean instructions](https://discord.com/channels/384426173821616128/750313515482480699/1013705389205897236) + +## Extracting fonts from the built game + +### Building the game and opening it in UABE + +1. Build/export the project to a known location +2. Install the latest version of UABE [from the UABE repository](https://github.com/SeriousCache/UABE) +3. Open the .sharedassets from the built game using UABE + * **Open it straight from the game directory! don't copy it somewhere else, as UABE will read some information from the game I think** +4. If a popup appears, select the closest unity version to the version you just exported the dummy game with + +### Getting the texture atlas .png + +1. Find the SDF atlas file, called `[FONT NAME] SDF Atlas` in the file browser, of type Texture2D (my example was "Binggrae SDF Atlas") +2. Click on that item to select it +3. Click "Plugins" on the right +4. Click "export to .png", press OK and save it somewhere. Rename it to something you can remember like `FONT_NAME_sdf_atlas.png` + +### Getting the monobehavior + +1. Repeat the above, but find the file called "MonoBehaviour [FONT NAME] SDF" +2. Click "Export Dump" +3. If a popup appears asking to 'extract extra information', **click Yes**. If another popup appears asking to select a file, **click cancel**. For some reason, even if you click cancel, you'll get the detailed dump. +4. Save the text file with a memorable name like `FONT_NAME_monobehavior_sdf.txt` + +## Merging Monobehaviors + +1. Repeat "Getting the monobehavior", but this time do it on the game to be modded. In Rei, you're looking for a file called "MonoBehaviour msgothic_2 SDF". +2. Open the unmodded game's `MonoBehaviour msgothic_2 SDF` in one text editor (It's suggested you use Visual Studio Code) +3. Open the exported game's `[FONT NAME] SDF Atlas` (`MonoBehaviour Binggrae_2 SDF` for example) in another text editor window/tab/panel + * In Visual Studio Code, dragging the second file to the right hand side of the window will open it side-by-side +4. Delete everything below and including the `0 FaceInfo m_FaceInfo` line +5. Copy everything below and including the dummy game's 0 FaceInfo m_FaceInfo line into the game's YAML file + * The above korean instructions state you may need to manually adjust it, but it seems to work even if you just copy everything in the YAML file after the 0 FaceInfo m_FaceInfo line + +Basically, you just open the two files side by side, then copy everything below `0 FaceInfo m_FaceInfo` into the other file at the corresponding location. + +## Extracting files for our scripts + +1. Open the game's sharedassets.assets file again in UABE +2. Find and select the SDF atlas called "msgothic_2 SDF Atlas" +3. click plugins +4. click "edit texture" +5. Select the .png file you extracted earlier. +6. Click "Export Raw", and save it as your final atlas .dat (?? hopefully you can do it without saving??) +7. Find and select the "MonoBehaviour msgothic_2 SDF" +8. Click "Import Dump" +9. Choose the merged YAML file you created earlier +10. Click "Export Raw", and save it as your final monobehavior .dat (?? hopefully you can do it without saving??) + +## Renaming/moving file so our script ui-editing-scripts can use it + +1. Put both the .dat files in the `assets\files-2019.4` folder (or in the future, one matching your unity version) +2. Rename the texture atlas as `msgothic_2 SDF Atlas_Texture2D.dat` to make the script replace by name and type(or use the "replace by Path ID" method?) +3. Rename the monobehavior as `948_MonoBehaviour msgothic_2 SDF.dat` to replace the asset with PathID #948 + +Once this part is done, the fonts will be automatically included in the output .assets diff --git a/docs/developer/voice/automated-voice-inserter.md b/docs/developer/voice/automated-voice-inserter.md new file mode 100644 index 0000000..83b380e --- /dev/null +++ b/docs/developer/voice/automated-voice-inserter.md @@ -0,0 +1,87 @@ +Written by [@enumag](https://github.com/enumag/). + +How it works +---- + +In the `dev` directory there are voice catalogue files from PS3 Sui release. The voice inserter first parses these files and creates a map which voice file matches what text. Then it parses the scripts of the MG Higurashi release and tries to match correct voice lines based on the japanese text. The voice line is inserted using the `PlaySE` command (intentionally NOT `PlayVoice` or `ModPlayVoiceLS`). If the script thinks a line should have a voice but can't find it an empty placeholder `PlaySE(4, "", 128, 64);` is inserted. + +Known issues: + +- Since PS3 text and MG text differs a manual review and fixing is necessary after running this script. +- PS2 specific voices are not automated since we don't have PS2 scripts. + +Notes: + +- The transition to `PlayVoice` and `ModPlayVoiceLS` commads is done later by other scripts (for dll update and lipsync). Don't worry about it for voice insertion. + + +Intallation +---- + +The voice inserter script is written in PHP and you can find it in this [repository](https://github.com/07th-mod/voice-inserter). + +To run it you'll need [PHP](http://php.net/) 7.1+ with mbstring and pdomysql modules, [composer](https://getcomposer.org/) to install some libraries and [MySQL](https://www.mysql.com/) database (MariaDB should work too). I also recommend to get a tool like [adminer](https://www.adminer.org/) to browse the database and run commands. When everything is installed check your installation using `php -v` and `composer --version`. + +Finally download or `git clone` the [voice inserter](https://github.com/07th-mod/voice-inserter) itself. + + +Preparation +---- + +Run `composer install` in the voice inserter root directory (where `composer.json` file is located). + +Create a MySQL database and run the `database.sql` file in it. I usually do it using adminer. + +Adjust the database connection parameters in `bootstrap.php` to match your database. + +Build the voice index using `php load.php`. This will take a few minutes to complete. + + +Usage +---- + +When the voice index is ready you can run the script using `php insert.php ` where `` is the path there you have the MG scripts for the new chapter. Running this script can take anywhere between 10 minutes and an hour depending on the length of the chapter and performance of your computer. + +Beware that this script will change those files so you need to create a backup first. In case the script fails, fix the problem and run it again on the clean files, not those that might have been partially changed by the first attempt. + +In some cases the voice inserter will say that it found some "long quotes". This indicates that there is most likely a missing ending quote somewhere in the text which confuses the voice inserter. If this happens, uncomment the `$voiceLine .= "\t// long quote\n";` line in `functions.php`, rerun the voice inserter, find "// long quote" in the updated script to see the location and fix the problem in the original scripts. Then run the voice inserter again on thew fixed files. + + +Development notes +---- + +This section is meant for anyone who wants to understand the code and change its behaviour. For normal usage you can skip this. + +The main logic is in the `functions.php` file. I'll explain here what each of those function does. + +### Functions for `load.php` + +#### `loadFile` + +Opens a voice catalogue file and calls `saveLine` for each line of the file. + +#### `saveLine` + +Splits the voice and japanese text and saves the parts to database. + +### Functions for `insert.php` + +#### `processFile` + +Opens a MG script file and passes every line to the `lineProcessor` coroutine. + +#### `lineProcessor` coroutine + +This is not a normal function but a coroutine implemented using PHP 5.5 [generator](http://php.net/manual/en/language.generators.php) (note the `yield` keyword used in the body). + +This couroutine looks for `OutputLine` commands, parses the japanese text from it and uses the `VoiceMatcher` class to find the correct voice. + +If a voice is found it inserts the `PlaySE` command in the output script. If a voice is not found but we're inside quites then it presumes there should be a voice and inserts an empty `PlaySE` command as placeholder. + +#### `VoiceMatcher` class + +This class simply recieves a japanese text line and tries to find a voice for it. + +For better results it remembers which voice catalogue file contained the last found line and prefers lines from that file on conflict. This helps to correctly match the voices for commonly used lines. + +It tries to find the voice using several different methods to avoid the most common reasons why a line doesn't have a full match. The most complicated one uses the [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) implemented as an SQL function (in `database.sql`). diff --git a/docs/developer/voice/voice-details.md b/docs/developer/voice/voice-details.md new file mode 100644 index 0000000..b1e4cd3 --- /dev/null +++ b/docs/developer/voice/voice-details.md @@ -0,0 +1,159 @@ +> Written by @ItaloKnox + +The first step for every patch is to make the voice patch. It all starts from there and branches out into graphics, ADV, and lipsync. + + +## Patching resources + +To make these patches, of course, we need some resources. In the list below you can find the links to every resource available in our project: + +* [Higurashi-Spectrum.zip](https://07th-mod.com/higurashi_resources/Higurashi-Spectrum.tar.bz2) - Spectrum files used to make lipsync more realistic. Check [Lipsync and Voice Analysis](https://github.com/07th-mod/higurashi-dev-guides/wiki/Lipsync-and-Voice-Analysis) for more information. +* [HigurashiPS3-Script.zip](https://07th-mod.com/archive/HigurashiPS3-Script.zip) - Full transcript of every voice in the game. Directly taken from the PS3 script, it's a requirement to use this file while doing manual work. See [Manual work details](https://github.com/07th-mod/resources/releases/download/Nipah/Manual-work-details). +* [HigurashiPS3-Voices01.zip](https://07th-mod.com/archive/HigurashiPS3-Voices01.zip) - Full set of voice files from the PS3 game. 1/4. +* [HigurashiPS3-Voices02.zip](https://07th-mod.com/archive/HigurashiPS3-Voices02.zip) - Full set of voice files from the PS3 game. 2/4. +* [HigurashiPS3-Voices03.zip](https://07th-mod.com/archive/HigurashiPS3-Voices03.zip) - Full set of voice files from the PS3 game. 3/4. +* [HigurashiPS3-Voices04.zip](https://07th-mod.com/archive/HigurashiPS3-Voices04.zip) - Full set of voice files from the PS3 game. 4/4. +* [HigurashiPS2-Voices01.zip](https://07th-mod.com/archive/HigurashiPS2-Voices01.zip) - Full set of voice files from the PS2 game. 1/2. +* [HigurashiPS2-Voices02.zip](https://07th-mod.com/archive/HigurashiPS2-Voices02.zip) - Full set of voice files from the PS2 game. 2/2. + + +## PlaySE + +### Assembly-CSharp.dll code + +```csharp + public void PlaySE(string filename, int channel, float volume, float pan) + { + AudioLayer item = this.channelDictionary[this.GetChannelByTypeChannel(AudioType.SE, channel)]; + if (item.IsPlaying()) + { + item.StopAudio(); + } + item.PlayAudio(filename, AudioType.SE, volume, false); + } +``` + +### Script usage + +In the example below, we have an excerpt from ``onik_001.txt``, in the voices-only format: + +``` +//「...その前に聞きたい@ あの漬物を漬けたのはレナか@ レナのお母さんか?@ + PlaySE( 4, "s19/01/hr_kei00130", 300, 64 ); + OutputLine(NULL, "「…その前に聞きたい。", + NULL, "\"...I'd like to ask something before that.", Line_WaitForInput); + PlaySE( 4, "s19/01/hr_kei00140", 300, 64 ); + OutputLine(NULL, " あの漬物を漬けたのはレナか?", + NULL, " Were you the one who pickled them, Rena?", Line_WaitForInput); + PlaySE( 4, "s19/01/hr_kei00150", 300, 64 ); + OutputLine(NULL, " レナのお母さんか?」", + NULL, " Or was it your mom?\"", Line_WaitForInput); + OutputLineAll(NULL, "\n", Line_ContinueAfterTyping); +``` + +#### Code breakdown + +* **PlaySE( 4, "s19/01/hr_kei00130", 300, 64 );** + * PlaySE(``channel``, "``filename``", ``volume``, ``pan``) + * ``PlaySE`` calls the "play sound effect" function. It's mainly used for sound effects but can also be used for voices. + * ``channel`` max value is **8**. It's a good habit to keep it **above __2__**. Voices that plays simultaneously with other voices need to be added one channel above. + * ``"filename"`` refers to the path where the voice is located. The extension (usually ``*.ogg``) is hidden by default. + * ``volume`` refers to the voice volume. Max value unknown, ``300`` should be loud. + * ``pan`` refers to audio panning. You can move the channel from left to right with this. It's never used, the default value is **64**. + + +## ModPlayVoiceLS + +### Assembly-CSharp.dll code + +```csharp + public void MODPlayVoiceLS(string filename, int channel, float volume, int character) + { + MODTextController.MODCurrentVoiceLayerDetect = channel; + AudioLayer audio = this.channelDictionary[this.GetChannelByTypeChannel(AudioType.Voice, channel)]; + if (this.currentAudio[AudioType.Voice].ContainsKey(channel)) + { + this.currentAudio[AudioType.Voice].Remove(channel); + } + this.currentAudio[AudioType.Voice].Add(channel, new AudioInfo(volume, filename)); + if (audio.IsPlaying()) + { + audio.StopAudio(); + } + audio.PlayAudio(filename, AudioType.Voice, volume, false); + if (MODSystem.instance.modSceneController.MODLipSyncBoolCheck(character)) + { + GameSystem.Instance.SceneController.MODLipSyncStart(character, channel, filename); + } + if (GameSystem.Instance.IsAuto) + { + audio.OnLoadCallback(delegate + { + GameSystem.Instance.AddWait(new Wait(audio.GetRemainingPlayTime(), WaitTypes.WaitForVoice, null)); + }); + } + } +``` + +### Script usage + +In newer patches, however, the code is fully processed to be ready for features such as the lipsync and ADV. Because of that (see [Assembly-CSharp.dll](https://github.com/07th-mod/higurashi-dev-guides/wiki/assembly-csharp.dll)), the syntax requires a new function called ``ModPlayVoiceLS``. It also comes with a new structure: + +``` +//「...その前に聞きたい@ あの漬物を漬けたのはレナか@ レナのお母さんか?@ + if (GetGlobalFlag(GADVMode)) { OutputLine("圭一", NULL, "Keiichi", NULL, Line_ContinueAfterTyping); } + ModPlayVoiceLS(3, 1, "s19/01/hr_kei00130", 256, TRUE); + OutputLine(NULL, "「…その前に聞きたい。", + NULL, "\"...I'd like to ask something before that.", Line_WaitForInput); + ModPlayVoiceLS(3, 1, "s19/01/hr_kei00140", 256, TRUE); + OutputLine(NULL, " あの漬物を漬けたのはレナか?", + NULL, " Were you the one who pickled them, Rena?", Line_WaitForInput); + ModPlayVoiceLS(3, 1, "s19/01/hr_kei00150", 256, TRUE); + OutputLine(NULL, " レナのお母さんか?」", + NULL, " Or was it your mom?\"", GetGlobalFlag(GLinemodeSp)); + if (GetGlobalFlag(GADVMode)) { ClearMessage(); } else { OutputLineAll(NULL, "\n", Line_ContinueAfterTyping); } +``` + +#### Code breakdown + +* **ModPlayVoiceLS(3, 1, "s19/01/hr_kei00130", 256, TRUE);** + * ModPlayVoiceLS(``channel``, ``character``, "``filename``", ``volume``, ``TRUE/FALSE``); + * ``ModPlayVoiceLS`` calls the new "Mod Play Voice LipSync" function. It's a newer version independent of ``PlaySE`` that was previously used to play voices. + * ``channel`` max value is **8**. It's a good habit to keep it **above 2**. Voices that plays simultaneously with other voices need to be added one channel above. + * ``character`` is the folder where the sprites for the character will be loaded. In the example, the voice is ``s19/01/hr_kei00130``, so ``01`` is also the character folder for sprites. + * ``"filename"`` refers to the path where the voice is located. The extension (usually ``*.ogg``) is hidden by default. + * ``volume`` refers to the voice volume. Max value unknown, ``256`` should be loud. + * ``TRUE/FALSE`` turns on or off the lipsync feature for that specific voice. It's recommended to leave this option always on ``TRUE``. + +It is good to remember that the code snippet above does not only include syntax for the voices and lipsync, it also includes syntax for the ADV Mode. Check out [ADV Mode](https://github.com/07th-mod/higurashi-dev-guides/wiki/ADV-Mode) for more information. + +## ModDrawCharacterWithFiltering + +### Assembly-CSharp.dll code + +```csharp + +``` + +### Script usage + +``` +ModDrawCharacterWithFiltering(3, 10, "sprite/normal/iri1_def2_", "1", "right", 1, 160, 0, FALSE, 0, 0, 0, 0, 0, 20, 300, TRUE ); +``` + +#### Code breakdown + +* **ModDrawCharacterWithFiltering(3, 10, "sprite/normal/iri1_def2_", "1", "right", 1, 160, 0, FALSE, 0, 0, 0, 0, 0, 20, 300, TRUE );** + * ModDrawCharacterWithFiltering(``sprite layer``, ``voice folder``, "``sprite image``", ``static sprite``, ``fade-in mask``, ``?``, ``horizontal``, ``?``, ``?``, ``?``, ``?``, ``?``, ``?``, ``?``, ``level``, ``fade-in``, ``parallel``); + * ``ModDrawCharacterWithFiltering`` calls a function for drawing a character with a fade-in. + * ``sprite layer`` Numerical. + * ``voice folder`` is the folder where the voices for the character will be loaded. + * ``sprite image`` refers to the path of the sprite image to use minus the last character(e.g. "sprite/normal/iri1def2") + * ``static sprite`` which sprite to use if lipsync is disabled. + * ``fade-in mask`` the mask that controls the fade-in animation. + * ``horizontal`` horizontal coordinates to draw the character at. + * ``level`` the level that characters are drawn at (if they overlap, the one with a lower level is drawn on top of the other). + * ``fade-in`` the speed in milliseconds the sprite fades in. + * ``parallel`` determines if the system should continue before the sprite finishes drawing (setting it to TRUE allows you to have multiple sprites fading in at once). + +This page continues on [Automated voice inserter](automated-voice-inserter.md). diff --git a/mkdocs.yml b/mkdocs.yml index 8e5fa26..5e1012b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,7 +37,29 @@ nav: - Support and Reporting Issues: Support.md - Contribution Guide: Beginners-guide.md - Credits: Credits.md - - Developer Wiki: https://07th-mod.com/dev-wiki/ + - Developer Information: + - Overview: + - Introduction: developer/overview/introduction.md + - Features: developer/overview/features.md + - Preparing a New Chapter: developer/overview/overview.md + - Patch Folder Structure: developer/overview/patch-folder-structure.md + - Higurashi Patch Compiler: developer/patch-compiler/how-to-use-higurashi-patch-compiler.md + - Game Scripting: + - Scripting Tutorial: developer/scripting/on-scripting.md + - Mod Syntax Reference: developer/scripting/syntax.md + - Other Languages, UI, Fonts: + - Other Languages: developer/sharedassets/other-languages.md + - Ui Editing Scripts: developer/sharedassets/ui-editing-scripts.md + - Voices: + - Voice Details: developer/voice/voice-details.md + - Automated Voice Inserter: developer/voice/automated-voice-inserter.md + - Misc: + - Git Line Endings: developer/misc/git-line-endings.md + - Legacy Pages (out of date): + - Old Batch File Installer: developer/legacy/code-overview.md + - Old Advanced Installer: developer/legacy/technical-details.md + - Adv Mode and Lipsync: developer/legacy/adv-mode-and-lipsync.md + theme: name: material features: